..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

5
src/irc/Makefile.am Normal file
View file

@ -0,0 +1,5 @@
SUBDIRS = core dcc flood notifylist
noinst_LTLIBRARIES = libirc.la
libirc_la_SOURCES = irc.c

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

14
src/irc/dcc/Makefile.am Normal file
View file

@ -0,0 +1,14 @@
noinst_LTLIBRARIES = libirc_dcc.la
INCLUDES = $(GLIB_CFLAGS) \
-I$(top_srcdir)/src -I$(top_srcdir)/src/core/ -I$(top_srcdir)/src/irc/core/
libirc_dcc_la_SOURCES = \
dcc.c \
dcc-chat.c \
dcc-files.c
noinst_HEADERS = \
dcc.h \
dcc-chat.h \
dcc-files.h

371
src/irc/dcc/dcc-chat.c Normal file
View file

@ -0,0 +1,371 @@
/*
dcc-chat.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 "commands.h"
#include "network.h"
#include "net-nonblock.h"
#include "line-split.h"
#include "settings.h"
#include "masks.h"
#include "irc.h"
#include "server-setup.h"
#include "dcc.h"
/* Send text to DCC chat */
static void dcc_chat_write(gchar *data)
{
DCC_REC *dcc;
gchar *params, *text, *target;
gint len;
g_return_if_fail(text != NULL);
params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &text);
if (*target == '=')
{
/* dcc msg */
dcc = dcc_find_item(DCC_TYPE_CHAT, ++target, NULL);
if (dcc != NULL)
{
len = strlen(text);
/* FIXME: we need output queue! */
if (net_transmit(dcc->handle, text, len) != len)
g_warning("dcc_chat_write() : could not send all data!");
net_transmit(dcc->handle, "\n", 1);
}
}
g_free(params);
}
static void dcc_chat_me(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
DCC_REC *dcc;
char *str;
g_return_if_fail(data != NULL);
dcc = irc_item_dcc_chat(item);
if (dcc == NULL) return;
str = g_strdup_printf("ACTION %s", data);
dcc_ctcp_message(dcc->nick, NULL, dcc, FALSE, str);
g_free(str);
}
static void dcc_chat_action(const char *data, IRC_SERVER_REC *server)
{
char *params, *target, *text;
DCC_REC *dcc;
char *str;
g_return_if_fail(data != NULL);
if (*data != '=') {
/* handle only DCC actions */
return;
}
params = cmd_get_params(data, 3 | PARAM_FLAG_GETREST, &target, &text);
if (*target == '\0' || *text == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
dcc = dcc_find_item(DCC_TYPE_CHAT, target+1, NULL);
if (dcc != NULL) {
str = g_strdup_printf("ACTION %s", data);
dcc_ctcp_message(dcc->nick, NULL, dcc, FALSE, str);
g_free(str);
}
g_free(params);
}
static void dcc_chat_ctcp(const char *data, IRC_SERVER_REC *server)
{
char *params, *target, *ctcpcmd, *ctcpdata;
DCC_REC *dcc;
char *str;
g_return_if_fail(data != NULL);
if (server == NULL || !server->connected) 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);
if (*target != '=') {
/* handle only DCC CTCPs */
g_free(params);
return;
}
dcc = dcc_find_item(DCC_TYPE_CHAT, target+1, NULL);
if (dcc != NULL) {
g_strup(ctcpcmd);
str = g_strdup_printf("%s %s", ctcpcmd, ctcpdata);
dcc_ctcp_message(dcc->nick, NULL, dcc, FALSE, str);
g_free(str);
}
g_free(params);
}
/* DCC CHAT: text received */
static void dcc_chat_msg(DCC_REC *dcc, gchar *msg)
{
gchar *cmd, *ptr;
gboolean reply;
g_return_if_fail(dcc != NULL);
g_return_if_fail(msg != NULL);
reply = FALSE;
if (g_strncasecmp(msg, "CTCP_MESSAGE ", 13) != 0)
{
if (g_strncasecmp(msg, "CTCP_REPLY ", 11) != 0)
{
/* Use the mirc style CTCPing from now on.. */
dcc->mirc_ctcp = TRUE;
}
else
{
/* bitchx (and ircii?) sends this */
msg += 11;
reply = TRUE;
dcc->mirc_ctcp = FALSE;
}
}
else
{
/* bitchx (and ircii?) sends this */
msg += 13;
dcc->mirc_ctcp = FALSE;
}
/* Handle only DCC CTCPs */
if (*msg != 1)
return;
msg = g_strdup(msg+1);
/* remove the later \001 */
ptr = strrchr(msg, 1);
if (ptr != NULL) *ptr = '\0';
/* get ctcp command */
cmd = g_strconcat(reply ? "dcc reply " : "dcc ctcp ", msg, NULL);
ptr = strchr(cmd+9, ' ');
if (ptr != NULL) *ptr++ = '\0'; else ptr = "";
g_strdown(cmd+9);
if (!signal_emit(cmd, 2, ptr, dcc))
signal_emit(reply ? "default dcc reply" : "default dcc ctcp", 2, msg, dcc);
g_free(cmd);
g_free(msg);
signal_stop();
}
/* input function: DCC CHAT received some data.. */
static void dcc_chat_input(DCC_REC *dcc)
{
char tmpbuf[512], *str;
int recvlen, ret;
g_return_if_fail(dcc != NULL);
do {
recvlen = net_receive(dcc->handle, tmpbuf, sizeof(tmpbuf));
ret = line_split(tmpbuf, recvlen, &str, (LINEBUF_REC **) &dcc->databuf);
if (ret == -1) {
/* connection lost */
dcc->destroyed = TRUE;
signal_emit("dcc closed", 1, dcc);
dcc_destroy(dcc);
break;
}
if (ret > 0) {
dcc->transfd += ret;
signal_emit("dcc chat message", 2, dcc, str);
}
} while (ret > 0);
}
/* input function: DCC CHAT - someone tried to connect to our socket */
static void dcc_chat_listen(DCC_REC *dcc)
{
IPADDR ip;
gint handle, port;
g_return_if_fail(dcc != NULL);
/* accept connection */
handle = net_accept(dcc->handle, &ip, &port);
if (handle == -1)
return;
/* FIXME: add paranoia checking, check if host ip is the same as to who
we sent the DCC CHAT request.. */
g_source_remove(dcc->tagread);
close(dcc->handle);
dcc->starttime = time(NULL);
dcc->handle = handle;
memcpy(&dcc->addr, &ip, sizeof(IPADDR));
net_ip2host(&dcc->addr, dcc->addrstr);
dcc->port = port;
dcc->tagread = g_input_add(handle, G_INPUT_READ,
(GInputFunction) dcc_chat_input, dcc);
signal_emit("dcc connected", 1, dcc);
}
/* callback: DCC CHAT - net_connect_nonblock() finished */
static void dcc_chat_connect(DCC_REC *dcc)
{
g_return_if_fail(dcc != NULL);
g_source_remove(dcc->tagread);
if (net_geterror(dcc->handle) != 0)
{
/* error connecting */
signal_emit("dcc error connect", 1, dcc);
dcc_destroy(dcc);
return;
}
/* connect ok. */
dcc->starttime = time(NULL);
dcc->tagread = g_input_add(dcc->handle, G_INPUT_READ,
(GInputFunction) dcc_chat_input, dcc);
signal_emit("dcc connected", 1, dcc);
}
/* command: DCC CHAT */
static void cmd_dcc_chat(gchar *data, IRC_SERVER_REC *server)
{
DCC_REC *dcc;
IPADDR addr;
gchar *str;
gint port, handle;
g_return_if_fail(data != NULL);
if (*data == '\0') cmd_return_error(CMDERR_NOT_ENOUGH_PARAMS);
dcc = dcc_find_item(DCC_TYPE_CHAT, data, NULL);
if (dcc != NULL)
{
if (dcc->addrstr[0] == '\0' || dcc->starttime != 0)
{
/* already sent a chat request / already chatting */
return;
}
/* found from dcc list - so we're the connecting side.. */
dcc->handle = net_connect_ip(&dcc->addr, dcc->port,
source_host_ok ? source_host_ip : NULL);
if (dcc->handle != -1)
{
dcc->tagread = g_input_add(dcc->handle, G_INPUT_WRITE,
(GInputFunction) dcc_chat_connect, dcc);
}
else
{
/* error connecting */
signal_emit("dcc error connect", 1, dcc);
dcc_destroy(dcc);
}
return;
}
/* send dcc chat request */
if (server == NULL || !server->connected)
cmd_return_error(CMDERR_NOT_CONNECTED);
if (!net_getsockname(server->handle, &addr, NULL))
cmd_return_error(CMDERR_GETSOCKNAME);
port = settings_get_int("dcc_port");
handle = net_listen(&addr, &port);
if (handle == -1)
cmd_return_error(CMDERR_LISTEN);
dcc = dcc_create(DCC_TYPE_CHAT, handle, data, "chat", server, NULL);
dcc->tagread = g_input_add(dcc->handle, G_INPUT_READ,
(GInputFunction) dcc_chat_listen, dcc);
/* send the request */
str = g_strdup_printf("PRIVMSG %s :\001DCC CHAT CHAT %s %d\001",
data, dcc_make_address(&addr), port);
irc_send_cmd(server, str);
g_free(str);
}
static void cmd_mircdcc(gchar *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
DCC_REC *dcc;
g_return_if_fail(data != NULL);
dcc = irc_item_dcc_chat(item);
if (dcc == NULL) return;
dcc->mirc_ctcp = toupper(*data) == 'N' ? FALSE : TRUE;
}
static void dcc_ctcp_redirect(gchar *msg, DCC_REC *dcc)
{
g_return_if_fail(msg != NULL);
g_return_if_fail(dcc != NULL);
signal_emit("ctcp msg dcc", 6, msg, dcc->server, dcc->nick, "dcc", dcc->mynick, dcc);
}
void dcc_chat_init(void)
{
command_bind("msg", NULL, (SIGNAL_FUNC) dcc_chat_write);
command_bind("me", NULL, (SIGNAL_FUNC) dcc_chat_me);
command_bind("action", NULL, (SIGNAL_FUNC) dcc_chat_action);
command_bind("ctcp", NULL, (SIGNAL_FUNC) dcc_chat_ctcp);
command_bind("dcc chat", NULL, (SIGNAL_FUNC) cmd_dcc_chat);
signal_add_first("dcc chat message", (SIGNAL_FUNC) dcc_chat_msg);
command_bind("mircdcc", NULL, (SIGNAL_FUNC) cmd_mircdcc);
signal_add("dcc ctcp dcc", (SIGNAL_FUNC) dcc_ctcp_redirect);
}
void dcc_chat_deinit(void)
{
command_unbind("msg", (SIGNAL_FUNC) dcc_chat_write);
command_unbind("me", (SIGNAL_FUNC) dcc_chat_me);
command_unbind("action", (SIGNAL_FUNC) dcc_chat_action);
command_unbind("ctcp", (SIGNAL_FUNC) dcc_chat_ctcp);
command_unbind("dcc chat", (SIGNAL_FUNC) cmd_dcc_chat);
signal_remove("dcc chat message", (SIGNAL_FUNC) dcc_chat_msg);
command_unbind("mircdcc", (SIGNAL_FUNC) cmd_mircdcc);
signal_remove("dcc ctcp dcc", (SIGNAL_FUNC) dcc_ctcp_redirect);
}

7
src/irc/dcc/dcc-chat.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef __DCC_CHAT_H
#define __DCC_CHAT_H
void dcc_chat_init(void);
void dcc_chat_deinit(void);
#endif

