..adding new files..

git-svn-id: http://svn.irssi.org/repos/irssi/trunk@171 dbcabf3a-b0e7-0310-adc4-f8d773084564
This commit is contained in:
Timo Sirainen 2000-04-26 08:03:38 +00:00 committed by cras
commit c95034c6de
206 changed files with 31247 additions and 0 deletions

61
src/irc/core/Makefile.am Normal file
View file

@ -0,0 +1,61 @@
noinst_LTLIBRARIES = libirc_core.la
INCLUDES = \
$(GLIB_CFLAGS) \
-DSYSCONFDIR=\""$(sysconfdir)"\" \
-I$(top_srcdir)/src -I$(top_srcdir)/src/core
libirc_core_la_SOURCES = \
bans.c \
ctcp.c \
channels.c \
channels-query.c \
channels-setup.c \
channel-events.c \
ignore.c \
irc.c \
irc-core.c \
irc-commands.c \
irc-rawlog.c \
irc-server.c \
irc-special-vars.c \
ircnet-setup.c \
lag.c \
masks.c \
massjoin.c \
modes.c \
mode-lists.c \
netsplit.c \
nicklist.c \
query.c \
server-idle.c \
server-reconnect.c \
server-setup.c
noinst_HEADERS = \
bans.h \
ctcp.h \
channels.h \
channels-query.h \
channels-setup.h \
channel-events.h \
ignore.h \
irc.h \
irc-core.h \
irc-commands.h \
irc-rawlog.h \
irc-server.h \
irc-special-vars.h \
ircnet-setup.h \
lag.h \
masks.h \
massjoin.h \
modes.h \
mode-lists.h \
module.h \
netsplit.h \
nicklist.h \
query.h \
server-idle.h \
server-reconnect.h \
server-setup.h

218
src/irc/core/bans.c Normal file
View file

@ -0,0 +1,218 @@
/*
bans.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 "misc.h"
#include "signals.h"
#include "masks.h"
#include "modes.h"
#include "mode-lists.h"
#include "irc.h"
#include "nicklist.h"
static int bantype;
/* Get ban mask */
char *ban_get_mask(CHANNEL_REC *channel, const char *nick)
{
NICK_REC *rec;
char *str, *user, *host;
g_return_val_if_fail(channel != NULL, NULL);
g_return_val_if_fail(nick != NULL, NULL);
rec = nicklist_find(channel, nick);
if (rec == NULL || rec->host == NULL) return NULL;
str = irc_get_mask(nick, rec->host, bantype);
/* there's a limit of 10 characters in user mask. so, banning
someone with user mask of 10 characters gives us "*1234567890",
which is one too much.. so, replace the 10th character with '*' */
user = strchr(str, '!');
if (user == NULL) return str;
host = strchr(++user, '@');
if (host == NULL) return str;
if ((int) (host-user) < 10) {
/* too long user mask */
user[9] = '*';
g_memmove(user+10, host, strlen(host)+1);
}
return str;
}
void ban_set_type(const char *type)
{
char bantypestr[5], **list;
int n, max;
g_return_if_fail(type != NULL);
if (toupper(type[0]) == 'N') {
bantype = IRC_MASK_USER | IRC_MASK_DOMAIN;
strcpy(bantypestr, "UD");
}
else if (toupper(type[0]) == 'H') {
bantype = IRC_MASK_HOST | IRC_MASK_DOMAIN;
strcpy(bantypestr, "HD");
}
else if (toupper(type[0]) == 'D') {
bantype = IRC_MASK_DOMAIN;
strcpy(bantypestr, "D");
}
else if (toupper(type[0]) == 'C') {
list = g_strsplit(type, " ", -1);
max = strarray_length(list);
bantype = 0;
for (n = 1; n < max; n++) {
if (toupper(list[n][0]) == 'N')
bantype |= IRC_MASK_NICK;
else if (toupper(list[n][0]) == 'U')
bantype |= IRC_MASK_USER;
else if (toupper(list[n][0]) == 'H')
bantype |= IRC_MASK_HOST;
else if (toupper(list[n][0]) == 'D')
bantype |= IRC_MASK_DOMAIN;
}
g_strfreev(list);
bantypestr[0] = '\0';
if (bantype & IRC_MASK_NICK) strcat(bantypestr, "N");
if (bantype & IRC_MASK_USER) strcat(bantypestr, "U");
if (bantype & IRC_MASK_HOST) strcat(bantypestr, "H");
if (bantype & IRC_MASK_DOMAIN) strcat(bantypestr, "D");
}
signal_emit("ban type changed", 1, bantypestr);
}
void ban_set(CHANNEL_REC *channel, const char *bans)
{
GString *str;
char **ban, **banlist;
g_return_if_fail(bans != NULL);
str = g_string_new(NULL);
banlist = g_strsplit(bans, " ", -1);
for (ban = banlist; *ban != NULL; ban++) {
if (strchr(*ban, '!') != NULL) {
/* explicit ban */
g_string_sprintfa(str, " %s", *ban);
continue;
}
/* ban nick */
*ban = ban_get_mask(channel, *ban);
if (*ban != NULL) {
g_string_sprintfa(str, " %s", *ban);
g_free(*ban);
}
}
g_strfreev(banlist);
channel_set_singlemode(channel->server, channel->name, str->str, "+b");
g_string_free(str, TRUE);
}
void ban_remove(CHANNEL_REC *channel, const char *bans)
{
GString *str;
GSList *tmp;
char **ban, **banlist;
str = g_string_new(NULL);
banlist = g_strsplit(bans, " ", -1);
for (ban = banlist; *ban != NULL; ban++) {
for (tmp = channel->banlist; tmp != NULL; tmp = tmp->next) {
BAN_REC *rec = tmp->data;
if (match_wildcards(*ban, rec->ban))
g_string_sprintfa(str, "%s ", rec->ban);
}
}
g_strfreev(banlist);
channel_set_singlemode(channel->server, channel->name, str->str, "-b");
g_string_free(str, TRUE);
}
static void command_set_ban(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item, int set)
{
CHANNEL_REC *chanrec;
char *params, *channel, *nicks;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 2 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST,
item, &channel, &nicks);
if (!ischannel(*channel)) cmd_param_error(CMDERR_NOT_JOINED);
if (*nicks == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
chanrec = channel_find(server, channel);
if (chanrec == NULL) cmd_param_error(CMDERR_CHAN_NOT_FOUND);
if (!chanrec->wholist) cmd_param_error(CMDERR_CHAN_NOT_SYNCED);
if (set)
ban_set(chanrec, nicks);
else
ban_remove(chanrec, nicks);
g_free(params);
}
static void cmd_bantype(const char *data)
{
ban_set_type(data);
}
static void cmd_ban(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
command_set_ban(data, server, item, TRUE);
}
static void cmd_unban(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
command_set_ban(data, server, item, FALSE);
}
void bans_init(void)
{
/* default bantype */
bantype = IRC_MASK_USER | IRC_MASK_DOMAIN;
command_bind("bantype", NULL, (SIGNAL_FUNC) cmd_bantype);
command_bind("ban", NULL, (SIGNAL_FUNC) cmd_ban);
command_bind("unban", NULL, (SIGNAL_FUNC) cmd_unban);
}
void bans_deinit(void)
{
command_unbind("bantype", (SIGNAL_FUNC) cmd_bantype);
command_unbind("ban", (SIGNAL_FUNC) cmd_ban);
command_unbind("unban", (SIGNAL_FUNC) cmd_unban);
}

15
src/irc/core/bans.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef __BANS_H
#define __BANS_H
#include "channels.h"
void bans_init(void);
void bans_deinit(void);
char *ban_get_mask(CHANNEL_REC *channel, const char *nick);
void ban_set_type(const char *type);
void ban_set(CHANNEL_REC *channel, const char *bans);
void ban_remove(CHANNEL_REC *channel, const char *ban);
#endif

View file

@ -0,0 +1,241 @@
/*
channel-events.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"
#include "irc.h"
static void event_cannot_join(const char *data, IRC_SERVER_REC *server)
{
CHANNEL_REC *chanrec;
char *params, *channel;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
if (channel[0] == '!' && channel[1] == '!')
channel++; /* server didn't understand !channels */
chanrec = channel_find(server, channel);
if (chanrec != NULL && !chanrec->names_got) {
chanrec->left = TRUE;
channel_destroy(chanrec);
}
g_free(params);
}
static void event_target_unavailable(const char *data, IRC_SERVER_REC *server)
{
char *params, *channel;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
if (ischannel(*channel)) {
/* channel is unavailable. */
event_cannot_join(data, server);
}
g_free(params);
}
static void channel_change_topic(IRC_SERVER_REC *server, const char *channel, const char *topic)
{
CHANNEL_REC *chanrec;
chanrec = channel_find(server, channel);
if (chanrec != NULL) {
g_free_not_null(chanrec->topic);
chanrec->topic = *topic == '\0' ? NULL : g_strdup(topic);
signal_emit("channel topic changed", 1, chanrec);
}
}
static void event_topic_get(const char *data, IRC_SERVER_REC *server)
{
char *params, *channel, *topic;
g_return_if_fail(data != NULL);
params = event_get_params(data, 3, NULL, &channel, &topic);
channel_change_topic(server, channel, topic);
g_free(params);
}
static void event_topic(const char *data, IRC_SERVER_REC *server)
{
char *params, *channel, *topic;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, &channel, &topic);
channel_change_topic(server, channel, topic);
g_free(params);
}
static void event_join(const char *data, IRC_SERVER_REC *server, const char *nick, const char *address)
{
char *params, *channel, *tmp;
CHANNEL_REC *chanrec;
g_return_if_fail(data != NULL);
if (g_strcasecmp(nick, server->nick) != 0) {
/* someone else joined channel, no need to do anything */
return;
}
if (server->userhost == NULL)
server->userhost = g_strdup(address);
params = event_get_params(data, 1, &channel);
tmp = strchr(channel, 7); /* ^G does something weird.. */
if (tmp != NULL) *tmp = '\0';
if (*channel == '!') {
/* !channels have 5 chars long identification string before
it's name, it's not known when /join is called so rename
!channel here to !ABCDEchannel */
char *shortchan;
shortchan = g_strdup(channel);
sprintf(shortchan, "!%s", channel+6);
chanrec = channel_find(server, shortchan);
if (chanrec != NULL) {
g_free(chanrec->name);
chanrec->name = g_strdup(channel);
}
g_free(shortchan);
}
chanrec = channel_find(server, channel);
if (chanrec == NULL) {
/* didn't get here with /join command.. */
chanrec = channel_create(server, channel, TRUE);
}
g_free(params);
}
static void event_part(const char *data, IRC_SERVER_REC *server, const char *nick)
{
char *params, *channel, *reason;
CHANNEL_REC *chanrec;
g_return_if_fail(data != NULL);
if (g_strcasecmp(nick, server->nick) != 0) {
/* someone else part, no need to do anything here */
return;
}
params = event_get_params(data, 2, &channel, &reason);
chanrec = channel_find(server, channel);
if (chanrec != NULL) {
chanrec->left = TRUE;
channel_destroy(chanrec);
}
g_free(params);
}
static void event_kick(const char *data, IRC_SERVER_REC *server)
{
CHANNEL_REC *chanrec;
char *params, *channel, *nick, *reason;
g_return_if_fail(data != NULL);
params = event_get_params(data, 3, &channel, &nick, &reason);
if (g_strcasecmp(nick, server->nick) != 0) {
/* someone else was kicked, no need to do anything */
g_free(params);
return;
}
chanrec = channel_find(server, channel);
if (chanrec != NULL) {
chanrec->kicked = TRUE;
channel_destroy(chanrec);
}
g_free(params);
}
static void event_invite(const char *data, IRC_SERVER_REC *server)
{
char *params, *channel;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
g_free_not_null(server->last_invite);
server->last_invite = g_strdup(channel);
g_free(params);
}
void channel_events_init(void)
{
signal_add_first("event 403", (SIGNAL_FUNC) event_cannot_join); /* no such channel */
signal_add_first("event 405", (SIGNAL_FUNC) event_cannot_join); /* too many channels */
signal_add_first("event 407", (SIGNAL_FUNC) event_cannot_join); /* duplicate channel */
signal_add_first("event 471", (SIGNAL_FUNC) event_cannot_join); /* channel is full */
signal_add_first("event 473", (SIGNAL_FUNC) event_cannot_join); /* invite only */
signal_add_first("event 474", (SIGNAL_FUNC) event_cannot_join); /* banned */
signal_add_first("event 475", (SIGNAL_FUNC) event_cannot_join); /* bad channel key */
signal_add_first("event 476", (SIGNAL_FUNC) event_cannot_join); /* bad channel mask */
signal_add("event topic", (SIGNAL_FUNC) event_topic);
signal_add("event join", (SIGNAL_FUNC) event_join);
signal_add("event part", (SIGNAL_FUNC) event_part);
signal_add("event kick", (SIGNAL_FUNC) event_kick);
signal_add("event invite", (SIGNAL_FUNC) event_invite);
signal_add("event 332", (SIGNAL_FUNC) event_topic_get);
signal_add_first("event 437", (SIGNAL_FUNC) event_target_unavailable); /* channel/nick unavailable */
}
void channel_events_deinit(void)
{
signal_remove("event 403", (SIGNAL_FUNC) event_cannot_join); /* no such channel */
signal_remove("event 405", (SIGNAL_FUNC) event_cannot_join); /* too many channels */
signal_remove("event 407", (SIGNAL_FUNC) event_cannot_join); /* duplicate channel */
signal_remove("event 471", (SIGNAL_FUNC) event_cannot_join); /* channel is full */
signal_remove("event 473", (SIGNAL_FUNC) event_cannot_join); /* invite only */
signal_remove("event 474", (SIGNAL_FUNC) event_cannot_join); /* banned */
signal_remove("event 475", (SIGNAL_FUNC) event_cannot_join); /* bad channel key */
signal_remove("event 476", (SIGNAL_FUNC) event_cannot_join); /* bad channel mask */
signal_remove("event topic", (SIGNAL_FUNC) event_topic);
signal_remove("event join", (SIGNAL_FUNC) event_join);
signal_remove("event part", (SIGNAL_FUNC) event_part);
signal_remove("event kick", (SIGNAL_FUNC) event_kick);
signal_remove("event invite", (SIGNAL_FUNC) event_invite);
signal_remove("event 332", (SIGNAL_FUNC) event_topic_get);
signal_remove("event 437", (SIGNAL_FUNC) event_target_unavailable); /* channel/nick unavailable */
}

View file

@ -0,0 +1,7 @@
#ifndef __CHANNEL_EVENTS_H
#define __CHANNEL_EVENTS_H
void channel_events_init(void);
void channel_events_deinit(void);
#endif

View file

@ -0,0 +1,594 @@
/*
channels-query.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
*/
/*
How the thing works:
- After channel is joined and NAMES list is got, send "channel query" signal
- "channel query" : add channel to server->quries lists
loop:
- Wait for NAMES list from all channels before doing anything else..
- After got the last NAMES list, start sending the queries ..
- find the query to send, check where server->queries list isn't NULL
(mode, who, banlist, ban exceptions, invite list)
- if not found anything -> all channels are synced
- send "command #chan1,#chan2,#chan3,.." command to server
- wait for reply from server, then check if it was last query to be sent to
server. If it was, send "channel sync" signal
- check if the reply was for last channel in the command list. If so,
goto loop
*/
#include "module.h"
#include "modules.h"
#include "misc.h"
#include "signals.h"
#include "channels.h"
#include "irc.h"
#include "modes.h"
#include "mode-lists.h"
#include "nicklist.h"
#include "irc-server.h"
#include "server-redirect.h"
enum {
CHANNEL_QUERY_MODE,
CHANNEL_QUERY_WHO,
CHANNEL_QUERY_BMODE,
CHANNEL_QUERY_EMODE,
CHANNEL_QUERY_IMODE,
CHANNEL_QUERIES
};
#define CHANNEL_IS_MODE_QUERY(a) ((a) != CHANNEL_QUERY_WHO)
typedef struct {
char *last_query_chan;
GSList *queries[CHANNEL_QUERIES];
} SERVER_QUERY_REC;
static void sig_connected(IRC_SERVER_REC *server)
{
SERVER_QUERY_REC *rec;
g_return_if_fail(server != NULL);
if (!irc_server_check(server)) return;
rec = g_new0(SERVER_QUERY_REC, 1);
server->chanqueries = rec;
}
static void sig_disconnected(IRC_SERVER_REC *server)
{
SERVER_QUERY_REC *rec;
int n;
g_return_if_fail(server != NULL);
if (!irc_server_check(server)) return;
rec = server->chanqueries;
g_return_if_fail(rec != NULL);
for (n = 0; n < CHANNEL_QUERIES; n++)
g_slist_free(rec->queries[n]);
g_free_not_null(rec->last_query_chan);
g_free(rec);
}
/* Add channel to query list */
static void channel_query_add(CHANNEL_REC *channel, int query)
{
SERVER_QUERY_REC *rec;
g_return_if_fail(channel != NULL);
rec = channel->server->chanqueries;
g_return_if_fail(rec != NULL);
rec->queries[query] = g_slist_append(rec->queries[query], channel);
}
static void channel_query_remove_all(CHANNEL_REC *channel)
{
SERVER_QUERY_REC *rec;
int n;
rec = channel->server->chanqueries;
g_return_if_fail(rec != NULL);
/* remove channel from query lists */
for (n = 0; n < CHANNEL_QUERIES; n++)
rec->queries[n] = g_slist_remove(rec->queries[n], channel);
}
static void sig_channel_destroyed(CHANNEL_REC *channel)
{
g_return_if_fail(channel != NULL);
if (channel->server != NULL && !channel->synced)
channel_query_remove_all(channel);
}
static int channels_have_all_names(IRC_SERVER_REC *server)
{
GSList *tmp;
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *rec = tmp->data;
if (!rec->names_got)
return 0;
}
return 1;
}
static int find_next_query(SERVER_QUERY_REC *server)
{
int n;
for (n = 0; n < CHANNEL_QUERIES; n++) {
if (server->queries[n] != NULL)
return n;
}
return -1;
}
static void channel_send_query(IRC_SERVER_REC *server, int query)
{
SERVER_QUERY_REC *rec;
CHANNEL_REC *chanrec;
GSList *tmp, *chans;
char *cmd, *chanstr_commas, *chanstr;
int onlyone;
rec = server->chanqueries;
g_return_if_fail(rec != NULL);
onlyone = (server->no_multi_who && query == CHANNEL_QUERY_WHO) ||
(server->no_multi_mode && CHANNEL_IS_MODE_QUERY(query));
if (onlyone) {
chanrec = rec->queries[query]->data;
chans = g_slist_append(NULL, chanrec);
chanstr_commas = g_strdup(chanrec->name);
chanstr = g_strdup(chanrec->name);
} else {
char *chanstr_spaces;
chans = rec->queries[query];
chanstr_commas = gslist_to_string(rec->queries[query], G_STRUCT_OFFSET(CHANNEL_REC, name), ",");
chanstr_spaces = gslist_to_string(rec->queries[query], G_STRUCT_OFFSET(CHANNEL_REC, name), " ");
chanstr = g_strconcat(chanstr_commas, " ", chanstr_spaces, NULL);
g_free(chanstr_spaces);
}
switch (query) {
case CHANNEL_QUERY_MODE:
cmd = g_strdup_printf("MODE %s", chanstr_commas);
for (tmp = chans; tmp != NULL; tmp = tmp->next) {
chanrec = tmp->data;
server_redirect_event((SERVER_REC *) server, chanstr, 3,
"event 403", "chanquery mode abort", 1,
"event 442", "chanquery mode abort", 1, /* "you're not on that channel" */
"event 324", "chanquery mode", 1, NULL);
}
break;
case CHANNEL_QUERY_WHO:
cmd = g_strdup_printf("WHO %s", chanstr_commas);
for (tmp = chans; tmp != NULL; tmp = tmp->next) {
chanrec = tmp->data;
server_redirect_event((SERVER_REC *) server, chanstr, 2,
"event 401", "chanquery who abort", 1,
"event 315", "chanquery who end", 1,
"event 352", "silent event who", 1, NULL);
}
break;
case CHANNEL_QUERY_BMODE:
cmd = g_strdup_printf("MODE %s b", chanstr_commas);
for (tmp = chans; tmp != NULL; tmp = tmp->next) {
chanrec = tmp->data;
server_redirect_event((SERVER_REC *) server, chanrec->name, 2,
"event 403", "chanquery mode abort", 1,
"event 368", "chanquery ban end", 1,
"event 367", "chanquery ban", 1, NULL);
}
break;
case CHANNEL_QUERY_EMODE:
cmd = g_strdup_printf("MODE %s e", chanstr_commas);
for (tmp = chans; tmp != NULL; tmp = tmp->next) {
chanrec = tmp->data;
server_redirect_event((SERVER_REC *) server, chanrec->name, 4,
"event 403", "chanquery mode abort", 1,
"event 349", "chanquery eban end", 1,
"event 348", "chanquery eban", 1, NULL);
}
break;
case CHANNEL_QUERY_IMODE:
cmd = g_strdup_printf("MODE %s I", chanstr_commas);
for (tmp = chans; tmp != NULL; tmp = tmp->next) {
chanrec = tmp->data;
server_redirect_event((SERVER_REC *) server, chanrec->name, 4,
"event 403", "chanquery mode abort", 1,
"event 347", "chanquery ilist end", 1,
"event 346", "chanquery ilist", 1, NULL);
}
break;
default:
cmd = NULL;
}
g_free(chanstr);
g_free(chanstr_commas);
/* Get the channel of last query */
chanrec = g_slist_last(chans)->data;
rec->last_query_chan = g_strdup(chanrec->name);
if (!onlyone) {
/* all channels queried, set to NULL */
g_slist_free(rec->queries[query]);
rec->queries[query] = NULL;
} else {
/* remove the first channel from list */
rec->queries[query] = g_slist_remove(rec->queries[query], chans->data);
}
/* send the command */
irc_send_cmd(server, cmd);
g_free(cmd);
}
static void channels_query_check(IRC_SERVER_REC *server)
{
SERVER_QUERY_REC *rec;
int query;
g_return_if_fail(server != NULL);
rec = server->chanqueries;
g_return_if_fail(rec != NULL);
g_free_and_null(rec->last_query_chan);
if (!channels_have_all_names(server)) {
/* all channels haven't sent /NAMES list yet */
return;
}
query = find_next_query(rec);
if (query == -1) {
/* no queries left */
return;
}
channel_send_query(server, query);
}
static void sig_channel_query(CHANNEL_REC *channel)
{
SERVER_QUERY_REC *rec;
g_return_if_fail(channel != NULL);
/* Add channel to query lists */
if (!channel->no_modes)
channel_query_add(channel, CHANNEL_QUERY_MODE);
channel_query_add(channel, CHANNEL_QUERY_WHO);
if (!channel->no_modes) {
channel_query_add(channel, CHANNEL_QUERY_BMODE);
if (channel->server->emode_known) {
channel_query_add(channel, CHANNEL_QUERY_EMODE);
channel_query_add(channel, CHANNEL_QUERY_IMODE);
}
}
rec = channel->server->chanqueries;
if (rec->last_query_chan == NULL)
channels_query_check(channel->server);
}
/* if there's no more queries in queries in buffer, send the sync signal */
static void channel_checksync(CHANNEL_REC *channel)
{
SERVER_QUERY_REC *rec;
int n;
g_return_if_fail(channel != NULL);
if (channel->synced)
return; /* already synced */
rec = channel->server->chanqueries;
g_return_if_fail(rec != NULL);
for (n = 0; n < CHANNEL_QUERIES; n++) {
if (g_slist_find(rec->queries[n], channel))
return;
}
channel->synced = TRUE;
signal_emit("channel sync", 1, channel);
}
static void channel_got_query(IRC_SERVER_REC *server, CHANNEL_REC *chanrec, const char *channel)
{
SERVER_QUERY_REC *rec;
g_return_if_fail(server != NULL);
g_return_if_fail(channel != NULL);
rec = server->chanqueries;
g_return_if_fail(rec != NULL);
g_return_if_fail(rec->last_query_chan != NULL);
/* check if we need to get another query.. */
if (g_strcasecmp(rec->last_query_chan, channel) == 0)
channels_query_check(server);
/* check if channel is synced */
if (chanrec != NULL) channel_checksync(chanrec);
}
static void event_channel_mode(char *data, IRC_SERVER_REC *server, const char *nick)
{
CHANNEL_REC *chanrec;
char *params, *channel, *mode;
g_return_if_fail(data != NULL);
params = event_get_params(data, 3 | PARAM_FLAG_GETREST, NULL, &channel, &mode);
chanrec = channel_find(server, channel);
if (chanrec != NULL)
parse_channel_modes(chanrec, nick, mode);
channel_got_query(server, chanrec, channel);
g_free(params);
}
static void multi_query_remove(IRC_SERVER_REC *server, const char *event, const char *data)
{
GSList *queue;
while ((queue = server_redirect_getqueue((SERVER_REC *) server, event, data)) != NULL)
server_redirect_remove_next((SERVER_REC *) server, event, queue);
}
static void event_end_of_who(const char *data, IRC_SERVER_REC *server)
{
CHANNEL_REC *chanrec;
NICK_REC *nick;
char *params, *channel, **chans;
int n, onewho;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
onewho = strchr(channel, ',') != NULL;
if (onewho) {
/* instead of multiple End of WHO replies we get
only this one... */
server->one_endofwho = TRUE;
multi_query_remove(server, "event 315", data);
/* check that the WHO actually did return something
(that it understood #chan1,#chan2,..) */
chanrec = channel_find(server, channel);
nick = nicklist_find(chanrec, server->nick);
if (nick->host == NULL)
server->no_multi_who = TRUE;
}
chans = g_strsplit(channel, ",", -1);
for (n = 0; chans[n] != NULL; n++) {
chanrec = channel_find(server, chans[n]);
if (chanrec == NULL)
continue;
if (onewho && server->no_multi_who) {
channel_query_add(chanrec, CHANNEL_QUERY_WHO);
continue;
}
chanrec->wholist = TRUE;
signal_emit("channel wholist", 1, chanrec);
/* check if we need can send another query */
channel_got_query(server, chanrec, chans[n]);
}
g_strfreev(chans);
g_free(params);
if (onewho && server->no_multi_who) {
/* server didn't understand multiple WHO replies,
send them again separately */
channels_query_check(server);
}
}
static void event_end_of_banlist(const char *data, IRC_SERVER_REC *server)
{
CHANNEL_REC *chanrec;
char *params, *channel;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
chanrec = channel_find(server, channel);
channel_got_query(server, chanrec, channel);
g_free(params);
}
static void event_end_of_ebanlist(const char *data, IRC_SERVER_REC *server)
{
CHANNEL_REC *chanrec;
char *params, *channel;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
chanrec = channel_find(server, channel);
channel_got_query(server, chanrec, channel);
g_free(params);
}
static void event_end_of_invitelist(const char *data, IRC_SERVER_REC *server)
{
CHANNEL_REC *chanrec;
char *params, *channel;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
chanrec = channel_find(server, channel);
channel_got_query(server, chanrec, channel);
g_free(params);
}
static void channel_lost(IRC_SERVER_REC *server, const char *channel)
{
CHANNEL_REC *chanrec;
chanrec = channel_find(server, channel);
if (chanrec != NULL) {
/* channel not found - probably created a new channel
and left it immediately. */
channel_query_remove_all(chanrec);
}
channel_got_query(server, chanrec, channel);
}
static void multi_command_error(IRC_SERVER_REC *server, const char *data, int query, const char *event)
{
CHANNEL_REC *chanrec;
char *params, *channel, **chans;
int n;
multi_query_remove(server, event, data);
params = event_get_params(data, 2, NULL, &channel);
chans = g_strsplit(channel, ",", -1);
for (n = 0; chans[n] != NULL; n++)
{
chanrec = channel_find(server, chans[n]);
if (chanrec != NULL)
channel_query_add(chanrec, query);
}
g_strfreev(chans);
g_free(params);
channels_query_check(server);
}
static void event_mode_abort(const char *data, IRC_SERVER_REC *server)
{
char *params, *channel;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
if (strchr(channel, ',') == NULL) {
channel_lost(server, channel);
} else {
server->no_multi_mode = TRUE;
multi_command_error(server, data, CHANNEL_QUERY_MODE, "event 324");
}
g_free(params);
}
static void event_who_abort(const char *data, IRC_SERVER_REC *server)
{
char *params, *channel;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
if (strchr(channel, ',') == NULL) {
channel_lost(server, channel);
} else {
server->no_multi_who = TRUE;
multi_command_error(server, data, CHANNEL_QUERY_WHO, "event 315");
}
g_free(params);
}
void channels_query_init(void)
{
signal_add("server connected", (SIGNAL_FUNC) sig_connected);
signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected);
signal_add("channel query", (SIGNAL_FUNC) sig_channel_query);
signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
signal_add("chanquery mode", (SIGNAL_FUNC) event_channel_mode);
signal_add("chanquery who end", (SIGNAL_FUNC) event_end_of_who);
signal_add("chanquery eban end", (SIGNAL_FUNC) event_end_of_ebanlist);
signal_add("chanquery ban end", (SIGNAL_FUNC) event_end_of_banlist);
signal_add("chanquery ilist end", (SIGNAL_FUNC) event_end_of_invitelist);
signal_add("chanquery mode abort", (SIGNAL_FUNC) event_mode_abort);
signal_add("chanquery who abort", (SIGNAL_FUNC) event_who_abort);
}
void channels_query_deinit(void)
{
signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
signal_remove("channel query", (SIGNAL_FUNC) sig_channel_query);
signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
signal_remove("chanquery mode", (SIGNAL_FUNC) event_channel_mode);
signal_remove("chanquery who end", (SIGNAL_FUNC) event_end_of_who);
signal_remove("chanquery eban end", (SIGNAL_FUNC) event_end_of_ebanlist);
signal_remove("chanquery ban end", (SIGNAL_FUNC) event_end_of_banlist);
signal_remove("chanquery ilist end", (SIGNAL_FUNC) event_end_of_invitelist);
signal_remove("chanquery mode abort", (SIGNAL_FUNC) event_mode_abort);
signal_remove("chanquery who abort", (SIGNAL_FUNC) event_who_abort);
}

View file

@ -0,0 +1,7 @@
#ifndef __CHANNELS_QUERY
#define __CHANNELS_QUERY
void channels_query_init(void);
void channels_query_deinit(void);
#endif

View file

@ -0,0 +1,215 @@
/*
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 "channels.h"
#include "channels-setup.h"
#include "nicklist.h"
#include "irc-server.h"
#include "server-setup.h"
#include "lib-config/iconfig.h"
#include "settings.h"
GSList *setupchannels;
#define ircnet_match(a, b) \
((a[0]) == '\0' || (b != NULL && g_strcasecmp(a, b) == 0))
SETUP_CHANNEL_REC *channels_setup_find(const char *channel, IRC_SERVER_REC *server)
{
GSList *tmp;
g_return_val_if_fail(channel != NULL, NULL);
g_return_val_if_fail(server != NULL, NULL);
for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) {
SETUP_CHANNEL_REC *rec = tmp->data;
if (g_strcasecmp(rec->name, channel) == 0 &&
ircnet_match(rec->ircnet, server->connrec->ircnet))
return rec;
}
return NULL;
}
void channels_setup_destroy(SETUP_CHANNEL_REC *channel)
{
g_return_if_fail(channel != NULL);
g_free(channel->name);
g_free(channel->ircnet);
g_free_not_null(channel->password);
g_free_not_null(channel->botmasks);
g_free_not_null(channel->autosendcmd);
g_free_not_null(channel->background);
g_free_not_null(channel->font);
g_free(channel);
setupchannels = g_slist_remove(setupchannels, channel);
}
/* connected to server, autojoin to channels. */
static void event_connected(IRC_SERVER_REC *server)
{
GString *chans, *keys;
GSList *tmp;
int use_keys;
g_return_if_fail(server != NULL);
if (server->connrec->reconnection)
return;
/* join to the channels marked with autojoin in setup */
chans = g_string_new(NULL);
keys = g_string_new(NULL);
use_keys = FALSE;
for (tmp = setupchannels; tmp != NULL; tmp = tmp->next) {
SETUP_CHANNEL_REC *rec = tmp->data;
if (!rec->autojoin || !ircnet_match(rec->ircnet, server->connrec->ircnet))
continue;
g_string_sprintfa(chans, "%s,", rec->name);
g_string_sprintfa(keys, "%s,", rec->password == NULL ? "x" : rec->password);
if (rec->password != NULL)
use_keys = TRUE;
}
if (chans->len > 0) {
g_string_truncate(chans, chans->len-1);
g_string_truncate(keys, keys->len-1);
if (use_keys) g_string_sprintfa(chans, " %s", keys->str);
channels_join(server, chans->str, TRUE);
}
g_string_free(chans, TRUE);
g_string_free(keys, TRUE);
}
/* channel wholist received: send the auto send command */
static void channel_wholist(CHANNEL_REC *channel)
{
SETUP_CHANNEL_REC *rec;
NICK_REC *nick;
char **bots, **bot, *str;
g_return_if_fail(channel != NULL);
rec = channels_setup_find(channel->name, channel->server);
if (rec == NULL || rec->autosendcmd == NULL || !*rec->autosendcmd)
return;
if (rec->botmasks == NULL || !*rec->botmasks) {
/* just send the command. */
signal_emit("send command", 3, rec->autosendcmd, channel->server, channel);
}
/* find first available bot.. */
bots = g_strsplit(rec->botmasks, " ", -1);
for (bot = bots; *bot != NULL; bot++) {
nick = nicklist_find(channel, *bot);
if (nick == NULL)
continue;
/* got one! */
str = g_strdup_printf(rec->autosendcmd, nick->nick);
signal_emit("send command", 3, str, channel->server, channel);
g_free(str);
break;
}
g_strfreev(bots);
}
static SETUP_CHANNEL_REC *setupchannel_add(CONFIG_NODE *node)
{
SETUP_CHANNEL_REC *rec;
char *channel, *ircnet, *password, *botmasks, *autosendcmd, *background, *font;
g_return_val_if_fail(node != NULL, NULL);
channel = config_node_get_str(node, "name", NULL);
ircnet = config_node_get_str(node, "ircnet", NULL);
if (channel == NULL || ircnet == 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);
background = config_node_get_str(node, "background", NULL);
font = config_node_get_str(node, "font", NULL);
rec = g_new(SETUP_CHANNEL_REC, 1);
rec->autojoin = config_node_get_bool(node, "autojoin", FALSE);
rec->name = g_strdup(channel);
rec->ircnet = g_strdup(ircnet);
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);
rec->background = (background == NULL || *background == '\0') ? NULL : g_strdup(background);
rec->font = (font == NULL || *font == '\0') ? NULL : g_strdup(font);
setupchannels = g_slist_append(setupchannels, rec);
return rec;
}
static void channels_read_config(void)
{
CONFIG_NODE *node;
GSList *tmp;
while (setupchannels != NULL)
channels_setup_destroy(setupchannels->data);
/* Read channels */
node = iconfig_node_traverse("channels", FALSE);
if (node != NULL) {
for (tmp = node->value; tmp != NULL; tmp = tmp->next)
setupchannel_add(tmp->data);
}
}
void channels_setup_init(void)
{
source_host_ok = FALSE;
channels_read_config();
signal_add("event connected", (SIGNAL_FUNC) event_connected);
signal_add("channel wholist", (SIGNAL_FUNC) channel_wholist);
signal_add("setup reread", (SIGNAL_FUNC) channels_read_config);
}
void channels_setup_deinit(void)
{
while (setupchannels != NULL)
channels_setup_destroy(setupchannels->data);
signal_remove("event connected", (SIGNAL_FUNC) event_connected);
signal_remove("channel wholist", (SIGNAL_FUNC) channel_wholist);
signal_remove("setup reread", (SIGNAL_FUNC) channels_read_config);
}

View file

@ -0,0 +1,27 @@
#ifndef __CHANNELS_SETUP_H
#define __CHANNELS_SETUP_H
typedef struct {
int autojoin;
char *name;
char *ircnet;
char *password;
char *botmasks;
char *autosendcmd;
char *background;
char *font;
} SETUP_CHANNEL_REC;
extern GSList *setupchannels;
void channels_setup_init(void);
void channels_setup_deinit(void);
void channels_setup_destroy(SETUP_CHANNEL_REC *channel);
SETUP_CHANNEL_REC *channels_setup_find(const char *channel, IRC_SERVER_REC *server);
#endif

231
src/irc/core/channels.c Normal file
View file

@ -0,0 +1,231 @@
/*
channels.c : irssi
Copyright (C) 1999 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 "modules.h"
#include "misc.h"
#include "bans.h"
#include "channels.h"
#include "channel-events.h"
#include "channels-query.h"
#include "channels-setup.h"
#include "irc.h"
#include "modes.h"
#include "levels.h"
#include "mode-lists.h"
#include "massjoin.h"
#include "nicklist.h"
GSList *channels; /* List of all channels */
CHANNEL_REC *channel_create(IRC_SERVER_REC *server, const char *channel, int automatic)
{
CHANNEL_REC *rec;
g_return_val_if_fail(channel != NULL, NULL);
rec = g_new0(CHANNEL_REC, 1);
channels = g_slist_append(channels, rec);
if (server != NULL)
server->channels = g_slist_append(server->channels, rec);
MODULE_DATA_INIT(rec);
rec->type = module_get_uniq_id("IRC", WI_IRC_CHANNEL);
rec->name = g_strdup(channel);
rec->server = server;
rec->createtime = time(NULL);
if (*channel == '+')
rec->no_modes = TRUE;
signal_emit("channel created", 2, rec, GINT_TO_POINTER(automatic));
return rec;
}
void channel_destroy(CHANNEL_REC *channel)
{
g_return_if_fail(channel != NULL);
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);
if (channel->server != NULL && !channel->left && !channel->kicked) {
/* destroying channel record without actually left the channel yet */
irc_send_cmdv(channel->server, "PART %s", channel->name);
}
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(IRC_SERVER_REC *server, const char *channel)
{
GSList *tmp;
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *rec = tmp->data;
if (g_strcasecmp(channel, rec->name) == 0)
return rec;
/* check after removing ABCDE from !ABCDEchannel */
if (*channel == '!' && *rec->name == '!' &&
g_strcasecmp(channel+1, rec->name+6) == 0)
return rec;
}
return NULL;
}
CHANNEL_REC *channel_find(IRC_SERVER_REC *server, const char *channel)
{
g_return_val_if_fail(channel != NULL, NULL);
if (server != NULL)
return channel_find_server(server, channel);
/* find from any server */
return gslist_foreach_find(servers, (FOREACH_FIND_FUNC) channel_find_server, (void *) channel);
}
char *channel_get_mode(CHANNEL_REC *channel)
{
GString *mode;
char *ret;
g_return_val_if_fail(channel != NULL, NULL);
mode = g_string_new(NULL);
if (channel->mode_secret) g_string_append_c(mode, 's');
if (channel->mode_private) g_string_append_c(mode, 'p');
if (channel->mode_moderate) g_string_append_c(mode, 'm');
if (channel->mode_invite) g_string_append_c(mode, 'i');
if (channel->mode_nomsgs) g_string_append_c(mode, 'n');
if (channel->mode_optopic) g_string_append_c(mode, 't');
if (channel->mode_anonymous) g_string_append_c(mode, 'a');
if (channel->mode_reop) g_string_append_c(mode, 'r');
if (channel->mode_key) g_string_append_c(mode, 'k');
if (channel->limit > 0) g_string_append_c(mode, 'l');
if (channel->mode_key) g_string_sprintfa(mode, " %s", channel->key);
if (channel->limit > 0) g_string_sprintfa(mode, " %d", channel->limit);
ret = mode->str;
g_string_free(mode, FALSE);
return ret;
}
#define get_join_key(key) \
(((key) == NULL || *(key) == '\0') ? "x" : (key))
void channels_join(IRC_SERVER_REC *server, const char *data, int automatic)
{
CHANNEL_REC *chanrec;
GString *outchans, *outkeys;
char *params, *channels, *keys;
char **chanlist, **keylist, **tmp, **tmpkey, *channel;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &channels, &keys);
if (*channels == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
chanlist = g_strsplit(channels, ",", -1);
keylist = g_strsplit(keys, ",", -1);
outchans = g_string_new(NULL);
outkeys = g_string_new(NULL);
tmpkey = keylist;
for (tmp = chanlist; *tmp != NULL; tmp++) {
channel = ischannel(**tmp) ? g_strdup(*tmp) :
g_strdup_printf("#%s", *tmp);
chanrec = channel_find(server, channel);
if (chanrec != NULL) {
/* already joined this channel */
signal_emit("gui channel open", 1, chanrec);
} else {
g_string_sprintfa(outchans, "%s,", channel);
if (*keys != '\0')
g_string_sprintfa(outkeys, "%s,", get_join_key(*tmpkey));
channel_create(server, channel + (channel[0] == '!' && channel[1] == '!'), automatic);
}
g_free(channel);
if (*tmpkey != NULL)
tmpkey++;
}
if (outchans->len > 0) {
irc_send_cmdv(server, *keys == '\0' ? "JOIN %s" : "JOIN %s %s",
outchans->str, outkeys->str);
}
g_string_free(outchans, TRUE);
g_string_free(outkeys, TRUE);
g_strfreev(chanlist);
g_strfreev(keylist);
g_free(params);
}
void channels_init(void)
{
channel_events_init();
channels_query_init();
channels_setup_init();
bans_init();
modes_init();
mode_lists_init();
massjoin_init();
nicklist_init();
}
void channels_deinit(void)
{
channel_events_deinit();
channels_query_deinit();
channels_setup_deinit();
bans_deinit();
modes_deinit();
mode_lists_deinit();
massjoin_deinit();
nicklist_deinit();
}

73
src/irc/core/channels.h Normal file
View file

@ -0,0 +1,73 @@
#ifndef __CHANNELS_H
#define __CHANNELS_H
#include "irc-server.h"
typedef struct {
int type;
GHashTable *module_data;
IRC_SERVER_REC *server;
char *name;
int new_data;
time_t createtime;
GHashTable *nicks; /* list of nicks */
GSList *banlist; /* list of bans */
GSList *ebanlist; /* list of ban exceptions */
GSList *invitelist; /* invite list */
char *topic;
int limit; /* user limit */
char *key; /* password key */
/* channel mode */
int no_modes:1; /* channel doesn't support modes */
int mode_invite:1;
int mode_secret:1;
int mode_private:1;
int mode_moderate:1;
int mode_nomsgs:1;
int mode_optopic:1;
int mode_key:1;
int mode_anonymous:1;
int mode_reop:1;
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 left:1; /* You just left the channel */
int kicked:1; /* You just got kicked */
int destroying:1;
time_t massjoin_start; /* Massjoin start time */
int massjoins; /* Number of nicks waiting for massjoin signal.. */
int last_massjoins; /* Massjoins when last checked in timeout function */
GSList *lastmsgs; /* List of nicks who last send message */
GSList *lastownmsgs; /* List of nicks who last send message to you */
} CHANNEL_REC;
extern GSList *channels;
void channels_init(void);
void channels_deinit(void);
/* Create new channel record */
CHANNEL_REC *channel_create(IRC_SERVER_REC *server, const char *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(IRC_SERVER_REC *server, const char *channel);
char *channel_get_mode(CHANNEL_REC *channel);
/* Join to channels. `data' contains channels and channel keys */
void channels_join(IRC_SERVER_REC *server, const char *data, int automatic);
#endif

193
src/irc/core/ctcp.c Normal file
View file

@ -0,0 +1,193 @@
/*
ctcp.c : irssi
Copyright (C) 1999 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 "levels.h"
#include "special-vars.h"
#include "settings.h"
#include "irssi-version.h"
#include "irc.h"
#include "irc-server.h"
#include "server-idle.h"
#include "ignore.h"
static void ctcp_queue_clean(IRC_SERVER_REC *server)
{
GSList *tmp, *next;
for (tmp = server->ctcpqueue; tmp != NULL; tmp = tmp->next) {
next = tmp->next;
if (!server_idle_find(server, GPOINTER_TO_INT(tmp->data)))
server->ctcpqueue = g_slist_remove(server->ctcpqueue, tmp->data);
}
}
/* Send CTCP reply with flood protection */
void ctcp_send_reply(IRC_SERVER_REC *server, const char *data)
{
int tag;
g_return_if_fail(server != NULL);
g_return_if_fail(data != NULL);
ctcp_queue_clean(server);
if (g_slist_length(server->ctcpqueue) < settings_get_int("max_ctcp_queue")) {
/* Add to first in idle queue */
tag = server_idle_add_first(server, data, NULL, 0, NULL);
server->ctcpqueue = g_slist_append(server->ctcpqueue, GINT_TO_POINTER(tag));
}
}
/* CTCP ping */
static void ctcp_ping(const char *data, IRC_SERVER_REC *server, const char *nick)
{
char *str;
g_return_if_fail(data != NULL);
g_return_if_fail(server != NULL);
g_return_if_fail(nick != NULL);
str = g_strdup_printf("NOTICE %s :\001PING %s\001", nick, data);
ctcp_send_reply(server, str);
g_free(str);
}
/* CTCP version */
static void ctcp_version(const char *data, IRC_SERVER_REC *server, const char *nick)
{
char *str, *reply;
g_return_if_fail(server != NULL);
g_return_if_fail(nick != NULL);
reply = parse_special_string(settings_get_str("ctcp_version_reply"), server, NULL, "", NULL);
str = g_strdup_printf("NOTICE %s :\001VERSION %s\001", nick, reply);
ctcp_send_reply(server, str);
g_free(str);
g_free(reply);
}
static void ctcp_msg(const char *data, IRC_SERVER_REC *server, const char *nick, const char *addr, const char *target)
{
char *args, *str;
if (ignore_check(server, nick, addr, target, data, MSGLEVEL_CTCPS))
return;
str = g_strconcat("ctcp msg ", data, NULL);
args = strchr(str+9, ' ');
if (args != NULL) *args++ = '\0'; else args = "";
g_strdown(str+9);
if (!signal_emit(str, 5, args, server, nick, addr, target))
signal_emit("default ctcp msg", 5, data, server, nick, addr, target);
g_free(str);
}
static void ctcp_reply(const char *data, IRC_SERVER_REC *server, const char *nick, const char *addr, const char *target)
{
char *args, *str;
if (ignore_check(server, nick, addr, target, data, MSGLEVEL_CTCPS))
return;
str = g_strconcat("ctcp reply ", data, NULL);
args = strchr(str+11, ' ');
if (args != NULL) *args++ = '\0'; else args = "";
g_strdown(str+11);
if (!signal_emit(str, 5, args, server, nick, addr, target))
signal_emit("default ctcp reply", 5, data, server, nick, addr, target);
g_free(str);
}
static void event_privmsg(const char *data, IRC_SERVER_REC *server, const char *nick, const char *addr)
{
char *params, *target, *msg, *ptr;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, &target, &msg);
/* handle only ctcp messages.. */
if (*msg == 1) {
/* remove the later \001 */
ptr = strrchr(++msg, 1);
if (ptr != NULL) *ptr = '\0';
signal_emit("ctcp msg", 5, msg, server, nick, addr, target);
}
g_free(params);
}
static void event_notice(const char *data, IRC_SERVER_REC *server, const char *nick, const char *addr)
{
char *params, *target, *ptr, *msg;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, &target, &msg); /* Channel or nick name */
/* handle only ctcp replies */
if (*msg == 1) {
ptr = strrchr(++msg, 1);
if (ptr != NULL) *ptr = '\0';
signal_emit("ctcp reply", 5, msg, server, nick, addr, target);
}
g_free(params);
}
static void ctcp_deinit_server(IRC_SERVER_REC *server)
{
g_return_if_fail(server != NULL);
g_slist_free(server->ctcpqueue);
}
void ctcp_init(void)
{
settings_add_str("misc", "ctcp_version_reply", PACKAGE" v$J - running on $sysname $sysrelease");
settings_add_int("flood", "max_ctcp_queue", 5);
signal_add("server disconnected", (SIGNAL_FUNC) ctcp_deinit_server);
signal_add("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_add("event notice", (SIGNAL_FUNC) event_notice);
signal_add("ctcp msg", (SIGNAL_FUNC) ctcp_msg);
signal_add("ctcp reply", (SIGNAL_FUNC) ctcp_reply);
signal_add("ctcp msg ping", (SIGNAL_FUNC) ctcp_ping);
signal_add("ctcp msg version", (SIGNAL_FUNC) ctcp_version);
}
void ctcp_deinit(void)
{
signal_remove("server disconnected", (SIGNAL_FUNC) ctcp_deinit_server);
signal_remove("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_remove("event notice", (SIGNAL_FUNC) event_notice);
signal_remove("ctcp msg", (SIGNAL_FUNC) ctcp_msg);
signal_remove("ctcp reply", (SIGNAL_FUNC) ctcp_reply);
signal_remove("ctcp msg ping", (SIGNAL_FUNC) ctcp_ping);
signal_remove("ctcp msg version", (SIGNAL_FUNC) ctcp_version);
}

10
src/irc/core/ctcp.h Normal file
View file

@ -0,0 +1,10 @@
#ifndef __CTCP_H
#define __CTCP_H
void ctcp_init(void);
void ctcp_deinit(void);
/* Send CTCP reply with flood protection */
void ctcp_send_reply(SERVER_REC *server, gchar *data);
#endif

296
src/irc/core/ignore.c Normal file
View file

@ -0,0 +1,296 @@
/*
ignore.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 "levels.h"
#include "lib-config/iconfig.h"
#include "settings.h"
#include "irc.h"
#include "masks.h"
#include "irc-server.h"
#include "ignore.h"
GSList *ignores;
int ignore_check(IRC_SERVER_REC *server, const char *nick, const char *host,
const char *channel, const char *text, int level)
{
GSList *tmp;
int ok, mask_len, patt_len;
int best_mask, best_patt, best_ignore;
g_return_val_if_fail(server != NULL, 0);
best_mask = 0; best_patt = 0; best_ignore = FALSE;
for (tmp = ignores; tmp != NULL; tmp = tmp->next) {
IGNORE_REC *rec = tmp->data;
if ((level & (rec->level|rec->except_level)) == 0)
continue;
/* server */
if (rec->servertag != NULL && g_strcasecmp(server->tag, rec->servertag) != 0)
continue;
/* nick mask */
mask_len = 0;
if (rec->mask != NULL) {
if (nick == NULL)
continue;
mask_len = strlen(rec->mask);
if (mask_len <= best_mask) continue;
ok = ((host == NULL || *host == '\0')) ?
match_wildcards(rec->mask, nick) :
irc_mask_match_address(rec->mask, nick, host);
if (!ok) continue;
}
/* channel list */
if (rec->channels != NULL) {
if (channel == NULL || !ischannel(*channel))
continue;
if (strarray_find(rec->channels, channel) == -1)
continue;
}
/* pattern */
patt_len = 0;
if (rec->pattern != NULL) {
if (!mask_len && !best_mask) {
patt_len = strlen(rec->pattern);
if (patt_len <= best_patt) continue;
}
ok = rec->regexp ? regexp_match(text, rec->pattern) :
rec->fullword ? stristr_full(text, rec->pattern) != NULL :
stristr(text, rec->pattern) != NULL;
if (!ok) continue;
}
if (mask_len || best_mask)
best_mask = mask_len;
else if (patt_len)
best_patt = patt_len;
best_ignore = (rec->level & level) != 0;
}
return best_ignore;
}
IGNORE_REC *ignore_find(const char *servertag, const char *mask, char **channels)
{
GSList *tmp;
char **chan;
int ignore_servertag;
if (mask != NULL && *mask == '\0') mask = NULL;
ignore_servertag = servertag != NULL && strcmp(servertag, "*") == 0;
for (tmp = ignores; tmp != NULL; tmp = tmp->next) {
IGNORE_REC *rec = tmp->data;
if (!ignore_servertag) {
if ((servertag == NULL && rec->servertag != NULL) ||
(servertag != NULL && rec->servertag == NULL))
continue;
if (servertag != NULL && g_strcasecmp(servertag, rec->servertag) != 0)
continue;
}
if ((rec->mask == NULL && mask != NULL) ||
(rec->mask != NULL && mask == NULL)) continue;
if (rec->mask != NULL && g_strcasecmp(rec->mask, mask) != 0)
continue;
if ((channels == NULL && rec->channels == NULL))
return rec; /* no channels - ok */
if (channels != NULL && strcmp(*channels, "*") == 0)
return rec; /* ignore channels */
if (channels == NULL || rec->channels == NULL)
continue; /* other doesn't have channels */
if (strarray_length(channels) != strarray_length(rec->channels))
continue; /* different amount of channels */
/* check that channels match */
for (chan = channels; *chan != NULL; chan++) {
if (strarray_find(rec->channels, *chan) == -1)
break;
}
if (*chan == NULL)
return rec; /* channels ok */
}
return NULL;
}
static void ignore_set_config(IGNORE_REC *rec)
{
CONFIG_NODE *node;
char *levelstr;
if (rec->level == 0 && rec->except_level == 0)
return;
node = iconfig_node_traverse("(ignores", TRUE);
node = config_node_section(node, NULL, NODE_TYPE_BLOCK);
if (rec->mask != NULL) config_node_set_str(node, "mask", rec->mask);
if (rec->level) {
levelstr = bits2level(rec->level);
config_node_set_str(node, "level", levelstr);
g_free(levelstr);
}
if (rec->except_level) {
levelstr = bits2level(rec->except_level);
config_node_set_str(node, "except_level", levelstr);
g_free(levelstr);
}
config_node_set_str(node, "pattern", rec->pattern);
if (rec->regexp) config_node_set_bool(node, "regexp", TRUE);
if (rec->fullword) config_node_set_bool(node, "fullword", TRUE);
if (rec->channels != NULL && *rec->channels != NULL) {
node = config_node_section(node, "channels", NODE_TYPE_LIST);
config_node_add_list(node, rec->channels);
}
}
static int ignore_index(IGNORE_REC *find)
{
GSList *tmp;
int index;
index = 0;
for (tmp = ignores; tmp != NULL; tmp = tmp->next) {
IGNORE_REC *rec = tmp->data;
if (rec->servertag != NULL)
continue;
if (rec == find)
return index;
index++;
}
return -1;
}
static void ignore_remove_config(IGNORE_REC *rec)
{
CONFIG_NODE *node;
node = iconfig_node_traverse("ignores", FALSE);
if (node != NULL) config_node_list_remove(node, ignore_index(rec));
}
void ignore_add_rec(IGNORE_REC *rec)
{
ignores = g_slist_append(ignores, rec);
ignore_set_config(rec);
}
static void ignore_destroy(IGNORE_REC *rec)
{
ignores = g_slist_remove(ignores, rec);
if (rec->channels != NULL) g_strfreev(rec->channels);
g_free_not_null(rec->mask);
g_free_not_null(rec->servertag);
g_free_not_null(rec->pattern);
g_free(rec);
}
void ignore_update_rec(IGNORE_REC *rec)
{
if (rec->level == 0 && rec->except_level == 0) {
/* unignored everything */
ignore_remove_config(rec);
ignore_destroy(rec);
} else {
/* unignore just some levels.. */
ignore_remove_config(rec);
ignores = g_slist_remove(ignores, rec);
ignores = g_slist_append(ignores, rec);
ignore_set_config(rec);
}
}
static void read_ignores(void)
{
IGNORE_REC *rec;
CONFIG_NODE *node;
GSList *tmp;
while (ignores != NULL)
ignore_destroy(ignores->data);
node = iconfig_node_traverse("ignores", FALSE);
if (node == NULL) return;
for (tmp = node->value; tmp != NULL; tmp = tmp->next) {
node = tmp->data;
if (node->type != NODE_TYPE_BLOCK)
continue;
rec = g_new0(IGNORE_REC, 1);
ignores = g_slist_append(ignores, rec);
rec->mask = g_strdup(config_node_get_str(node, "mask", NULL));
rec->pattern = g_strdup(config_node_get_str(node, "pattern", NULL));
rec->level = level2bits(config_node_get_str(node, "level", 0));
rec->except_level = level2bits(config_node_get_str(node, "except_level", 0));
rec->regexp = config_node_get_bool(node, "regexp", FALSE);
rec->fullword = config_node_get_bool(node, "fullword", FALSE);
node = config_node_section(node, "channels", -1);
if (node != NULL) rec->channels = config_node_get_list(node);
}
}
void ignore_init(void)
{
ignores = NULL;
read_ignores();
signal_add("setup reread", (SIGNAL_FUNC) read_ignores);
}
void ignore_deinit(void)
{
while (ignores != NULL)
ignore_destroy(ignores->data);
signal_remove("setup reread", (SIGNAL_FUNC) read_ignores);
}