577
src/irc/dcc/dcc-files.c Normal file
View file

@ -0,0 +1,577 @@
/*
dcc-files.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 "commands.h"
#include "network.h"
#include "line-split.h"
#include "misc.h"
#include "settings.h"
#include "masks.h"
#include "irc.h"
#include "server-setup.h"
#include "dcc.h"
static gint dcc_file_create_mode;
static gchar *dcc_prepare_path(gchar *fname)
{
gchar *str, *ptr, *downpath;
/* strip all paths from file. */
ptr = strrchr(fname, '/');
if (ptr == NULL) ptr = fname; else ptr++;
downpath = convert_home(settings_get_str("dcc_download_path"));
str = g_strdup_printf("%s/%s", downpath, ptr);
g_free(downpath);
return str;
}
/* input function: DCC GET received data */
static void dcc_receive(DCC_REC *dcc)
{
guint32 recd;
gint len, ret;
g_return_if_fail(dcc != NULL);
for (;;)
{
len = net_receive(dcc->handle, dcc->databuf, dcc->databufsize);
if (len == 0) break;
if (len < 0)
{
/* socket closed - transmit complete (or other side died..) */
signal_emit("dcc closed", 1, dcc);
dcc_destroy(dcc);
return;
}
write(dcc->fhandle, dcc->databuf, len);
dcc->transfd += len;
}
/* send number of total bytes received - if for some reason we couldn't
send the 4 characters last time, try to somehow fix it this time by
sending missing amount of 0 characters.. */
if (dcc->trans_bytes != 0)
{
recd = (guint32) htonl(0);
dcc->trans_bytes += net_transmit(dcc->handle, ((gchar *) &recd)+dcc->trans_bytes, 4-dcc->trans_bytes);
if (dcc->trans_bytes == 4) dcc->trans_bytes = 0;
}
if (dcc->trans_bytes == 0)
{
recd = (guint32) htonl(dcc->transfd);
ret = net_transmit(dcc->handle, ((gchar *) &recd), 4);
if (ret > 0 && ret < 4) dcc->trans_bytes = ret;
}
signal_emit("dcc transfer update", 1, dcc);
}
/* callback: net_connect() finished for DCC GET */
static void dcc_get_connect(DCC_REC *dcc)
{
struct stat statbuf;
g_return_if_fail(dcc != NULL);
g_source_remove(dcc->tagread);
if (net_geterror(dcc->handle) != 0)
{
/* error connecting */
signal_emit("dcc error connect", 1, dcc);
dcc_destroy(dcc);
return;
}
dcc->file = dcc_prepare_path(dcc->arg);
/* if some plugin wants to change the file name/path here.. */
signal_emit("dcc get receive", 1, dcc);
if (stat(dcc->file, &statbuf) == 0 &&
(dcc->get_type == DCC_GET_RENAME || dcc->get_type == DCC_GET_DEFAULT))
{
/* file exists, rename.. */
GString *newname;
gint num;
newname = g_string_new(NULL);
for (num = 1; ; num++)
{
g_string_sprintf(newname, "%s.%d", dcc->file, num);
if (stat(newname->str, &statbuf) != 0) break;
}
g_free(dcc->file);
dcc->file = newname->str;
g_string_free(newname, FALSE);
}
if (dcc->get_type != DCC_GET_RESUME)
{
dcc->fhandle = open(dcc->file, O_WRONLY | O_TRUNC | O_CREAT, dcc_file_create_mode);
if (dcc->fhandle == -1)
{
signal_emit("dcc error file create", 2, dcc, dcc->file);
dcc_destroy(dcc);
return;
}
}
dcc->databufsize = settings_get_int("dcc_block_size") > 0 ? settings_get_int("dcc_block_size") : 2048;
dcc->databuf = g_malloc(dcc->databufsize);
dcc->starttime = time(NULL);
dcc->tagread = g_input_add(dcc->handle, G_INPUT_READ,
(GInputFunction) dcc_receive, dcc);
signal_emit("dcc connected", 1, dcc);
}
/* command: DCC GET */
static void cmd_dcc_get(gchar *data)
{
DCC_REC *dcc;
GSList *tmp, *next;
gchar *params, *nick, *fname;
gboolean found;
g_return_if_fail(data != NULL);
params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &nick, &fname);
if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
dcc = NULL; found = FALSE;
for (tmp = dcc_conns; tmp != NULL; tmp = next)
{
dcc = tmp->data;
next = tmp->next;
if (dcc->dcc_type == DCC_TYPE_GET && dcc->handle == -1 && g_strcasecmp(dcc->nick, nick) == 0 &&
(*fname == '\0' || strcmp(dcc->arg, fname) == 0))
{
/* found! */
found = TRUE;
dcc->handle = net_connect_ip(&dcc->addr, dcc->port,
source_host_ok ? source_host_ip : NULL);
if (dcc->handle != -1)
{
dcc->tagread = g_input_add(dcc->handle, G_INPUT_WRITE,
(GInputFunction) dcc_get_connect, dcc);
}
else
{
/* error connecting */
signal_emit("dcc error connect", 1, dcc);
dcc_destroy(dcc);
}
}
}
if (!found)
signal_emit("dcc error get not found", 1, nick);
g_free(params);
}
/* resume setup: DCC SEND - we either said resume on get, or when we sent,
someone chose resume */
static void dcc_resume_setup(DCC_REC *dcc, gint port)
{
gchar *str;
/* Check for DCC_SEND_RESUME */
if (dcc->dcc_type == DCC_TYPE_SEND)
{
if (lseek(dcc->fhandle, dcc->transfd, SEEK_SET) == -1)
{
signal_emit("dcc closed", 1, dcc);
dcc_destroy(dcc);
return;
}
else
{
str = g_strdup_printf("DCC ACCEPT %s %d %lu",
dcc->arg, port, dcc->transfd);
dcc_ctcp_message(dcc->nick, dcc->server, dcc->chat, FALSE, str);
g_free(str);
}
}
/* Check for DCC_GET_RESUME */
if (dcc->dcc_type == DCC_TYPE_GET && dcc->get_type == DCC_GET_RESUME)
{
dcc->handle = net_connect_ip(&dcc->addr, dcc->port,
source_host_ok ? source_host_ip : NULL);
if (dcc->handle != -1)
{
dcc->tagread = g_input_add(dcc->handle, G_INPUT_WRITE,
(GInputFunction) dcc_get_connect, dcc);
}
else
{
/* error connecting */
signal_emit("dcc error connect", 1, dcc);
dcc_destroy(dcc);
}
}
}
static void dcc_ctcp_msg(gchar *data, IRC_SERVER_REC *server, gchar *sender, gchar *sendaddr, gchar *target, DCC_REC *chat)
{
gchar *params, *type, *arg, *portstr, *sizestr;
gulong size;
gint port;
DCC_REC *dcc;
g_return_if_fail(data != NULL);
g_return_if_fail(sender != NULL);
params = cmd_get_params(data, 4, &type, &arg, &portstr, &sizestr);
if (g_strcasecmp(type, "RESUME") == 0 || g_strcasecmp(type, "ACCEPT") == 0)
{
if (sscanf(portstr, "%d", &port) != 1) port = 0;
if (sscanf(sizestr, "%lu", &size) != 1) size = 0;
dcc = dcc_find_by_port(sender, port);
if (dcc != NULL && (dcc->dcc_type == DCC_TYPE_GET || dcc->transfd == 0))
{
dcc->transfd = size;
dcc->skipped = size;
dcc_resume_setup(dcc, port);
}
}
g_free(params);
}
static void dcc_resume_rec(DCC_REC *dcc)
{
gchar *str;
dcc->file = dcc_prepare_path(dcc->arg);
dcc->fhandle = open(dcc->file, O_WRONLY, dcc_file_create_mode);
if (dcc->fhandle == -1)
{
signal_emit("dcc error file not found", 2, dcc, dcc->file);
dcc_destroy(dcc);
}
else
{
dcc->transfd = lseek(dcc->fhandle, 0, SEEK_END);
if (dcc->transfd < 0) dcc->transfd = 0;
dcc->skipped = dcc->transfd;
str = g_strdup_printf("DCC RESUME %s %d %lu",
dcc->arg, dcc->port, dcc->transfd);
dcc_ctcp_message(dcc->nick, dcc->server, dcc->chat, FALSE, str);
g_free(str);
}
}
/* command: DCC RESUME */
static void cmd_dcc_resume(gchar *data)
{
DCC_REC *dcc;
GSList *tmp;
gchar *params, *nick, *fname;
gboolean found;
g_return_if_fail(data != NULL);
params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &nick, &fname);
if (*nick == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
dcc = NULL; found = FALSE;
for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next)
{
dcc = tmp->data;
if (dcc->dcc_type == DCC_TYPE_GET && dcc->handle == -1 && g_strcasecmp(dcc->nick, nick) == 0 &&
(*fname == '\0' || strcmp(dcc->arg, fname) == 0))
{
/* found! */
dcc->get_type = DCC_GET_RESUME;
dcc_resume_rec(dcc);
found = TRUE;
}
}
if (!found)
signal_emit("dcc error get not found", 1, nick);
g_free(params);
}
/* input function: DCC SEND send more data */
static void dcc_send_data(DCC_REC *dcc)
{
gint n;
g_return_if_fail(dcc != NULL);
if (!dcc->fastsend && !dcc->gotalldata)
{
/* haven't received everything we've send there yet.. */
return;
}
n = read(dcc->fhandle, dcc->databuf, dcc->databufsize);
if (n <= 0)
{
/* end of file .. or some error .. */
if (dcc->fastsend)
{
/* no need to call this function anymore.. in fact it just eats
all the cpu.. */
dcc->waitforend = TRUE;
g_source_remove(dcc->tagwrite);
dcc->tagwrite = -1;
}
else
{
signal_emit("dcc closed", 1, dcc);
dcc_destroy(dcc);
}
return;
}
dcc->transfd += net_transmit(dcc->handle, dcc->databuf, n);
dcc->gotalldata = FALSE;
lseek(dcc->fhandle, dcc->transfd, SEEK_SET);
signal_emit("dcc transfer update", 1, dcc);
}
/* input function: DCC SEND received some data */
static void dcc_send_read_size(DCC_REC *dcc)
{
guint32 bytes;
gint ret;
g_return_if_fail(dcc != NULL);
if (dcc->read_pos == 4)
return;
/* we need to get 4 bytes.. */
ret = net_receive(dcc->handle, dcc->read_buf+dcc->read_pos, 4-dcc->read_pos);
if (ret == -1)
{
signal_emit("dcc closed", 1, dcc);
dcc_destroy(dcc);
return;
}
dcc->read_pos += ret;
if (dcc->read_pos == 4)
{
bytes = 0; memcpy(&bytes, dcc->read_buf, 4);
bytes = (guint32) ntohl(bytes);
dcc->gotalldata = bytes == dcc->transfd;
dcc->read_pos = 0;
if (!dcc->fastsend)
{
/* send more data.. */
dcc_send_data(dcc);
}
if (dcc->waitforend && dcc->gotalldata)
{
/* file is sent */
signal_emit("dcc closed", 1, dcc);
dcc_destroy(dcc);
return;
}
}
}
/* input function: DCC SEND - someone tried to connect to our socket */
static void dcc_send_init(DCC_REC *dcc)
{
gint handle, port;
IPADDR addr;
g_return_if_fail(dcc != NULL);
/* accept connection */
handle = net_accept(dcc->handle, &addr, &port);
if (handle == -1)
return;
/* FIXME: add paranoia checking, check if host ip is the same as to who
we sent the DCC SEND request.. */
g_source_remove(dcc->tagread);
close(dcc->handle);
dcc->fastsend = settings_get_bool("toggle_dcc_fast_send");
dcc->handle = handle;
memcpy(&dcc->addr, &addr, sizeof(IPADDR));
net_ip2host(&dcc->addr, dcc->addrstr);
dcc->port = port;
dcc->databufsize = settings_get_int("dcc_block_size") > 0 ? settings_get_int("dcc_block_size") : 2048;
dcc->databuf = g_malloc(dcc->databufsize);
dcc->starttime = time(NULL);
dcc->tagread = g_input_add(handle, G_INPUT_READ,
(GInputFunction) dcc_send_read_size, dcc);
dcc->tagwrite = !dcc->fastsend ? -1 :
g_input_add(handle, G_INPUT_WRITE, (GInputFunction) dcc_send_data, dcc);
signal_emit("dcc connected", 1, dcc);
if (!dcc->fastsend)
{
/* send first block */
dcc->gotalldata = TRUE;
dcc_send_data(dcc);
}
}
/* command: DCC SEND */
static void cmd_dcc_send(gchar *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
gchar *params, *target, *fname, *str, *ptr;
gint fh, h, port;
glong fsize;
DCC_REC *dcc, *chat;
IPADDR addr;
g_return_if_fail(data != NULL);
params = cmd_get_params(data, 2 | PARAM_FLAG_GETREST, &target, &fname);
/* if we're in dcc chat, send the request via it. */
chat = irc_item_dcc_chat(item);
if (chat != NULL && (chat->mirc_ctcp || g_strcasecmp(target, chat->nick) != 0))
chat = NULL;
if ((server == NULL || !server->connected) && chat == NULL)
cmd_param_error(CMDERR_NOT_CONNECTED);
if (*target == '\0' || *fname == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
if (dcc_find_item(DCC_TYPE_SEND, target, fname))
{
signal_emit("dcc error send exists", 2, target, fname);
g_free(params);
return;
}
str = convert_home(fname);
if (*str != '/')
{
gchar *path;
g_free(str);
path = convert_home(settings_get_str("dcc_upload_path"));
str = g_strconcat(path, "/", fname, NULL);
g_free(path);
}
fh = open(str, O_RDONLY);
g_free(str);
if (fh == -1)
{
signal_emit("dcc error file not found", 2, target, fname);
g_free(params);
return;
}
fsize = lseek(fh, 0, SEEK_END);
lseek(fh, 0, SEEK_SET);
/* get the IP address we use with IRC server */
if (!net_getsockname(chat != NULL ? chat->handle : server->handle, &addr, NULL))
{
close(fh);
cmd_param_error(CMDERR_GETSOCKNAME);
}
/* start listening */
port = settings_get_int("dcc_port");
h = net_listen(&addr, &port);
if (h == -1)
{
close(fh);
cmd_param_error(CMDERR_LISTEN);
}
/* skip path */
ptr = strrchr(fname, '/');
if (ptr != NULL) fname = ptr+1;
/* change all spaces to _ */
fname = g_strdup(fname);
for (ptr = fname; *ptr != '\0'; ptr++)
if (*ptr == ' ') *ptr = '_';
dcc = dcc_create(DCC_TYPE_SEND, h, target, fname, server, chat);
dcc->port = port;
dcc->size = fsize;
dcc->fhandle = fh;
dcc->tagread = g_input_add(h, G_INPUT_READ,
(GInputFunction) dcc_send_init, dcc);
/* send DCC request */
str = g_strdup_printf("DCC SEND %s %s %d %lu",
fname, dcc_make_address(&addr), port, fsize);
dcc_ctcp_message(target, server, chat, FALSE, str);
g_free(str);
g_free(fname);
g_free(params);
}
static void read_settings(void)
{
dcc_file_create_mode = octal2dec(settings_get_int("dcc_file_create_mode"));
}
void dcc_files_init(void)
{
signal_add("ctcp msg dcc", (SIGNAL_FUNC) dcc_ctcp_msg);
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
signal_add("irssi init finished", (SIGNAL_FUNC) read_settings);
command_bind("dcc send", NULL, (SIGNAL_FUNC) cmd_dcc_send);
command_bind("dcc get", NULL, (SIGNAL_FUNC) cmd_dcc_get);
command_bind("dcc resume", NULL, (SIGNAL_FUNC) cmd_dcc_resume);
}
void dcc_files_deinit(void)
{
signal_remove("ctcp msg dcc", (SIGNAL_FUNC) dcc_ctcp_msg);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
signal_remove("irssi init finished", (SIGNAL_FUNC) read_settings);
command_unbind("dcc send", (SIGNAL_FUNC) cmd_dcc_send);
command_unbind("dcc get", (SIGNAL_FUNC) cmd_dcc_get);
command_unbind("dcc resume", (SIGNAL_FUNC) cmd_dcc_resume);
}

7
src/irc/dcc/dcc-files.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef __DCC_FILES_H
#define __DCC_FILES_H
void dcc_files_init(void);
void dcc_files_deinit(void);
#endif

550
src/irc/dcc/dcc.c Normal file
View file

@ -0,0 +1,550 @@
/*
dcc.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 "network.h"
#include "line-split.h"
#include "settings.h"
#include "masks.h"
#include "irc.h"
#include "dcc.h"
#define DCC_TYPES 5
static gchar *dcc_types[] =
{
"CHAT",
"SEND",
"GET",
"RESUME",
"ACCEPT"
};
GSList *dcc_conns;
static gint dcc_timeouttag;
/* Create new DCC record */
DCC_REC *dcc_create(gint type, gint handle, gchar *nick, gchar *arg, IRC_SERVER_REC *server, DCC_REC *chat)
{
DCC_REC *dcc;
g_return_val_if_fail(nick != NULL, NULL);
g_return_val_if_fail(arg != NULL, NULL);
dcc = g_new0(DCC_REC, 1);
dcc->type = type == DCC_TYPE_CHAT ? module_get_uniq_id("IRC", WI_IRC_DCC_CHAT) : -1;
dcc->mirc_ctcp = settings_get_bool("toggle_dcc_mirc_ctcp");
dcc->created = time(NULL);
dcc->chat = chat;
dcc->dcc_type = type;
dcc->arg = g_strdup(arg);
dcc->nick = g_strdup(nick);
dcc->handle = handle;
dcc->fhandle = -1;
dcc->tagread = dcc->tagwrite = -1;
dcc->server = server;
dcc->mynick = g_strdup(server != NULL ? server->nick :
chat != NULL ? chat->nick : "??");
dcc->ircnet = server == NULL ?
chat == NULL || chat->ircnet == NULL ? NULL : g_strdup(chat->ircnet) :
server->connrec->ircnet == NULL ? NULL : g_strdup(server->connrec->ircnet);
dcc_conns = g_slist_append(dcc_conns, dcc);
signal_emit("dcc created", 1, dcc);
return dcc;
}
/* Destroy DCC record */
void dcc_destroy(DCC_REC *dcc)
{
GSList *tmp;
g_return_if_fail(dcc != NULL);
dcc_conns = g_slist_remove(dcc_conns, dcc);
/* remove dcc chat references.. */
for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next)
{
DCC_REC *rec = tmp->data;
if (rec->chat == dcc)
rec->chat = NULL;
}
signal_emit("dcc destroyed", 1, dcc);
if (dcc->fhandle != -1) close(dcc->fhandle);
if (dcc->handle != -1) net_disconnect(dcc->handle);
if (dcc->tagread != -1) g_source_remove(dcc->tagread);
if (dcc->tagwrite != -1) g_source_remove(dcc->tagwrite);
if (dcc->dcc_type == DCC_TYPE_CHAT)
line_split_free((LINEBUF_REC *) dcc->databuf);
else if (dcc->databuf != NULL) g_free(dcc->databuf);
if (dcc->file != NULL) g_free(dcc->file);
if (dcc->ircnet != NULL) g_free(dcc->ircnet);
g_free(dcc->mynick);
g_free(dcc->nick);
g_free(dcc->arg);
g_free(dcc);
}
gchar *dcc_make_address(IPADDR *ip)
{
static gchar str[MAX_IP_LEN];
gulong addr;
if (is_ipv6_addr(ip))
{
/* IPv6 */
net_ip2host(ip, str);
}
else
{
memcpy(&addr, &ip->addr, 4);
sprintf(str, "%lu", (unsigned long) htonl(addr));
}
return str;
}
/* Find DCC record, arg can be NULL */
DCC_REC *dcc_find_item(gint type, gchar *nick, gchar *arg)
{
DCC_REC *dcc;
GSList *tmp;
g_return_val_if_fail(nick != NULL, NULL);
for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next)
{
dcc = tmp->data;
if (dcc->dcc_type == type && g_strcasecmp(dcc->nick, nick) == 0 &&
(arg == NULL || strcmp(dcc->arg, arg) == 0))
return dcc;
}
return NULL;
}
/* Find DCC record by port # */
DCC_REC *dcc_find_by_port(gchar *nick, gint port)
{
DCC_REC *dcc;
GSList *tmp;
for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next)
{
dcc = tmp->data;
if (dcc->port == port && ((dcc->dcc_type == DCC_TYPE_GET || dcc->dcc_type == DCC_TYPE_SEND) && g_strcasecmp(dcc->nick, nick) == 0))
{
/* found! */
return dcc;
}
}
return NULL;
}
gchar *dcc_type2str(gint type)
{
g_return_val_if_fail(type >= 1 && type <= DCC_TYPES, NULL);
return dcc_types[type-1];
}
gint dcc_str2type(gchar *type)
{
gint num;
for (num = 0; num < DCC_TYPES; num++)
if (g_strcasecmp(dcc_types[num], type) == 0) return num+1;
return 0;
}
void dcc_ctcp_message(gchar *target, IRC_SERVER_REC *server, DCC_REC *chat, gboolean notice, gchar *msg)
{
gchar *str;
if (chat != NULL)
{
/* send it via open DCC chat */
/* FIXME: we need output queue! */
str = g_strdup_printf("%s\001%s\001\n", chat->mirc_ctcp ? "" :
notice ? "CTCP_REPLY " : "CTCP_MESSAGE ", msg);
net_transmit(chat->handle, str, strlen(str));
}
else
{
str = g_strdup_printf("%s %s :\001%s\001",
notice ? "NOTICE" : "PRIVMSG", target, msg);
irc_send_cmd(server, str);
}
g_free(str);
}
/* Server connected, check if there's any open dcc sessions for this ircnet.. */
static void dcc_server_connected(IRC_SERVER_REC *server)
{
GSList *tmp;
g_return_if_fail(server != NULL);
if (server->connrec->ircnet == NULL)
return;
for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next) {
DCC_REC *dcc = tmp->data;
if (dcc->server == NULL && dcc->ircnet != NULL &&
g_strcasecmp(dcc->ircnet, server->connrec->ircnet) == 0) {
dcc->server = server;
g_free(dcc->mynick);
dcc->mynick = g_strdup(server->nick);
}
}
}
/* Server disconnected, remove it from all dcc records */
static void dcc_server_disconnected(IRC_SERVER_REC *server)
{
GSList *tmp;
g_return_if_fail(server != NULL);
for (tmp = dcc_conns; tmp != NULL; tmp = tmp->next)
{
DCC_REC *dcc = tmp->data;
if (dcc->server == server)
{
if (dcc->ircnet == NULL)
dcc->server = NULL;
else
{
dcc->server = (IRC_SERVER_REC *) server_find_ircnet(dcc->ircnet);
if (dcc->server != NULL)
{
g_free(dcc->mynick);
dcc->mynick = g_strdup(dcc->server->nick);
}
}
}
}
}
static void dcc_get_address(gchar *str, IPADDR *ip)
{
gulong addr;
if (strchr(str, ':') == NULL)
{
/* normal IPv4 address */
if (sscanf(str, "%lu", &addr)!=1)
addr = 0;
ip->family = AF_INET;
addr = (gulong) ntohl(addr);
memcpy(&ip->addr, &addr, 4);
}
else
{
/* IPv6 */
net_host2ip(str, ip);
}
}
/* Handle incoming DCC CTCP messages */
static void dcc_ctcp_msg(gchar *data, IRC_SERVER_REC *server, gchar *sender, gchar *sendaddr, gchar *target, DCC_REC *chat)
{
gchar *params, *type, *arg, *addrstr, *portstr, *sizestr, *str;
const char *cstr;
DCC_REC *dcc;
gulong size;
gint port;
g_return_if_fail(data != NULL);
g_return_if_fail(sender != NULL);
params = cmd_get_params(data, 5, &type, &arg, &addrstr, &portstr, &sizestr);
if (sscanf(portstr, "%d", &port) != 1) port = 0;
if (sscanf(sizestr, "%lu", &size) != 1) size = 0;
dcc = dcc_create(SWAP_SENDGET(dcc_str2type(type)), -1, sender, arg, server, chat);
dcc_get_address(addrstr, &dcc->addr);
net_ip2host(&dcc->addr, dcc->addrstr);
dcc->port = port;
dcc->size = size;
switch (dcc->dcc_type)
{
case DCC_TYPE_GET:
cstr = settings_get_str("dcc_autoget_masks");
/* check that autoget masks match */
if (settings_get_bool("toggle_dcc_autoget") && (*cstr == '\0' || irc_masks_match(cstr, sender, sendaddr)) &&
/* check file size limit, FIXME: it's possible to send a bogus file size and then just send what ever sized file.. */
(settings_get_int("dcc_max_autoget_size") <= 0 || (settings_get_int("dcc_max_autoget_size") > 0 && size <= settings_get_int("dcc_max_autoget_size")*1024)))
{
/* automatically get */
str = g_strdup_printf("GET %s %s", dcc->nick, dcc->arg);
signal_emit("command dcc", 2, str, server);
g_free(str);
}
else
{
/* send request */
signal_emit("dcc request", 1, dcc);
}
break;
case DCC_TYPE_CHAT:
cstr = settings_get_str("dcc_autochat_masks");
if (*cstr != '\0' && irc_masks_match(cstr, sender, sendaddr))
{
/* automatically accept chat */
str = g_strdup_printf("CHAT %s", dcc->nick);
signal_emit("command dcc", 2, str, server);
g_free(str);
}
else
{
/* send request */
signal_emit("dcc request", 1, dcc);
}
break;
case DCC_TYPE_RESUME:
case DCC_TYPE_ACCEPT:
/* handle this in dcc-files.c */
dcc_destroy(dcc);
break;
default:
/* unknown DCC command */
signal_emit("dcc unknown ctcp", 3, data, sender, sendaddr);
dcc_destroy(dcc);
break;
}
g_free(params);
}
/* Handle incoming DCC CTCP replies */
static void dcc_ctcp_reply(gchar *data, IRC_SERVER_REC *server, gchar *sender, gchar *sendaddr)
{
gchar *params, *cmd, *subcmd, *args;
gint type;
DCC_REC *dcc;
g_return_if_fail(data != NULL);
g_return_if_fail(sender != NULL);
params = cmd_get_params(data, 3 | PARAM_FLAG_GETREST, &cmd, &subcmd, &args);
if (g_strcasecmp(cmd, "REJECT") == 0)
{
type = dcc_str2type(subcmd);
dcc = dcc_find_item(type, sender, type == DCC_TYPE_CHAT ? NULL : args);
if (dcc != NULL)
{
dcc->destroyed = TRUE;
signal_emit("dcc closed", 1, dcc);
dcc_destroy(dcc);
}
}
else
{
/* unknown dcc ctcp reply */
signal_emit("dcc unknown reply", 3, data, sender, sendaddr);
}
g_free(params);
}
static void dcc_reject(DCC_REC *dcc, IRC_SERVER_REC *server)
{
gchar *str;
g_return_if_fail(dcc != NULL);
if (dcc->server != NULL) server = dcc->server;
if (server != NULL && (dcc->dcc_type != DCC_TYPE_CHAT || dcc->starttime == 0))
{
signal_emit("dcc rejected", 1, dcc);
str = g_strdup_printf("NOTICE %s :\001DCC REJECT %s %s\001",
dcc->nick, dcc_type2str(SWAP_SENDGET(dcc->dcc_type)), dcc->arg);
irc_send_cmd(server, str);
g_free(str);
}
dcc->destroyed = TRUE;
signal_emit("dcc closed", 1, dcc);
dcc_destroy(dcc);
}
/* command: DCC CLOSE */
static void cmd_dcc_close(gchar *data, IRC_SERVER_REC *server)
{
DCC_REC *dcc;
GSList *tmp, *next;
gchar *params, *type, *nick, *arg;
gboolean found;
gint itype;
g_return_if_fail(data != NULL);
params = cmd_get_params(data, 3, &type, &nick, &arg);
g_strup(type);
itype = dcc_str2type(type);
if (itype == 0)
{
signal_emit("dcc error unknown type", 1, type);
g_free(params);
return;
}
dcc = NULL; found = FALSE;
for (tmp = dcc_conns; tmp != NULL; tmp = next)
{
dcc = tmp->data;
next = tmp->next;
if (dcc->dcc_type == itype && g_strcasecmp(nick, dcc->nick) == 0)
{
dcc_reject(dcc, server);
found = TRUE;
}
}
if (!found)
signal_emit("dcc error close not found", 3, type, nick, arg);
g_free(params);
}
static void cmd_dcc(const char *data, IRC_SERVER_REC *server, WI_IRC_REC *item)
{
command_runsub("dcc", data, server, item);
}
static int dcc_timeout_func(void)
{
GSList *tmp, *next;
time_t now;
now = time(NULL)-settings_get_int("dcc_timeout");
for (tmp = dcc_conns; tmp != NULL; tmp = next)
{
DCC_REC *rec = tmp->data;
next = tmp->next;
if (rec->tagread == -1 && now > rec->created)
{
/* timed out. */
dcc_reject(rec, NULL);
}
}
return 1;
}
static void event_no_such_nick(gchar *data, IRC_SERVER_REC *server)
{
gchar *params, *nick;
GSList *tmp, *next;
g_return_if_fail(data != NULL);
params = event_get_params(data, 2, NULL, &nick);
/* check if we've send any dcc requests to this nick.. */
for (tmp = dcc_conns; tmp != NULL; tmp = next)
{
DCC_REC *rec = tmp->data;
next = tmp->next;
if (g_strcasecmp(rec->nick, nick) == 0 && rec->starttime == 0)
{
/* timed out. */
rec->destroyed = TRUE;
signal_emit("dcc closed", 1, rec);
dcc_destroy(rec);
}
}
g_free(params);
}
void dcc_init(void)
{
dcc_conns = NULL;
dcc_timeouttag = g_timeout_add(1000, (GSourceFunc) dcc_timeout_func, NULL);
settings_add_bool("dcc", "toggle_dcc_autorename", FALSE);
settings_add_bool("dcc", "toggle_dcc_autogete", FALSE);
settings_add_int("dcc", "dcc_max_autoget_size", 1000);
settings_add_str("dcc", "dcc_download_path", "~");
settings_add_int("dcc", "dcc_file_create_mode", 644);
settings_add_str("dcc", "dcc_autoget_masks", "");
settings_add_str("dcc", "dcc_autochat_masks", "");
settings_add_bool("dcc", "toggle_dcc_fast_send", TRUE);
settings_add_str("dcc", "dcc_upload_path", "~");
settings_add_bool("dcc", "toggle_dcc_mirc_ctcp", FALSE);
settings_add_bool("dcc", "toggle_dcc_autodisplay_dialog", TRUE);
settings_add_int("dcc", "dcc_block_size", 2048);
settings_add_int("dcc", "dcc_port", 0);
settings_add_int("dcc", "dcc_timeout", 300);
signal_add("server connected", (SIGNAL_FUNC) dcc_server_connected);
signal_add("server disconnected", (SIGNAL_FUNC) dcc_server_disconnected);
signal_add("ctcp reply dcc", (SIGNAL_FUNC) dcc_ctcp_reply);
signal_add("ctcp msg dcc", (SIGNAL_FUNC) dcc_ctcp_msg);
command_bind("dcc", NULL, (SIGNAL_FUNC) cmd_dcc);
command_bind("dcc close", NULL, (SIGNAL_FUNC) cmd_dcc_close);
signal_add("event 401", (SIGNAL_FUNC) event_no_such_nick);
}
void dcc_deinit(void)
{
signal_remove("server connected", (SIGNAL_FUNC) dcc_server_connected);
signal_remove("server disconnected", (SIGNAL_FUNC) dcc_server_disconnected);
signal_remove("ctcp reply dcc", (SIGNAL_FUNC) dcc_ctcp_reply);
signal_remove("ctcp msg dcc", (SIGNAL_FUNC) dcc_ctcp_msg);
command_unbind("dcc", (SIGNAL_FUNC) cmd_dcc);
command_unbind("dcc close", (SIGNAL_FUNC) cmd_dcc_close);
signal_remove("event 401", (SIGNAL_FUNC) event_no_such_nick);
g_source_remove(dcc_timeouttag);
while (dcc_conns != NULL)
dcc_destroy(dcc_conns->data);
}