30
src/irc/core/ignore.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef __IGNORE_H
#define __IGNORE_H
typedef struct {
char *mask; /* nick mask */
char *servertag; /* this is for autoignoring */
char **channels; /* ignore only in these channels */
char *pattern; /* text body must match this pattern */
int level; /* ignore these levels */
int except_level; /* don't ignore these levels */
int regexp:1;
int fullword:1;
} IGNORE_REC;
extern GSList *ignores;
int ignore_check(IRC_SERVER_REC *server, const char *nick, const char *host,
const char *channel, const char *text, int level);
IGNORE_REC *ignore_find(const char *servertag, const char *mask, char **channels);
void ignore_add_rec(IGNORE_REC *rec);
void ignore_update_rec(IGNORE_REC *rec);
void ignore_init(void);
void ignore_deinit(void);
#endif

878
src/irc/core/irc-commands.c Normal file
View file

@ -0,0 +1,878 @@
/*
irc-commands.c : irssi
Copyright (C) 1999 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 "misc.h"
#include "special-vars.h"
#include "settings.h"
#include "common-setup.h"
#include "bans.h"
#include "channels.h"
#include "irc-server.h"
#include "irc.h"
#include "nicklist.h"
#include "server-redirect.h"
#include "server-setup.h"
typedef struct {
CHANNEL_REC *channel;
char *ban;
int timeleft;
} KNOCKOUT_REC;
static GString *tmpstr;
static int knockout_tag;
static IRC_SERVER_REC *connect_server(const char *data)
{
IRC_SERVER_CONNECT_REC *conn;
IRC_SERVER_REC *server;
char *params, *addr, *portstr, *password, *nick;
int port;
g_return_val_if_fail(data != NULL, NULL);
params = cmd_get_params(data, 4, &addr, &portstr, &password, &nick);
if (*addr == '\0') return NULL;
if (strcmp(password, "-") == 0)
*password = '\0';
port = 6667;
if (*portstr != '\0')
sscanf(portstr, "%d", &port);
/* connect to server */
conn = irc_server_create_conn(addr, port, password, nick);
server = irc_server_connect(conn);
g_free(params);
return server;
}
static void cmd_connect(const char *data)
{
if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
connect_server(data);
}
static void cmd_disconnect(const char *data, IRC_SERVER_REC *server)
{
IRC_SERVER_REC *ircserver;
char *params, *tag, *msg;
g_return_if_fail(data != NULL);
if (g_strncasecmp(data, "RECON-", 6) == 0)
return; /* remove reconnection, handle in server-reconnect.c */
params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &tag, &msg);
if (*tag != '\0' && strcmp(tag, "*") != 0)
server = (IRC_SERVER_REC *) server_find_tag(tag);
if (server == NULL || !irc_server_check(server))
cmd_param_error(CMDERR_NOT_CONNECTED);
ircserver = (IRC_SERVER_REC *) server;
if (ircserver->handle != -1 && ircserver->buffer != NULL) {
/* flush transmit queue */
g_slist_foreach(ircserver->cmdqueue, (GFunc) g_free, NULL);
g_slist_free(ircserver->cmdqueue);
ircserver->cmdqueue = NULL;
ircserver->cmdcount = 0;
/* then send quit message */
if (*msg == '\0') msg = (char *) settings_get_str("default_quit_message");
irc_send_cmdv(ircserver, "QUIT :%s", msg);
}
g_free(params);
server_disconnect((SERVER_REC *) server);
}
static void cmd_server(const char *data, IRC_SERVER_REC *server)
{
char *channels, *away_reason, *usermode, *ircnet;
g_return_if_fail(data != NULL);
if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
if (*data == '+' || server == NULL) {
channels = away_reason = usermode = ircnet = NULL;
} else {
ircnet = g_strdup(server->connrec->ircnet);
channels = irc_server_get_channels((IRC_SERVER_REC *) server);
if (*channels == '\0')
g_free_and_null(channels);
usermode = g_strdup(server->usermode);
away_reason = !server->usermode_away ? NULL :
g_strdup(server->away_reason);
cmd_disconnect("* Changing server", server);
}
server = connect_server(data + (*data == '+' ? 1 : 0));
if (*data == '+' || server == NULL ||
(ircnet != NULL && server->connrec->ircnet != NULL &&
g_strcasecmp(ircnet, server->connrec->ircnet) != 0)) {
g_free_not_null(channels);
g_free_not_null(usermode);
g_free_not_null(away_reason);
} else if (server != NULL) {
server->connrec->reconnection = TRUE;
server->connrec->channels = channels;
server->connrec->usermode = usermode;
server->connrec->away_reason = away_reason;
}
g_free_not_null(ircnet);
}
static void cmd_quit(const char *data)
{
GSList *tmp, *next;
const char *quitmsg;
char *str;
g_return_if_fail(data != NULL);
quitmsg = *data != '\0' ? data :
settings_get_str("default_quit_message");
/* disconnect from every server */
for (tmp = servers; tmp != NULL; tmp = next) {
next = tmp->next;
str = g_strdup_printf("* %s", quitmsg);
cmd_disconnect(str, tmp->data);
g_free(str);
}
signal_emit("gui exit", 0);
}
static void cmd_msg(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
char *params, *target, *msg;
int free_ret;
g_return_if_fail(data != NULL);
params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &msg);
if (*target == '\0' || *msg == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
if (*target == '=') {
/* dcc msg - don't even try to handle here.. */
g_free(params);
return;
}
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_param_error(CMDERR_NOT_CONNECTED);
free_ret = FALSE;
if (strcmp(target, ",") == 0 || strcmp(target, ".") == 0)
target = parse_special(&target, server, item, NULL, &free_ret, NULL);
else if (strcmp(target, "*") == 0 &&
(irc_item_channel(item) || irc_item_query(item)))
target = item->name;
if (target != NULL) {
g_string_sprintf(tmpstr, "PRIVMSG %s :%s", target, msg);
irc_send_cmd_split(server, tmpstr->str, 2, server->max_msgs_in_cmd);
}
if (free_ret && target != NULL) g_free(target);
g_free(params);
}
static void cmd_notice(const char *data, IRC_SERVER_REC *server)
{
char *params, *target, *msg;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &msg);
if (*target == '\0' || *msg == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
g_string_sprintf(tmpstr, "NOTICE %s :%s", target, msg);
irc_send_cmd_split(server, tmpstr->str, 2, server->max_msgs_in_cmd);
g_free(params);
}
static void cmd_ctcp(const char *data, IRC_SERVER_REC *server)
{
char *params, *target, *ctcpcmd, *ctcpdata;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 3 | PARAM_FLAG_GETREST, &target, &ctcpcmd, &ctcpdata);
if (*target == '\0' || *ctcpcmd == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
g_strup(ctcpcmd);
if (*ctcpdata == '\0')
g_string_sprintf(tmpstr, "PRIVMSG %s :\001%s\001", target, ctcpcmd);
else
g_string_sprintf(tmpstr, "PRIVMSG %s :\001%s %s\001", target, ctcpcmd, ctcpdata);
irc_send_cmd_split(server, tmpstr->str, 2, server->max_msgs_in_cmd);
g_free(params);
}
static void cmd_nctcp(const char *data, IRC_SERVER_REC *server)
{
char *params, *target, *ctcpcmd, *ctcpdata;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 3 | PARAM_FLAG_GETREST, &target, &ctcpcmd, &ctcpdata);
if (*target == '\0' || *ctcpcmd == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
g_strup(ctcpcmd);
g_string_sprintf(tmpstr, "NOTICE %s :\001%s %s\001", target, ctcpcmd, ctcpdata);
irc_send_cmd_split(server, tmpstr->str, 2, server->max_msgs_in_cmd);
g_free(params);
}
static void cmd_join(const char *data, IRC_SERVER_REC *server)
{
if (*data == '\0' || g_strncasecmp(data, "-invite", 7) == 0) {
if (server->last_invite != NULL)
channels_join(server, server->last_invite, FALSE);
} else
channels_join(server, data, FALSE);
}
static void cmd_part(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
char *params, *channame, *msg;
CHANNEL_REC *chanrec;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 2 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST, item, &channame, &msg);
if (*channame == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
chanrec = channel_find(server, channame);
if (chanrec == NULL) cmd_param_error(CMDERR_CHAN_NOT_FOUND);
irc_send_cmdv(server, *msg == '\0' ? "PART %s" : "PART %s %s",
channame, msg);
g_free(params);
}
static void cmd_kick(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
char *params, *channame, *nicks, *reason;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 3 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST,
item, &channame, &nicks, &reason);
if (*channame == '\0' || *nicks == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
if (!ischannel(*channame)) cmd_param_error(CMDERR_NOT_JOINED);
g_string_sprintf(tmpstr, "KICK %s %s :%s", channame, nicks, reason);
irc_send_cmd_split(server, tmpstr->str, 3, server->max_kicks_in_cmd);
g_free(params);
}
static void cmd_topic(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
char *params, *channame, *topic;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 2 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST, item, &channame, &topic);
irc_send_cmdv(server, *topic == '\0' ? "TOPIC %s" : "TOPIC %s :%s",
channame, topic);
g_free(params);
}
static void cmd_invite(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
char *params, *nick, *channame;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 2, &nick, &channame);
if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
if (*channame == '\0' || strcmp(channame, "*") == 0) {
if (!irc_item_channel(item))
cmd_param_error(CMDERR_NOT_JOINED);
channame = item->name;
}
irc_send_cmdv(server, "INVITE %s %s", nick, channame);
g_free(params);
}
static void cmd_list(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
char *params, *args, *str;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 2 | PARAM_FLAG_OPTARGS | PARAM_FLAG_GETREST, &args, &str);
if (*str == '\0' && stristr(args, "-yes") == NULL)
cmd_param_error(CMDERR_NOT_GOOD_IDEA);
irc_send_cmdv(server, "LIST %s", str);
g_free(params);
/* add default redirection */
server_redirect_default((SERVER_REC *) server, "bogus command list");
}
static void cmd_who(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
char *params, *channel, *args, *rest;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 3 | PARAM_FLAG_OPTARGS | PARAM_FLAG_GETREST, &args, &channel, &rest);
if (strcmp(channel, "*") == 0 || *channel == '\0') {
if (!irc_item_check(item))
cmd_return_error(CMDERR_NOT_JOINED);
data = item->name;
}
if (strcmp(channel, "**") == 0) {
/* ** displays all nicks.. */
*channel = '\0';
}
irc_send_cmdv(server, *rest == '\0' ? "WHO %s" : "WHO %s %s",
channel, rest);
g_free(params);
/* add default redirection */
server_redirect_default((SERVER_REC *) server, "bogus command who");
}
static void cmd_names(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
if (*data == '\0') cmd_return_error(CMDERR_NOT_GOOD_IDEA);
if (strcmp(data, "*") == 0) {
if (!irc_item_channel(item))
cmd_return_error(CMDERR_NOT_JOINED);
data = item->name;
}
if (g_strcasecmp(data, "-YES") == 0)
irc_send_cmd(server, "NAMES");
else
irc_send_cmdv(server, "NAMES %s", data);
}
static void cmd_whois(const char *data, IRC_SERVER_REC *server)
{
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
while (*data == ' ') data++;
if (*data == '\0') data = server->nick;
g_string_sprintf(tmpstr, "WHOIS %s", data);
irc_send_cmd_split(server, tmpstr->str, 2, server->max_whois_in_cmd);
}
static void cmd_whowas(const char *data, IRC_SERVER_REC *server)
{
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
while (*data == ' ') data++;
if (*data == '\0') data = server->nick;
irc_send_cmdv(server, "WHOWAS %s", data);
}
static void cmd_ping(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
GTimeVal tv;
char *str;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
if (*data == '\0' || strcmp(data, "*") == 0) {
if (!irc_item_check(item))
cmd_return_error(CMDERR_NOT_JOINED);
data = item->name;
}
g_get_current_time(&tv);
str = g_strdup_printf("%s PING %ld %ld", data, tv.tv_sec, tv.tv_usec);
signal_emit("command ctcp", 3, str, server, item);
g_free(str);
}
static void server_send_away(IRC_SERVER_REC *server, const char *reason)
{
g_free_not_null(server->away_reason);
server->away_reason = g_strdup(reason);
irc_send_cmdv(server, "AWAY :%s", reason);
}
static void cmd_away(const char *data, IRC_SERVER_REC *server)
{
char *params, *args, *reason;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 2 | PARAM_FLAG_OPTARGS | PARAM_FLAG_GETREST, &args, &reason);
if (stristr(args, "-all") != NULL)
g_slist_foreach(servers, (GFunc) server_send_away, reason);
else
server_send_away(server, reason);
g_free(params);
}
static void cmd_deop(const char *data, IRC_SERVER_REC *server)
{
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
if (*data == '\0')
irc_send_cmdv(server, "MODE %s -o", server->nick);
}
static void cmd_sconnect(const char *data, IRC_SERVER_REC *server)
{
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
irc_send_cmdv(server, "CONNECT %s", data);
}
static void cmd_quote(const char *data, IRC_SERVER_REC *server)
{
g_return_if_fail(data != NULL);
if (server == NULL || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
irc_send_cmd(server, data);
}
static void cmd_wall_hash(gpointer key, NICK_REC *nick, GSList **nicks)
{
if (nick->op) *nicks = g_slist_append(*nicks, nick);
}
static void cmd_wall(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
char *params, *channame, *msg;
CHANNEL_REC *chanrec;
GSList *tmp, *nicks;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 2 | PARAM_FLAG_OPTCHAN | PARAM_FLAG_GETREST, item, &channame, &msg);
if (*msg == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
chanrec = channel_find(server, channame);
if (chanrec == NULL) cmd_param_error(CMDERR_CHAN_NOT_FOUND);
/* send notice to all ops */
nicks = NULL;
g_hash_table_foreach(chanrec->nicks, (GHFunc) cmd_wall_hash, &nicks);
for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
NICK_REC *rec = tmp->data;
irc_send_cmdv(server, "NOTICE %s :%s", rec->nick, msg);
}
g_slist_free(nicks);
g_free(params);
}
static void cmd_cycle(gchar *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
char *params, *channame, *msg;
CHANNEL_REC *chanrec;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 2 | PARAM_FLAG_OPTCHAN, item, &channame, &msg);
if (*channame == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
chanrec = channel_find(server, channame);
if (chanrec == NULL) cmd_param_error(CMDERR_CHAN_NOT_FOUND);
irc_send_cmdv(server, *msg == '\0' ? "PART %s" : "PART %s %s",
channame, msg);
irc_send_cmdv(server, chanrec->key == NULL ? "JOIN %s" : "JOIN %s %s",
channame, chanrec->key);
g_free(params);
}
static void cmd_kickban(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
char *params, *nick;
g_return_if_fail(data != NULL);
params = cmd_get_params(data, 1, &nick);
if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
signal_emit("command ban", 3, nick, server, item);
signal_emit("command kick", 3, data, server, item);
g_free(params);
}
static void knockout_destroy(IRC_SERVER_REC *server, KNOCKOUT_REC *rec)
{
server->knockoutlist = g_slist_remove(server->knockoutlist, rec);
g_free(rec->ban);
g_free(rec);
}
/* timeout function: knockout */
static void knockout_timeout_server(IRC_SERVER_REC *server)
{
GSList *tmp, *next;
time_t t;
g_return_if_fail(server != NULL);
t = server->knockout_lastcheck == 0 ? 0 :
time(NULL)-server->knockout_lastcheck;
server->knockout_lastcheck = time(NULL);
for (tmp = server->knockoutlist; tmp != NULL; tmp = next) {
KNOCKOUT_REC *rec = tmp->data;
next = tmp->next;
if (rec->timeleft > t)
rec->timeleft -= t;
else {
/* timeout, unban. */
ban_remove(rec->channel, rec->ban);
knockout_destroy(server, rec);
}
}
}
static int knockout_timeout(void)
{
g_slist_foreach(servers, (GFunc) knockout_timeout_server, NULL);
return 1;
}
static void cmd_knockout(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
KNOCKOUT_REC *rec;
CHANNEL_REC *channel;
char *params, *nick, *reason, *timeoutstr, *str;
int timeleft;
g_return_if_fail(data != NULL);
if (server == NULL) cmd_return_error(CMDERR_NOT_CONNECTED);
channel = irc_item_channel(item);
if (channel == NULL) cmd_return_error(CMDERR_NOT_JOINED);
if (is_numeric(data, ' ')) {
/* first argument is the timeout */
params = cmd_get_params(data, 3 | PARAM_FLAG_GETREST, &timeoutstr, &nick, &reason);
timeleft = atol(timeoutstr);
} else {
timeleft = 0;
params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &nick, &reason);
}
if (timeleft == 0) timeleft = settings_get_int("knockout_time");
if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
signal_emit("command ban", 3, nick, server, channel);
str = g_strdup_printf("%s %s", nick, reason);
signal_emit("command kick", 3, str, server, channel);
g_free(str);
/* create knockout record */
rec = g_new(KNOCKOUT_REC, 1);
rec->timeleft = timeleft;
rec->channel = channel;
rec->ban = ban_get_mask(channel, nick);
server->knockoutlist = g_slist_append(server->knockoutlist, rec);
g_free(params);
}
/* destroy all knockouts in server */
static void sig_server_disconnected(IRC_SERVER_REC *server)
{
g_return_if_fail(server != NULL);
while (server->knockoutlist != NULL)
knockout_destroy(server, server->knockoutlist->data);
}
/* destroy all knockouts in channel */
static void sig_channel_destroyed(CHANNEL_REC *channel)
{
GSList *tmp, *next;
g_return_if_fail(channel != NULL);
if (channel->server == NULL) return;
for (tmp = channel->server->knockoutlist; tmp != NULL; tmp = next) {
KNOCKOUT_REC *rec = tmp->data;
next = tmp->next;
if (rec->channel == channel)
knockout_destroy(channel->server, rec);
}
}
static void command_self(const char *data, IRC_SERVER_REC *server)
{
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
irc_send_cmdv(server, *data == '\0' ? "%s" : "%s %s", current_command, data);
}
static void command_1self(const char *data, IRC_SERVER_REC *server)
{
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
irc_send_cmdv(server, "%s :%s", current_command, data);
}
static void command_2self(const char *data, IRC_SERVER_REC *server)
{
char *params, *target, *text;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &text);
if (*target == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
irc_send_cmdv(server, "%s %s :%s", current_command, target, text);
g_free(params);
}
static void sig_connected(IRC_SERVER_REC *server)
{
g_return_if_fail(server != NULL);
server_redirect_init((SERVER_REC *) server, "", 2, "event 318", "event 402", "event 401",
"event 301", "event 311", "event 312", "event 313",
"event 317", "event 319", NULL);
/* gui-gnome can use server_redirect_event() in who/list commands so
we can't use "command who" or list here.. */
server_redirect_init((SERVER_REC *) server, "bogus command who", 2, "event 401", "event 315", "event 352", NULL);
server_redirect_init((SERVER_REC *) server, "bogus command list", 1, "event 321", "event 322", "event 323", NULL);
}
void irc_commands_init(void)
{
tmpstr = g_string_new(NULL);
settings_add_str("misc", "default_quit_message", "leaving");
settings_add_int("misc", "knockout_time", 300);
knockout_tag = g_timeout_add(KNOCKOUT_TIMECHECK, (GSourceFunc) knockout_timeout, NULL);
signal_add("server connected", (SIGNAL_FUNC) sig_connected);
command_bind("server", NULL, (SIGNAL_FUNC) cmd_server);
command_bind("connect", NULL, (SIGNAL_FUNC) cmd_connect);
command_bind("disconnect", NULL, (SIGNAL_FUNC) cmd_disconnect);
command_bind("msg", NULL, (SIGNAL_FUNC) cmd_msg);
command_bind("notice", NULL, (SIGNAL_FUNC) cmd_notice);
command_bind("ctcp", NULL, (SIGNAL_FUNC) cmd_ctcp);
command_bind("nctcp", NULL, (SIGNAL_FUNC) cmd_nctcp);
command_bind("quit", NULL, (SIGNAL_FUNC) cmd_quit);
command_bind("join", NULL, (SIGNAL_FUNC) cmd_join);
command_bind("part", NULL, (SIGNAL_FUNC) cmd_part);
command_bind("kick", NULL, (SIGNAL_FUNC) cmd_kick);
command_bind("topic", NULL, (SIGNAL_FUNC) cmd_topic);
command_bind("invite", NULL, (SIGNAL_FUNC) cmd_invite);
command_bind("list", NULL, (SIGNAL_FUNC) cmd_list);
command_bind("who", NULL, (SIGNAL_FUNC) cmd_who);
command_bind("names", NULL, (SIGNAL_FUNC) cmd_names);
command_bind("nick", NULL, (SIGNAL_FUNC) command_self);
command_bind("note", NULL, (SIGNAL_FUNC) command_self);
command_bind("whois", NULL, (SIGNAL_FUNC) cmd_whois);
command_bind("whowas", NULL, (SIGNAL_FUNC) cmd_whowas);
command_bind("ping", NULL, (SIGNAL_FUNC) cmd_ping);
command_bind("kill", NULL, (SIGNAL_FUNC) command_2self);
command_bind("away", NULL, (SIGNAL_FUNC) cmd_away);
command_bind("ison", NULL, (SIGNAL_FUNC) command_1self);
command_bind("admin", NULL, (SIGNAL_FUNC) command_self);
command_bind("info", NULL, (SIGNAL_FUNC) command_self);
command_bind("links", NULL, (SIGNAL_FUNC) command_self);
command_bind("lusers", NULL, (SIGNAL_FUNC) command_self);
command_bind("map", NULL, (SIGNAL_FUNC) command_self);
command_bind("motd", NULL, (SIGNAL_FUNC) command_self);
command_bind("stats", NULL, (SIGNAL_FUNC) command_self);
command_bind("time", NULL, (SIGNAL_FUNC) command_self);
command_bind("trace", NULL, (SIGNAL_FUNC) command_self);
command_bind("version", NULL, (SIGNAL_FUNC) command_self);
command_bind("servlist", NULL, (SIGNAL_FUNC) command_self);
command_bind("silence", NULL, (SIGNAL_FUNC) command_self);
command_bind("sconnect", NULL, (SIGNAL_FUNC) cmd_sconnect);
command_bind("squery", NULL, (SIGNAL_FUNC) command_2self);
command_bind("deop", NULL, (SIGNAL_FUNC) cmd_deop);
command_bind("die", NULL, (SIGNAL_FUNC) command_self);
command_bind("hash", NULL, (SIGNAL_FUNC) command_self);
command_bind("oper", NULL, (SIGNAL_FUNC) command_self);
command_bind("restart", NULL, (SIGNAL_FUNC) command_self);
command_bind("rping", NULL, (SIGNAL_FUNC) command_self);
command_bind("squit", NULL, (SIGNAL_FUNC) command_2self);
command_bind("uping", NULL, (SIGNAL_FUNC) command_self);
command_bind("quote", NULL, (SIGNAL_FUNC) cmd_quote);
command_bind("wall", NULL, (SIGNAL_FUNC) cmd_wall);
command_bind("wallops", NULL, (SIGNAL_FUNC) command_1self);
command_bind("wallchops", NULL, (SIGNAL_FUNC) command_2self);
command_bind("cycle", NULL, (SIGNAL_FUNC) cmd_cycle);
command_bind("kickban", NULL, (SIGNAL_FUNC) cmd_kickban);
command_bind("knockout", NULL, (SIGNAL_FUNC) cmd_knockout);
signal_add("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
signal_add("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
}
void irc_commands_deinit(void)
{
g_source_remove(knockout_tag);
signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
command_unbind("server", (SIGNAL_FUNC) cmd_server);
command_unbind("connect", (SIGNAL_FUNC) cmd_connect);
command_unbind("disconnect", (SIGNAL_FUNC) cmd_disconnect);
command_unbind("msg", (SIGNAL_FUNC) cmd_msg);
command_unbind("notice", (SIGNAL_FUNC) cmd_notice);
command_unbind("ctcp", (SIGNAL_FUNC) cmd_ctcp);
command_unbind("nctcp", (SIGNAL_FUNC) cmd_nctcp);
command_unbind("quit", (SIGNAL_FUNC) cmd_quit);
command_unbind("join", (SIGNAL_FUNC) cmd_join);
command_unbind("part", (SIGNAL_FUNC) cmd_part);
command_unbind("kick", (SIGNAL_FUNC) cmd_kick);
command_unbind("topic", (SIGNAL_FUNC) cmd_topic);
command_unbind("invite", (SIGNAL_FUNC) cmd_invite);
command_unbind("list", (SIGNAL_FUNC) cmd_list);
command_unbind("who", (SIGNAL_FUNC) cmd_who);
command_unbind("names", (SIGNAL_FUNC) cmd_names);
command_unbind("nick", (SIGNAL_FUNC) command_self);
command_unbind("note", (SIGNAL_FUNC) command_self);
command_unbind("whois", (SIGNAL_FUNC) cmd_whois);
command_unbind("whowas", (SIGNAL_FUNC) cmd_whowas);
command_unbind("ping", (SIGNAL_FUNC) cmd_ping);
command_unbind("kill", (SIGNAL_FUNC) command_2self);
command_unbind("away", (SIGNAL_FUNC) cmd_away);
command_unbind("ison", (SIGNAL_FUNC) command_1self);
command_unbind("admin", (SIGNAL_FUNC) command_self);
command_unbind("info", (SIGNAL_FUNC) command_self);
command_unbind("links", (SIGNAL_FUNC) command_self);
command_unbind("lusers", (SIGNAL_FUNC) command_self);
command_unbind("map", (SIGNAL_FUNC) command_self);
command_unbind("motd", (SIGNAL_FUNC) command_self);
command_unbind("stats", (SIGNAL_FUNC) command_self);
command_unbind("time", (SIGNAL_FUNC) command_self);
command_unbind("trace", (SIGNAL_FUNC) command_self);
command_unbind("version", (SIGNAL_FUNC) command_self);
command_unbind("servlist", (SIGNAL_FUNC) command_self);
command_unbind("silence", (SIGNAL_FUNC) command_self);
command_unbind("sconnect", (SIGNAL_FUNC) cmd_sconnect);
command_unbind("squery", (SIGNAL_FUNC) command_2self);
command_unbind("deop", (SIGNAL_FUNC) cmd_deop);
command_unbind("die", (SIGNAL_FUNC) command_self);
command_unbind("hash", (SIGNAL_FUNC) command_self);
command_unbind("oper", (SIGNAL_FUNC) command_self);
command_unbind("restart", (SIGNAL_FUNC) command_self);
command_unbind("rping", (SIGNAL_FUNC) command_self);
command_unbind("squit", (SIGNAL_FUNC) command_2self);
command_unbind("uping", (SIGNAL_FUNC) command_self);
command_unbind("quote", (SIGNAL_FUNC) cmd_quote);
command_unbind("wall", (SIGNAL_FUNC) cmd_wall);
command_unbind("wallops", (SIGNAL_FUNC) command_1self);
command_unbind("wallchops", (SIGNAL_FUNC) command_2self);
command_unbind("cycle", (SIGNAL_FUNC) cmd_cycle);
command_unbind("kickban", (SIGNAL_FUNC) cmd_kickban);
command_unbind("knockout", (SIGNAL_FUNC) cmd_knockout);
signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
signal_remove("server disconnected", (SIGNAL_FUNC) sig_server_disconnected);
g_string_free(tmpstr, TRUE);
}

View file

@ -0,0 +1,7 @@
#ifndef __IRC_COMMANDS_H
#define __IRC_COMMANDS_H
void irc_commands_init(void);
void irc_commands_deinit(void);
#endif

63
src/irc/core/irc-core.c Normal file
View file

@ -0,0 +1,63 @@
/*
irc-core.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 "irc-server.h"
#include "channels.h"
#include "ctcp.h"
#include "irc-commands.h"
#include "irc-rawlog.h"
#include "irc-special-vars.h"
#include "ignore.h"
#include "irc.h"
#include "lag.h"
#include "netsplit.h"
void irc_core_init(void)
{
irc_servers_init();
channels_init();
ctcp_init();
irc_commands_init();
irc_irc_init();
lag_init();
netsplit_init();
ignore_init();
irc_rawlog_init();
irc_special_vars_init();
}
void irc_core_deinit(void)
{
irc_special_vars_deinit();
irc_rawlog_deinit();
ignore_deinit();
netsplit_deinit();
lag_deinit();
irc_irc_deinit();
irc_commands_deinit();
ctcp_deinit();
channels_deinit();
irc_servers_deinit();
}

7
src/irc/core/irc-core.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef __IRC_CORE_H
#define __IRC_CORE_H
void irc_core_init(void);
void irc_core_deinit(void);
#endif

70
src/irc/core/irc-log.c Normal file
View file

@ -0,0 +1,70 @@
static void sig_log(SERVER_REC *server, const char *channel, gpointer level, const char *str)
{
gint loglevel;
g_return_if_fail(str != NULL);
loglevel = GPOINTER_TO_INT(level);
if (loglevel == MSGLEVEL_NEVER || logs == NULL) return;
/* Check if line should be saved in logs */
log_file_write(server, channel, loglevel, str);
}
static void event_away(const char *data, IRC_SERVER_REC *server)
{
LOG_REC *log;
const char *fname, *level;
fname = settings_get_str("awaylog_file");
level = settings_get_str("awaylog_level");
if (*fname == '\0' || *level == '\0') return;
log = log_file_find(fname);
if (log != NULL) {
/* awaylog already created */
if (log->handle == -1) {
/* ..but not open, open it. */
log_file_open(log);
}
return;
}
log = log_create(fname, level);
if (log != NULL) log_file_open(log);
}
static void event_unaway(const char *data, IRC_SERVER_REC *server)
{
LOG_REC *rec;
const char *fname;
fname = settings_get_str("awaylog_file");
if (*fname == '\0') return;
rec = log_file_find(fname);
if (rec == NULL || rec->handle == -1) {
/* awaylog not open */
return;
}
log_file_destroy(rec);
}
void log_init(void)
{
settings_add_str("misc", "awaylog_file", "~/.irssi/away.log");
settings_add_str("misc", "awaylog_level", "-all +msgs +hilight");
signal_add("print text stripped", (SIGNAL_FUNC) sig_log);
signal_add("event 306", (SIGNAL_FUNC) event_away);
signal_add("event 305", (SIGNAL_FUNC) event_unaway);
}
void log_deinit(void)
{
signal_remove("print text stripped", (SIGNAL_FUNC) sig_log);
signal_remove("event 306", (SIGNAL_FUNC) event_away);
signal_remove("event 305", (SIGNAL_FUNC) event_unaway);
}

77
src/irc/core/irc-rawlog.c Normal file
View file

@ -0,0 +1,77 @@
/*
irc-rawlog.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 "rawlog.h"
#include "modules.h"
#include "signals.h"
#include "misc.h"
#include "commands.h"
#include "server.h"
#include "settings.h"
static void cmd_rawlog(const char *data, SERVER_REC *server, void *item)
{
command_runsub("rawlog", data, server, item);
}
static void cmd_rawlog_save(const char *data, SERVER_REC *server)
{
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected) cmd_return_error(CMDERR_NOT_CONNECTED);
if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
rawlog_save(server->rawlog, data);
}
static void cmd_rawlog_open(const char *data, SERVER_REC *server)
{
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected) cmd_return_error(CMDERR_NOT_CONNECTED);
if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
rawlog_open(server->rawlog, data);
}
static void cmd_rawlog_close(const char *data, SERVER_REC *server)
{
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected) cmd_return_error(CMDERR_NOT_CONNECTED);
rawlog_close(server->rawlog);
}
void irc_rawlog_init(void)
{
command_bind("rawlog", NULL, (SIGNAL_FUNC) cmd_rawlog);
command_bind("rawlog save", NULL, (SIGNAL_FUNC) cmd_rawlog_save);
command_bind("rawlog open", NULL, (SIGNAL_FUNC) cmd_rawlog_open);
command_bind("rawlog close", NULL, (SIGNAL_FUNC) cmd_rawlog_close);
}
void irc_rawlog_deinit(void)
{
command_unbind("rawlog", (SIGNAL_FUNC) cmd_rawlog);
command_unbind("rawlog save", (SIGNAL_FUNC) cmd_rawlog_save);
command_unbind("rawlog open", (SIGNAL_FUNC) cmd_rawlog_open);
command_unbind("rawlog close", (SIGNAL_FUNC) cmd_rawlog_close);
}

View file

@ -0,0 +1,7 @@
#ifndef __IRC_RAWLOG_H
#define __IRC_RAWLOG_H
void irc_rawlog_init(void);
void irc_rawlog_deinit(void);
#endif

457
src/irc/core/irc-server.c Normal file
View file

@ -0,0 +1,457 @@
/*
irc-server.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 "net-nonblock.h"
#include "line-split.h"
#include "signals.h"
#include "modules.h"
#include "rawlog.h"
#include "misc.h"
#include "irc-server.h"
#include "server-idle.h"
#include "server-reconnect.h"
#include "server-setup.h"
#include "ircnet-setup.h"
#include "channels.h"
#include "modes.h"
#include "irc.h"
#include "query.h"
#include "settings.h"
#define DEFAULT_MAX_KICKS 1
#define DEFAULT_MAX_MODES 3
#define DEFAULT_MAX_WHOIS 4
#define DEFAULT_MAX_MSGS 1
#define DEFAULT_USER_MODE "+i"
#define DEFAULT_CMD_QUEUE_SPEED 2200
#define DEFAULT_CMDS_MAX_AT_ONCE 5
static int cmd_tag;
void irc_server_connect_free(IRC_SERVER_CONNECT_REC *rec)
{
g_return_if_fail(rec != NULL);
g_free_not_null(rec->proxy);
g_free_not_null(rec->proxy_string);
g_free_not_null(rec->ircnet);
g_free_not_null(rec->password);
g_free_not_null(rec->nick);
g_free_not_null(rec->alternate_nick);
g_free_not_null(rec->username);
g_free_not_null(rec->realname);
g_free_not_null(rec->own_ip);
g_free_not_null(rec->channels);
g_free_not_null(rec->away_reason);
g_free_not_null(rec->usermode);
g_free(rec->address);
g_free(rec);
}
static void server_init(IRC_SERVER_REC *server)
{
IRC_SERVER_CONNECT_REC *conn;
g_return_if_fail(server != NULL);
conn = server->connrec;
if (conn->proxy_string != NULL)
irc_send_cmdv(server, conn->proxy_string, conn->address, conn->port);
if (conn->password != NULL && *conn->password != '\0') {
/* send password */
server->cmdcount = 0;
irc_send_cmdv(server, "PASS %s", conn->password);
}
/* send nick */
server->cmdcount = 0;
irc_send_cmdv(server, "NICK %s", conn->nick);
/* send user/realname */
server->cmdcount = 0;
irc_send_cmdv(server, "USER %s - - :%s", conn->username, conn->realname);
server->cmdcount = 0;
}
IRC_SERVER_REC *irc_server_connect(IRC_SERVER_CONNECT_REC *conn)
{
IRC_SERVER_REC *server;
g_return_val_if_fail(conn != NULL, NULL);
if (conn->address == NULL || *conn->address == '\0') return NULL;
if (conn->nick == NULL || *conn->nick == '\0') return NULL;
server = g_new0(IRC_SERVER_REC, 1);
server->type = module_get_uniq_id("IRC SERVER", SERVER_TYPE_IRC);
server->connrec = conn;
if (conn->port <= 0) conn->port = 6667;
if (conn->username == NULL || *conn->username == '\0') {
g_free_not_null(conn->username);
conn->username = g_get_user_name();
if (*conn->username == '\0') conn->username = "-";
conn->username = g_strdup(conn->username);
}
if (conn->realname == NULL || *conn->realname == '\0') {
g_free_not_null(conn->realname);
conn->realname = g_get_real_name();
if (*conn->realname == '\0') conn->realname = "-";
conn->realname = g_strdup(conn->realname);
}
server->nick = g_strdup(conn->nick);
server->cmd_queue_speed = conn->cmd_queue_speed > 0 ?
conn->cmd_queue_speed : settings_get_int("cmd_queue_speed");
server->max_cmds_at_once = conn->max_cmds_at_once > 0 ?
conn->max_cmds_at_once : settings_get_int("cmds_max_at_once");
server->max_kicks_in_cmd = conn->max_kicks > 0 ?
conn->max_kicks : DEFAULT_MAX_KICKS;
server->max_modes_in_cmd = conn->max_modes > 0 ?
conn->max_modes : DEFAULT_MAX_MODES;
server->max_whois_in_cmd = conn->max_whois > 0 ?
conn->max_whois : DEFAULT_MAX_WHOIS;
server->max_msgs_in_cmd = conn->max_msgs > 0 ?
conn->max_msgs : DEFAULT_MAX_MSGS;
if (!server_connect((SERVER_REC *) server)) {
irc_server_connect_free(conn);
g_free(server->nick);
g_free(server);
return NULL;
}
return server;
}
static void sig_connected(IRC_SERVER_REC *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);
server->splits = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal);
server_init(server);
}
static int server_remove_channels(IRC_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;
}
static void sig_disconnected(IRC_SERVER_REC *server)
{
int chans;
/* close all channels */
chans = server_remove_channels(server);
g_slist_foreach(server->cmdqueue, (GFunc) g_free, NULL);
g_slist_free(server->cmdqueue);
if (server->handle != -1) {
if (!chans || server->connection_lost)
net_disconnect(server->handle);
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(server->handle);
}
server->handle = -1;
}
irc_server_connect_free(server->connrec);
g_free_not_null(server->real_address);
g_free_not_null(server->version);
g_free_not_null(server->usermode);
g_free_not_null(server->userhost);
g_free_not_null(server->last_invite);
g_free_not_null(server->away_reason);
}
static void sig_connect_failed(IRC_SERVER_REC *server)
{
server_remove_channels(server);
irc_server_connect_free(server->connrec);
}
static void server_cmd_timeout(IRC_SERVER_REC *server, GTimeVal *now)
{
long usecs;
char *cmd;
int len, ret, add_rawlog;
if (server->cmdcount == 0 && server->cmdqueue == NULL)
return;
if (!server->cmd_last_split) {
usecs = get_timeval_diff(now, &server->last_cmd);
if (usecs < server->cmd_queue_speed)
return;
}
server->cmdcount--;
if (server->cmdqueue == NULL) return;
/* send command */
cmd = server->cmdqueue->data;
len = strlen(cmd);
add_rawlog = !server->cmd_last_split;
ret = net_transmit(server->handle, cmd, len);
if (ret != len) {
/* we didn't transmit all data, try again a bit later.. */
if (ret > 0) {
cmd = g_strdup((char *) (server->cmdqueue->data) + ret);
g_free(server->cmdqueue->data);
server->cmdqueue->data = cmd;
}
server->cmd_last_split = TRUE;
server->cmdcount++;
} else {
memcpy(&server->last_cmd, now, sizeof(GTimeVal));
if (server->cmd_last_split)
server->cmd_last_split = FALSE;
}
if (add_rawlog) {
/* add to rawlog without CR+LF */
int slen;
slen = strlen(cmd);
cmd[slen-2] = '\0';
rawlog_output(server->rawlog, cmd);
cmd[slen-2] = '\r';
}
if (ret == len) {
/* remove from queue */
g_free(cmd);
server->cmdqueue = g_slist_remove(server->cmdqueue, cmd);
}
}
/* check every now and then if there's data to be sent in command buffer */
static int servers_cmd_timeout(void)
{
GTimeVal now;
g_get_current_time(&now);
g_slist_foreach(servers, (GFunc) server_cmd_timeout, &now);
return 1;
}
/* Return a string of all channels (and keys, if any have them) in server,
like "#a,#b,#c,#d x,b_chan_key,x,x" or just "#e,#f,#g" */
char *irc_server_get_channels(IRC_SERVER_REC *server)
{
GSList *tmp;
GString *chans, *keys;
char *ret;
int use_keys;
g_return_val_if_fail(server != NULL, FALSE);
chans = g_string_new(NULL);
keys = g_string_new(NULL);
use_keys = FALSE;
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *channel = tmp->data;
g_string_sprintfa(chans, "%s,", channel->name);
g_string_sprintfa(keys, "%s,", channel->key == NULL ? "x" : channel->key);
if (channel->key != NULL)
use_keys = TRUE;
}
if (chans->len > 0) {
g_string_truncate(chans, chans->len-1);
g_string_truncate(keys, keys->len-1);
if (use_keys) g_string_sprintfa(chans, " %s", keys->str);
}
ret = chans->str;
g_string_free(chans, FALSE);
g_string_free(keys, TRUE);
return ret;
}
static int sig_set_user_mode(IRC_SERVER_REC *server)
{
const char *mode;
char *newmode;
if (g_slist_find(servers, server) == NULL)
return 0; /* got disconnected */
mode = settings_get_str("default_user_mode");
newmode = modes_join(server->usermode, mode);
if (strcmp(newmode, server->usermode) != 0)
irc_send_cmdv(server, "MODE %s %s", server->nick, mode);
g_free(newmode);
return 0;
}
static void event_connected(const char *data, IRC_SERVER_REC *server, const char *from)
{
char *params, *nick;
const char *mode;
g_return_if_fail(server != NULL);
params = event_get_params(data, 1, &nick);
if (strcmp(server->nick, nick) != 0) {
/* nick changed unexpectedly .. connected via proxy, etc. */
g_free(server->nick);
server->nick = g_strdup(nick);
}
if (server->real_address == NULL) {
/* set the server address */
server->real_address = g_strdup(from);
}
/* last welcome message found - commands can be sent to server now. */
server->connected = 1;
if (!server->connrec->reconnection) {
/* wait a second and then send the user mode */
mode = settings_get_str("default_user_mode");
if (*mode != '\0')
g_timeout_add(1000, (GSourceFunc) sig_set_user_mode, server);
}
signal_emit("event connected", 1, server);
g_free(params);
}
static void event_server_info(const char *data, IRC_SERVER_REC *server)
{
char *params, *ircd_version, *usermodes, *chanmodes;
g_return_if_fail(server != NULL);
params = event_get_params(data, 5, NULL, NULL, &ircd_version, &usermodes, &chanmodes);
/* check if server understands I and e channel modes */
if (strchr(chanmodes, 'I') && strchr(chanmodes, 'e'))
server->emode_known = TRUE;
/* save server version */
g_free_not_null(server->version);
server->version = g_strdup(ircd_version);
g_free(params);
}
static void event_ping(const char *data, IRC_SERVER_REC *server)
{
char *str;
g_return_if_fail(data != NULL);
str = g_strdup_printf("PONG %s", data);
irc_send_cmd_now(server, str);
g_free(str);
}
static void event_empty(void)
{
}
void irc_servers_init(void)
{
settings_add_str("misc", "default_user_mode", DEFAULT_USER_MODE);
settings_add_int("flood", "cmd_queue_speed", DEFAULT_CMD_QUEUE_SPEED);
settings_add_int("flood", "cmds_max_at_once", DEFAULT_CMDS_MAX_AT_ONCE);
cmd_tag = g_timeout_add(500, (GSourceFunc) servers_cmd_timeout, NULL);
signal_add_first("server connected", (SIGNAL_FUNC) sig_connected);
signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected);
signal_add_last("server connect failed", (SIGNAL_FUNC) sig_connect_failed);
signal_add("event 001", (SIGNAL_FUNC) event_connected);
signal_add("event 004", (SIGNAL_FUNC) event_server_info);
signal_add("event ping", (SIGNAL_FUNC) event_ping);
signal_add("event empty", (SIGNAL_FUNC) event_empty);
servers_setup_init();
ircnets_setup_init();
servers_idle_init();
servers_reconnect_init();
}
void irc_servers_deinit(void)
{
while (servers != NULL)
server_disconnect(servers->data);
while (lookup_servers != NULL)
server_disconnect(lookup_servers->data);
g_source_remove(cmd_tag);
signal_remove("server connected", (SIGNAL_FUNC) sig_connected);
signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
signal_remove("server connect failed", (SIGNAL_FUNC) sig_connect_failed);
signal_remove("event 001", (SIGNAL_FUNC) event_connected);
signal_remove("event 004", (SIGNAL_FUNC) event_server_info);
signal_remove("event ping", (SIGNAL_FUNC) event_ping);
signal_remove("event empty", (SIGNAL_FUNC) event_empty);
servers_setup_deinit();
ircnets_setup_deinit();
servers_idle_deinit();
servers_reconnect_deinit();
}

146
src/irc/core/irc-server.h Normal file
View file

@ -0,0 +1,146 @@
#ifndef __IRC_SERVER_H
#define __IRC_SERVER_H
#include "server.h"
enum {
SERVER_TYPE_IRC
};
/* return if `server' doesn't point to IRC server record. */
#define irc_server_check(server) \
((server) != NULL && module_find_id("IRC SERVER", (server)->type) != -1)
/* all strings should be either NULL or dynamically allocated */
/* address and nick are mandatory, rest are optional */
typedef struct {
/* -- GENERIC SERVER_CONNECT_REC - don't change! -- */
/* if we're connecting via proxy, or just NULLs */
char *proxy;
int proxy_port;
char *proxy_string;
/* server where we want to connect */
char *address;
int port;
char *ircnet;
IPADDR *own_ip;
/* -- IRC specific - change if you wish -- */
char *password;
char *nick, *alternate_nick;
char *username;
char *realname;
int max_cmds_at_once;
int cmd_queue_speed;
int max_kicks, max_msgs, max_modes, max_whois;
/* when reconnecting, the old server status */
int reconnection:1; /* we're trying to reconnect */
char *channels;
char *away_reason;
char *usermode;
} IRC_SERVER_CONNECT_REC;
typedef struct {
/* -- GENERIC SERVER_REC - don't change! -- */
int type; /* server type */
IRC_SERVER_CONNECT_REC *connrec;
time_t connect_time; /* connection time */
char *tag; /* tag name for addressing server */
char *nick; /* current nick */
int connected:1; /* connected to server */
int connection_lost:1; /* Connection lost unintentionally */
int handle; /* socket handle */
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;
/* -- IRC specific - change if you wish -- */
char *real_address; /* address the irc server gives */
char *version; /* server version - taken from 004 event */
char *usermode; /* The whole mode string .. */
char *userhost; /* /USERHOST <nick> - set when joined to first channel */
char *last_invite; /* channel where you were last invited */
char *away_reason;
int usermode_away:1;
int server_operator:1;
int whois_coming:1; /* Mostly just to display away message right.. */
int emode_known:1; /* Server understands ban exceptions and invite lists */
int no_multi_mode:1; /* Server doesn't understand MODE #chan1,#chan2,... */
int no_multi_who:1; /* Server doesn't understand WHO #chan1,#chan2,... */
int one_endofwho:1; /* /WHO #a,#b,.. replies only with one End of WHO message */
int max_kicks_in_cmd; /* max. number of people to kick with one /KICK command */
int max_modes_in_cmd; /* max. number of mode changes in one /MODE command */
int max_whois_in_cmd; /* max. number of nicks in one /WHOIS command */
int max_msgs_in_cmd; /* max. number of targets in one /MSG */
/* Command sending queue */
int cmdcount; /* number of commands in `cmdqueue'. Can be more than
there actually is, to make flood control remember
how many messages can be sent before starting the
flood control */
int cmd_last_split; /* Last command wasn't sent entirely to server.
First item in `cmdqueue' should be re-sent. */
GSList *cmdqueue;
GTimeVal last_cmd; /* last time command was sent to server */
int max_cmds_at_once; /* How many messages can be sent immediately before timeouting starts */
int cmd_queue_speed; /* Timeout between sending commands */
GSList *idles; /* Idle queue - send these commands to server
if there's nothing else to do */
GSList *ctcpqueue; /* CTCP flood protection - list of tags in idle queue */
/* /knockout ban list */
GSList *knockoutlist;
time_t knockout_lastcheck;
GSList *lastmsgs; /* List of nicks who last send you msg */
GHashTable *splits; /* For keeping track of netsplits */
time_t lag_sent; /* 0 or time when last lag query was sent to server */
time_t lag_last_check; /* last time we checked lag */
int lag; /* server lag in milliseconds */
GSList *channels;
GSList *queries;
gpointer chanqueries;
} IRC_SERVER_REC;
IRC_SERVER_REC *irc_server_connect(IRC_SERVER_CONNECT_REC *conn);
/* Return a string of all channels (and keys, if any have them) in server,
like "#a,#b,#c,#d x,b_chan_key,x,x" or just "#e,#f,#g" */
char *irc_server_get_channels(IRC_SERVER_REC *server);
/* INTERNAL: Free memory used by connection record */
void irc_server_connect_free(IRC_SERVER_CONNECT_REC *rec);
void irc_servers_init(void);
void irc_servers_deinit(void);
#endif

View file