91
src/irc/dcc/dcc.h Normal file
View file

@ -0,0 +1,91 @@
#ifndef __DCC_H
#define __DCC_H
#include "network.h"
enum
{
DCC_TYPE_CHAT = 1,
DCC_TYPE_SEND,
DCC_TYPE_GET,
DCC_TYPE_RESUME,
DCC_TYPE_ACCEPT
};
enum
{
DCC_GET_DEFAULT = 0,
DCC_GET_OVERWRITE,
DCC_GET_RENAME,
DCC_GET_RESUME
};
#define SWAP_SENDGET(a) ((a) == DCC_TYPE_SEND ? DCC_TYPE_GET : \
(a) == DCC_TYPE_GET ? DCC_TYPE_SEND : (a))
typedef struct DCC_REC
{
int type;
GHashTable *module_data;
IRC_SERVER_REC *server;
gchar *nick;
struct DCC_REC *chat; /* if the request came through DCC chat */
gchar *ircnet;
gchar *mynick;
gchar *arg;
gchar *file; /* file name we're really moving, arg is just the reference.. */
time_t created;
gint dcc_type;
IPADDR addr; /* address we're connected in */
gchar addrstr[MAX_IP_LEN]; /* in readable form */
gint port; /* port we're connected in */
glong size, transfd, skipped; /* file size / bytes transferred / skipped at start */
gint handle; /* socket handle */
gint tagread, tagwrite;
gint fhandle; /* file handle */
time_t starttime; /* transfer start time */
gint trans_bytes;
gboolean fastsend; /* fastsending (just in case that global fastsend toggle changes while transferring..) */
gboolean waitforend; /* DCC fast send: file is sent, just wait for the replies from the other side */
gboolean gotalldata; /* DCC fast send: got all acks from the other end (needed to make sure the end of transfer works right) */
gint get_type; /* DCC get: what to do if file exists? */
gboolean mirc_ctcp; /* DCC chat: Send CTCPs without the CTCP_MESSAGE prefix */
gboolean destroyed; /* We're about to destroy this DCC recond */
/* read counter buffer */
gchar read_buf[4];
gint read_pos;
gchar *databuf; /* buffer for receiving/transmitting data */
gint databufsize;
}
DCC_REC;
extern GSList *dcc_conns;
void dcc_init(void);
void dcc_deinit(void);
/* Find DCC record, arg can be NULL */
DCC_REC *dcc_find_item(gint type, gchar *nick, gchar *arg);
DCC_REC *dcc_find_by_port(gchar *nick, gint port);
gchar *dcc_type2str(gint type);
gint dcc_str2type(gchar *type);
gchar *dcc_make_address(IPADDR *ip);
DCC_REC *dcc_create(gint type, gint handle, gchar *nick, gchar *arg, IRC_SERVER_REC *server, DCC_REC *chat);
void dcc_destroy(DCC_REC *dcc);
void dcc_ctcp_message(gchar *target, IRC_SERVER_REC *server, DCC_REC *chat, gboolean notice, gchar *msg);
#endif