@ -0,0 +1,332 @@
/*
irc-special-vars.c : irssi
Copyright (C) 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 "misc.h"
#include "special-vars.h"
#include "settings.h"
#include "irc.h"
#include "irc-server.h"
#include "channels.h"
#include "query.h"
static char *last_privmsg_from;
static char *last_sent_msg, *last_sent_msg_body;
static char *last_join, *last_public_from;
/* last person who sent you a MSG */
static char *expando_lastmsg(void *server, void *item, int *free_ret)
{
return last_privmsg_from;
}
/* last person to whom you sent a MSG */
static char *expando_lastmymsg(void *server, void *item, int *free_ret)
{
return last_sent_msg;
}
/* last person to join a channel you are on */
static char *expando_lastjoin(void *server, void *item, int *free_ret)
{
return last_join;
}
/* last person to send a public message to a channel you are on */
static char *expando_lastpublic(void *server, void *item, int *free_ret)
{
return last_public_from;
}
/* text of your AWAY message, if any */
static char *expando_awaymsg(void *server, void *item, int *free_ret)
{
IRC_SERVER_REC *ircserver = server;
return ircserver == NULL ? "" : ircserver->away_reason;
}
/* body of last MSG you sent */
static char *expando_lastmymsg_body(void *server, void *item, int *free_ret)
{
return last_sent_msg_body;
}
/* current channel */
static char *expando_channel(void *server, void *item, int *free_ret)
{
CHANNEL_REC *channel;
channel = irc_item_channel(item);
return channel == NULL ? NULL : channel->name;
}
/* current server numeric being processed */
static char *expando_server_numeric(void *server, void *item, int *free_ret)
{
return current_server_event == NULL ||
!is_numeric(current_server_event, 0) ? NULL :
current_server_event;
}
/* channel you were last INVITEd to */
static char *expando_last_invite(void *server, void *item, int *free_ret)
{
IRC_SERVER_REC *ircserver = server;
return ircserver == NULL ? "" : ircserver->last_invite;
}
/* modes of current channel, if any */
static char *expando_chanmode(void *server, void *item, int *free_ret)
{
CHANNEL_REC *channel;
channel = irc_item_channel(item);
if (channel == NULL) return NULL;
*free_ret = TRUE;
return channel_get_mode(channel);
}
/* current nickname */
static char *expando_nick(void *server, void *item, int *free_ret)
{
IRC_SERVER_REC *ircserver = server;
return ircserver == NULL ? "" : ircserver->nick;
}
/* value of STATUS_OPER if you are an irc operator */
static char *expando_statusoper(void *server, void *item, int *free_ret)
{
IRC_SERVER_REC *ircserver = server;
return ircserver == NULL || !ircserver->server_operator ? "" :
(char *) settings_get_str("STATUS_OPER");
}
/* if you are a channel operator in $C, expands to a '@' */
static char *expando_chanop(void *server, void *item, int *free_ret)
{
CHANNEL_REC *channel;
channel = irc_item_channel(item);
if (channel == NULL) return NULL;
return channel->chanop ? "@" : "";
}
/* nickname of whomever you are QUERYing */
static char *expando_query(void *server, void *item, int *free_ret)
{
QUERY_REC *query;
query = irc_item_query(item);
return query == NULL ? NULL : query->nick;
}
/* version of current server */
static char *expando_serverversion(void *server, void *item, int *free_ret)
{
IRC_SERVER_REC *ircserver = server;
return ircserver == NULL ? "" : ircserver->version;
}
/* current server name */
static char *expando_servername(void *server, void *item, int *free_ret)
{
IRC_SERVER_REC *ircserver = server;
return ircserver == NULL ? "" : ircserver->real_address;
}
/* target of current input (channel or QUERY nickname) */
static char *expando_target(void *server, void *item, int *free_ret)
{
if (!irc_item_check(item))
return NULL;
return ((WI_IRC_REC *) item)->name;
}
/* your /userhost $N address (user@host) */
static char *expando_userhost(void *server, void *item, int *free_ret)
{
IRC_SERVER_REC *ircserver = server;
const char *username;
char hostname[100];
/* prefer the _real_ /userhost reply */
if (ircserver != NULL && ircserver->userhost != NULL)
return ircserver->userhost;
/* haven't received userhost reply yet. guess something */
*free_ret = TRUE;
if (server == NULL)
username = settings_get_str("user_name");
else
username = ircserver->connrec->username;
if (gethostname(hostname, sizeof(hostname)) != 0 || *hostname == '\0')
strcpy(hostname, "??");
return g_strconcat(username, "@", hostname, NULL);;
}
/* value of REALNAME */
static char *expando_realname(void *server, void *item, int *free_ret)
{
IRC_SERVER_REC *ircserver = server;
return ircserver == NULL ? "" : ircserver->connrec->realname;
}
/* Server tag */
static char *expando_servertag(void *server, void *item, int *free_ret)
{
IRC_SERVER_REC *ircserver = server;
return ircserver == NULL ? "" : ircserver->tag;
}
/* Server ircnet */
static char *expando_ircnet(void *server, void *item, int *free_ret)
{
IRC_SERVER_REC *ircserver = server;
return ircserver == NULL ? "" : ircserver->connrec->ircnet;
}
static void event_privmsg(const char *data, IRC_SERVER_REC *server, const char *nick, const char *addr)
{
char *params, *target, *msg;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &msg);
if (*msg != 1) {
if (!ischannel(*target)) {
g_free_not_null(last_privmsg_from);
last_privmsg_from = g_strdup(nick);
} else {
g_free_not_null(last_public_from);
last_public_from = g_strdup(nick);
}
}
g_free(params);
}
static void cmd_msg(const char *data, IRC_SERVER_REC *server)
{
char *params, *target, *msg;
g_return_if_fail(data != NULL);
params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &msg);
if (*target != '\0' && *msg != '\0' && !ischannel(*target) && isalpha(*target)) {
g_free_not_null(last_sent_msg);
g_free_not_null(last_sent_msg_body);
last_sent_msg = g_strdup(target);
last_sent_msg_body = g_strdup(msg);
}
g_free(params);
}
static void event_join(const char *data, IRC_SERVER_REC *server, const char *nick, const char *address)
{
g_return_if_fail(nick != NULL);
if (g_strcasecmp(nick, server->nick) != 0) {
g_free_not_null(last_join);
last_join = g_strdup(nick);
}
}
void irc_special_vars_init(void)
{
settings_add_str("misc", "STATUS_OPER", "*");
last_privmsg_from = NULL;
last_sent_msg = NULL; last_sent_msg_body = NULL;
last_join = NULL; last_public_from = NULL;
expando_create(",", expando_lastmsg);
expando_create(".", expando_lastmymsg);
expando_create(":", expando_lastjoin);
expando_create(";", expando_lastpublic);
expando_create("A", expando_awaymsg);
expando_create("B", expando_lastmymsg_body);
expando_create("C", expando_channel);
expando_create("H", expando_server_numeric);
expando_create("I", expando_last_invite);
expando_create("M", expando_chanmode);
expando_create("N", expando_nick);
expando_create("O", expando_statusoper);
expando_create("P", expando_chanop);
expando_create("Q", expando_query);
expando_create("R", expando_serverversion);
expando_create("S", expando_servername);
expando_create("T", expando_target);
expando_create("X", expando_userhost);
expando_create("Y", expando_realname);
expando_create("tag", expando_servertag);
expando_create("ircnet", expando_ircnet);
signal_add("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_add("event join", (SIGNAL_FUNC) event_join);
signal_add("command msg", (SIGNAL_FUNC) cmd_msg);
}
void irc_special_vars_deinit(void)
{
g_free_not_null(last_privmsg_from);
g_free_not_null(last_sent_msg); g_free_not_null(last_sent_msg_body);
g_free_not_null(last_join); g_free_not_null(last_public_from);
expando_destroy(",", expando_lastmsg);
expando_destroy(".", expando_lastmymsg);
expando_destroy(":", expando_lastjoin);
expando_destroy(";", expando_lastpublic);
expando_destroy("A", expando_awaymsg);
expando_destroy("B", expando_lastmymsg_body);
expando_destroy("C", expando_channel);
expando_destroy("H", expando_server_numeric);
expando_destroy("I", expando_last_invite);
expando_destroy("M", expando_chanmode);
expando_destroy("N", expando_nick);
expando_destroy("O", expando_statusoper);
expando_destroy("P", expando_chanop);
expando_destroy("Q", expando_query);
expando_destroy("R", expando_serverversion);
expando_destroy("S", expando_servername);
expando_destroy("T", expando_target);
expando_destroy("X", expando_userhost);
expando_destroy("Y", expando_realname);
expando_destroy("tag", expando_servertag);
expando_destroy("ircnet", expando_ircnet);
signal_remove("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_remove("event join", (SIGNAL_FUNC) event_join);
signal_remove("command msg", (SIGNAL_FUNC) cmd_msg);
}

View file

@ -0,0 +1,7 @@
#ifndef __IRC_SPECIAL_VARS_H
#define __IRC_SPECIAL_VARS_H
void irc_special_vars_init(void);
void irc_special_vars_deinit(void);
#endif

440
src/irc/core/irc.c Normal file
View file

@ -0,0 +1,440 @@
/*
irc.c : irssi
Copyright (C) 1999 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 "modules.h"
#include "network.h"
#include "line-split.h"
#include "rawlog.h"
#include "irc.h"
#include "irc-server.h"
#include "channels.h"
#include "server-redirect.h"
char *current_server_event;
static int signal_send_command;
static int signal_default_event;
static int signal_server_event;
static int signal_server_incoming;
static void cmd_send(IRC_SERVER_REC *server, const char *cmd, int send_now, int immediate)
{
char str[513], *ptr;
int len, ret;
server->cmdcount++;
if (send_now)
rawlog_output(server->rawlog, cmd);
/* just check that we don't send any longer commands than 512 bytes.. */
strncpy(str, cmd, 510);
len = strlen(cmd);
str[len++] = 13; str[len++] = 10; str[len] = '\0';
ptr = str;
if (send_now) {
ret = net_transmit(server->handle, str, len);
if (ret == len) {
g_get_current_time(&server->last_cmd);
return;
}
/* we didn't transmit all data, try again a bit later.. */
ptr += ret;
server->cmd_last_split = TRUE;
}
/* add to queue */
ptr = g_strdup(ptr);
if (!immediate)
server->cmdqueue = g_slist_append(server->cmdqueue, ptr);
else if (send_now)
server->cmdqueue = g_slist_prepend(server->cmdqueue, ptr);
else
server->cmdqueue = g_slist_insert(server->cmdqueue, ptr, 1);
}
/* Send command to IRC server */
void irc_send_cmd(IRC_SERVER_REC *server, const char *cmd)
{
int send_now;
g_return_if_fail(cmd != NULL);
if (server == NULL) return;
send_now = !server->cmd_last_split &&
(server->cmdcount < server->max_cmds_at_once ||
server->cmd_queue_speed <= 0);
cmd_send(server, cmd, send_now, FALSE);
}
/* Send command to IRC server */
void irc_send_cmdv(IRC_SERVER_REC *server, const char *cmd, ...)
{
va_list args;
char *str;
va_start(args, cmd);
str = g_strdup_vprintf(cmd, args);
irc_send_cmd(server, str);
g_free(str);
va_end(args);
}
/* Send command to server immediately bypassing all flood protections
and queues. */
void irc_send_cmd_now(IRC_SERVER_REC *server, const char *cmd)
{
g_return_if_fail(cmd != NULL);
if (server == NULL) return;
cmd_send(server, cmd, !server->cmd_last_split, TRUE);
}
static char *split_nicks(const char *cmd, char **pre, char **nicks, char **post, int arg)
{
char *p;
*pre = g_strdup(cmd);
*post = *nicks = NULL;
for (p = *pre; *p != '\0'; p++) {
if (!isspace(*p))
continue;
if (arg == 1) {
/* text after nicks */
*p++ = '\0';
while (isspace(*p)) p++;
*post = p;
break;
}
/* find nicks */
while (isspace(p[1])) p++;
if (--arg == 1) {
*p = '\0';
*nicks = p+1;
}
}
return *pre;
}
void irc_send_cmd_split(IRC_SERVER_REC *server, const char *cmd,
int nickarg, int max_nicks)
{
char *str, *pre, *post, *nicks;
char **nicklist, **tmp;
GString *nickstr;
int count;
g_return_if_fail(server != NULL);
g_return_if_fail(cmd != NULL);
str = split_nicks(cmd, &pre, &nicks, &post, nickarg);
/* split the nicks */
nickstr = g_string_new(NULL);
nicklist = g_strsplit(nicks, ",", -1); count = 0;
tmp = nicklist;
for (;; tmp++) {
if (*tmp != NULL) {
g_string_sprintfa(nickstr, "%s,", *tmp);
if (++count < max_nicks)
continue;
}
count = 0;
g_string_truncate(nickstr, nickstr->len-1);
irc_send_cmdv(server, post == NULL ? "%s %s" : "%s %s %s",
pre, nickstr->str, post);
g_string_truncate(nickstr, 0);
if (*tmp == NULL || tmp[1] == NULL)
break;
}
g_strfreev(nicklist);
g_string_free(nickstr, TRUE);
g_free(str);
}
/* Nick can be in format "servertag/nick" - Update `nick' to
position "nick" and return "servertag" which you need to free */
char *irc_nick_get_server(char **nick)
{
char *ptr, *tag;
ptr = strchr(*nick, '/');
if (ptr == NULL) return NULL;
if (ptr == *nick) {
(*nick)++;
return NULL;
}
tag = g_strndup(*nick, (int) (ptr-*nick));
*nick = ptr+1;
return tag;
}
/* Get next parameter */
char *event_get_param(char **data)
{
char *pos;
g_return_val_if_fail(data != NULL, NULL);
g_return_val_if_fail(*data != NULL, NULL);
if (**data == ':') {
/* last parameter */
pos = *data;
*data += strlen(*data);
return pos+1;
}
pos = *data;
while (**data != '\0' && **data != ' ') (*data)++;
if (**data == ' ') *(*data)++ = '\0';
return pos;
}
/* Get count parameters from data */
char *event_get_params(const char *data, int count, ...)
{
char **str, *tmp, *duprec, *datad;
gboolean rest;
va_list args;
g_return_val_if_fail(data != NULL, NULL);
va_start(args, count);
duprec = datad = g_strdup(data);
rest = count & PARAM_FLAG_GETREST;
count = PARAM_WITHOUT_FLAGS(count);
while (count-- > 0) {
str = (char **) va_arg(args, char **);
if (count == 0 && rest) {
/* put the rest to last parameter */
tmp = *datad == ':' ? datad+1 : datad;
} else {
tmp = event_get_param(&datad);
}
if (str != NULL) *str = tmp;
}
va_end(args);
return duprec;
}
static void irc_server_event(const char *line, IRC_SERVER_REC *server, const char *nick, const char *address)
{
char *event, *args, *callcmd;
GSList *list;
g_return_if_fail(line != NULL);
/* get command.. */
event = g_strconcat("event ", line, NULL);
args = strchr(event+6, ' ');
if (args != NULL) *args++ = '\0'; else args = "";
while (*args == ' ') args++;
list = server_redirect_getqueue((SERVER_REC *) server, event, args);
if (list == NULL)
callcmd = g_strdup(event);
else {
/* event is redirected somewhere else.. */
REDIRECT_REC *rec;
rec = list->data;
callcmd = g_strdup(rec->name);
rawlog_redirect(server->rawlog, callcmd);
server_redirect_remove_next((SERVER_REC *) server, event, list);
}
current_server_event = event+6;
g_strdown(callcmd);
if (!signal_emit(callcmd, 4, args, server, nick, address))
signal_emit_id(signal_default_event, 4, line, server, nick, address);
current_server_event = NULL;
g_free(callcmd);
g_free(event);
}
/* Read line from server */
static int irc_receive_line(SERVER_REC *server, char **str)
{
char tmpbuf[512];
int recvlen, ret;
g_return_val_if_fail(server != NULL, -1);
g_return_val_if_fail(str != NULL, -1);
recvlen = net_receive(server->handle, tmpbuf, sizeof(tmpbuf));
ret = line_split(tmpbuf, recvlen, str, (LINEBUF_REC **) &server->buffer);
if (ret == -1) {
/* connection lost */
server->connection_lost = TRUE;
server_disconnect(server);
}
return ret;
}
static char *irc_parse_prefix(char *line, char **nick, char **address)
{
*nick = *address = NULL;
if (*line != ':')
return line;
*nick = ++line;
while (*line != '\0' && *line != ' ') {
if (*line == '!') {
*line = '\0';
*address = line+1;
}
line++;
}
if (*line == ' ') {
*line++ = '\0';
while (*line == ' ') line++;
}
return line;
}
/* Parse command line sent by server */
static void irc_parse_incoming_line(IRC_SERVER_REC *server, char *line)
{
char *nick, *address;
g_return_if_fail(server != NULL);
g_return_if_fail(line != NULL);
line = irc_parse_prefix(line, &nick, &address);
if (*line != '\0')
signal_emit_id(signal_server_event, 4, line, server, nick, address);
}
/* input function: handle incoming server messages */
static void irc_parse_incoming(SERVER_REC *server)
{
char *str;
g_return_if_fail(server != NULL);
while (irc_receive_line(server, &str) > 0) {
rawlog_input(server->rawlog, str);
signal_emit_id(signal_server_incoming, 2, server, str);
}
}
static void irc_init_server(IRC_SERVER_REC *server)
{
g_return_if_fail(server != NULL);
server->readtag =
g_input_add(server->handle, G_INPUT_READ,
(GInputFunction) irc_parse_incoming, server);
}
static void irc_deinit_server(IRC_SERVER_REC *server)
{
g_return_if_fail(server != NULL);
if (server->readtag > 0)
g_source_remove(server->readtag);
}
#define isoptchan(a) \
(ischannel((a)[0]) || ((a)[0] == '*' && ((a)[1] == '\0' || (a)[1] == ' ')))
static char *irc_cmd_get_func(const char *data, int *count, va_list *vargs)
{
WI_IRC_REC *item;
CHANNEL_REC *channel;
char *ret, *args, *chan, *p;
if ((*count & PARAM_FLAG_OPTCHAN) == 0)
return g_strdup(data);
*count &= ~PARAM_FLAG_OPTCHAN;
item = (WI_IRC_REC *) va_arg(*vargs, WI_IRC_REC *);
channel = irc_item_channel(item);
/* change first argument in data to full channel name. */
p = args = g_strdup(data);
chan = isoptchan(args) ? cmd_get_param(&args) : NULL;
if (chan != NULL && *chan == '!') {
/* whenever trying to send something to !channel,
change it to the real joined !XXXXXchannel */
channel = channel_find(channel->server, chan);
if (channel != NULL) chan = channel->name;
}
if (chan == NULL || strcmp(chan, "*") == 0) {
chan = channel == NULL ? "*" : channel->name;
}
ret = g_strconcat(chan, " ", args, NULL);
g_free(p);
return ret;
}
void irc_irc_init(void)
{
cmd_get_add_func(irc_cmd_get_func);
signal_add("server event", (SIGNAL_FUNC) irc_server_event);
signal_add("server connected", (SIGNAL_FUNC) irc_init_server);
signal_add_first("server disconnected", (SIGNAL_FUNC) irc_deinit_server);
signal_add("server incoming", (SIGNAL_FUNC) irc_parse_incoming_line);
current_server_event = NULL;
signal_send_command = module_get_uniq_id_str("signals", "send command");
signal_default_event = module_get_uniq_id_str("signals", "default event");
signal_server_event = module_get_uniq_id_str("signals", "server event");
signal_server_incoming = module_get_uniq_id_str("signals", "server incoming");
}
void irc_irc_deinit(void)
{
signal_remove("server event", (SIGNAL_FUNC) irc_server_event);
signal_remove("server connected", (SIGNAL_FUNC) irc_init_server);
signal_remove("server disconnected", (SIGNAL_FUNC) irc_deinit_server);
signal_remove("server incoming", (SIGNAL_FUNC) irc_parse_incoming_line);
module_uniq_destroy("IRC");
module_uniq_destroy("IRC SERVER");
}

90
src/irc/core/irc.h Normal file
View file

@ -0,0 +1,90 @@
#ifndef __IRC_H
#define __IRC_H
#include "modules.h"
#include "irc-server.h"
/* From ircd 2.9.5:
none I line with ident
^ I line with OTHER type ident
~ I line, no ident
+ i line with ident
= i line with OTHER type ident
- i line, no ident
*/
#define ishostflag(a) ((a) == '^' || (a) == '~' || (a) == '+' || (a) == '=' || (a) == '-')
#define isnickflag(a) ((a) == '@' || (a) == '+' || (a) == '-' || (a) == '~')
#define ischannel(a) ((a) == '#' || (a) == '&' || (a) == '!' || (a) == '+')
/* values returned by module_category() */
enum {
WI_IRC_CHANNEL,
WI_IRC_QUERY,
WI_IRC_DCC_CHAT
};
/* *MUST* have the same contents as WI_ITEM_REC in same order. */
typedef struct {
int type;
GHashTable *module_data;
IRC_SERVER_REC *server;
char *name;
int new_data;
} WI_IRC_REC;
/* return TRUE if `item' is an IRC type. */
#define irc_item_check(item) \
(item != NULL && module_find_id("IRC", ((WI_IRC_REC *) (item))->type) != -1)
/* return `item' type, or -1 if it's not IRC type. */
#define irc_item_get(item) \
(item == NULL ? -1 : module_find_id("IRC", ((WI_IRC_REC *) (item))->type))
/* Return `item' if it's channel, NULL if it isn't. */
#define irc_item_channel(item) \
(item != NULL && module_find_id("IRC", ((WI_IRC_REC *) (item))->type) == WI_IRC_CHANNEL ? \
(void *) (item) : NULL)
/* Return `item' if it's query, NULL if it isn't. */
#define irc_item_query(item) \
(item != NULL && module_find_id("IRC", ((WI_IRC_REC *) (item))->type) == WI_IRC_QUERY ? \
(void *) (item) : NULL)
/* Return `item' if it's DCC chat, NULL if it isn't. */
#define irc_item_dcc_chat(item) \
(item != NULL && module_find_id("IRC", ((WI_IRC_REC *) (item))->type) == WI_IRC_DCC_CHAT ? \
(void *) (item) : NULL)
extern char *current_server_event; /* current server event being processed */
/* Send command to IRC server */
void irc_send_cmd(IRC_SERVER_REC *server, const char *cmd);
void irc_send_cmdv(IRC_SERVER_REC *server, const char *cmd, ...) G_GNUC_PRINTF (2, 3);;
/* Send command to IRC server, split to multiple commands if necessary so
that command will never have more target nicks than `max_nicks'. Nicks
are separated with commas. (works with /msg, /kick, ...) */
void irc_send_cmd_split(IRC_SERVER_REC *server, const char *cmd,
int nickarg, int max_nicks);
/* Send command to server immediately bypassing all flood protections
and queues. */
void irc_send_cmd_now(IRC_SERVER_REC *server, const char *cmd);
/* Nick can be in format "servertag/nick" - Update `nick' to
position "nick" and return "servertag" which you need to free */
char *irc_nick_get_server(char **nick);
#include "commands.h" /* contains the generic PARAM_FLAG_xxx defines */
/* IRC specific: optional channel in first argument */
#define PARAM_FLAG_OPTCHAN 0x10000000
/* Get count parameters from data */
char *event_get_param(char **data);
char *event_get_params(const char *data, int count, ...);
void irc_irc_init(void);
void irc_irc_deinit(void);
#endif

116
src/irc/core/ircnet-setup.c Normal file
View file

@ -0,0 +1,116 @@
/*
ircnet-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 "network.h"
#include "signals.h"
#include "lib-config/iconfig.h"
#include "settings.h"
#include "irc-server.h"
#include "ircnet-setup.h"
GSList *ircnets; /* list of available ircnets */
/* Find the irc network by name */
IRCNET_REC *ircnet_find(const char *name)
{
GSList *tmp;
g_return_val_if_fail(name != NULL, NULL);
for (tmp = ircnets; tmp != NULL; tmp = tmp->next) {
IRCNET_REC *rec = tmp->data;
if (g_strcasecmp(rec->name, name) == 0)
return rec;
}
return NULL;
}
static void ircnet_destroy(IRCNET_REC *rec)
{
ircnets = g_slist_remove(ircnets, rec);
g_free(rec->name);
if (rec->nick != NULL) g_free(rec->nick);
if (rec->username != NULL) g_free(rec->username);
if (rec->realname != NULL) g_free(rec->realname);
g_free(rec);
}
static IRCNET_REC *ircnet_add(CONFIG_NODE *node)
{
IRCNET_REC *rec;
char *name, *nick, *username, *realname;
g_return_val_if_fail(node != NULL, NULL);
name = config_node_get_str(node, "name", NULL);
if (name == NULL) return NULL;
nick = config_node_get_str(node, "nick", NULL);
username = config_node_get_str(node, "username", NULL);
realname = config_node_get_str(node, "realname", NULL);
rec = g_new0(IRCNET_REC, 1);
rec->max_kicks = config_node_get_int(node, "max_kicks", 0);
rec->max_msgs = config_node_get_int(node, "max_msgs", 0);
rec->max_modes = config_node_get_int(node, "max_modes", 0);
rec->max_whois = config_node_get_int(node, "max_whois", 0);
rec->name = g_strdup(name);
rec->nick = (nick == NULL || *nick == '\0') ? NULL : g_strdup(nick);
rec->username = (username == NULL || *username == '\0') ? NULL : g_strdup(username);
rec->realname = (realname == NULL || *realname == '\0') ? NULL : g_strdup(realname);
ircnets = g_slist_append(ircnets, rec);
return rec;
}
static void read_ircnets(void)
{
CONFIG_NODE *node;
GSList *tmp;
while (ircnets != NULL)
ircnet_destroy(ircnets->data);
/* read ircnets */
node = iconfig_node_traverse("ircnets", FALSE);
if (node != NULL) {
for (tmp = node->value; tmp != NULL; tmp = tmp->next)
ircnet_add(tmp->data);
}
}
void ircnets_setup_init(void)
{
signal_add("setup reread", (SIGNAL_FUNC) read_ircnets);
}
void ircnets_setup_deinit(void)
{
while (ircnets != NULL)
ircnet_destroy(ircnets->data);
signal_remove("setup reread", (SIGNAL_FUNC) read_ircnets);
}

View file

@ -0,0 +1,23 @@
#ifndef __IRCNET_SETUP_H
#define __IRCNET_SETUP_H
typedef struct {
char *name;
char *nick;
char *username;
char *realname;
/* max. number of kicks/msgs/mode/whois per command */
int max_kicks, max_msgs, max_modes, max_whois;
} IRCNET_REC;
extern GSList *ircnets; /* list of available ircnets */
/* Find the irc network by name */
IRCNET_REC *ircnet_find(const char *name);
void ircnets_setup_init(void);
void ircnets_setup_deinit(void);
#endif

178
src/irc/core/lag.c Normal file
View file

@ -0,0 +1,178 @@
/*
lag.c : irssi
Copyright (C) 1999 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 "settings.h"
#include "irc.h"
#include "irc-server.h"
typedef struct {
IRC_SERVER_REC *server;
GTimeVal time;
} LAG_REC;
static gint timeout_tag;
static GSList *lags;
static LAG_REC *lag_find(IRC_SERVER_REC *server)
{
GSList *tmp;
for (tmp = lags; tmp != NULL; tmp = tmp->next) {
LAG_REC *lag = tmp->data;
if (lag->server == server)
return lag;
}
return NULL;
}
static void lag_free(LAG_REC *rec)
{
lags = g_slist_remove(lags, rec);
g_free(rec);
}
static void lag_get(IRC_SERVER_REC *server)
{
LAG_REC *lag;
g_return_if_fail(server != NULL);
lag = g_new0(LAG_REC, 1);
lags = g_slist_append(lags, lag);
lag->server = server;
g_get_current_time(&lag->time);
if (server->lag_sent == 0)
server->lag_sent = time(NULL);
server->lag_last_check = time(NULL);
/* NOTE: this will fail if there's any nick changes in buffer -
that's why this function should be called only when the buffer
is empty */
irc_send_cmdv(server, "NOTICE %s :\001IRSSILAG %ld %ld\001",
server->nick, lag->time.tv_sec, lag->time.tv_usec);
}
/* we use "ctcp reply" signal here, because "ctcp reply irssilag" can be
ignored with /IGNORE * CTCPS */
static void sig_irssilag(const char *data, IRC_SERVER_REC *server, const char *nick, const char *addr, const char *target)
{
GTimeVal now, sent;
LAG_REC *lag;
g_return_if_fail(data != NULL);
if (strncmp(data, "IRSSILAG ", 9) != 0)
return;
data += 9;
lag = lag_find(server);
if (lag == NULL) {
/* not expecting lag reply.. */
return;
}
if (g_strcasecmp(nick, server->nick) != 0) {
/* we didn't sent this - not a lag notice */
return;
}
/* OK, it is a lag notice. */
server->lag_sent = 0;
if (sscanf(data, "%ld %ld", &sent.tv_sec, &sent.tv_usec) == 2) {
g_get_current_time(&now);
server->lag = (int) get_timeval_diff(&now, &sent);
signal_emit("server lag", 1, server);
}
lag_free(lag);
}
static int sig_check_lag(void)
{
GSList *tmp, *next;
time_t now;
int lag_check_time, max_lag;
lag_check_time = settings_get_int("lag_check_time");
max_lag = settings_get_int("lag_max_before_disconnect");
if (lag_check_time <= 0)
return 1;
now = time(NULL);
for (tmp = servers; tmp != NULL; tmp = next) {
IRC_SERVER_REC *rec = tmp->data;
next = tmp->next;
if (!irc_server_check(rec))
continue;
if (rec->lag_sent != 0) {
/* waiting for lag reply */
if (max_lag > 1 && now-rec->lag_sent > max_lag) {
/* too much lag, disconnect */
signal_emit("server lag disconnect", 1, rec);
rec->connection_lost = TRUE;
server_disconnect((SERVER_REC *) rec);
}
}
else if (rec->lag_last_check+lag_check_time < now &&
rec->cmdcount == 0 && rec->connected) {
/* no commands in buffer - get the lag */
lag_get(rec);
}
}
return 1;
}
static void sig_empty(void)
{
/* don't print the "CTCP IRSSILAG reply .." text */
}
void lag_init(void)
{
settings_add_int("misc", "lag_check_time", 30);
settings_add_int("misc", "lag_max_before_disconnect", 300);
lags = NULL;
timeout_tag = g_timeout_add(1000, (GSourceFunc) sig_check_lag, NULL);
signal_add("ctcp reply", (SIGNAL_FUNC) sig_irssilag);
signal_add("ctcp reply irssilag", (SIGNAL_FUNC) sig_empty);
}
void lag_deinit(void)
{
g_source_remove(timeout_tag);
while (lags != NULL)
lag_free(lags->data);
signal_remove("ctcp reply", (SIGNAL_FUNC) sig_irssilag);
signal_remove("ctcp reply irssilag", (SIGNAL_FUNC) sig_empty);
}