3
src/irc/dcc/module.h Normal file
View file

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

12
src/irc/flood/Makefile.am Normal file
View file

@ -0,0 +1,12 @@
noinst_LTLIBRARIES = libirc_flood.la
INCLUDES = $(GLIB_CFLAGS) \
-I$(top_srcdir)/src -I$(top_srcdir)/src/core/ -I$(top_srcdir)/src/irc/core/
libirc_flood_la_SOURCES = \
autoignore.c \
flood.c
noinst_HEADERS = \
autoignore.h \
flood.h

250
src/irc/flood/autoignore.c Normal file
View file

@ -0,0 +1,250 @@
/*
autoignore.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 "modules.h"
#include "signals.h"
#include "commands.h"
#include "levels.h"
#include "misc.h"
#include "settings.h"
#include "common-setup.h"
#include "irc-server.h"
#include "ignore.h"
#include "autoignore.h"
static int ignore_tag;
GSList *server_autoignores(IRC_SERVER_REC *server)
{
MODULE_SERVER_REC *rec;
g_return_val_if_fail(server != NULL, NULL);
rec = MODULE_DATA(server);
return rec->ignorelist;
}
static void autoignore_remove_rec(IRC_SERVER_REC *server, AUTOIGNORE_REC *rec)
{
MODULE_SERVER_REC *mserver;
g_return_if_fail(server != NULL);
g_return_if_fail(rec != NULL);
signal_emit("autoignore remove", 2, server, rec);
g_free(rec->nick);
g_free(rec);
mserver = MODULE_DATA(server);
mserver->ignorelist = g_slist_remove(mserver->ignorelist, rec);
}
static AUTOIGNORE_REC *autoignore_find(IRC_SERVER_REC *server, const char *mask)
{
MODULE_SERVER_REC *mserver;
GSList *tmp;
g_return_val_if_fail(server != NULL, NULL);
g_return_val_if_fail(mask != NULL, NULL);
mserver = MODULE_DATA(server);
for (tmp = mserver->ignorelist; tmp != NULL; tmp = tmp->next) {
AUTOIGNORE_REC *rec = tmp->data;
if (g_strcasecmp(rec->nick, mask) == 0)
return rec;
}
return NULL;
}
/* timeout function: unignore old ignores.. */
static void autoignore_timeout_server(IRC_SERVER_REC *server)
{
MODULE_SERVER_REC *mserver;
GSList *tmp, *next;
time_t t;
g_return_if_fail(server != NULL);
mserver = MODULE_DATA(server);
t = time(NULL);
t -= mserver->ignore_lastcheck;
for (tmp = mserver->ignorelist; tmp != NULL; tmp = next) {
AUTOIGNORE_REC *rec = tmp->data;
next = tmp->next;
if (rec->timeleft > t)
rec->timeleft -= t;
else
autoignore_remove_rec(server, rec);
}
mserver->ignore_lastcheck = time(NULL);
}
static int autoignore_timeout(void)
{
g_slist_foreach(servers, (GFunc) autoignore_timeout_server, NULL);
return 1;
}
static void autoignore_init_server(IRC_SERVER_REC *server)
{
MODULE_SERVER_REC *mserver;
g_return_if_fail(server != NULL);
mserver = MODULE_DATA(server);
mserver->ignorelist = NULL;
mserver->ignore_lastcheck = time(NULL)-AUTOIGNORE_TIMECHECK;
}
static void autoignore_deinit_server(IRC_SERVER_REC *server)
{
MODULE_SERVER_REC *mserver;
g_return_if_fail(server != NULL);
mserver = MODULE_DATA(server);
while (mserver->ignorelist != NULL)
autoignore_remove_rec(server, (AUTOIGNORE_REC *) mserver->ignorelist->data);
}
IGNORE_REC *ignore_find_server(IRC_SERVER_REC *server, const char *mask)
{
GSList *tmp;
for (tmp = ignores; tmp != NULL; tmp = tmp->next) {
IGNORE_REC *rec = tmp->data;
if (rec->servertag != NULL &&
g_strcasecmp(rec->mask, mask) == 0 &&
g_strcasecmp(rec->servertag, server->tag) == 0)
return rec;
}
return NULL;
}
void autoignore_add(IRC_SERVER_REC *server, const char *nick, int level)
{
MODULE_SERVER_REC *mserver;
AUTOIGNORE_REC *rec;
IGNORE_REC *irec;
int igtime;
g_return_if_fail(server != NULL);
g_return_if_fail(nick != NULL);
if (level == 0) return;
igtime = settings_get_int("autoignore_time");
if (igtime <= 0) return;
irec = ignore_find_server(server, nick);
if (irec == NULL) {
irec = g_new0(IGNORE_REC, 1);
irec->servertag = g_strdup(server->tag);
irec->mask = g_strdup(nick);
irec->level = level;
ignore_add_rec(irec);
} else {
irec->level |= level;
ignore_update_rec(irec);
}
rec = autoignore_find(server, nick);
if (rec != NULL) {
/* already being ignored */
rec->timeleft = igtime;
return;
}
rec = g_new(AUTOIGNORE_REC, 1);
rec->nick = g_strdup(nick);
rec->timeleft = igtime;
rec->level = level;
mserver = MODULE_DATA(server);
mserver->ignorelist = g_slist_append(mserver->ignorelist, rec);
signal_emit("autoignore new", 2, server, rec);
}
int autoignore_remove(IRC_SERVER_REC *server, const char *mask, int level)
{
AUTOIGNORE_REC *rec;
IGNORE_REC *irec;
g_return_val_if_fail(server != NULL, FALSE);
g_return_val_if_fail(mask != NULL, FALSE);
irec = ignore_find_server(server, mask);
if (irec != NULL) {
irec->level &= ~level;
ignore_update_rec(irec);
}
rec = autoignore_find(server, mask);
if (rec != NULL && (level & rec->level)) {
rec->level &= ~level;
if (rec->level == 0) autoignore_remove_rec(server, rec);
return TRUE;
}
return FALSE;
}
static void sig_flood(IRC_SERVER_REC *server, const char *nick, const char *host, const char *levelstr)
{
int level, check_level;
level = level2bits(levelstr);
check_level = level2bits(settings_get_str("autoignore_levels"));
if (level & check_level)
autoignore_add(server, nick, level);
}
void autoignore_init(void)
{
settings_add_int("flood", "autoignore_time", 300);
settings_add_str("flood", "autoignore_levels", "ctcps");
ignore_tag = g_timeout_add(AUTOIGNORE_TIMECHECK, (GSourceFunc) autoignore_timeout, NULL);
signal_add("server connected", (SIGNAL_FUNC) autoignore_init_server);
signal_add("server disconnected", (SIGNAL_FUNC) autoignore_deinit_server);
signal_add("flood", (SIGNAL_FUNC) sig_flood);
}
void autoignore_deinit(void)
{
g_source_remove(ignore_tag);
signal_remove("server connected", (SIGNAL_FUNC) autoignore_init_server);
signal_remove("server disconnected", (SIGNAL_FUNC) autoignore_deinit_server);
signal_remove("flood", (SIGNAL_FUNC) sig_flood);
}