7
src/irc/core/lag.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef __LAG_H
#define __LAG_H
void lag_init(void);
void lag_deinit(void);
#endif

170
src/irc/core/masks.c Normal file
View file

@ -0,0 +1,170 @@
/*
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 "irc.h"
#include "masks.h"
static int check_mask(const char *mask, int *wildcards)
{
while (*mask != '\0') {
if (*mask == '!')
return TRUE;
if (*mask == '?' || *mask == '*')
*wildcards = TRUE;
mask++;
}
return FALSE;
}
int irc_mask_match(const char *mask, const char *nick, const char *user, const char *host)
{
char *str;
int ret, wildcards;
if (!check_mask(mask, &wildcards)) {
return wildcards ?
match_wildcards(mask, nick) :
g_strcasecmp(mask, nick) == 0;
}
str = g_strdup_printf("%s!%s@%s", nick, user, host);
ret = match_wildcards(mask, str);
g_free(str);
return ret;
}
int irc_mask_match_address(const char *mask, const char *nick, const char *address)
{
char *str;
int ret, wildcards;
g_return_val_if_fail(address != NULL, FALSE);
if (!check_mask(mask, &wildcards)) {
return wildcards ?
match_wildcards(mask, nick) :
g_strcasecmp(mask, nick) == 0;
}
str = g_strdup_printf("%s!%s", nick, address);
ret = match_wildcards(mask, str);
g_free(str);
return ret;
}
int irc_masks_match(const char *masks, const char *nick, const char *address)
{
char **list, **tmp, *mask;
g_return_val_if_fail(masks != NULL, 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)
break;
if (match_wildcards(*tmp, mask))
break;
}
g_strfreev(list);
g_free(mask);
return *tmp != NULL;
}
static char *get_domain_mask(char *host)
{
char *ptr;
if (strchr(host, '.') == NULL) {
/* no dots - toplevel domain or IPv6 address */
ptr = strrchr(host, ':');
if (ptr != NULL) {
/* IPv6 address, ban the last 64k addresses */
if (ptr[1] != '\0') strcpy(ptr+1, "*");
}
return host;
}
if (is_ipv4_address(host)) {
/* it's an IP address, change last digit to * */
ptr = strrchr(host, '.');
if (ptr != NULL && isdigit(ptr[1]))
strcpy(ptr+1, "*");
} else {
/* if more than one dot, skip the first
(dyn123.blah.net -> *.blah.net) */
ptr = strchr(host, '.');
if (ptr != NULL && strchr(ptr+1, '.') != NULL) {
host = ptr-1;
host[0] = '*';
}
}
return host;
}
char *irc_get_mask(const char *nick, const char *address, int flags)
{
char *ret, *user, *host;
/* strip -, ^ or ~ from start.. */
user = g_strconcat("*", ishostflag(*address) ? address+1 : address, NULL);
/* split user and host */
host = strchr(user, '@');
if (host == NULL) {
g_free(user);
return NULL;
}
*host++ = '\0';
switch (flags & (IRC_MASK_HOST|IRC_MASK_DOMAIN)) {
case IRC_MASK_HOST:
/* we already have the host */
break;
case IRC_MASK_DOMAIN:
/* domain - *.blah.org */
host = get_domain_mask(host);
break;
default:
/* no domain/host */
host = "*";
break;
}
ret = g_strdup_printf("%s!%s@%s",
(flags & IRC_MASK_NICK) ? nick : "*",
(flags & IRC_MASK_USER) ? user : "*",
host);
g_free(user);
return ret;
}

15
src/irc/core/masks.h Normal file
View file

@ -0,0 +1,15 @@
#ifndef __MASKS_H
#define __MASKS_H
#define IRC_MASK_NICK 0x01
#define IRC_MASK_USER 0x02
#define IRC_MASK_HOST 0x04
#define IRC_MASK_DOMAIN 0x08
int irc_mask_match(const char *mask, const char *nick, const char *user, const char *host);
int irc_mask_match_address(const char *mask, const char *nick, const char *address);
int irc_masks_match(const char *masks, const char *nick, const char *address);
char *irc_get_mask(const char *nick, const char *address, int flags);
#endif

254
src/irc/core/massjoin.c Normal file
View file

@ -0,0 +1,254 @@
/*
massjoin.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 "common-setup.h"
#include "channels.h"
#include "irc.h"
#include "nicklist.h"
#include "irc-server.h"
static int massjoin_tag;
/* Massjoin support - really useful when trying to do things (like op/deop)
to people after netjoins. It sends
"massjoin #channel nick!user@host nick2!user@host ..." signals */
static void event_join(const char *data, IRC_SERVER_REC *server, const char *nick, const char *address)
{
char *params, *channel, *ptr;
CHANNEL_REC *chanrec;
NICK_REC *nickrec;
GSList *nicks, *tmp;
g_return_if_fail(data != NULL);
if (g_strcasecmp(nick, server->nick) == 0) {
/* You joined, no need to do anything here */
return;
}
params = event_get_params(data, 1, &channel);
ptr = strchr(channel, 7); /* ^G does something weird.. */
if (ptr != NULL) *ptr = '\0';
/* find channel */
chanrec = channel_find(server, channel);
g_free(params);
if (chanrec == NULL) return;
/* add user to nicklist */
nickrec = nicklist_insert(chanrec, nick, FALSE, FALSE, TRUE);
nickrec->host = g_strdup(address);
if (chanrec->massjoins == 0) {
/* no nicks waiting in massjoin queue */
chanrec->massjoin_start = time(NULL);
chanrec->last_massjoins = 0;
}
/* Check if user is already in some other channel,
get the realname from there */
nicks = nicklist_get_same(server, nick);
for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
NICK_REC *rec = tmp->next->data;
if (rec->realname != NULL) {
nickrec->last_check = rec->last_check;
nickrec->realname = g_strdup(rec->realname);
nickrec->gone = rec->gone;
}
}
g_slist_free(nicks);
chanrec->massjoins++;
}
static void event_part(const char *data, IRC_SERVER_REC *server, const char *nick, const char *addr)
{
char *params, *channel, *reason;
CHANNEL_REC *chanrec;
NICK_REC *nickrec;
g_return_if_fail(data != NULL);
if (g_strcasecmp(nick, server->nick) == 0) {
/* you left channel, no need to do anything here */
return;
}
params = event_get_params(data, 2, &channel, &reason);
/* find channel */
chanrec = channel_find(server, channel);
if (chanrec == NULL) {
g_free(params);
return;
}
/* remove user from nicklist */
nickrec = nicklist_find(chanrec, nick);
if (nickrec != NULL) {
if (nickrec->send_massjoin) {
/* quick join/part after which it's useless to send
nick in massjoin */
chanrec->massjoins--;
}
nicklist_remove(chanrec, nickrec);
}
g_free(params);
}
static void event_quit(const char *data, IRC_SERVER_REC *server, const char *nick)
{
CHANNEL_REC *channel;
NICK_REC *nickrec;
GSList *nicks, *tmp;
g_return_if_fail(data != NULL);
if (g_strcasecmp(nick, server->nick) == 0) {
/* you quit, don't do anything here */
return;
}
/* Remove nick from all channels */
nicks = nicklist_get_same(server, nick);
for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
channel = tmp->data;
nickrec = tmp->next->data;
if (nickrec->send_massjoin) {
/* quick join/quit after which it's useless to
send nick in massjoin */
channel->massjoins--;
}
nicklist_remove(channel, nickrec);
}
g_slist_free(nicks);
}
static void event_kick(const char *data, IRC_SERVER_REC *server)
{
char *params, *channel, *nick, *reason;
CHANNEL_REC *chanrec;
NICK_REC *nickrec;
g_return_if_fail(data != NULL);
params = event_get_params(data, 3, &channel, &nick, &reason);
if (g_strcasecmp(nick, server->nick) == 0) {
/* you were kicked, no need to do anything */
g_free(params);
return;
}
/* Remove user from nicklist */
chanrec = channel_find(server, channel);
nickrec = chanrec == NULL ? NULL : nicklist_find(chanrec, nick);
if (chanrec != NULL && nickrec != NULL) {
if (nickrec->send_massjoin) {
/* quick join/kick after which it's useless to
send nick in massjoin */
chanrec->massjoins--;
}
nicklist_remove(chanrec, nickrec);
}
g_free(params);
}
static void massjoin_send_hash(gpointer key, NICK_REC *nick, GSList **list)
{
if (nick->send_massjoin) {
nick->send_massjoin = FALSE;
*list = g_slist_append(*list, nick);
}
}
/* Send channel's massjoin list signal */
static void massjoin_send(CHANNEL_REC *channel)
{
GSList *list;
list = NULL;
g_hash_table_foreach(channel->nicks, (GHFunc) massjoin_send_hash, &list);
channel->massjoins = 0;
signal_emit("massjoin", 2, channel, list);
g_slist_free(list);
}
static void server_check_massjoins(IRC_SERVER_REC *server, time_t max)
{
GSList *tmp;
/* Scan all channels through for massjoins */
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *rec = tmp->data;
if (rec->massjoins <= 0)
continue;
if (rec->massjoin_start < max || /* We've waited long enough */
rec->massjoins-5 < rec->last_massjoins) { /* Less than 5 joins since last check */
/* send them */
massjoin_send(rec);
} else {
/* Wait for some more.. */
rec->last_massjoins = rec->massjoins;
}
}
}
static int sig_massjoin_timeout(void)
{
GSList *tmp;
time_t max;
max = time(NULL)-MAX_MASSJOIN_WAIT;
for (tmp = servers; tmp != NULL; tmp = tmp->next)
server_check_massjoins(tmp->data, max);
return 1;
}
void massjoin_init(void)
{
massjoin_tag = g_timeout_add(1000, (GSourceFunc) sig_massjoin_timeout, NULL);
signal_add("event join", (SIGNAL_FUNC) event_join);
signal_add("event part", (SIGNAL_FUNC) event_part);
signal_add("event kick", (SIGNAL_FUNC) event_kick);
signal_add("event quit", (SIGNAL_FUNC) event_quit);
}
void massjoin_deinit(void)
{
g_source_remove(massjoin_tag);
signal_remove("event join", (SIGNAL_FUNC) event_join);
signal_remove("event part", (SIGNAL_FUNC) event_part);
signal_remove("event kick", (SIGNAL_FUNC) event_kick);
signal_remove("event quit", (SIGNAL_FUNC) event_quit);
}

7
src/irc/core/massjoin.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef __MASSJOIN_H
#define __MASSJOIN_H
void massjoin_init(void);
void massjoin_deinit(void);
#endif

234
src/irc/core/mode-lists.c Normal file
View file

@ -0,0 +1,234 @@
/*
mode-lists.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 "misc.h"
#include "signals.h"
#include "irc.h"
#include "mode-lists.h"
static void ban_free(GSList **list, BAN_REC *rec)
{
g_return_if_fail(list != NULL);
g_return_if_fail(rec != NULL);
g_free(rec->ban);
g_free_not_null(rec->setby);
g_free(rec);
*list = g_slist_remove(*list, rec);
}
void banlist_free(GSList *banlist)
{
while (banlist != NULL)
ban_free(&banlist, banlist->data);
}
BAN_REC *banlist_add(CHANNEL_REC *channel, const char *ban, const char *nick, time_t time)
{
BAN_REC *rec;
g_return_val_if_fail(channel != NULL, NULL);
g_return_val_if_fail(ban != NULL, NULL);
g_return_val_if_fail(nick != NULL, NULL);
rec = g_new(BAN_REC, 1);
rec->ban = g_strdup(ban);
rec->setby = g_strdup(nick);
rec->time = time;
channel->banlist = g_slist_append(channel->banlist, rec);
signal_emit("ban new", 1, rec);
return rec;
}
void banlist_remove(CHANNEL_REC *channel, const char *ban)
{
GSList *tmp;
g_return_if_fail(ban != NULL);
for (tmp = channel->banlist; tmp != NULL; tmp = tmp->next)
{
BAN_REC *rec = tmp->data;
if (g_strcasecmp(rec->ban, ban) == 0)
{
signal_emit("ban remove", 1, rec);
ban_free(&channel->banlist, rec);
break;
}
}
}
BAN_REC *banlist_exception_add(CHANNEL_REC *channel, const char *ban, const char *nick, time_t time)
{
BAN_REC *rec;
g_return_val_if_fail(channel != NULL, NULL);
g_return_val_if_fail(ban != NULL, NULL);
g_return_val_if_fail(nick != NULL, NULL);
rec = g_new(BAN_REC, 1);
rec->ban = g_strdup(ban);
rec->setby = g_strdup(nick);
rec->time = time;
channel->ebanlist = g_slist_append(channel->ebanlist, rec);
signal_emit("ban exception new", 1, rec);
return rec;
}
void banlist_exception_remove(CHANNEL_REC *channel, const char *ban)
{
GSList *tmp;
g_return_if_fail(ban != NULL);
for (tmp = channel->ebanlist; tmp != NULL; tmp = tmp->next)
{
BAN_REC *rec = tmp->data;
if (g_strcasecmp(rec->ban, ban) == 0)
{
signal_emit("ban exception remove", 1, rec);
ban_free(&channel->ebanlist, rec);
break;
}
}
}
static void invitelist_free(CHANNEL_REC *channel)
{
g_return_if_fail(channel != NULL);
g_slist_foreach(channel->invitelist, (GFunc) g_free, NULL);
g_slist_free(channel->invitelist);
}
void invitelist_add(CHANNEL_REC *channel, const char *mask)
{
g_return_if_fail(channel != NULL);
g_return_if_fail(mask != NULL);
channel->invitelist = g_slist_append(channel->invitelist, g_strdup(mask));
signal_emit("invitelist new", 2, channel, mask);
}
void invitelist_remove(CHANNEL_REC *channel, const char *mask)
{
GSList *tmp;
g_return_if_fail(channel != NULL);
g_return_if_fail(mask != NULL);
tmp = gslist_find_icase_string(channel->invitelist, mask);
if (tmp == NULL) return;
signal_emit("invitelist remove", 2, channel, tmp->data);
g_free(tmp->data);
channel->invitelist = g_slist_remove(channel->invitelist, tmp->data);
}
static void channel_destroyed(CHANNEL_REC *channel)
{
g_return_if_fail(channel != NULL);
banlist_free(channel->banlist);
banlist_free(channel->ebanlist);
invitelist_free(channel);
}
static void event_banlist(const char *data, IRC_SERVER_REC *server)
{
CHANNEL_REC *chanrec;
char *params, *channel, *ban, *setby, *tims;
time_t tim;
g_return_if_fail(data != NULL);
params = event_get_params(data, 5, NULL, &channel, &ban, &setby, &tims);
chanrec = channel_find(server, channel);
if (chanrec != NULL) {
if (sscanf(tims, "%ld", (long *) &tim) != 1)
tim = time(NULL);
banlist_add(chanrec, ban, setby, tim);
}
g_free(params);
}
static void event_ebanlist(const char *data, IRC_SERVER_REC *server)
{
CHANNEL_REC *chanrec;
char *params, *channel, *ban, *setby, *tims;
time_t tim;
g_return_if_fail(data != NULL);
params = event_get_params(data, 5, NULL, &channel, &ban, &setby, &tims);
chanrec = channel_find(server, channel);
if (chanrec != NULL) {
if (sscanf(tims, "%ld", (long *) &tim) != 1)
tim = time(NULL);
banlist_exception_add(chanrec, ban, setby, tim);
}
g_free(params);
}
static void event_invite_list(const char *data, IRC_SERVER_REC *server)
{
CHANNEL_REC *chanrec;
char *params, *channel, *invite;
g_return_if_fail(data != NULL);
params = event_get_params(data, 3, NULL, &channel, &invite);
chanrec = channel_find(server, channel);
if (chanrec != NULL)
invitelist_add(chanrec, invite);
g_free(params);
}
void mode_lists_init(void)
{
signal_add("channel destroyed", (SIGNAL_FUNC) channel_destroyed);
signal_add("chanquery ban", (SIGNAL_FUNC) event_banlist);
signal_add("chanquery eban", (SIGNAL_FUNC) event_ebanlist);
signal_add("chanquery ilist", (SIGNAL_FUNC) event_invite_list);
}
void mode_lists_deinit(void)
{
signal_remove("channel destroyed", (SIGNAL_FUNC) channel_destroyed);
signal_remove("chanquery ban", (SIGNAL_FUNC) event_banlist);
signal_remove("chanquery eban", (SIGNAL_FUNC) event_ebanlist);
signal_remove("chanquery ilist", (SIGNAL_FUNC) event_invite_list);
}

24
src/irc/core/mode-lists.h Normal file
View file

@ -0,0 +1,24 @@
#ifndef __MODE_LISTS_H
#define __MODE_LISTS_H
#include "channels.h"
typedef struct {
char *ban;
char *setby;
time_t time;
} BAN_REC;
BAN_REC *banlist_add(CHANNEL_REC *channel, const char *ban, const char *nick, time_t time);
void banlist_remove(CHANNEL_REC *channel, const char *ban);
BAN_REC *banlist_exception_add(CHANNEL_REC *channel, const char *ban, const char *nick, time_t time);
void banlist_exception_remove(CHANNEL_REC *channel, const char *ban);
void invitelist_add(CHANNEL_REC *channel, const char *mask);
void invitelist_remove(CHANNEL_REC *channel, const char *mask);
void mode_lists_init(void);
void mode_lists_deinit(void);
#endif

451
src/irc/core/modes.c Normal file
View file

@ -0,0 +1,451 @@
/*
modes.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 "signals.h"
#include "irc.h"
#include "mode-lists.h"
#include "nicklist.h"
/* Change nick's mode in channel */
static void nick_mode_change(CHANNEL_REC *channel, const char *nick, const char mode, gboolean set)
{
NICK_REC *nickrec;
g_return_if_fail(channel != NULL);
g_return_if_fail(nick != NULL);
nickrec = nicklist_find(channel, nick);
if (nickrec == NULL) return; /* No /names list got yet */
if (mode == '@') nickrec->op = set;
if (mode == '+') nickrec->voice = set;
signal_emit("nick mode changed", 2, channel, nickrec);
}
/* Parse channel mode string */
void parse_channel_modes(CHANNEL_REC *channel, const char *setby, const char *mode)
{
char *dup, *modestr, *ptr, *curmode, type;
g_return_if_fail(channel != NULL);
g_return_if_fail(setby != NULL);
g_return_if_fail(modestr != NULL);
type = '+';
dup = modestr = g_strdup(mode);
curmode = cmd_get_param(&modestr);
while (*curmode != '\0') {
switch (*curmode) {
case '+':
case '-':
type = *curmode;
break;
case 'b':
ptr = cmd_get_param(&modestr);
if (*ptr == '\0') break;
if (type == '+')
banlist_add(channel, ptr, setby, time(NULL));
else
banlist_remove(channel, ptr);
break;
case 'e':
ptr = cmd_get_param(&modestr);
if (*ptr == '\0') break;
if (type == '+')
banlist_exception_add(channel, ptr, setby, time(NULL));
else
banlist_exception_remove(channel, ptr);
break;
case 'I':
ptr = cmd_get_param(&modestr);
if (*ptr == '\0') break;
if (type == '+')
invitelist_add(channel, ptr);
else
invitelist_remove(channel, ptr);
break;
case 'v':
ptr = cmd_get_param(&modestr);
if (*ptr != '\0')
nick_mode_change(channel, ptr, '+', type == '+');
break;
case 'o':
ptr = cmd_get_param(&modestr);
if (*ptr == '\0') break;
if (strcmp(channel->server->nick, ptr) == 0)
channel->chanop = type == '+' ? TRUE : FALSE;
nick_mode_change(channel, ptr, '@', type == '+');
break;
case 'l':
if (type == '-')
channel->limit = 0;
else {
ptr = cmd_get_param(&modestr);
sscanf(ptr, "%d", &channel->limit);
}
signal_emit("channel mode changed", 1, channel);
break;
case 'k':
ptr = cmd_get_param(&modestr);
if (*ptr != '\0' || type == '-') {
g_free_and_null(channel->key);
if (type == '+') {
channel->key = g_strdup(ptr);
channel->mode_key = TRUE;
}
}
signal_emit("channel mode changed", 1, channel);
break;
default:
switch (*curmode) {
case 'i':
channel->mode_invite = type == '+';
break;
case 'm':
channel->mode_moderate = type == '+';
break;
case 's':
channel->mode_secret = type == '+';
break;
case 'p':
channel->mode_private = type == '+';
break;
case 'n':
channel->mode_nomsgs = type == '+';
break;
case 't':
channel->mode_optopic = type == '+';
break;
case 'a':
channel->mode_anonymous = type == '+';
break;
case 'r':
channel->mode_reop = type == '+';
break;
}
signal_emit("channel mode changed", 1, channel);
break;
}
curmode++;
}
g_free(dup);
if (!channel->mode_key && channel->key != NULL) {
/* join was used with key but there's no key set
in channel modes.. */
g_free(channel->key);
channel->key = NULL;
}
}
static int compare_char(const void *p1, const void *p2)
{
const char *c1 = p1, *c2 = p2;
return *c1 < *c2 ? -1 :
(*c1 > *c2 ? 1 : 0);
}
/* add `mode' to `old' - return newly allocated mode. */
char *modes_join(const char *old, const char *mode)
{
GString *newmode;
char type, *p;
g_return_val_if_fail(mode != NULL, NULL);
type = '+';
newmode = g_string_new(old);
while (*mode != '\0' && *mode != ' ') {
if (*mode == '+' || *mode == '-') {
type = *mode;
} else {
p = strchr(newmode->str, *mode);
if (type == '+' && p == NULL)
g_string_append_c(newmode, *mode);
else if (type == '-' && p != NULL)
g_string_erase(newmode, (int) (p-newmode->str), 1);
}
mode++;
}
qsort(newmode->str, sizeof(char), newmode->len, compare_char);
p = newmode->str;
g_string_free(newmode, FALSE);
return p;
}
/* Parse user mode string */
static void parse_user_mode(IRC_SERVER_REC *server, const char *modestr)
{
char *newmode, *oldmode;
g_return_if_fail(server != NULL);
g_return_if_fail(modestr != NULL);
newmode = modes_join(server->usermode, modestr);
oldmode = server->usermode;
server->usermode = newmode;
server->server_operator = (strchr(newmode, 'o') != NULL);
signal_emit("user mode changed", 2, server, oldmode);
g_free_not_null(oldmode);
}
static void event_user_mode(const char *data, IRC_SERVER_REC *server)
{
char *params, *nick, *mode;
g_return_if_fail(data != NULL);
params = event_get_params(data, 3, NULL, &nick, &mode);
parse_user_mode(server, mode);
g_free(params);
}
static void event_mode(const char *data, IRC_SERVER_REC *server, const char *nick)
{
CHANNEL_REC *chanrec;
char *params, *channel, *mode;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &channel, &mode);
if (!ischannel(*channel)) {
/* user mode change */
parse_user_mode(server, mode);
} else {
/* channel mode change */
chanrec = channel_find(server, channel);
if (chanrec != NULL)
parse_channel_modes(chanrec, nick, mode);
}
g_free(params);
}
static void event_away(const char *data, IRC_SERVER_REC *server)
{
g_return_if_fail(server != NULL);
server->usermode_away = TRUE;
signal_emit("away mode changed", 1, server);
}
static void event_unaway(const char *data, IRC_SERVER_REC *server)
{
g_return_if_fail(server != NULL);
server->usermode_away = FALSE;
g_free_and_null(server->away_reason);
signal_emit("away mode changed", 1, server);
}
void channel_set_singlemode(IRC_SERVER_REC *server, const char *channel, const char *nicks, const char *mode)
{
GString *str;
int num, modepos;
char **nick, **nicklist;
g_return_if_fail(server != NULL);
g_return_if_fail(channel != NULL);
g_return_if_fail(nicks != NULL);
g_return_if_fail(mode != NULL);
if (*nicks == '\0') return;
num = modepos = 0;
str = g_string_new(NULL);
nicklist = g_strsplit(nicks, " ", -1);
for (nick = nicklist; *nick != NULL; nick++) {
if (num == 0)
{
g_string_sprintf(str, "MODE %s %s", channel, mode);
modepos = str->len;
} else {
/* insert the mode string */
g_string_insert(str, modepos, mode);
}
g_string_sprintfa(str, " %s", *nick);
if (++num == server->connrec->max_modes) {
/* max. modes / command reached, send to server */
irc_send_cmd(server, str->str);
num = 0;
}
}
if (num > 0) irc_send_cmd(server, str->str);
g_strfreev(nicklist);
g_string_free(str, TRUE);
}
#define MODE_HAS_ARG(c) ((c) == 'b' || (c) == 'e' || (c) == 'I' || \
(c) == 'v' || (c) == 'o' || (c) == 'l' || (c) == 'k')
void channel_set_mode(IRC_SERVER_REC *server, const char *channel, const char *mode)
{
char *modestr, *curmode, type, *orig;
GString *tmode, *targs;
int count;
g_return_if_fail(server != NULL);
g_return_if_fail(channel != NULL);
g_return_if_fail(modestr != NULL);
tmode = g_string_new(NULL);
targs = g_string_new(NULL);
type = '+'; count = 0;
orig = modestr = g_strdup(mode);
curmode = cmd_get_param(&modestr);
for (; *curmode != '\0'; curmode++) {
if (*curmode == '+' || *curmode == '-') {
type = *curmode;
continue;
}
if (count == server->connrec->max_modes && MODE_HAS_ARG(*curmode)) {
irc_send_cmdv(server, "MODE %s %s%s", channel, tmode->str, targs->str);
count = 0;
g_string_truncate(tmode, 0);
g_string_truncate(targs, 0);
}
g_string_append_c(tmode, *curmode);
if (MODE_HAS_ARG(*curmode)) {
char *arg;
count++;
arg = cmd_get_param(&modestr);
if (*arg != '\0') g_string_sprintfa(targs, " %s", arg);
}
}
if (tmode->len > 0)
irc_send_cmdv(server, "MODE %s %s%s", channel, tmode->str, targs->str);
g_string_free(tmode, TRUE);
g_string_free(targs, TRUE);
g_free(orig);
}
static void cmd_op(gchar *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
if (!irc_item_channel(item)) return;
channel_set_singlemode(server, item->name, data, "+o");
}
static void cmd_deop(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
if (!irc_item_channel(item)) return;
channel_set_singlemode(server, item->name, data, "-o");
}
static void cmd_voice(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
if (!irc_item_channel(item)) return;
channel_set_singlemode(server, item->name, data, "+v");
}
static void cmd_devoice(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
if (!irc_item_channel(item)) return;
channel_set_singlemode(server, item->name, data, "-v");
}
static void cmd_mode(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
char *params, *target, *mode;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected || !irc_server_check(server))
cmd_return_error(CMDERR_NOT_CONNECTED);
params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &mode);
if (strcmp(target, "*") == 0) {
if (!irc_item_channel(item))
cmd_return_error(CMDERR_NOT_JOINED);
target = item->name;
}
if (*target == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
if (ischannel(*target))
channel_set_mode(server, target, mode);
else
irc_send_cmdv(server, "MODE %s %s", target, mode);
g_free(params);
}
void modes_init(void)
{
signal_add("event 221", (SIGNAL_FUNC) event_user_mode);
signal_add("event 305", (SIGNAL_FUNC) event_unaway);
signal_add("event 306", (SIGNAL_FUNC) event_away);
signal_add("event mode", (SIGNAL_FUNC) event_mode);
command_bind("op", NULL, (SIGNAL_FUNC) cmd_op);
command_bind("deop", NULL, (SIGNAL_FUNC) cmd_deop);
command_bind("voice", NULL, (SIGNAL_FUNC) cmd_voice);
command_bind("devoice", NULL, (SIGNAL_FUNC) cmd_devoice);
command_bind("mode", NULL, (SIGNAL_FUNC) cmd_mode);
}
void modes_deinit(void)
{
signal_remove("event 221", (SIGNAL_FUNC) event_user_mode);
signal_remove("event 305", (SIGNAL_FUNC) event_unaway);
signal_remove("event 306", (SIGNAL_FUNC) event_away);
signal_remove("event mode", (SIGNAL_FUNC) event_mode);
command_unbind("op", (SIGNAL_FUNC) cmd_op);
command_unbind("deop", (SIGNAL_FUNC) cmd_deop);
command_unbind("voice", (SIGNAL_FUNC) cmd_voice);
command_unbind("devoice", (SIGNAL_FUNC) cmd_devoice);
command_unbind("mode", (SIGNAL_FUNC) cmd_mode);
}