View file

@ -0,0 +1,18 @@
#ifndef __AUTOIGNORE_H
#define __AUTOIGNORE_H
typedef struct {
char *nick;
int timeleft;
int level;
} AUTOIGNORE_REC;
GSList *server_autoignores(IRC_SERVER_REC *server);
void autoignore_add(IRC_SERVER_REC *server, const char *nick, int level);
int autoignore_remove(IRC_SERVER_REC *server, const char *mask, int level);
void autoignore_init(void);
void autoignore_deinit(void);
#endif

212
src/irc/flood/flood.c Normal file
View file

@ -0,0 +1,212 @@
/*
flood.c : Flood protection (see also ctcp.c)
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 "signals.h"
#include "levels.h"
#include "misc.h"
#include "settings.h"
#include "irc.h"
#include "irc-server.h"
#include "autoignore.h"
#include "ignore.h"
typedef struct {
char *nick;
int level;
int msgcount;
} FLOOD_REC;
static int flood_tag;
static int flood_max_msgs;
static int flood_hash_deinit(const char *key, FLOOD_REC *rec)
{
g_return_val_if_fail(key != NULL, FALSE);
g_return_val_if_fail(rec != NULL, FALSE);
g_free(rec->nick);
g_free(rec);
return TRUE;
}
/* timeout function: flood protection */
static int flood_timeout(void)
{
MODULE_SERVER_REC *mserver;
GSList *tmp;
/* remove everyone from flood list */
for (tmp = servers; tmp != NULL; tmp = tmp->next) {
IRC_SERVER_REC *rec = tmp->data;
mserver = MODULE_DATA(rec);
g_hash_table_foreach_remove(mserver->floodlist, (GHRFunc) flood_hash_deinit, NULL);
}
return 1;
}
/* Initialize flood protection */
static void flood_init_server(IRC_SERVER_REC *server)
{
MODULE_SERVER_REC *rec;
g_return_if_fail(server != NULL);
rec = g_new0(MODULE_SERVER_REC, 1);
MODULE_DATA_SET(server, rec);
rec->floodlist = g_hash_table_new((GHashFunc) g_istr_hash, (GCompareFunc) g_istr_equal);
}
/* Deinitialize flood protection */
static void flood_deinit_server(IRC_SERVER_REC *server)
{
MODULE_SERVER_REC *mserver;
g_return_if_fail(server != NULL);
mserver = MODULE_DATA(server);
if (mserver != NULL && mserver->floodlist != NULL) {
g_hash_table_freeze(mserver->floodlist);
g_hash_table_foreach(mserver->floodlist, (GHFunc) flood_hash_deinit, NULL);
g_hash_table_thaw(mserver->floodlist);
g_hash_table_destroy(mserver->floodlist);
}
g_free(mserver);
}
/* All messages should go through here.. */
static void flood_newmsg(IRC_SERVER_REC *server, int level, const char *nick, const char *host, const char *target)
{
MODULE_SERVER_REC *mserver;
FLOOD_REC *rec;
char *levelstr;
g_return_if_fail(server != NULL);
g_return_if_fail(nick != NULL);
mserver = MODULE_DATA(server);
rec = g_hash_table_lookup(mserver->floodlist, nick);
if (rec != NULL) {
if (++rec->msgcount > flood_max_msgs) {
/* flooding! */
levelstr = bits2level(rec->level);
signal_emit("flood", 5, server, nick, host, levelstr, target);
g_free(levelstr);
}
return;
}
rec = g_new(FLOOD_REC, 1);
rec->level = level;
rec->msgcount = 1;
rec->nick = g_strdup(nick);
g_hash_table_insert(mserver->floodlist, rec->nick, rec);
}
static void flood_privmsg(const char *data, IRC_SERVER_REC *server, const char *nick, const char *addr)
{
int publiclevel;
char *params, *target, *text;
g_return_if_fail(data != NULL);
g_return_if_fail(server != NULL);
if (nick == NULL) {
/* don't try to ignore server messages.. */
return;
}
params = event_get_params(data, 2, &target, &text);
if (*text == 1) {
/* CTCP */
if (!ignore_check(server, nick, addr, target, text, MSGLEVEL_CTCPS))
flood_newmsg(server, MSGLEVEL_CTCPS, nick, addr, target);
} else {
publiclevel = ischannel(*target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS;
if (addr != NULL && !ignore_check(server, nick, addr, target, text, publiclevel))
flood_newmsg(server, publiclevel, nick, addr, target);
}
g_free(params);
}
static void flood_notice(const char *data, IRC_SERVER_REC *server, const char *nick, const char *addr)
{
char *params, *target, *text;
g_return_if_fail(text != NULL);
g_return_if_fail(server != NULL);
if (nick == NULL) {
/* don't try to ignore server messages.. */
return;
}
params = event_get_params(data, 2, &target, &text);
if (addr != NULL && !ignore_check(server, nick, addr, target, text, MSGLEVEL_NOTICES))
flood_newmsg(server, MSGLEVEL_NOTICES | ischannel(*target) ? MSGLEVEL_PUBLIC : MSGLEVEL_MSGS, nick, addr, target);
g_free(params);
}
static void read_settings(void)
{
if (flood_tag != -1) g_source_remove(flood_tag);
flood_tag = g_timeout_add(settings_get_int("flood_timecheck"), (GSourceFunc) flood_timeout, NULL);
flood_max_msgs = settings_get_int("flood_max_msgs");
}
void flood_init(void)
{
settings_add_int("flood", "flood_timecheck", 1000);
settings_add_int("flood", "flood_max_msgs", 5);
flood_tag = -1;
read_settings();
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
signal_add_first("server connected", (SIGNAL_FUNC) flood_init_server);
signal_add("server disconnected", (SIGNAL_FUNC) flood_deinit_server);
signal_add("event privmsg", (SIGNAL_FUNC) flood_privmsg);
signal_add("event notice", (SIGNAL_FUNC) flood_notice);
autoignore_init();
}
void flood_deinit(void)
{
autoignore_deinit();
if (flood_tag != -1) g_source_remove(flood_tag);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
signal_remove("server connected", (SIGNAL_FUNC) flood_init_server);
signal_remove("server disconnected", (SIGNAL_FUNC) flood_deinit_server);
signal_remove("event privmsg", (SIGNAL_FUNC) flood_privmsg);
signal_remove("event notice", (SIGNAL_FUNC) flood_notice);
}

7
src/irc/flood/flood.h Normal file
View file

@ -0,0 +1,7 @@
#ifndef __FLOOD_H
#define __FLOOD_H
void flood_init(void);
void flood_deinit(void);
#endif

12
src/irc/flood/module.h Normal file
View file

@ -0,0 +1,12 @@
#include "common.h"
typedef struct {
/* Flood protection */
GHashTable *floodlist;
/* Auto ignore list */
GSList *ignorelist;
time_t ignore_lastcheck;
} MODULE_SERVER_REC;
#define MODULE_NAME "irc/flood"

27
src/irc/irc.c Normal file
View file

@ -0,0 +1,27 @@
void irc_core_init(void);
void irc_core_deinit(void);
void dcc_init(void);
void dcc_deinit(void);
void flood_init(void);
void flood_deinit(void);
void notifylist_init(void);
void notifylist_deinit(void);
void irc_init(void)
{
irc_core_init();
dcc_init();
flood_init();
notifylist_init();
}
void irc_deinit(void)
{
notifylist_deinit();
flood_deinit();
dcc_deinit();
irc_core_deinit();
}

View file

@ -0,0 +1,15 @@
noinst_LTLIBRARIES = libirc_notifylist.la
INCLUDES = $(GLIB_CFLAGS) \
-I$(top_srcdir)/src -I$(top_srcdir)/src/core/ -I$(top_srcdir)/src/irc/core/
libirc_notifylist_la_SOURCES = \
notifylist.c \
notify-commands.c \
notify-ison.c \
notify-setup.c \
notify-whois.c
noinst_HEADERS = \
notifylist.h \
notify-setup.h

View file

@ -0,0 +1,44 @@
#include "common.h"
#define MODULE_NAME "irc/notifylist"
#define ISON_EVENT "event 303"
typedef struct {
char *nick;
char *user, *host, *realname, *awaymsg;
time_t idle_time;
int host_ok:1; /* host matches the one in notifylist = this is the right person*/
int away_ok:1; /* not away, or we don't care about it */
int idle_ok:1; /* idle time is low enough, or we don't care about it */
int away:1; /* nick is away */
int join_announced:1; /* join to IRC has been announced */
int idle_changed:1; /* idle time is lower than in last check */
time_t last_whois;
} NOTIFY_NICK_REC;
typedef struct {
GSList *notify_users; /* NOTIFY_NICK_REC's of notifylist people who are in IRC */
GSList *ison_tempusers; /* Temporary list for saving /ISON events.. */
} MODULE_SERVER_REC;
#include "irc-server.h"
NOTIFY_NICK_REC *notify_nick_create(IRC_SERVER_REC *server, const char *nick);
void notify_nick_destroy(NOTIFY_NICK_REC *rec);
NOTIFY_NICK_REC *notify_nick_find(IRC_SERVER_REC *server, const char *nick);
void notifylist_left(IRC_SERVER_REC *server, NOTIFY_NICK_REC *rec);
void notifylist_destroy_all(void);
void notifylist_commands_init(void);
void notifylist_commands_deinit(void);
void notifylist_whois_init(void);
void notifylist_whois_deinit(void);
void notifylist_ison_init(void);
void notifylist_ison_deinit(void);

View file

@ -0,0 +1,81 @@
/*
notify-commands.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 "commands.h"
#include "misc.h"
#include "settings.h"
#include "notifylist.h"
#define DEFAULT_NOTIFY_IDLE_TIME 60
static void cmd_notify(gchar *data)
{
char *params, *mask, *ircnets, *args, *idletime;
int away_check, idle_check_time;
g_return_if_fail(data != NULL);
args = "@idle";
params = cmd_get_params(data, 4 | PARAM_FLAG_MULTIARGS | PARAM_FLAG_GETREST, &args, &idletime, &mask, &ircnets);
if (*mask == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
if (stristr(args, "-idle") == NULL)
idle_check_time = 0;
else {
idle_check_time = is_numeric(idletime, 0) ? (atol(idletime)*60) :
(settings_get_int("notify_idle_time")*60);
}
away_check = stristr(args, "-away") != NULL;
notifylist_remove(mask);
notifylist_add(mask, ircnets, away_check, idle_check_time);
g_free(params);
}
static void cmd_unnotify(const char *data)
{
char *params, *mask;
g_return_if_fail(data != NULL);
params = cmd_get_params(data, 1, &mask);
if (*mask == '\0') cmd_param_error(CMDERR_NOT_ENOUGH_PARAMS);
notifylist_remove(mask);
g_free(params);
}
void notifylist_commands_init(void)
{
settings_add_int("misc", "notify_idle_time", DEFAULT_NOTIFY_IDLE_TIME);
command_bind("notify", NULL, (SIGNAL_FUNC) cmd_notify);
command_bind("unnotify", NULL, (SIGNAL_FUNC) cmd_unnotify);
}
void notifylist_commands_deinit(void)
{
command_unbind("notify", (SIGNAL_FUNC) cmd_notify);
command_unbind("unnotify", (SIGNAL_FUNC) cmd_unnotify);
}

View file

@ -0,0 +1,354 @@
/*
notify-ison.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 "settings.h"
#include "irc.h"
#include "irc-server.h"
#include "server-redirect.h"
#include "notifylist.h"
#define DEFAULT_NOTIFY_CHECK_TIME 60
#define DEFAULT_NOTIFY_WHOIS_TIME (60*5)
typedef struct {
char *nick;
int hostok;
} ISON_REC;
static int notify_tag;
static int notify_whois_time;
NOTIFY_NICK_REC *notify_nick_create(IRC_SERVER_REC *server, const char *nick)
{
MODULE_SERVER_REC *mserver;
NOTIFY_NICK_REC *rec;
mserver = MODULE_DATA(server);
rec = g_new0(NOTIFY_NICK_REC, 1);
rec->nick = g_strdup(nick);
mserver->notify_users = g_slist_append(mserver->notify_users, rec);
return rec;
}
void notify_nick_destroy(NOTIFY_NICK_REC *rec)
{
g_free(rec->nick);
g_free_not_null(rec->user);
g_free_not_null(rec->host);
g_free_not_null(rec->realname);
g_free_not_null(rec->awaymsg);
g_free(rec);
}
NOTIFY_NICK_REC *notify_nick_find(IRC_SERVER_REC *server, const char *nick)
{
MODULE_SERVER_REC *mserver;
NOTIFY_NICK_REC *rec;
GSList *tmp;
mserver = MODULE_DATA(server);
for (tmp = mserver->notify_users; tmp != NULL; tmp = tmp->next) {
rec = tmp->data;
if (g_strcasecmp(rec->nick, nick) == 0)
return rec;
}
return NULL;
}
static int is_ison_queue_empty(IRC_SERVER_REC *server)
{
GSList *tmp;
tmp = server_redirect_getqueue((SERVER_REC *) server, ISON_EVENT, NULL);
for (; tmp != NULL; tmp = tmp->next) {
REDIRECT_REC *rec = tmp->data;
if (strcmp(rec->name, "notifylist event") == 0)
return FALSE;
}
return TRUE;
}
static void ison_send(IRC_SERVER_REC *server, GString *cmd)
{
g_string_truncate(cmd, cmd->len-1);
g_string_prepend(cmd, "ISON :");
irc_send_cmd(server, cmd->str);
server_redirect_event((SERVER_REC *) server, NULL, 1, ISON_EVENT, "notifylist event", -1, NULL);
g_string_truncate(cmd, 0);
}
/* timeout function: send /ISON commands to server to check if someone in
notify list is in IRC */
static void notifylist_timeout_server(IRC_SERVER_REC *server)
{
GSList *tmp;
GString *cmd;
char *nick, *ptr;
int len;
g_return_if_fail(server != NULL);
if (!is_ison_queue_empty(server)) {
/* still not received all replies to previous /ISON commands.. */
return;
}
cmd = g_string_new(NULL);
for (tmp = notifies; tmp != NULL; tmp = tmp->next) {
NOTIFYLIST_REC *rec = tmp->data;
if (!notify_ircnets_match(rec, server->connrec->ircnet))
continue;
nick = g_strdup(rec->mask);
ptr = strchr(nick, '!');
if (ptr != NULL) *ptr = '\0';
len = strlen(nick);
if (cmd->len+len+1 > 510)
ison_send(server, cmd);
g_string_sprintfa(cmd, "%s ", nick);
g_free(nick);
}
if (cmd->len > 0)
ison_send(server, cmd);
g_string_free(cmd, TRUE);
}
static int notifylist_timeout_func(void)
{
g_slist_foreach(servers, (GFunc) notifylist_timeout_server, NULL);
return 1;
}
static void ison_save_users(MODULE_SERVER_REC *mserver, char *online)
{
char *ptr;
while (online != NULL && *online != '\0') {
ptr = strchr(online, ' ');
if (ptr != NULL) *ptr++ = '\0';
mserver->ison_tempusers =
g_slist_append(mserver->ison_tempusers, g_strdup(online));
online = ptr;
}
}
static void whois_send(IRC_SERVER_REC *server, char *nicks)
{
char *p, *str;
irc_send_cmdv(server, "WHOIS %s", nicks);
/* "nick1,nick2" -> "nick1,nick2 nick1 nick2" because
End of WHOIS give nick1,nick2 while other whois events give
nick1 or nick2 */
str = g_strconcat(nicks, " ", nicks, NULL);
for (p = str+strlen(nicks)+1; *p != '\0'; p++)
if (*p == ',') *p = ' ';
server_redirect_event((SERVER_REC *) server, str, 2,
"event 318", "notifylist event whois end", 1,
"event 402", "event empty", -1,
"event 401", "event empty", 1,
"event 311", "notifylist event whois", 1,
"event 301", "notifylist event whois away", 1,
"event 312", "event empty", 1,
"event 313", "event empty", 1,
"event 317", "notifylist event whois idle", 1,
"event 319", "event empty", 1, NULL);
g_free(str);
}
static void whois_send_server(IRC_SERVER_REC *server, char *nick)
{
char *str;
str = g_strdup_printf("%s %s", nick, nick);
whois_send(server, str);
g_free(str);
}
/* try to send as many nicks in one WHOIS as possible */
static void whois_list_send(IRC_SERVER_REC *server, GSList *nicks)
{
GSList *tmp;
GString *str;
char *nick;
int count;
str = g_string_new(NULL);
count = 0;
for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
nick = tmp->data;
count++;
g_string_sprintfa(str, "%s,", nick);
if (count >= server->max_whois_in_cmd) {
g_string_truncate(str, str->len-1);
whois_send(server, str->str);
count = 0;
}
}
if (str->len > 0) {
g_string_truncate(str, str->len-1);
whois_send(server, str->str);
}
g_string_free(str, TRUE);
}
static void ison_check_joins(IRC_SERVER_REC *server)
{
MODULE_SERVER_REC *mserver;
NOTIFYLIST_REC *notify;
NOTIFY_NICK_REC *rec;
GSList *tmp, *newnicks;
int send_whois;
time_t now;
mserver = MODULE_DATA(server);
now = time(NULL);
newnicks = NULL;
for (tmp = mserver->ison_tempusers; tmp != NULL; tmp = tmp->next) {
char *nick = tmp->data;
notify = notifylist_find(nick, server->connrec->ircnet);
send_whois = notify != NULL &&
(notify->away_check || notify->idle_check_time > 0);
rec = notify_nick_find(server, nick);
if (rec != NULL) {
/* check if we want to send WHOIS yet.. */
if (now-rec->last_whois < notify_whois_time)
continue;
} else {
rec = notify_nick_create(server, nick);
if (!send_whois) newnicks = g_slist_append(newnicks, nick);
}
if (send_whois) {
/* we need away message or idle time -
send the WHOIS reply to the nick's server */
rec->last_whois = now;
whois_send_server(server, nick);
}
}
whois_list_send(server, newnicks);
g_slist_free(newnicks);
}
static void ison_check_parts(IRC_SERVER_REC *server)
{
MODULE_SERVER_REC *mserver;
GSList *tmp, *next;
mserver = MODULE_DATA(server);
for (tmp = mserver->notify_users; tmp != NULL; tmp = next) {
NOTIFY_NICK_REC *rec = tmp->data;
next = tmp->next;
if (gslist_find_icase_string(mserver->ison_tempusers, rec->nick) != NULL)
continue;
notifylist_left(server, rec);
notify_nick_destroy(rec);
}
}
static void event_ison(const char *data, IRC_SERVER_REC *server)
{
MODULE_SERVER_REC *mserver;
char *params, *online;
g_return_if_fail(data != NULL);
g_return_if_fail(server != NULL);
params = event_get_params(data, 2, NULL, &online);
mserver = MODULE_DATA(server);
ison_save_users(mserver, online);
if (!is_ison_queue_empty(server)) {
/* wait for the rest of the /ISON replies */
g_free(params);
return;
}
ison_check_joins(server);
ison_check_parts(server);
/* free memory used by temp list */
g_slist_foreach(mserver->ison_tempusers, (GFunc) g_free, NULL);
g_slist_free(mserver->ison_tempusers);
mserver->ison_tempusers = NULL;
g_free(params);
}
static void read_settings(void)
{
if (notify_tag != -1) g_source_remove(notify_tag);
notify_tag = g_timeout_add(1000*settings_get_int("notify_check_time"), (GSourceFunc) notifylist_timeout_func, NULL);
notify_whois_time = settings_get_int("notify_whois_time");
}
void notifylist_ison_init(void)
{
settings_add_int("misc", "notify_check_time", DEFAULT_NOTIFY_CHECK_TIME);
settings_add_int("misc", "notify_whois_time", DEFAULT_NOTIFY_WHOIS_TIME);
notify_tag = -1;
read_settings();
signal_add("notifylist event", (SIGNAL_FUNC) event_ison);
signal_add("setup changed", (SIGNAL_FUNC) read_settings);
}
void notifylist_ison_deinit(void)
{
g_source_remove(notify_tag);
signal_remove("notifylist event", (SIGNAL_FUNC) event_ison);
signal_remove("setup changed", (SIGNAL_FUNC) read_settings);
}

View file

@ -0,0 +1,84 @@
/*
notify-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 "lib-config/iconfig.h"
#include "settings.h"
#include "irc-server.h"
#include "notifylist.h"
void notifylist_add_config(NOTIFYLIST_REC *rec)
{
CONFIG_NODE *node;
node = iconfig_node_traverse("notifies", TRUE);
node = config_node_section(node, rec->mask, NODE_TYPE_BLOCK);
if (rec->away_check)
config_node_set_bool(node, "away_check", TRUE);
else
config_node_set_str(node, "away_check", NULL);
if (rec->idle_check_time > 0)
config_node_set_int(node, "idle_check_time", rec->idle_check_time/60);
else
config_node_set_str(node, "idle_check_time", NULL);
config_node_set_str(node, "ircnets", NULL);
if (rec->ircnets != NULL && *rec->ircnets != NULL) {
node = config_node_section(node, "ircnets", NODE_TYPE_LIST);
config_node_add_list(node, rec->ircnets);
}
}
void notifylist_remove_config(NOTIFYLIST_REC *rec)
{
iconfig_set_str("notifies", rec->mask, NULL);
}
void notifylist_read_config(void)
{
CONFIG_NODE *node;
NOTIFYLIST_REC *rec;
GSList *tmp;
notifylist_destroy_all();
node = iconfig_node_traverse("notifies", 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(NOTIFYLIST_REC, 1);
notifies = g_slist_append(notifies, rec);
rec->mask = g_strdup(node->key);
rec->away_check = config_node_get_bool(node, "away_check", FALSE);
rec->idle_check_time = config_node_get_int(node, "idle_check_time", 0)*60;
node = config_node_section(node, "ircnets", -1);
if (node != NULL) rec->ircnets = config_node_get_list(node);
}
}

View file

@ -0,0 +1,9 @@
#ifndef __NOTIFY_SETUP_H
#define __NOTIFY_SETUP_H
void notifylist_add_config(NOTIFYLIST_REC *rec);
void notifylist_remove_config(NOTIFYLIST_REC *rec);
void notifylist_read_config(void);
#endif

View file

@ -0,0 +1,186 @@
/*
notify-whois.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 "special-vars.h"
#include "irc.h"
#include "irc-server.h"
#include "masks.h"
#include "notifylist.h"
static char *last_notify_nick;
static void event_whois(const char *data, IRC_SERVER_REC *server)
{
char *params, *nick, *user, *host, *realname;
NOTIFY_NICK_REC *nickrec;
NOTIFYLIST_REC *notify;
g_return_if_fail(data != NULL);
g_return_if_fail(server != NULL);
params = event_get_params(data, 6, NULL, &nick, &user, &host, NULL, &realname);
notify = notifylist_find(nick, server->connrec->ircnet);
if (notify != NULL && !irc_mask_match(notify->mask, nick, user, host)) {
/* user or host didn't match */
g_free(params);
return;
}
nickrec = notify_nick_find(server, nick);
if (nickrec != NULL) {
g_free_not_null(last_notify_nick);
last_notify_nick = g_strdup(nick);
g_free_not_null(nickrec->user);
g_free_not_null(nickrec->host);
g_free_not_null(nickrec->realname);
g_free_and_null(nickrec->awaymsg);
nickrec->user = g_strdup(user);
nickrec->host = g_strdup(host);
nickrec->realname = g_strdup(realname);
nickrec->away = FALSE;
nickrec->host_ok = TRUE;
nickrec->idle_ok = TRUE;
}
g_free(params);
}
static void event_whois_idle(const char *data, IRC_SERVER_REC *server)
{
NOTIFY_NICK_REC *nickrec;
NOTIFYLIST_REC *notify;
char *params, *nick, *secstr;
long secs;
g_return_if_fail(data != NULL);
params = event_get_params(data, 3, NULL, &nick, &secstr);
secs = atol(secstr);
notify = notifylist_find(nick, server->connrec->ircnet);
nickrec = notify_nick_find(server, nick);
if (notify != NULL && nickrec != NULL) {
time_t now = time(NULL);
nickrec->idle_changed = secs < now-nickrec->idle_time &&
now-nickrec->idle_time > notify->idle_check_time;
nickrec->idle_time = now-secs;
}
g_free(params);
}
static void event_whois_away(const char *data, IRC_SERVER_REC *server)
{
NOTIFY_NICK_REC *nickrec;
char *params, *nick, *awaymsg;
g_return_if_fail(data != NULL);
params = event_get_params(data, 3, NULL, &nick, &awaymsg);
nickrec = notify_nick_find(server, nick);
if (nickrec != NULL) {
nickrec->awaymsg = g_strdup(awaymsg);
nickrec->away = TRUE;
}
g_free(params);
}
/* All WHOIS replies got, now announce all the changes at once. */
static void event_whois_end(const char *data, IRC_SERVER_REC *server)
{
MODULE_SERVER_REC *mserver;
NOTIFYLIST_REC *notify;
NOTIFY_NICK_REC *rec;
GSList *tmp;
const char *event;
int away_ok;
time_t now;
now = time(NULL);
mserver = MODULE_DATA(server);
for (tmp = mserver->notify_users; tmp != NULL; tmp = tmp->next) {
rec = tmp->data;
if (rec->realname == NULL)
continue;
notify = notifylist_find(rec->nick, server->connrec->ircnet);
if (notify == NULL) continue;
away_ok = !notify->away_check || !rec->away;
event = NULL;
if (!rec->join_announced) {
rec->join_announced = TRUE;
rec->idle_time = now;
if (away_ok) event = "notifylist joined";
} else if (notify->away_check && rec->away_ok == rec->away)
event = "notifylist away changed";
else if (notify->idle_check_time > 0 && rec->idle_changed)
event = "notifylist unidle";
if (event != NULL) {
signal_emit(event, 6, server, rec->nick,
rec->user, rec->host,
rec->realname, rec->awaymsg);
}
rec->idle_ok = notify->idle_check_time <= 0 ||
now-rec->idle_time <= notify->idle_check_time;
rec->idle_changed = FALSE;
rec->away_ok = away_ok;
}
}
/* last person that NOTIFY detected a signon for */
static char *expando_lastnotify(void *server, void *item, int *free_ret)
{
return last_notify_nick;
}
void notifylist_whois_init(void)
{
last_notify_nick = NULL;
signal_add("notifylist event whois", (SIGNAL_FUNC) event_whois);
signal_add("notifylist event whois away", (SIGNAL_FUNC) event_whois_away);
signal_add("notifylist event whois idle", (SIGNAL_FUNC) event_whois_idle);
signal_add("notifylist event whois end", (SIGNAL_FUNC) event_whois_end);
expando_create("D", expando_lastnotify);
}
void notifylist_whois_deinit(void)
{
g_free_not_null(last_notify_nick);
signal_remove("notifylist event whois", (SIGNAL_FUNC) event_whois);
signal_remove("notifylist event whois away", (SIGNAL_FUNC) event_whois_away);
signal_remove("notifylist event whois idle", (SIGNAL_FUNC) event_whois_idle);
signal_remove("notifylist event whois end", (SIGNAL_FUNC) event_whois_end);
expando_destroy("D", expando_lastnotify);
}