18
src/irc/core/modes.h Normal file
View file

@ -0,0 +1,18 @@
#ifndef __MODES_H
#define __MODES_H
#include "server.h"
#include "channels.h"
void modes_init(void);
void modes_deinit(void);
/* add `mode' to `old' - return newly allocated mode. */
char *modes_join(const char *old, const char *mode);
void parse_channel_modes(CHANNEL_REC *channel, const char *setby, const char *modestr);
void channel_set_singlemode(IRC_SERVER_REC *server, const char *channel, const char *nicks, const char *mode);
void channel_set_mode(IRC_SERVER_REC *server, const char *channel, const char *mode);
#endif

4
src/irc/core/module.h Normal file
View file

@ -0,0 +1,4 @@
#include "common.h"
#define MODULE_NAME "irc/core"

270
src/irc/core/netsplit.c Normal file
View file

@ -0,0 +1,270 @@
/*
netsplit.c : irssi
Copyright (C) 1999 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 "commands.h"
#include "misc.h"
#include "common-setup.h"
#include "irc-server.h"
#include "netsplit.h"
static int split_tag;
static NETSPLIT_REC *netsplit_add(IRC_SERVER_REC *server, const char *nick, const char *address, const char *servers)
{
NETSPLIT_REC *rec;
NETSPLIT_CHAN_REC *splitchan;
NICK_REC *nickrec;
GSList *tmp;
char *p, *dupservers;
g_return_val_if_fail(server != NULL, NULL);
g_return_val_if_fail(nick != NULL, NULL);
g_return_val_if_fail(address != NULL, NULL);
dupservers = g_strdup(servers);
p = strchr(dupservers, ' ');
if (p == NULL) {
g_free(dupservers);
g_return_val_if_fail(p != NULL, NULL);
}
*p++ = '\0';
rec = g_new0(NETSPLIT_REC, 1);
rec->nick = g_strdup(nick);
rec->address = g_strdup(address);
rec->destroy = time(NULL)+NETSPLIT_MAX_REMEMBER;
/* get splitted servers */
rec->server = g_strdup(dupservers);
rec->destserver = g_strdup(p);
g_free(dupservers);
/* copy the channel nick records.. */
for (tmp = server->channels; tmp != NULL; tmp = tmp->next) {
CHANNEL_REC *channel = tmp->data;
nickrec = nicklist_find(channel, nick);
if (nickrec == NULL)
continue;
splitchan = g_new0(NETSPLIT_CHAN_REC, 1);
splitchan->name = g_strdup(channel->name);
memcpy(&splitchan->nick, nickrec, sizeof(NICK_REC));
rec->channels = g_slist_append(rec->channels, splitchan);
}
g_hash_table_insert(server->splits, rec->nick, rec);
signal_emit("netsplit add", 1, rec);
return rec;
}
static void netsplit_destroy(NETSPLIT_REC *rec)
{
GSList *tmp;
g_return_if_fail(rec != NULL);
signal_emit("netsplit remove", 1, rec);
for (tmp = rec->channels; tmp != NULL; tmp = tmp->next) {
NETSPLIT_CHAN_REC *rec = tmp->data;
g_free(rec->name);
g_free(rec);
}
g_free(rec->server);
g_free(rec->destserver);
g_free(rec->nick);
g_free(rec->address);
g_free(rec);
}
static void netsplit_destroy_hash(gpointer key, NETSPLIT_REC *rec)
{
netsplit_destroy(rec);
}
NETSPLIT_REC *netsplit_find(IRC_SERVER_REC *server, const char *nick, const char *address)
{
NETSPLIT_REC *rec;
g_return_val_if_fail(server != NULL, NULL);
g_return_val_if_fail(nick != NULL, NULL);
rec = g_hash_table_lookup(server->splits, nick);
if (rec == NULL) return NULL;
return (address == NULL || g_strcasecmp(rec->address, address) == 0) ? rec : NULL;
}
NICK_REC *netsplit_find_channel(IRC_SERVER_REC *server, const char *nick, const char *address, const char *channel)
{
NETSPLIT_REC *rec;
GSList *tmp;
rec = netsplit_find(server, nick, address);
if (rec == NULL) return NULL;
for (tmp = rec->channels; tmp != NULL; tmp = tmp->next) {
NETSPLIT_CHAN_REC *rec = tmp->data;
if (g_strcasecmp(rec->name, channel) == 0)
return &rec->nick;
}
return NULL;
}
static int is_split(const char *msg)
{
char *params, *host1, *host2, *p;
int ok;
g_return_val_if_fail(msg != NULL, FALSE);
/* must have only two words */
p = strchr(msg, ' ');
if (p == NULL || strchr(p+1, ' ') != NULL) return FALSE;
/* check that it looks ok.. */
if (!match_wildcards("*.* *.*", msg) ||
match_wildcards("*..*", msg) || strstr(msg, "))") != NULL)
return FALSE;
/* get the two hosts */
ok = FALSE;
params = cmd_get_params(msg, 2, &host1, &host2);
if (g_strcasecmp(host1, host2) != 0) { /* hosts can't be same.. */
/* check that domain length is 2 or 3 */
p = strrchr(host1, '.');
if (p != NULL && (strlen(p+1) == 2 || strlen(p+1) == 3)) {
p = strrchr(host2, '.');
if (p != NULL && (strlen(p+1) == 2 || strlen(p+1) == 3)) {
/* it looks just like a netsplit to me. */
ok = TRUE;
}
}
}
g_free(params);
return ok;
}
static void split_set_timeout(gpointer key, NETSPLIT_REC *rec, NETSPLIT_REC *orig)
{
if (rec == orig) {
/* original nick, destroy it in a few seconds.. */
rec->destroy = time(NULL)+4;
} else if (g_strcasecmp(rec->server, orig->server) == 0 &&
g_strcasecmp(rec->destserver, orig->destserver) == 0) {
/* same servers -> split over -> destroy old records sooner.. */
rec->destroy = time(NULL)+60;
}
}
static void event_join(gchar *data, IRC_SERVER_REC *server, gchar *nick, gchar *address)
{
NETSPLIT_REC *rec;
/* check if split is over */
rec = g_hash_table_lookup(server->splits, nick);
if (rec == NULL) return;
if (g_strcasecmp(rec->address, address) == 0) {
/* yep, looks like it is. for same people that had the same
splitted servers set the timeout to one minute.
.. if the user just changed server, he/she can't use the
same nick (unless the server is broken) so don't bother
checking that the nick's server matches the split. */
g_hash_table_foreach(server->splits, (GHFunc) split_set_timeout, rec);
} else {
/* back from different address.. just destroy it. */
g_hash_table_remove(server->splits, rec->nick);
netsplit_destroy(rec);
}
}
static void event_quit(const char *data, IRC_SERVER_REC *server, const char *nick, const char *address)
{
g_return_if_fail(data != NULL);
if (*data == ':') data++;
if (g_strcasecmp(nick, server->nick) != 0 && is_split(data)) {
/* netsplit! */
netsplit_add(server, nick, address, data);
}
}
static void sig_disconnected(IRC_SERVER_REC *server)
{
g_return_if_fail(server != NULL);
g_hash_table_foreach(server->splits, (GHFunc) netsplit_destroy_hash, NULL);
g_hash_table_destroy(server->splits);
}
static int split_server_check(gpointer key, NETSPLIT_REC *rec, IRC_SERVER_REC *server)
{
/* Check if this split record is too old.. */
if (rec->destroy > time(NULL))
return FALSE;
netsplit_destroy(rec);
return TRUE;
}
static int split_check_old(void)
{
GSList *tmp;
for (tmp = servers; tmp != NULL; tmp = tmp->next) {
IRC_SERVER_REC *server = tmp->data;
g_hash_table_foreach_remove(server->splits, (GHRFunc) split_server_check, server);
}
return 1;
}
void netsplit_init(void)
{
split_tag = g_timeout_add(1000, (GSourceFunc) split_check_old, NULL);
signal_add_first("event join", (SIGNAL_FUNC) event_join);
signal_add_first("event quit", (SIGNAL_FUNC) event_quit);
signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected);
}
void netsplit_deinit(void)
{
GSList *tmp;
for (tmp = servers; tmp != NULL; tmp = tmp->next)
sig_disconnected(tmp->data);
g_source_remove(split_tag);
signal_remove("event join", (SIGNAL_FUNC) event_join);
signal_remove("event quit", (SIGNAL_FUNC) event_quit);
signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
}

27
src/irc/core/netsplit.h Normal file
View file

@ -0,0 +1,27 @@
#ifndef __NETSPLIT_H
#define __NETSPLIT_H
#include "nicklist.h"
typedef struct {
char *nick;
char *address;
char *server;
char *destserver;
GSList *channels;
time_t destroy;
} NETSPLIT_REC;
typedef struct {
char *name;
NICK_REC nick;
} NETSPLIT_CHAN_REC;
void netsplit_init(void);
void netsplit_deinit(void);
NETSPLIT_REC *netsplit_find(IRC_SERVER_REC *server, const char *nick, const char *address);
NICK_REC *netsplit_find_channel(IRC_SERVER_REC *server, const char *nick, const char *address, const char *channel);
#endif

566
src/irc/core/nicklist.c Normal file
View file

@ -0,0 +1,566 @@
/*
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 "channels.h"
#include "irc.h"
#include "masks.h"
#include "nicklist.h"
#include "irc-server.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(channel != NULL, 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(channel != NULL);
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 (irc_mask_match_address(mask, nick->nick, nick->host == NULL ? "" : nick->host))
break;
}
g_slist_free(nicks);
return tmp == NULL ? NULL : nick;
}
/* 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(channel != NULL, 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;
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(IRC_SERVER_REC *server, const char *nick)
{
NICKLIST_GET_SAME_REC rec;
GSList *tmp;
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);
}
#define isnickchar(a) \
(isalnum(a) || (a) == '`' || (a) == '-' || (a) == '_' || \
(a) == '[' || (a) == ']' || (a) == '{' || (a) == '}' || \
(a) == '|' || (a) == '\\' || (a) == '^')
char *nick_strip(const char *nick)
{
char *stripped, *spos;
g_return_val_if_fail(nick != NULL, NULL);
spos = stripped = g_strdup(nick);
while (isnickchar(*nick)) {
if (isalnum((gint) *nick)) *spos++ = *nick;
nick++;
}
if ((unsigned char) *nick >= 128)
*spos++ = *nick; /* just add it so that nicks won't match.. */
*spos = '\0';
return stripped;
}
static void event_names_list(const char *data, IRC_SERVER_REC *server)
{
CHANNEL_REC *chanrec;
char *params, *type, *channel, *names, *ptr;
g_return_if_fail(data != NULL);
params = event_get_params(data, 4, NULL, &type, &channel, &names);
chanrec = channel_find(server, channel);
if (chanrec == NULL || chanrec->names_got) {
/* unknown channel / names list already read */
g_free(params);
return;
}
/* type = '=' = public, '*' = private, '@' = secret.
This is actually pretty useless to check here, but at least we
get to know if the channel is +p or +s a few seconds before
we receive the MODE reply... */
if (*type == '*')
chanrec->mode_private = TRUE;
else if (*type == '@')
chanrec->mode_secret = TRUE;
while (*names != '\0') {
while (*names == ' ') names++;
ptr = names;
while (*names != '\0' && *names != ' ') names++;
if (*names != '\0') *names++ = '\0';
if (*ptr == '@' && strcmp(server->nick, ptr+1) == 0)
chanrec->chanop = TRUE;
nicklist_insert(chanrec, ptr+isnickflag(*ptr), *ptr == '@', *ptr == '+', FALSE);
}
g_free(params);
}
static void event_end_of_names(const char *data, IRC_SERVER_REC *server)
{
char *params, *channel;
CHANNEL_REC *chanrec;
g_return_if_fail(server != NULL);
params = event_get_params(data, 2, NULL, &channel);
chanrec = channel_find(server, channel);
if (chanrec != NULL && !chanrec->names_got) {
chanrec->names_got = TRUE;
signal_emit("channel query", 1, chanrec);
}
g_free(params);
}
static void nicklist_update_flags(IRC_SERVER_REC *server, const char *nick, int gone, int ircop)
{
GSList *nicks, *tmp;
CHANNEL_REC *channel;
NICK_REC *rec;
g_return_if_fail(server != NULL);
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 (ircop != -1 && rec->ircop != ircop) {
rec->ircop = ircop;
signal_emit("nick ircop changed", 2, channel, rec);
}
}
g_slist_free(nicks);
}
static void event_who(const char *data, IRC_SERVER_REC *server)
{
char *params, *nick, *channel, *user, *host, *stat, *realname, *hops;
CHANNEL_REC *chanrec;
NICK_REC *nickrec;
g_return_if_fail(data != NULL);
params = event_get_params(data, 8, NULL, &channel, &user, &host, NULL, &nick, &stat, &realname);
/* get hop count */
hops = realname;
while (*realname != '\0' && *realname != ' ') realname++;
*realname++ = '\0';
while (*realname == ' ') realname++;
/* update host, realname, hopcount */
chanrec = channel_find(server, channel);
nickrec = chanrec == NULL ? NULL : nicklist_find(chanrec, nick);
if (nickrec != NULL) {
if (nickrec->host == NULL)
nickrec->host = g_strdup_printf("%s@%s", user, host);
if (nickrec->realname == NULL)
nickrec->realname = g_strdup(realname);
sscanf(hops, "%d", &nickrec->hops);
}
nicklist_update_flags(server, nick,
strchr(stat, 'G') != NULL, /* gone */
strchr(stat, '*') != NULL); /* ircop */
g_free(params);
}
static void event_whois(const char *data, IRC_SERVER_REC *server)
{
char *params, *nick, *realname;
GSList *nicks, *tmp;
NICK_REC *rec;
g_return_if_fail(data != NULL);
server->whois_coming = TRUE;
/* first remove the gone-flag, if user is gone it will be set later.. */
params = event_get_params(data, 6, NULL, &nick, NULL, NULL, NULL, &realname);
nicks = nicklist_get_same(server, nick);
for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
rec = tmp->next->data;
if (rec->realname == NULL)
rec->realname = g_strdup(realname);
}
g_slist_free(nicks);
/* reset gone and ircop status, we'll handle them in the following
WHOIS replies */
nicklist_update_flags(server, nick, FALSE, FALSE);
g_free(params);
}
static void event_whois_away(const char *data, IRC_SERVER_REC *server)
{
char *params, *nick, *awaymsg;
g_return_if_fail(data != NULL);
/* set user's gone flag.. */
params = event_get_params(data, 3, NULL, &nick, &awaymsg);
nicklist_update_flags(server, nick, TRUE, -1);
g_free(params);
}
static void event_whois_ircop(const char *data, IRC_SERVER_REC *server)
{
char *params, *nick, *awaymsg;
g_return_if_fail(data != NULL);
/* set user's gone flag.. */
params = event_get_params(data, 3, NULL, &nick, &awaymsg);
nicklist_update_flags(server, nick, -1, TRUE);
g_free(params);
}
static void event_end_of_whois(const char *data, IRC_SERVER_REC *server)
{
server->whois_coming = FALSE;
}
static void event_nick_in_use(const char *data, IRC_SERVER_REC *server)
{
char *str;
int n;
g_return_if_fail(data != NULL);
if (server->connected) {
/* Already connected, no need to handle this anymore. */
return;
}
/* nick already in use - need to change it .. */
if (strcmp(server->nick, server->connrec->nick) == 0) {
/* first try, so try the alternative nick.. */
g_free(server->nick);
server->nick = g_strdup(server->connrec->alternate_nick);
}
else if (strlen(server->nick) < 9) {
/* keep adding '_' to end of nick.. */
str = g_strdup_printf("%s_", server->nick);
g_free(server->nick);
server->nick = str;
} else {
/* nick full, keep adding number at the end */
for (n = 8; n > 0; n--) {
if (server->nick[n] < '0' || server->nick[n] > '9') {
server->nick[n] = '1';
break;
}
if (server->nick[n] < '9') {
server->nick[n]++;
break;
}
server->nick[n] = '0';
}
}
irc_send_cmdv(server, "NICK %s", server->nick);
}
static void event_target_unavailable(const char *data, IRC_SERVER_REC *server)
{
char *params, *channel;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &channel);
if (!ischannel(*channel)) {
/* nick is unavailable. */
event_nick_in_use(data, server);
}
g_free(params);
}
static void event_nick(const char *data, IRC_SERVER_REC *server, const char *orignick)
{
CHANNEL_REC *channel;
NICK_REC *nickrec;
GSList *nicks, *tmp;
char *params, *nick;
g_return_if_fail(data != NULL);
params = event_get_params(data, 1, &nick);
if (g_strcasecmp(orignick, server->nick) == 0) {
/* You changed your nick */
g_free(server->connrec->nick);
g_free(server->nick);
server->connrec->nick = g_strdup(nick);
server->nick = g_strdup(nick);
signal_emit("server nick changed", 1, server);
}
nicks = nicklist_get_same(server, nick);
for (tmp = nicks; tmp != NULL; tmp = tmp->next->next) {
channel = tmp->data;
nickrec = tmp->next->data;
/* remove old nick from hash table */
g_hash_table_remove(channel->nicks, nickrec->nick);
g_free(nickrec->nick);
nickrec->nick = g_strdup(nick);
/* add new nick to hash table */
g_hash_table_insert(channel->nicks, nickrec->nick, nickrec);
signal_emit("nicklist changed", 3, channel, nickrec, orignick);
}
g_slist_free(nicks);
g_free(params);
}
static void event_userhost(const char *data, IRC_SERVER_REC *server)
{
char *params, *hosts, **phosts, **pos, *ptr;
g_return_if_fail(data != NULL);
/* set user's gone flag.. */
params = event_get_params(data, 2, NULL, &hosts);
phosts = g_strsplit(hosts, " ", -1);
for (pos = phosts; pos != NULL; pos++) {
ptr = strchr(*pos, '=');
if (ptr == NULL) continue;
*ptr++ = '\0';
nicklist_update_flags(server, *pos, *ptr == '-', -1);
}
g_strfreev(phosts);
g_free(params);
}
static void sig_usermode(IRC_SERVER_REC *server)
{
g_return_if_fail(server != NULL);
nicklist_update_flags(server, server->nick, server->usermode_away, -1);
}
static void sig_channel_created(CHANNEL_REC *channel)
{
g_return_if_fail(channel != NULL);
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(channel != NULL);
g_hash_table_foreach(channel->nicks, (GHFunc) nicklist_remove_hash, channel);
g_hash_table_destroy(channel->nicks);
}
void nicklist_init(void)
{
signal_add("event nick", (SIGNAL_FUNC) event_nick);
signal_add_first("event 352", (SIGNAL_FUNC) event_who);
signal_add("silent event who", (SIGNAL_FUNC) event_who);
signal_add("silent event whois", (SIGNAL_FUNC) event_whois);
signal_add_first("event 311", (SIGNAL_FUNC) event_whois);
signal_add_first("event 301", (SIGNAL_FUNC) event_whois_away);
signal_add_first("event 313", (SIGNAL_FUNC) event_whois_ircop);
signal_add("event 318", (SIGNAL_FUNC) event_end_of_whois);
signal_add("event 353", (SIGNAL_FUNC) event_names_list);
signal_add("event 366", (SIGNAL_FUNC) event_end_of_names);
signal_add("event 433", (SIGNAL_FUNC) event_nick_in_use);
signal_add("event 437", (SIGNAL_FUNC) event_target_unavailable);
signal_add("event 302", (SIGNAL_FUNC) event_userhost);
signal_add("userhost event", (SIGNAL_FUNC) event_userhost);
signal_add("user mode changed", (SIGNAL_FUNC) sig_usermode);
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("event nick", (SIGNAL_FUNC) event_nick);
signal_remove("event 352", (SIGNAL_FUNC) event_who);
signal_remove("silent event who", (SIGNAL_FUNC) event_who);
signal_remove("silent event whois", (SIGNAL_FUNC) event_whois);
signal_remove("event 311", (SIGNAL_FUNC) event_whois);
signal_remove("event 301", (SIGNAL_FUNC) event_whois_away);
signal_remove("event 313", (SIGNAL_FUNC) event_whois_ircop);
signal_remove("event 318", (SIGNAL_FUNC) event_end_of_whois);
signal_remove("event 353", (SIGNAL_FUNC) event_names_list);
signal_remove("event 366", (SIGNAL_FUNC) event_end_of_names);
signal_remove("event 433", (SIGNAL_FUNC) event_nick_in_use);
signal_remove("event 437", (SIGNAL_FUNC) event_target_unavailable);
signal_remove("event 302", (SIGNAL_FUNC) event_userhost);
signal_remove("userhost event", (SIGNAL_FUNC) event_userhost);
signal_remove("user mode changed", (SIGNAL_FUNC) sig_usermode);
signal_remove("channel created", (SIGNAL_FUNC) sig_channel_created);
signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed);
}

41
src/irc/core/nicklist.h Normal file
View file