View file

@ -0,0 +1,356 @@
/*
notifylist.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 "modules.h"
#include "signals.h"
#include "irc.h"
#include "irc-server.h"
#include "server-redirect.h"
#include "masks.h"
#include "nicklist.h"
#include "notifylist.h"
#include "notify-setup.h"
GSList *notifies;
NOTIFYLIST_REC *notifylist_add(const char *mask, const char *ircnets,
int away_check, int idle_check_time)
{
NOTIFYLIST_REC *rec;
g_return_val_if_fail(mask != NULL, NULL);
rec = g_new0(NOTIFYLIST_REC, 1);
rec->mask = g_strdup(mask);
rec->ircnets = ircnets == NULL || *ircnets == '\0' ? NULL :
g_strsplit(ircnets, " ", -1);
rec->away_check = away_check;
rec->idle_check_time = idle_check_time;
notifylist_add_config(rec);
notifies = g_slist_append(notifies, rec);
signal_emit("notifylist new", 1, rec);
return rec;
}
static void notify_destroy(NOTIFYLIST_REC *rec)
{
if (rec->ircnets != NULL) g_strfreev(rec->ircnets);
g_free(rec->mask);
g_free(rec);
}
void notifylist_destroy_all(void)
{
g_slist_foreach(notifies, (GFunc) notify_destroy, NULL);
g_slist_free(notifies);
notifies = NULL;
}
void notifylist_remove(const char *mask)
{
NOTIFYLIST_REC *rec;
g_return_if_fail(mask != NULL);
rec = notifylist_find(mask, "*");
if (rec == NULL) return;
notifylist_remove_config(rec);
notifies = g_slist_remove(notifies, rec);
signal_emit("notifylist remove", 1, rec);
notify_destroy(rec);
}
int notify_ircnets_match(NOTIFYLIST_REC *rec, const char *ircnet)
{
char **tmp;
if (rec->ircnets == NULL) return TRUE;
if (ircnet == NULL) return FALSE;
if (strcmp(ircnet, "*") == 0) return TRUE;
for (tmp = rec->ircnets; *tmp != NULL; tmp++) {
if (g_strcasecmp(*tmp, ircnet) == 0)
return TRUE;
}
return FALSE;
}
NOTIFYLIST_REC *notifylist_find(const char *mask, const char *ircnet)
{
NOTIFYLIST_REC *best;
GSList *tmp;
int len;
best = NULL;
len = strlen(mask);
for (tmp = notifies; tmp != NULL; tmp = tmp->next) {
NOTIFYLIST_REC *rec = tmp->data;
/* check mask */
if (g_strncasecmp(rec->mask, mask, len) != 0 ||
(rec->mask[len] != '\0' && rec->mask[len] != '!')) continue;
/* check ircnet */
if (rec->ircnets == NULL) {
best = rec;
continue;
}
if (notify_ircnets_match(rec, ircnet))
return rec;
}
return best;
}
int notifylist_ison_server(IRC_SERVER_REC *server, const char *nick)
{
NOTIFY_NICK_REC *rec;
g_return_val_if_fail(nick != NULL, FALSE);
g_return_val_if_fail(server != NULL, FALSE);
rec = notify_nick_find(server, nick);
return rec != NULL && rec->host_ok && rec->away_ok && rec->idle_ok;
}
static IRC_SERVER_REC *notifylist_ison_serverlist(const char *nick, const char *taglist)
{
IRC_SERVER_REC *server;
char **list, **tmp;
list = g_strsplit(taglist, " ", -1);
server = NULL;
for (tmp = list; *tmp != NULL; tmp++) {
server = (IRC_SERVER_REC *) server_find_ircnet(*tmp);
if (server != NULL && notifylist_ison_server(server, nick))
break;
}
g_strfreev(list);
return tmp == NULL ? NULL : server;
}
IRC_SERVER_REC *notifylist_ison(const char *nick, const char *serverlist)
{
GSList *tmp;
g_return_val_if_fail(nick != NULL, FALSE);
g_return_val_if_fail(serverlist != NULL, FALSE);
if (*serverlist != '\0')
return notifylist_ison_serverlist(nick, serverlist);
/* any server.. */
for (tmp = servers; tmp != NULL; tmp = tmp->next) {
if (notifylist_ison_server(tmp->data, nick))
return tmp->data;
}
return NULL;
}
static void notifylist_init_server(IRC_SERVER_REC *server)
{
MODULE_SERVER_REC *rec;
g_return_if_fail(server != NULL);
rec = g_new0(MODULE_SERVER_REC,1 );
MODULE_DATA_SET(server, rec);
server_redirect_init((SERVER_REC *) server, "command ison", 1, ISON_EVENT, NULL);
}
static void notifylist_deinit_server(IRC_SERVER_REC *server)
{
MODULE_SERVER_REC *mserver;
NOTIFY_NICK_REC *rec;
g_return_if_fail(server != NULL);
mserver = MODULE_DATA(server);
while (mserver->notify_users != NULL) {
rec = mserver->notify_users->data;
mserver->notify_users = g_slist_remove(mserver->notify_users, rec);
notify_nick_destroy(rec);
}
g_free(mserver);
}
void notifylist_left(IRC_SERVER_REC *server, NOTIFY_NICK_REC *rec)
{
MODULE_SERVER_REC *mserver;
mserver = MODULE_DATA(server);
mserver->notify_users = g_slist_remove(mserver->notify_users, rec);
if (rec->host_ok && rec->away_ok) {
signal_emit("notifylist left", 6,
server, rec->nick,
rec->user, rec->host,
rec->realname, rec->awaymsg);
}
}
static void notifylist_idle_reset(IRC_SERVER_REC *server, const char *nick)
{
NOTIFY_NICK_REC *rec;
NOTIFYLIST_REC *notify;
notify = notifylist_find(nick, server->connrec->ircnet);
rec = notify_nick_find(server, nick);
if (notify != NULL && rec != NULL && notify->idle_check_time > 0 &&
time(NULL)-rec->idle_time > notify->idle_check_time) {
rec->idle_time = time(NULL);
signal_emit("notifylist unidle", 6,
server, rec->nick,
rec->user, rec->host,
rec->realname, rec->awaymsg);
}
}
static void event_quit(const char *data, IRC_SERVER_REC *server, const char *nick)
{
NOTIFY_NICK_REC *rec;
if (*data == ':') data++; /* quit message */
rec = notify_nick_find(server, nick);
if (rec != NULL) notifylist_left(server, rec);
}
static void notifylist_check_join(IRC_SERVER_REC *server, const char *nick,
const char *userhost, const char *realname, int away)
{
NOTIFYLIST_REC *notify;
NOTIFY_NICK_REC *rec;
char *user, *host;
notify = notifylist_find(nick, server->connrec->ircnet);
if (notify == NULL) return;
rec = notify_nick_find(server, nick);
if (rec != NULL && rec->join_announced) return;
if (rec == NULL) rec = notify_nick_create(server, nick);
user = g_strdup(userhost);
host = strchr(user, '@');
if (host != NULL) *host++ = '\0'; else host = "";
if (!irc_mask_match(notify->mask, nick, user, host)) {
g_free(user);
return;
}
if (notify->away_check && away == -1) {
/* we need to know if the nick is away */
g_free(user);
return;
}
g_free_not_null(rec->user);
g_free_not_null(rec->host);
g_free_not_null(rec->realname);
rec->user = g_strdup(user);
rec->host = g_strdup(host);
rec->realname = *realname == '\0' ? NULL : g_strdup(realname);
if (away != -1) rec->away = away;
rec->host_ok = TRUE;
rec->join_announced = TRUE;
rec->idle_time = time(NULL);
signal_emit("notifylist joined", 6,
server, rec->nick, rec->user, rec->host, realname, NULL);
g_free(user);
}
static void event_privmsg(const char *data, IRC_SERVER_REC *server, const char *nick, const char *address)
{
if (nick != NULL) {
notifylist_check_join(server, nick, address, "", -1);
notifylist_idle_reset(server, nick);
}
}
static void event_join(const char *data, IRC_SERVER_REC *server, const char *nick, const char *address)
{
notifylist_check_join(server, nick, address, "", -1);
}
static void sig_channel_wholist(CHANNEL_REC *channel)
{
GSList *nicks, *tmp;
nicks = nicklist_getnicks(channel);
for (tmp = nicks; tmp != NULL; tmp = tmp->next) {
NICK_REC *rec = tmp->data;
notifylist_check_join(channel->server, rec->nick, rec->host, rec->realname, rec->gone);
}
g_slist_free(nicks);
}
void notifylist_init(void)
{
notifylist_read_config();
notifylist_commands_init();
notifylist_ison_init();
notifylist_whois_init();
signal_add("server connected", (SIGNAL_FUNC) notifylist_init_server);
signal_add("server disconnected", (SIGNAL_FUNC) notifylist_deinit_server);
signal_add("event quit", (SIGNAL_FUNC) event_quit);
signal_add("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_add("event join", (SIGNAL_FUNC) event_join);
signal_add("channel wholist", (SIGNAL_FUNC) sig_channel_wholist);
signal_add("setup reread", (SIGNAL_FUNC) notifylist_read_config);
}
void notifylist_deinit(void)
{
notifylist_commands_deinit();
notifylist_ison_deinit();
notifylist_whois_deinit();
signal_remove("server connected", (SIGNAL_FUNC) notifylist_init_server);
signal_remove("server disconnected", (SIGNAL_FUNC) notifylist_deinit_server);
signal_remove("event quit", (SIGNAL_FUNC) event_quit);
signal_remove("event privmsg", (SIGNAL_FUNC) event_privmsg);
signal_remove("event join", (SIGNAL_FUNC) event_join);
signal_remove("channel wholist", (SIGNAL_FUNC) sig_channel_wholist);
signal_remove("setup reread", (SIGNAL_FUNC) notifylist_read_config);
notifylist_destroy_all();
}

View file

@ -0,0 +1,32 @@
#ifndef __NOTIFYLIST_H
#define __NOTIFYLIST_H
typedef struct {
char *mask; /* nick part must not contain wildcards */
char **ircnets; /* if non-NULL, check only from these irc networks */
/* notify when AWAY status changes (uses /USERHOST) */
int away_check:1;
/* notify when idle time is reset and it was bigger than this
(uses /WHOIS and PRIVMSG events) */
int idle_check_time;
} NOTIFYLIST_REC;
extern GSList *notifies;
void notifylist_init(void);
void notifylist_deinit(void);
NOTIFYLIST_REC *notifylist_add(const char *mask, const char *ircnets,
int away_check, int idle_check_time);
void notifylist_remove(const char *mask);
IRC_SERVER_REC *notifylist_ison(const char *nick, const char *serverlist);
int notifylist_ison_server(IRC_SERVER_REC *server, const char *nick);
/* If `ircnet' is "*", it doesn't matter at all. */
NOTIFYLIST_REC *notifylist_find(const char *mask, const char *ircnet);
int notify_ircnets_match(NOTIFYLIST_REC *rec, const char *ircnet);
#endif