@ -0,0 +1,41 @@
#ifndef __NICKLIST_H
#define __NICKLIST_H
#include "channels.h"
typedef struct {
time_t last_check; /* last time gone was checked */
int send_massjoin; /* Waiting to be sent in massjoin signal */
char *nick;
char *host;
char *realname;
int hops;
int op:1;
int voice:1;
int gone:1;
int ircop: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 */
GSList *nicklist_getnicks(CHANNEL_REC *channel);
/* Get all the nick records of `nick'. Returns channel, nick, channel, ... */
GSList *nicklist_get_same(IRC_SERVER_REC *server, const char *nick);
/* nick record comparision for sort functions */
int nicklist_compare(NICK_REC *p1, NICK_REC *p2);
char *nick_strip(const char *nick);
void nicklist_init(void);
void nicklist_deinit(void);
#endif

158
src/irc/core/query.c Normal file
View file

@ -0,0 +1,158 @@
/*
query.c : irssi
Copyright (C) 1999 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 "misc.h"
#include "signals.h"
#include "modules.h"
#include "irc.h"
#include "query.h"
GSList *queries;
QUERY_REC *query_create(IRC_SERVER_REC *server, const char *nick, int automatic)
{
QUERY_REC *rec;
g_return_val_if_fail(server != NULL, NULL);
g_return_val_if_fail(nick != NULL, NULL);
rec = g_new0(QUERY_REC, 1);
queries = g_slist_append(queries, rec);
server->queries = g_slist_append(server->queries, rec);
MODULE_DATA_INIT(rec);
rec->type = module_get_uniq_id("IRC", WI_IRC_QUERY);
rec->nick = g_strdup(nick);
rec->server_tag = g_strdup(server->tag);
rec->server = server;
signal_emit("query created", 2, rec, GINT_TO_POINTER(automatic));
return rec;
}
void query_destroy(QUERY_REC *query)
{
g_return_if_fail(query != NULL);
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(query->nick);
g_free(query->server_tag);
g_free(query);
}
static QUERY_REC *query_find_server(IRC_SERVER_REC *server, const char *nick)
{
GSList *tmp;
for (tmp = server->queries; tmp != NULL; tmp = tmp->next) {
QUERY_REC *rec = tmp->data;
if (g_strcasecmp(nick, rec->nick) == 0)
return rec;
}
return NULL;
}
QUERY_REC *query_find(IRC_SERVER_REC *server, const char *nick)
{
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, IRC_SERVER_REC *server)
{
g_return_if_fail(query != NULL);
query->server = server;
signal_emit("query server changed", 2, query, server);
}
static void event_privmsg(const char *data, IRC_SERVER_REC *server, const char *nick, const char *addr)
{
char *params, *target, *msg;
QUERY_REC *query;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &msg);
if (addr != NULL && *msg != 1 && !ischannel(*target)) {
/* save nick's address to query */
query = query_find(server, nick);
if (query != NULL && (query->address == NULL || strcmp(query->address, addr) != 0)) {
g_free_not_null(query->address);
query->address = g_strdup(addr);
signal_emit("query address changed", 1, query);
}
}
g_free(params);
}
static void event_nick(const char *data, IRC_SERVER_REC *server, const char *orignick)
{
char *params, *nick;
GSList *tmp;
params = event_get_params(data, 1, &nick);
for (tmp = server->queries; tmp != NULL; tmp = tmp->next) {
QUERY_REC *rec = tmp->data;
if (g_strcasecmp(rec->nick, orignick) == 0) {
g_free(rec->nick);
rec->nick = g_strdup(nick);
signal_emit("query nick changed", 1, rec);
}
}
g_free(params);
}
void query_init(void)
{
signal_add_last("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_add("event nick", (SIGNAL_FUNC) event_nick);
}
void query_deinit(void)
{
signal_remove("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_remove("event nick", (SIGNAL_FUNC) event_nick);
}

30
src/irc/core/query.h Normal file
View file

@ -0,0 +1,30 @@
#ifndef __QUERY_H
#define __QUERY_H
#include "server.h"
typedef struct {
int type;
GHashTable *module_data;
IRC_SERVER_REC *server;
char *nick;
int new_data;
char *address;
char *server_tag;
int destroying:1;
} QUERY_REC;
extern GSList *queries;
QUERY_REC *query_create(IRC_SERVER_REC *server, const char *nick, int automatic);
void query_destroy(QUERY_REC *query);
/* Find query by name, if `server' is NULL, search from all servers */
QUERY_REC *query_find(IRC_SERVER_REC *server, const char *nick);
void query_change_server(QUERY_REC *query, IRC_SERVER_REC *server);
#endif

252
src/irc/core/server-idle.c Normal file
View file

@ -0,0 +1,252 @@
/*
server-idle.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 "irc-server.h"
#include "server-idle.h"
#include "server-redirect.h"
#include "irc.h"
typedef struct {
char *event;
char *signal;
int argpos;
} REDIRECT_IDLE_REC;
typedef struct {
char *cmd;
char *arg;
int tag;
int last;
GSList *redirects;
} SERVER_IDLE_REC;
static int idle_tag, idlepos;
/* Add new idle command to queue */
static SERVER_IDLE_REC *server_idle_create(const char *cmd, const char *arg, int last, va_list args)
{
REDIRECT_IDLE_REC *rrec;
SERVER_IDLE_REC *rec;
char *event;
g_return_val_if_fail(cmd != NULL, FALSE);
rec = g_new0(SERVER_IDLE_REC, 1);
rec->tag = ++idlepos;
rec->arg = arg == NULL ? NULL : g_strdup(arg);
rec->cmd = g_strdup(cmd);
rec->last = last;
while ((event = va_arg(args, char *)) != NULL) {
rrec = g_new(REDIRECT_IDLE_REC, 1);
rec->redirects = g_slist_append(rec->redirects, rrec);
rrec->event = g_strdup(event);
rrec->signal = g_strdup(va_arg(args, char *));
rrec->argpos = va_arg(args, int);
}
return rec;
}
static SERVER_IDLE_REC *server_idle_find_rec(IRC_SERVER_REC *server, int tag)
{
GSList *tmp;
g_return_val_if_fail(server != NULL, FALSE);
for (tmp = server->idles; tmp != NULL; tmp = tmp->next) {
SERVER_IDLE_REC *rec = tmp->data;
if (rec->tag == tag)
return rec;
}
return NULL;
}
/* Add new idle command to queue */
int server_idle_add(IRC_SERVER_REC *server, const char *cmd, const char *arg, int last, ...)
{
va_list args;
SERVER_IDLE_REC *rec;
g_return_val_if_fail(server != NULL, -1);
va_start(args, last);
rec = server_idle_create(cmd, arg, last, args);
server->idles = g_slist_append(server->idles, rec);
va_end(args);
return rec->tag;
}
/* Add new idle command to first of queue */
int server_idle_add_first(IRC_SERVER_REC *server, const char *cmd, const char *arg, int last, ...)
{
va_list args;
SERVER_IDLE_REC *rec;
g_return_val_if_fail(server != NULL, -1);
va_start(args, last);
rec = server_idle_create(cmd, arg, last, args);
server->idles = g_slist_prepend(server->idles, rec);
va_end(args);
return rec->tag;
}
/* Add new idle command to specified position of queue */
int server_idle_insert(IRC_SERVER_REC *server, const char *cmd, const char *arg, int tag, int last, ...)
{
va_list args;
SERVER_IDLE_REC *rec;
int pos;
g_return_val_if_fail(server != NULL, -1);
va_start(args, last);
/* find the position of tag in idle list */
rec = server_idle_find_rec(server, tag);
pos = g_slist_index(server->idles, rec);
rec = server_idle_create(cmd, arg, last, args);
server->idles = pos < 0 ?
g_slist_append(server->idles, rec) :
g_slist_insert(server->idles, rec, pos);
va_end(args);
return rec->tag;
}
static void server_idle_destroy(IRC_SERVER_REC *server, SERVER_IDLE_REC *rec)
{
GSList *tmp;
g_return_if_fail(server != NULL);
server->idles = g_slist_remove(server->idles, rec);
for (tmp = rec->redirects; tmp != NULL; tmp = tmp->next) {
REDIRECT_IDLE_REC *rec = tmp->data;
g_free(rec->event);
g_free(rec->signal);
g_free(rec);
}
g_slist_free(rec->redirects);
g_free_not_null(rec->arg);
g_free(rec->cmd);
g_free(rec);
}
/* Check if record is still in queue */
int server_idle_find(IRC_SERVER_REC *server, int tag)
{
return server_idle_find_rec(server, tag) != NULL;
}
/* Remove record from idle queue */
int server_idle_remove(IRC_SERVER_REC *server, int tag)
{
SERVER_IDLE_REC *rec;
g_return_val_if_fail(server != NULL, FALSE);
rec = server_idle_find_rec(server, tag);
if (rec == NULL)
return FALSE;
server_idle_destroy(server, rec);
return TRUE;
}
/* Execute next idle command */
static void server_idle_next(IRC_SERVER_REC *server)
{
SERVER_IDLE_REC *rec;
GSList *tmp;
int group;
g_return_if_fail(server != NULL);
if (server->idles == NULL) return;
rec = server->idles->data;
/* Send command */
irc_send_cmd(server, rec->cmd);
/* Add server redirections */
group = 0;
for (tmp = rec->redirects; tmp != NULL; tmp = tmp->next) {
REDIRECT_IDLE_REC *rrec = tmp->data;
group = server_redirect_single_event((SERVER_REC *) server, rec->arg, rec->last > 0,
group, rrec->event, rrec->signal, rrec->argpos);
if (rec->last > 0) rec->last--;
}
server_idle_destroy(server, rec);
}
static void sig_disconnected(IRC_SERVER_REC *server)
{
g_return_if_fail(server != NULL);
while (server->idles != NULL)
server_idle_destroy(server, server->idles->data);
}
static int sig_idle_timeout(void)
{
GSList *tmp;
/* Scan through every server */
for (tmp = servers; tmp != NULL; tmp = tmp->next) {
IRC_SERVER_REC *rec = tmp->data;
if (rec->idles != NULL && rec->cmdcount == 0) {
/* We're idling and we have idle commands to run! */
server_idle_next(rec);
}
}
return 1;
}
void servers_idle_init(void)
{
idlepos = 0;
idle_tag = g_timeout_add(1000, (GSourceFunc) sig_idle_timeout, NULL);
signal_add("server disconnected", (SIGNAL_FUNC) sig_disconnected);
}
void servers_idle_deinit(void)
{
g_source_remove(idle_tag);
signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected);
}

View file

@ -0,0 +1,24 @@
#ifndef __SERVER_IDLE_H
#define __SERVER_IDLE_H
#include "irc-server.h"
/* Add new idle command to queue */
int server_idle_add(IRC_SERVER_REC *server, const char *cmd, const char *arg, int last, ...);
/* Add new idle command to first of queue */
int server_idle_add_first(IRC_SERVER_REC *server, const char *cmd, const char *arg, int last, ...);
/* Add new idle command to specified position of queue */
int server_idle_insert(IRC_SERVER_REC *server, const char *cmd, const char *arg, int tag, int last, ...);
/* Check if record is still in queue */
int server_idle_find(IRC_SERVER_REC *server, int tag);
/* Remove record from idle queue */
int server_idle_remove(IRC_SERVER_REC *server, int tag);
void servers_idle_init(void);
void servers_idle_deinit(void);
#endif

View file

@ -0,0 +1,398 @@
/*
server-reconnect.c : irssi
Copyright (C) 1999 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 "modules.h"
#include "commands.h"
#include "network.h"
#include "signals.h"
#include "irc.h"
#include "modes.h"
#include "irc-server.h"
#include "server-setup.h"
#include "server-reconnect.h"
#include "settings.h"
#include "common-setup.h"
GSList *reconnects;
static int last_reconnect_tag;
static int reconnect_timeout_tag;
static int reconnect_time;
static void server_reconnect_add(IRC_SERVER_CONNECT_REC *conn, time_t next_connect)
{
RECONNECT_REC *rec;
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)
{
reconnects = g_slist_remove(reconnects, rec);
signal_emit("server reconnect remove", 1, rec);
if (free_conn) irc_server_connect_free(rec->conn);
g_free(rec);
if (reconnects == NULL)
last_reconnect_tag = 0;
}
static int server_reconnect_timeout(void)
{
IRC_SERVER_CONNECT_REC *conn;
GSList *tmp, *next;
time_t now;
now = time(NULL);
for (tmp = reconnects; tmp != NULL; tmp = next) {
RECONNECT_REC *rec = tmp->data;
next = tmp->next;
if (rec->next_connect <= now) {
conn = rec->conn;
server_reconnect_destroy(rec, FALSE);
irc_server_connect(conn);
}
}
return 1;
}
static void sserver_connect(SETUP_SERVER_REC *rec, IRC_SERVER_CONNECT_REC *conn)
{
conn->address = g_strdup(rec->server);
conn->port = rec->port;
conn->password = rec->password == NULL ? NULL :
g_strdup(rec->password);
if (rec->cmd_queue_speed > 0)
conn->cmd_queue_speed = rec->cmd_queue_speed;
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.. */
irc_server_connect(conn);
}
}
static void server_connect_copy_skeleton(IRC_SERVER_CONNECT_REC *dest, IRC_SERVER_CONNECT_REC *src)
{
dest->proxy = src->proxy == NULL ? NULL :
g_strdup(src->proxy);
dest->proxy_port = src->proxy_port;
dest->proxy_string = src->proxy_string == NULL ? NULL :
g_strdup(src->proxy_string);
dest->ircnet = src->ircnet == NULL ? NULL :
g_strdup(src->ircnet);
dest->nick = src->nick == NULL ? NULL :
g_strdup(src->nick);
dest->username = src->username == NULL ? NULL :
g_strdup(src->username);
dest->realname = src->realname == NULL ? NULL :
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->cmd_queue_speed = src->cmd_queue_speed;
dest->max_kicks = src->max_kicks;
dest->max_modes = src->max_modes;
dest->max_msgs = src->max_msgs;
}
static void sig_reconnect(IRC_SERVER_REC *server)
{
IRC_SERVER_CONNECT_REC *conn;
SETUP_SERVER_REC *sserver;
GSList *tmp;
int found, through;
time_t now;
g_return_if_fail(server != NULL);
if (reconnect_time == -1 || !server->connection_lost || !irc_server_check(server))
return;
conn = g_new0(IRC_SERVER_CONNECT_REC, 1);
conn->reconnection = TRUE;
server_connect_copy_skeleton(conn, server->connrec);
/* save the server status */
if (!server->connected) {
conn->channels = g_strdup(server->connrec->channels);
conn->away_reason = g_strdup(server->connrec->away_reason);
conn->usermode = g_strdup(server->connrec->usermode);
} else {
conn->channels = irc_server_get_channels(server);
conn->away_reason = !server->usermode_away ? NULL :
g_strdup(server->away_reason);
conn->usermode = g_strdup(server->usermode);
}
sserver = server_setup_find(server->connrec->address, server->connrec->port);
if (sserver == NULL) {
/* port specific record not found, try without port.. */
sserver = server_setup_find(server->connrec->address, -1);
}
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 (sserver == NULL || conn->ircnet == NULL) {
/* not in any ircnet, 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 */
irc_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) {
SETUP_SERVER_REC *rec = tmp->data;
if (rec->ircnet == NULL || g_strcasecmp(conn->ircnet, rec->ircnet) != 0)
continue;
if (!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; ) {
SETUP_SERVER_REC *rec = tmp->data;
if (!found && g_strcasecmp(rec->server, server->connrec->address) == 0 &&
server->connrec->port == rec->port)
found = TRUE;
else if (found && rec->ircnet != NULL && g_strcasecmp(conn->ircnet, rec->ircnet) == 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 ircnet in setup.. */
break;
}
tmp = setupservers;
found = through = TRUE;
}
}
static void sig_server_looking(IRC_SERVER_REC *server)
{
IRC_SERVER_CONNECT_REC *conn;
GSList *tmp, *next;
g_return_if_fail(server != NULL);
if (!irc_server_check(server))
return;
/* trying to connect somewhere, check if there's anything in reconnect
queue waiting to connect to same ircnet or same server+port.. */
conn = server->connrec;
for (tmp = reconnects; tmp != NULL; tmp = next) {
RECONNECT_REC *rec = tmp->data;
next = tmp->next;
if (g_strcasecmp(conn->address, rec->conn->address) == 0 &&
conn->port == rec->conn->port) {
server_reconnect_destroy(rec, TRUE);
}
else if (conn->ircnet != NULL && rec->conn->ircnet != NULL &&
g_strcasecmp(conn->ircnet, rec->conn->ircnet) == 0) {
server_reconnect_destroy(rec, TRUE);
}
}
}
/* Remove all servers from reconnect list */
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 */
static void cmd_reconnect(const char *data)
{
IRC_SERVER_CONNECT_REC *conn;
RECONNECT_REC *rec;
int tag;
if (g_strncasecmp(data, "RECON-", 6) == 0)
data += 6;
rec = sscanf(data, "%d", &tag) == 1 && tag > 0 ?
reconnect_find_tag(tag) : NULL;
if (rec == NULL)
signal_emit("server reconnect not found", 1, data);
else {
conn = rec->conn;
server_reconnect_destroy(rec, FALSE);
irc_server_connect(rec->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);
}
static int sig_set_user_mode(IRC_SERVER_REC *server)
{
const char *mode;
char *newmode;
if (g_slist_find(servers, server) == NULL)
return 0; /* got disconnected */
mode = server->connrec->usermode;
if (mode == NULL) return 0;
newmode = modes_join(server->usermode, mode);
if (strcmp(newmode, server->usermode) != 0)
irc_send_cmdv(server, "MODE %s -%s+%s", server->nick, server->usermode, mode);
g_free(newmode);
return 0;
}
static void sig_connected(IRC_SERVER_REC *server)
{
if (!server->connrec->reconnection)
return;
if (server->connrec->channels != NULL)
channels_join(server, server->connrec->channels, TRUE);
if (server->connrec->away_reason != NULL)
signal_emit("command away", 2, server->connrec->away_reason, server, NULL);
if (server->connrec->usermode != NULL) {
/* wait a second and then send the user mode */
g_timeout_add(1000, (GSourceFunc) sig_set_user_mode, server);
}
}
static void read_settings(void)
{
reconnect_time = settings_get_int("server_reconnect_time");
}
void servers_reconnect_init(void)
{
reconnects = NULL;
last_reconnect_tag = 0;
reconnect_timeout_tag = g_timeout_add(1000, (GSourceFunc) server_reconnect_timeout, NULL);
read_settings();
signal_add("server looking", (SIGNAL_FUNC) sig_server_looking);
signal_add("server connect failed", (SIGNAL_FUNC) sig_reconnect);
signal_add("server disconnected", (SIGNAL_FUNC) sig_reconnect);
signal_add("event connected", (SIGNAL_FUNC) sig_connected);
command_bind("rmreconns", NULL, (SIGNAL_FUNC) cmd_rmreconns);
command_bind("reconnect", NULL, (SIGNAL_FUNC) cmd_reconnect);
command_bind("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 looking", (SIGNAL_FUNC) sig_server_looking);
signal_remove("server connect failed", (SIGNAL_FUNC) sig_reconnect);
signal_remove("server disconnected", (SIGNAL_FUNC) sig_reconnect);
signal_remove("event connected", (SIGNAL_FUNC) sig_connected);
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);
}

View file

@ -0,0 +1,16 @@
#ifndef __SERVER_RECONNECT_H
#define __SERVER_RECONNECT_H
typedef struct {
int tag;
time_t next_connect;
IRC_SERVER_CONNECT_REC *conn;
} RECONNECT_REC;
extern GSList *reconnects;
void servers_reconnect_init(void);
void servers_reconnect_deinit(void);
#endif

317
src/irc/core/server-setup.c Normal file
View file

@ -0,0 +1,317 @@
/*
server-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 "common-setup.h"
#include "irc-server.h"
#include "server-setup.h"
#include "ircnet-setup.h"
GSList *setupservers; /* list of irc servers */
int source_host_ok; /* Use source_host_ip .. */
IPADDR *source_host_ip; /* Resolved address */
static void get_source_host_ip(void)
{
IPADDR ip;
/* FIXME: This will block! */
if (!source_host_ok) {
source_host_ok = *settings_get_str("hostname") != '\0' &&
net_gethostname(settings_get_str("hostname"), &ip) == 0;
if (source_host_ok) {
source_host_ip = g_new(IPADDR, 1);
memcpy(source_host_ip, &ip, sizeof(IPADDR));
}
}
}
/* Create server connection record. `address' is required, rest can be NULL */
static IRC_SERVER_CONNECT_REC *
create_addr_conn(const char *address, int port, const char *password,
const char *nick)
{
IRC_SERVER_CONNECT_REC *conn;
SETUP_SERVER_REC *sserver;
IRCNET_REC *ircnet;
g_return_val_if_fail(address != NULL, NULL);
conn = g_new0(IRC_SERVER_CONNECT_REC, 1);
conn->address = g_strdup(address);
conn->port = port > 0 ? port : 6667;
if (password && *password) conn->password = g_strdup(password);
if (nick && *nick) conn->nick = g_strdup(nick);
if (!conn->nick) conn->nick = g_strdup(settings_get_str("default_nick"));
conn->alternate_nick = g_strdup(settings_get_str("alternate_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("toggle_use_ircproxy")) {
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));
}
/* fill the information from setup */
sserver = server_setup_find(address, -1);
if (sserver == NULL) return conn;
sserver->last_connect = time(NULL);
if (sserver->ircnet) conn->ircnet = g_strdup(sserver->ircnet);
if (sserver->password && !conn->password)
conn->password = g_strdup(sserver->password);
if (sserver->cmd_queue_speed > 0)
conn->cmd_queue_speed = sserver->cmd_queue_speed;
if (sserver->max_cmds_at_once > 0)
conn->max_cmds_at_once = sserver->max_cmds_at_once;
/* fill the rest from IRC network settings */
ircnet = sserver->ircnet == NULL ? NULL : ircnet_find(sserver->ircnet);
if (ircnet == NULL) return conn;
if (ircnet->nick && !nick) {
g_free(conn->nick);
conn->nick = g_strdup(ircnet->nick);;
}
if (ircnet->username) {
g_free(conn->username);
conn->username = g_strdup(ircnet->username);;
}
if (ircnet->realname) {
g_free(conn->realname);
conn->realname = g_strdup(ircnet->realname);;
}
if (ircnet->max_kicks > 0) conn->max_kicks = ircnet->max_kicks;
if (ircnet->max_msgs > 0) conn->max_msgs = ircnet->max_msgs;
if (ircnet->max_modes > 0) conn->max_modes = ircnet->max_modes;
if (ircnet->max_whois > 0) conn->max_whois = ircnet->max_whois;
return conn;
}
/* Create server connection record. `dest' is required, rest can be NULL.
`dest' is either a server address or irc network */
IRC_SERVER_CONNECT_REC *
irc_server_create_conn(const char *dest, int port, const char *password, const char *nick)
{
GSList *tmp;
time_t now;
int n;
g_return_val_if_fail(dest != NULL, NULL);
/* check if `dest' is IRC network */
if (ircnet_find(dest) == NULL)
return create_addr_conn(dest, port, password, nick);
/* first try to find a server that hasn't had any connection failures
for the past half an hour. If that isn't found, try any server. */
now = time(NULL);
for (n = 0; n < 2; n++) {
for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
SETUP_SERVER_REC *rec = tmp->data;
if (rec->ircnet == NULL || g_strcasecmp(rec->ircnet, dest) != 0)
continue;
if (n == 1 || !rec->last_failed || rec->last_connect < now-FAILED_RECONNECT_WAIT)
return create_addr_conn(rec->server, port, password, nick);
}
}
return NULL;
}
/* Find matching server from setup. Set port to -1 if you don't care about it */
SETUP_SERVER_REC *server_setup_find(const char *address, int port)
{
GSList *tmp;
g_return_val_if_fail(address != NULL, NULL);
for (tmp = setupservers; tmp != NULL; tmp = tmp->next) {
SETUP_SERVER_REC *rec = tmp->data;
if (g_strcasecmp(rec->server, address) == 0 &&
(port == -1 || rec->port == port)) return rec;
}
return NULL;
}
static void init_userinfo(void)
{
const char *set, *default_nick, *user_name;
char *str;
/* check if nick/username/realname wasn't read from setup.. */
set = settings_get_str("real_name");
if (set == NULL || *set == '\0') {
str = g_getenv("IRCNAME");
iconfig_set_str("settings", "real_name",
str != NULL ? str : g_get_real_name());
g_free_not_null(str);
}
/* username */
user_name = settings_get_str("user_name");
if (user_name == NULL || *user_name == '\0') {
str = g_getenv("IRCUSER");
iconfig_set_str("settings", "user_name",
str != NULL ? str : g_get_user_name());
g_free_not_null(str);
user_name = settings_get_str("user_name");
}
/* nick */
default_nick = settings_get_str("default_nick");
if (default_nick == NULL || *default_nick == '\0') {
str = g_getenv("IRCNICK");
iconfig_set_str("settings", "default_nick",
str != NULL ? str : user_name);
g_free_not_null(str);
default_nick = settings_get_str("default_nick");
}
/* alternate nick */
set = settings_get_str("alternate_nick");
if (set == NULL || *set == '\0') {
if (strlen(default_nick) < 9)
str = g_strconcat(default_nick, "_", NULL);
else {
str = g_strdup(default_nick);
str[strlen(str)-1] = '_';
}
iconfig_set_str("settings", "alternate_nick", str);
g_free(str);
}
}
static SETUP_SERVER_REC *setupserver_add(CONFIG_NODE *node)
{
SETUP_SERVER_REC *rec;
char *ircnet, *server;
int port;
g_return_val_if_fail(node != NULL, NULL);
ircnet = config_node_get_str(node, "ircnet", NULL);
server = config_node_get_str(node, "server", NULL);
if (ircnet == NULL || server == NULL) return NULL;
port = config_node_get_int(node, "port", 6667);
if (server_setup_find(server, port) != NULL) {
/* already exists - don't let it get there twice or
server reconnects will screw up! */
return NULL;
}
rec = g_new0(SETUP_SERVER_REC, 1);
rec->ircnet = g_strdup(ircnet);
rec->server = g_strdup(server);
rec->password = g_strdup(config_node_get_str(node, "password", ""));
rec->port = port;
rec->autoconnect = config_node_get_bool(node, "autoconnect", FALSE);
rec->max_cmds_at_once = config_node_get_int(node, "cmds_max_at_once", 0);
rec->cmd_queue_speed = config_node_get_int(node, "cmd_queue_speed", 0);
setupservers = g_slist_append(setupservers, rec);
return rec;
}
static void setupserver_destroy(SETUP_SERVER_REC *rec)
{
setupservers = g_slist_remove(setupservers, rec);
g_free(rec->ircnet);
g_free(rec->server);
g_free(rec->password);
g_free(rec);
}
static void read_servers(void)
{
CONFIG_NODE *node;
GSList *tmp;
while (setupservers != NULL)
setupserver_destroy(setupservers->data);
/* Read servers */
node = iconfig_node_traverse("(setupservers", FALSE);
if (node != NULL) {
for (tmp = node->value; tmp != NULL; tmp = tmp->next)
setupserver_add(tmp->data);
}
}
void servers_setup_init(void)
{
source_host_ok = FALSE;
source_host_ip = NULL;
settings_add_int("server", "server_reconnect_time", 300);
settings_add_str("server", "hostname", "");
settings_add_bool("server", "toggle_skip_motd", FALSE);
settings_add_str("server", "default_nick", NULL);
settings_add_str("server", "alternate_nick", NULL);
settings_add_str("server", "user_name", NULL);
settings_add_str("server", "real_name", NULL);
settings_add_bool("ircproxy", "toggle_use_ircproxy", FALSE);
settings_add_str("ircproxy", "proxy_address", "");
settings_add_int("ircproxy", "proxy_port", 6667);
settings_add_str("ircproxy", "proxy_string", "CONNECT %s %d");
init_userinfo();
read_servers();
signal_add("setup reread", (SIGNAL_FUNC) read_servers);
}
void servers_setup_deinit(void)
{
while (setupservers != NULL)
setupserver_destroy(setupservers->data);
signal_remove("setup reread", (SIGNAL_FUNC) read_servers);
}

View file

@ -0,0 +1,40 @@
#ifndef __SERVER_SETUP_H
#define __SERVER_SETUP_H
#include "irc-server.h"
/* servers */
typedef struct {
char *server;
int port;
char *ircnet;
char *password;
int autoconnect;
int max_cmds_at_once; /* override the default if > 0 */
int cmd_queue_speed; /* override the default if > 0 */
char *own_address; /* address to use when connecting this server */
IPADDR *own_ip; /* resolved own_address or full of zeros */
time_t last_connect; /* to avoid reconnecting too fast.. */
int last_failed; /* if last connection attempt failed */
} SETUP_SERVER_REC;
extern GSList *setupservers; /* list of irc servers */
extern IPADDR *source_host_ip; /* Resolved address */
extern gboolean source_host_ok; /* Use source_host_ip .. */
/* Create server connection record. `dest' is required, rest can be NULL.
`dest' is either a server address or irc network */
IRC_SERVER_CONNECT_REC *
irc_server_create_conn(const char *dest, int port, const char *password, const char *nick);
/* Find matching server from setup. Set port to -1 if you don't care about it */
SETUP_SERVER_REC *server_setup_find(const char *address, int port);
void servers_setup_init(void);
void servers_setup_deinit(void);
#endif