#ifdef RCS
static char rcsid[]="$Id: server.c,v 1.2 2001/03/13 05:04:47 holsta Exp $";
#endif
/******************************************************************************
* Internetting Cooperating Programmers
* ----------------------------------------------------------------------------
*
* ____ PROJECT
* | _ \ __ _ _ __ ___ ___ _ __
* | | | |/ _` | '_ \ / __/ _ \ '__|
* | |_| | (_| | | | | (_| __/ |
* |____/ \__,_|_| |_|\___\___|_| the IRC bot
*
* All files in this archive are subject to the GNU General Public License.
*
* $Source: /cvsroot/dancer/dancer/src/server.c,v $
* $Revision: 1.2 $
* $Date: 2001/03/13 05:04:47 $
* $Author: holsta $
* $State: Exp $
* $Locker: $
*
* ---------------------------------------------------------------------------
*****************************************************************************/
#include "dancer.h"
#include "trio.h"
#include "strio.h"
#include "numeric.h" /* From the irc server source */
#include "list.h"
#include "function.h"
#include "user.h"
#include "command.h"
#include "ctcp.h"
#include "transfer.h"
#include "seen.h"
#include "server.h"
#include "bans.h"
#include "link.h"
#include "servfunc.h"
#include "fplrun.h"
/* --- Global ----------------------------------------------------- */
extern time_t now;
extern char cmodes[], chanmodes[], ckey[], chankey[];
extern char channel[], nickname[], servername[];
extern char botmatch[], myaddr[];
extern char askforops[];
extern char *errfrom;
extern bool nickflag, connected, restart, moderateflag;
extern bool lockmode, lockkey, locklimit;
extern bool netsplitmode;
extern bool deopprotect;
extern bool talkative;
extern bool warnmode;
extern bool multimode;
extern bool cleanup;
extern bool invitable;
extern bool autojoin;
extern bool autoop;
extern bool dispcomment;
extern bool autoop;
extern bool kickbans;
extern bool autounban;
extern bool strictopmode;
extern bool fakenamemode;
extern bool opprotect;
extern bool oplevel;
extern bool mute;
extern bool possiblyfresh;
extern bool bantimeouts;
extern bool chat;
extern bool public;
extern int possiblyfreshtime;
extern int pubbantime;
extern int retry;
extern long climit;
extern long levels[];
extern itemident *current;
time_t topictime = 0;
time_t lastservertime = 0;
char topic[BIGBUFFER]; /* Topic of the channel */
char topicwho[BIGBUFFER]; /* Who made the topic? */
bool botop = FALSE; /* TRUE if the bot has channel operator status */
long numofbotmodes;
long modecount[11] = { 0,0,0,0,0,0,0,0,0,0,0 };
long autoopswaiting = 0;
long limitc = 0;
#define MINMODEPARAMS 3
static int maxmodeparams;
/* ---------------------------------------------------------------- */
struct
{
char *name;
void (*function)(char *a, char *b);
} messages[] =
{
{"PRIVMSG", OnPrivmsg}, {"MODE", OnMode}, {"JOIN", OnJoin},
{"PART", OnPart}, {"QUIT", OnQuit}, {"NICK", OnNick},
{"KICK", OnKick}, {"PING", OnPing}, {"PONG", OnPong},
{"TOPIC", OnTopic}, {"NOTICE", OnNotice}, {"INVITE", OnInvite},
{"ERROR", OnError},
{"", (void(*)())(NULL)}
};
#ifndef HAVE_MEMMOVE
void memmove(char *to, char *from, int len)
{
int i;
if (to > from) {
for (i = len - 1; i >= 0; i--)
to[i] = from[i];
}
else {
for (i=0; i > len; i++)
to[i] = from[i];
}
}
#endif
/* --- ParseServer ------------------------------------------------ */
#include "server_gperf.c"
void ParseServer(char *line)
{
char *from, *command, *rest, *end;
snapshot;
if (':' == line[0]) {
from = line + 1;
command = StrIndex(from, ' ');
if (NULL == command)
return;
*command++ = (char)0;
}
else {
from = servername;
command = line;
}
rest = StrIndex(command, ' ');
if (NULL == rest)
return;
*rest++ = (char)0;
end = StrIndex(rest, '\n');
if (end) {
if ('\r' == end[-1])
end--;
*end = (char)0;
}
if (isdigit(command[0])) {
OnNumeric(from, atoi(command), rest);
}
else {
struct Servercmds *p;
p = FindServerKey(command, StrLength(command));
if (p) {
lastservertime = now;
p->function(from, rest);
}
}
}
/* --- OnPrivmsg -------------------------------------------------- */
void OnPrivmsg(char *from, char *line)
{
char nick[NICKLEN+1], userhost[MIDBUFFER];
char target[MIDBUFFER], buffer[BIGBUFFER];
char *xs;
snapshot;
if ('$' == line[0]) /* Ignore global messages */
return;
if ((2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
nick, userhost)) &&
(2 == StrScan(line, "%"MIDBUFFERTXT"s :%"BIGBUFFERTXT"[^\n]",
target, buffer))) {
errfrom = nick;
public = IsChannel(target);
chat = FALSE;
xs = StrIndex(buffer, '\001');
if (xs) {
char ctcpfrom[MIDBUFFER];
char ctcpbuffer[BIGBUFFER];
char *ys;
int len;
do {
ys = StrIndex(xs + 1, '\001');
if (NULL == ys)
break; /* do - while */
len = ys - xs - 1;
if (len > 0) {
memmove(ctcpbuffer, xs + 1, len);
ctcpbuffer[len] = (char)0;
len = StrLength(ys + 1);
if (len > 0 ) {
memmove(xs, ys + 1, len);
xs[len] = (char)0;
}
else
*xs = (char)0;
StrCopyMax(ctcpfrom, sizeof(ctcpfrom), from);
if (public)
PubCTCP(ctcpfrom, ctcpbuffer);
else
CTCP(ctcpfrom, ctcpbuffer);
}
else
xs += 2; /* Empty ctcp */
} while (xs = StrIndex(xs, '\001'));
}
if (buffer[0]) {
if (public)
PubCommand(nick, userhost, buffer);
else
Command(nick, userhost, buffer);
}
}
else
Debug("Parse error in OnPrivmsg(from = \"%s\", line = \"%s\")", from, line);
}
/* --- OnNotice --------------------------------------------------- */
void OnNotice(char *from, char *line)
{
char target[MIDBUFFER], buffer[BIGBUFFER];
snapshot;
if (2 == StrScan(line, "%"MIDBUFFERTXT"s :%"BIGBUFFERTXT"[^\n]",
target, buffer)) {
if (IsServer(from)) { /* Server notices */
char bywho[MIDBUFFER];
char channelbuffer[MIDBUFFER];
char modebuffer[BIGBUFFER];
if (3 == StrScan(buffer, "Fake: %"MIDBUFFERTXT"s MODE %"MIDBUFFERTXT"s %"BIGBUFFERTXT"[^\n]",
bywho, channelbuffer, modebuffer)) {
StrFormatMax(buffer, sizeof(buffer), "Fake: \"%s\" on channel %s by %s [%s]",
modebuffer, channelbuffer, bywho, from);
Log(LOG, buffer);
Multicast(REPORTCAST, buffer);
}
}
#if 0
else {
char nick[NICKLEN+1];
char userhost[MIDBUFFER];
itemguest *g;
if (2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
nick, userhost)) {
g = FindNick(nick);
if (g) {
floodcheck();
}
}
}
#endif
}
else
Debug("Parse error in OnNotice(from = \"%s\", line = \"%s\")", from, line);
}
/* --- OnTopic ---------------------------------------------------- */
void OnTopic(char *from, char *line)
{
char nick[NICKLEN+1], userhost[MIDBUFFER];
char target[MIDBUFFER], buffer[BIGBUFFER] = "";
snapshot;
if ((2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
nick, userhost)) &&
(1 <= StrScan(line, "%"MIDBUFFERTXT"s :%"BIGBUFFERTXT"[^\n]",
target, buffer))) {
Logf(LOGTOPIC, "\"%s\" by %s (%s)", buffer, nick, userhost);
#ifdef HAVE_LIBFPL
if (runfpl(RUN_TOPIC, line, FPLRUN_PRE))
return;
#endif
StrCopyMax(topic, sizeof(topic), buffer);
StrCopyMax(topicwho, sizeof(topicwho), from);
topictime = now;
runfpl(RUN_TOPIC, line, FPLRUN_POST);
}
else
Debug("Parse error in OnTopic(from = \"%s\", line = \"%s\")", from, line);
}
/* --- OnInvite --------------------------------------------------- */
void OnInvite(char *from, char *line)
{
char nick[NICKLEN+1], userhost[MIDBUFFER];
char target[MIDBUFFER];
itemuser *u;
snapshot;
if ((2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
nick, userhost)) &&
(1 == StrScan(line, "%*s :%"MIDBUFFERTXT"s", target))) {
Logf(LOG, "Invited to %s by %s", target, from);
#ifdef HAVE_LIBFPL
if (runfpl(RUN_INVITE, target, FPLRUN_PRE))
return;
#endif
u = FindUser(nick, userhost);
if (u && (u->level >= LEVELTRUST)) {
if (invitable) {
errfrom = nick;
CmdJoin(nick, target);
}
else
Send(nick, GetDefaultText(msg_no_invitation));
}
runfpl(RUN_INVITE, target, FPLRUN_POST);
}
else
Debug("Parse error in OnInvite(from = \"%s\", line = \"%s\")", from, line);
}
/* --- OnMode ----------------------------------------------------- */
#define MAXMODEPARAMS 6
#define MAXSOLOMODES 8
struct Modeparamlist {
char *param[MAXMODEPARAMS];
int index;
};
void OnMode(char *from, char *line)
{
char flagbuffer[MINIBUFFER], parambuffer[BIGBUFFER];
char domode[MAXSOLOMODES+1], undomode[MAXSOLOMODES+1];
char *userhost, *modes, *param;
char c, cursign = '+';
bool servermode, botmode;
bool keymode = FALSE, limitmode = FALSE;
bool dirty_hack = FALSE;
int params = 0, i = 0, j = 0;
itemguest *pc, *who;
struct Modeparamlist chopon, chopoff, voiceon, voiceoff;
struct Modeparamlist banon, banoff;
struct Modeparamlist bxcepton, bxceptoff, ixcepton, ixceptoff;
struct Modeparamlist temp;
snapshot;
StrTokenize(from, "!");
userhost = StrTokenize(NULL, "");
Logf(LOGMODE, "\"%s\" by %s%s%s%s", line, from,
userhost ? " (" : "",
userhost ? userhost : "",
userhost ? ")" : "");
if (!IsChannel(NextWord(line)))
return; /* Ignore user mode changes */
servermode = IsServer(from);
if (servermode) {
botmode = FALSE;
who = NULL;
}
else {
botmode = StrEqualCase(from, nickname);
who = FindNick(from); /* Who did the mode change? */
}
modes = NextWord(line);
if (NULL == modes)
return; /* Parse error! */
chopon.index = chopoff.index = voiceon.index = voiceoff.index = \
banon.index = banoff.index = bxcepton.index = bxceptoff.index = \
ixcepton.index = ixceptoff.index = 0;
while (c = *modes++) {
switch (c) {
case '+':
case '-':
cursign = c;
break;
case 'o':
param = NextWord(line);
if (param) {
params++;
if ('+' == cursign) {
modecount[0]++;
if (MAXMODEPARAMS > chopon.index)
chopon.param[chopon.index++] = param;
}
else {
if (MAXMODEPARAMS > chopoff.index)
chopoff.param[chopoff.index++] = param;
}
if (StrEqualCase(param, nickname)) {
/* This is us! */
if (talkative && !mute) {
if ('+' == cursign) {
if (!botop)
Actionf(GetDefaultText(msg_thanks_for_ops), from);
}
else {
if (botop)
Actionf(GetDefaultText(msg_slaps_for_deop), from);
}
}
botop = ('+' == cursign);
}
if (botmode) {
pc = FindNick(param);
if (pc) {
pc->flags.chanop = ('+' == cursign);
pc->flags.splitop = FALSE;
}
}
}
break;
case 'b':
param = NextWord(line);
if (param) {
params++;
if ('+' == cursign) {
modecount[1]++;
if (MAXMODEPARAMS > banon.index)
banon.param[banon.index++] = param;
}
else {
if (MAXMODEPARAMS > banoff.index)
banoff.param[banoff.index++] = param;
}
if (servermode) {
if ('+' == cursign)
AddToBanList(BAN_ACTUAL, from, NULL, param, 0, NULL);
}
else {
if ('+' == cursign) {
char *nick = NULL;
if (who) {
who->bans++;
if (!botmode &&
(who->ident->level < LEVELBOT) &&
IllegalBan(param)) {
/*
* Not set by bot AND the banner has too little level AND
* matches the bot or a ban-proctected person (when
* banprotection is on).
*/
Mode("%s-b%s%s %s",
dirty_hack ? "" : "-o",
dirty_hack ? "" : " ",
dirty_hack ? "" : from,
param);
/* Lets not deop the abuser again */
dirty_hack = TRUE;
}
}
/*
* We make an attempt to see if we can find a nick to attach
* with the specified ban pattern. This works [pretty good]
* in all cases when the ban is set before the kick...
*/
pc = FindHost(param);
if (pc) {
nick = pc->ident->nick;
}
else {
/*
* Ok, no user matched the banpattern that was set. We're
* going for more casual guesses. Now, let's take a quick look
* and see if any of the recent kicks match the pattern.
*
* We don't try the SEEN functions since they could be very
* extensive and take quite some time (a few months in #Amiga
* gives a lot more than 10000 hosts and 20000 nicks).
*/
itemkick *k;
k = KickMatch(param);
if (k)
nick = k->nick;
}
/* If we found the nick the 'guess-way', we set the GUESS bit */
AddToBanList(BAN_ACTUAL | (nick ? BAN_GUESSNICK : 0),
from, nick, param,
botmode ? -1 : pubbantime, NULL);
}
else
Unbanned(from, param, botmode);
}
if ('+' == cursign) {
int num;
num = CountBans(BAN_ACTUAL);
if (BANLIST_ALERTSIZE <= num) {
static time_t lastwarn = 0;
if (!autounban || !botop || !UnbanLoprio(from)) {
/*
* The bot can be told to automatically unban the least important
* ban when reaching this amount. If it isn't, or it isn't
* opped, or the unban failed for some reason, we procede
* (and make the ALERT output).
*/
if ((now - lastwarn) > 10*SECINMIN) {
/* Max-rate is every 10th minute! */
if (!mute)
Actionf(GetDefaultText(msg_alert_banlist_full), num);
Logf(LOGWARN, "Banlist contains %d bans!", num);
lastwarn = now;
}
}
}
}
}
break;
case 'e':
param = NextWord(line);
if (param) {
params++;
if ('+' == cursign) {
if (MAXMODEPARAMS > bxcepton.index)
bxcepton.param[bxcepton.index++] = param;
}
else {
if (MAXMODEPARAMS > bxceptoff.index)
bxceptoff.param[bxceptoff.index++] = param;
}
}
break;
case 'I':
param = NextWord(line);
if (param) {
params++;
if ('+' == cursign) {
if (MAXMODEPARAMS > ixcepton.index)
ixcepton.param[ixcepton.index++] = param;
}
else {
if (MAXMODEPARAMS > ixceptoff.index)
ixceptoff.param[ixceptoff.index++] = param;
}
}
break;
case 'k':
param = NextWord(line);
if (param) {
params++;
if ('+' == cursign) {
modecount[2]++;
StrCopyMax(chankey, 32, param);
}
else {
chankey[0] = (char)0;
}
}
keymode = TRUE;
break;
case 'l':
if ('+' == cursign) {
modecount[3]++;
param = NextWord(line);
if (param) {
params++;
limitc = atoi(param);
}
}
else {
limitc = 0;
}
limitmode = TRUE;
break;
case 'v':
param = NextWord(line);
if (param) {
params++;
if ('+' == cursign)
modecount[4]++;
pc = FindNick(param);
if (pc)
pc->flags.voice = ('+' == cursign);
}
break;
case 'p':
case 's':
case 'i':
case 't':
case 'n':
case 'm': /* undo changes */
if (StrIndex(cmodes, c)) {
if ('-' == cursign) {
if (MAXSOLOMODES > i)
domode[i++] = c;
}
}
else {
if ('+' == cursign) {
if (MAXSOLOMODES > j)
undomode[j++] = c;
}
}
if ('+' == cursign) {
switch(c) {
case 'p': modecount[5]++; break;
case 's': modecount[6]++; break;
case 'i': modecount[7]++; break;
case 't': modecount[8]++; break;
case 'n': modecount[9]++; break;
case 'm': modecount[10]++; break;
}
}
break;
} /* switch */
} /* while */
domode[i] = undomode[j] = (char)0;
if (maxmodeparams < params) {
maxmodeparams = params;
}
/* Reacts to non-bot modes only */
if (botmode) {
numofbotmodes++; /* Bot changed a mode in the channel */
}
else {
flagbuffer[0] = parambuffer[0] = (char)0;
params = 0;
/* Handle netsplit hacks, strictop and opprotect */
for (i = temp.index = 0; i < chopon.index; i++) {
pc = FindNick(chopon.param[i]);
if (pc) {
if (!pc->flags.chanop &&
(pc->ident->level < MAX(LEVELRECOG, oplevel))) {
if (servermode) {
if (netsplitmode && !pc->flags.splitop) {
temp.param[temp.index++] = chopon.param[i];
}
}
else {
/* Not by server */
if (strictopmode) {
if (!StrEqualCase(chopon.param[i], nickname) &&
who && (who->ident->level < MAX(LEVELBOT, oplevel))) {
temp.param[temp.index++] = chopon.param[i];
}
}
}
}
/* The guest *IS* chanop right now */
pc->flags.chanop = TRUE;
}
}
if (botop && temp.index) {
/* We won't inform mass-oppers */
if (!servermode && (1 == temp.index) && !mute) {
SendNickf(from, GetDefaultText(msg_no_ops_allowed), temp.param[0]);
}
StrCopyMax(flagbuffer, sizeof(flagbuffer), "-");
if (who && (who->ident->level < MAX(LEVELCHANOP, oplevel)) &&
!dirty_hack) {
dirty_hack = TRUE;
StrAppendMax(flagbuffer, sizeof(flagbuffer), "o");
StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s", from);
params++;
}
for (i = 0; i < temp.index; i++) {
if (maxmodeparams <= params) {
Mode("%s%s", flagbuffer, parambuffer);
StrCopyMax(flagbuffer, sizeof(flagbuffer), "-");
parambuffer[0] = (char)0;
params = 0;
}
StrAppendMax(flagbuffer, sizeof(flagbuffer), "o");
StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s", temp.param[i]);
params++;
}
}
for (i = temp.index = 0; i < chopoff.index; i++) {
pc = FindNick(chopoff.param[i]);
if (pc) {
if (pc->flags.chanop &&
(pc->ident->level >= MAX(LEVELCHANOP, oplevel))) {
if (servermode) {
if (netsplitmode) {
temp.param[temp.index++] = chopoff.param[i];
}
}
else {
if (opprotect) {
if (who && (who->ident->level < MAX(LEVELCHANOP, oplevel))) {
temp.param[temp.index++] = chopoff.param[i];
}
}
}
}
/* Guest *IS NOT* a chanop anymore */
pc->flags.chanop = pc->flags.splitop = FALSE;
}
}
if (botop && temp.index) {
if (who && (who->ident->level < MAX(LEVELCHANOP, oplevel)) &&
!dirty_hack) {
dirty_hack = TRUE;
if (maxmodeparams <= params) {
Mode("%s%s", flagbuffer, parambuffer);
params = 0;
}
if (0 == params) {
StrCopyMax(flagbuffer, sizeof(flagbuffer), "-");
parambuffer[0] = (char)0;
}
StrAppendMax(flagbuffer, sizeof(flagbuffer), "o");
StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s", from);
params++;
}
if (maxmodeparams > params) {
StrAppendMax(flagbuffer, sizeof(flagbuffer), "+");
}
for (i = 0; i < temp.index; i++) {
if (maxmodeparams <= params) {
Mode("%s%s", flagbuffer, parambuffer);
StrCopyMax(flagbuffer, sizeof(flagbuffer), "+");
parambuffer[0] = (char)0;
params = 0;
}
StrAppendMax(flagbuffer, sizeof(flagbuffer), "o");
StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s", temp.param[i]);
params++;
}
}
if (botop) {
if (flagbuffer[0]) {
Mode("%s%s", flagbuffer, parambuffer);
}
/* Only LEVELBOT or higher users are allowed to set/remove exceptions */
if (who && (who->ident->level < MAX(LEVELBOT, oplevel))) {
flagbuffer[0] = parambuffer[0] = (char)0;
if (bxcepton.index || ixcepton.index) {
StrAppendMax(flagbuffer, sizeof(flagbuffer), "-");
}
/* Remove user +e's */
for (i = 0; i < bxcepton.index; i++) {
StrAppendMax(flagbuffer, sizeof(flagbuffer), "e");
StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s",
bxcepton.param[i]);
}
/* Remove user +I's */
for (i = 0; i < ixcepton.index; i++) {
StrAppendMax(flagbuffer, sizeof(flagbuffer), "I");
StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s",
ixcepton.param[i]);
}
#if 0
/* We need to keep a list of exceptions to compare with */
if (bxceptoff.index || ixceptoff.index) {
StrAppendMax(flagbuffer, sizeof(flagbuffer), "+");
}
/* Reset user -e's */
for (i = 0; i < bxceptoff.index; i++) {
StrAppendMax(flagbuffer, sizeof(flagbuffer), "e");
StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s",
bxceptoff.param[i]);
}
/* Reset user -I's */
for (i = 0; i < ixceptoff.index; i++) {
StrAppendMax(flagbuffer, sizeof(flagbuffer), "I");
StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s",
ixceptoff.param[i]);
}
#endif
if (flagbuffer[0]) {
if (who->ident->level < MAX(LEVELCHANOP, oplevel))
Mode("-o %s", from);
Mode("%s%s", flagbuffer, parambuffer);
}
}
/* React to channel key changes */
if (lockkey && keymode) {
if (servermode || (who && (who->ident->level < MAX(LEVELBOT, oplevel)))) {
if (!StrEqualCase(ckey, chankey)) {
if (chankey[0])
Mode("-k %s", chankey);
if (ckey[0])
Mode("+k %s", ckey);
}
}
}
/* React to channel mode changes */
if (lockmode && (domode[0] || undomode[0])) {
if (servermode || (who && (who->ident->level < MAX(LEVELCHANOP, oplevel)))) {
Mode("%s%s%s%s", domode[0] ? "+" : "", domode,
undomode[0] ? "-" : "", undomode);
}
}
/* React to channel limit changes */
if (locklimit && limitmode) {
if (servermode || (who && (who->ident->level < MAX(LEVELCHANOP, oplevel)))) {
if (climit != limitc) {
/* We got a limit we don't like */
if (0 == climit)
Mode("-l");
else
Mode("+l %d", climit);
}
}
}
}
if (servermode) {
/* Server actions are only here on net-heals! */
NetHeal();
/* Remove bans set by server */
if (possiblyfreshtime &&
((now - possiblyfreshtime) < SERVERBANTIMEOUT)) {
for (i = 0; i < banon.index; i++) {
if (IsUnban(banon.param[i])) {
/* If one of them bans matched an unbanned one! It means that we
don't consider this a "fresh" split! */
possiblyfreshtime = 0;
break;
}
}
if (possiblyfreshtime) {
/* Don't unban serverbans */
banon.index = 0;
}
}
if (botop) {
flagbuffer[0] = parambuffer[0] = (char)0;
/* Remove +b server modes */
for (i = 0; i < banon.index; i++) {
StrAppendMax(flagbuffer, sizeof(flagbuffer), "b");
StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s",
banon.param[i]);
}
/* Remove all +e server modes for the moment */
for (i = 0; i < bxcepton.index; i++) {
StrAppendMax(flagbuffer, sizeof(flagbuffer), "e");
StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s",
bxcepton.param[i]);
}
/* Remove all +I server for the moment */
for (i = 0; i < ixcepton.index; i++) {
StrAppendMax(flagbuffer, sizeof(flagbuffer), "I");
StrFormatAppendMax(parambuffer, sizeof(parambuffer), " %s",
ixcepton.param[i]);
}
if (flagbuffer[0])
Mode("-%s%s", flagbuffer, parambuffer);
}
}
else {
/* Not server */
if (deopprotect && (temp.index > 1) && botop && who)
Warning(who, "deoppers", "Mass-deop detected");
}
}
}
/* --- OnJoin ----------------------------------------------------- */
void OnJoin(char *from, char *line)
{
char nick[NICKLEN+1], userhost[MIDBUFFER];
char target[MIDBUFFER];
char *servermodes, *pointer;
extern char unetserv[];
snapshot;
switch (line[0]) {
case ':':
pointer = &line[1];
break;
default:
pointer = line;
break;
}
if ((2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
nick, userhost)) &&
(1 == StrScan(pointer, "%"MIDBUFFERTXT"s", target))) {
runfpl(RUN_JOIN, nick, FPLRUN_PRE);
/* ircd2.9 uses '\a' to append modes (server ops/voice) */
servermodes = StrIndex(target, '\a');
if (servermodes)
*servermodes++ = (char)0;
if (StrEqualCase(nick, nickname)) {
StrCopyMax(channel, 200, target);
#ifdef NICKSERV
WriteServer("CHANSERV op %s %s", channel, nickname);
#endif
#ifdef UNDERNET
WriteServer("PRIVMSG %s :OP %s %s", unetserv, channel, nickname);
#endif
WriteServer("WHO %s", target); /* Who are on the channel */
WriteServer("MODE %s", target); /* What are the channel modes */
WriteServer("MODE %s b", target); /* What are the bans */
}
else {
bool nethealjoin = FALSE;
itemguest *g;
if (AddGuest(nick, userhost, FALSE, FALSE, FALSE, &g)) {
if (moderateflag && botop)
Mode("+v %s", nick);
}
else {
/* Netjoin or error */
nethealjoin = TRUE;
}
if (g) {
current = g->ident;
if (fakenamemode && g->ident->illegalname)
StickyKick(g, GetDefaultText(msg_illegal_name));
if (kickbans) {
/* Check done even if not opped for the purpose of being better aware
of the situation when later opped */
if (IsBan(from))
StickyKick(g, "Go away, you're banned here!");
}
if (servermodes) {
/* Make a fake mode change */
char xfrom[MIDBUFFER], xline[MIDBUFFER];
StrCopyMax(xfrom, sizeof(xfrom), servername);
StrFormatMax(xline, sizeof(xline), "%s +%s %s %s",
target, servermodes, nick,
(servermodes[1] ? nick : ""));
OnMode(xfrom, xline);
}
if (autoop && IsAutoOp(g)) {
/* DONT op anyone at this join, when several bots/people run services
like this the channel simply gets flooded after long lasting netheals.
Let's wait a (random) while before checking this user if it is op,
and if it still isn't, op the poor thing. */
g->op_this_person = now + Rnd()*10+10;
autoopswaiting++;
}
if (multimode && (g->ident->level < LEVELEXPERT) && !g->flags.bot) {
/* Do this check even if not opped, the knowledge can be good if we
are suddenly opped in the middle of an attack */
MultiCheck(g->ident->userdomain);
}
if (warnmode)
WarnCheck(nick, userhost);
if (dispcomment && !nethealjoin)
DispComment(g);
}
}
runfpl(RUN_JOIN, nick, FPLRUN_POST);
}
else
Debug("Parse error in OnJoin(from = \"%s\", line = \"%s\")", from, line);
}
/* --- OnPart ----------------------------------------------------- */
void OnPart(char *from, char *line)
{
char nick[NICKLEN+1], userhost[MIDBUFFER];
char target[MIDBUFFER], buffer[BIGBUFFER] = "";
snapshot;
if ((2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
nick, userhost)) &&
(1 <= StrScan(line, "%"MIDBUFFERTXT"s :%"BIGBUFFERTXT"[^\n]",
target, buffer))) {
Logf(LOGPART, "%s%s%s%s", nick,
buffer[0] ? " (" : "",
buffer[0] ? buffer : "",
buffer[0] ? ")" : "");
runfpl(RUN_LEAVE, nick, FPLRUN_PRE);
if (StrEqualCase(nick, nickname)) { /* I left */
SeenInsertAll(SEENLEFT, NULL, NULL);
DeleteGuests();
BanDisable(); /* No valid bans anymore */
}
else {
itemguest *g;
g = FindNick(nick);
if (g) {
SeenInsert(g, SEENLEAVE, NULL,
(buffer[0] && !StrEqualCase(buffer, nick)) ? buffer : NULL);
RemoveGuest(g);
}
else
Debug("Internal confusion. Unknown user \"%s\" left.", from);
}
runfpl(RUN_LEAVE, nick, FPLRUN_POST);
}
else
Debug("Parse error in OnPart(from = \"%s\", line = \"%s\")", from, line);
}
/* --- OnQuit ----------------------------------------------------- */
void OnQuit(char *from, char *line)
{
char nick[NICKLEN+1], userhost[MIDBUFFER];
char buffer[BIGBUFFER] = "";
snapshot;
/* There might be no reason at all, ie. only ":\000" */
StrScan(line, ":%"BIGBUFFERTXT"[^\n]", buffer);
if (2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
nick, userhost)) {
Logf(LOGQUIT, "%s (%s)", nick, buffer);
runfpl(RUN_QUIT, nick, FPLRUN_PRE);
if (StrEqualCase(nick, nickname)) { /* I quitted */
SeenInsertAll(SEENQUITED, NULL, NULL);
DeleteGuests();
BanDisable();
}
else {
bool split = FALSE;
itemguest *g;
g = FindNick(nick);
if (g) {
/* Enhanced split detection to trap even more faked ones */
if (PatternExist(buffer, "^[-A-Z0-9a-z.*]+[.][A-Za-z]+ [-A-Z0-9a-z.*]+[.][A-Za-z]+")) {
char name1[MIDBUFFER];
char name2[MIDBUFFER];
char *p;
if (2 == StrScan(buffer, "%"MIDBUFFERTXT"s %"MIDBUFFERTXT"s",
name1, name2)) {
p = StrIndexLast(name1, '.');
if (p && FindByCode(&p[1])) {
p = StrIndexLast(name2, '.');
if (p && FindByCode(&p[1]))
split = TRUE;
}
}
}
SeenInsert(g, SEENQUIT, NULL, buffer);
if (split)
AddSplitter(g, buffer);
else
RemoveGuest(g);
}
else
Debug("Internal confusion. Unknown user \"%s\" quit.", from);
}
runfpl(RUN_QUIT, nick, FPLRUN_POST);
}
else
Debug("Parse error in OnQuit(from = \"%s\", line = \"%s\")", from, line);
}
/* --- OnKick ----------------------------------------------------- */
void OnKick(char *from, char *line)
{
char nick[NICKLEN+1], userhost[MIDBUFFER];
char target[MIDBUFFER], victim[NICKLEN+1], buffer[BIGBUFFER];
snapshot;
if ((2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
nick, userhost)) &&
(3 == StrScan(line, "%"MIDBUFFERTXT"s %"NICKLENTXT"s :%"BIGBUFFERTXT"[^\n]",
target, victim, buffer))) {
Logf(LOGKICK, "(%s) by %s", line, from);
runfpl(RUN_KICK, line, FPLRUN_PRE);
if (StrEqualCase(victim, nickname)) { /* I was kicked */
SeenInsertAll(SEENKICKED, from, NULL);
DeleteGuests();
BanDisable(); /* No valid bans anymore */
if (autojoin)
WriteServer("JOIN %s %s", channel, chankey);
#ifdef HAVE_LIBFPL
if (!runfpl(RUN_KICKBOT, from, FPLRUN_PRE))
runfpl(RUN_KICKBOT, from, FPLRUN_POST);
#endif
}
else {
itemguest *g, *w;
w = FindNick(nick);
if (w)
w->kicks++;
g = FindNick(victim); /* This _CAN_ return NULL */
if (g) {
if (StrEqualCase(nick, nickname)) { /* Bot kick */
AddKick(g->ident, from, buffer, KICK_COMMON);
}
else { /* Not a bot kick */
AddKick(g->ident, from, buffer,
(g->ident->level < LEVELTRUST) ? KICK_COMMON : KICK_TRUSTEDUSER);
if (opprotect) {
if (botop && w &&
(w->ident->level < MAX(LEVELCHANOP, oplevel)) &&
(g->ident->level >= MAX(LEVELCHANOP, oplevel))) {
Mode("-o %s", nick);
}
}
}
SeenInsert(g, SEENKICK, from, buffer);
RemoveGuest(g);
}
else
Debug("Internal confusion. Unknown user \"%s\" was kicked.", victim);
}
runfpl(RUN_KICK, target, FPLRUN_POST);
}
else
Debug("Parse error in OnKick(from = \"%s\", line = \"%s\")", from, line);
}
/* --- OnNick ----------------------------------------------------- */
void OnNick(char *from, char *line)
{
char nick[NICKLEN+1], userhost[MIDBUFFER];
char newnick[NICKLEN+1];
snapshot;
if ((2 == StrScan(from, "%"NICKLENTXT"[^!]!%"MIDBUFFERTXT"s",
nick, userhost)) &&
(1 == StrScan(line, ":%"NICKLENTXT"s", newnick))) {
Logf(LOGNICK, "%s is now known as %s (%s)", nick, newnick, userhost);
runfpl(RUN_NICK, nick, FPLRUN_PRE);
if (StrEqualCase(nick, nickname)) {
StrCopy(nickname, newnick);
StrFormatMax(botmatch, MIDBUFFER, "%s!%s", newnick, myaddr);
SendLinkAll(IBCP_NICKNAME, "%s", newnick);
}
ChangeGuest(nick, newnick);
runfpl(RUN_NICK, newnick, FPLRUN_POST);
}
else
Debug("Parse error in OnNick(from = \"%s\", line = \"%s\")", from, line);
}
/* --- OnPing ----------------------------------------------------- */
void OnPing(char *from, char *line)
{
char forward[MIDBUFFER];
snapshot;
if (1 == StrScan(line, "%*s %"MIDBUFFERTXT"s", forward))
WriteServer("PONG %s", forward);
else
WriteServer("PONG %s", (':' == line[0]) ? &line[1] : line);
}
/* --- OnPong ----------------------------------------------------- */
int pendingpings = 0;
struct timeval pingval;
struct timeval pongval;
struct timeval delayval;
void OnPong(char *from, char *line)
{
snapshot;
pendingpings--;
gettimeofday(&pongval, NULL);
delayval.tv_sec = pongval.tv_sec - pingval.tv_sec;
delayval.tv_usec = pongval.tv_usec - pingval.tv_usec;
/* Normalize the time values */
while (delayval.tv_usec > MILLION) {
delayval.tv_usec -= MILLION;
delayval.tv_sec++;
}
while (delayval.tv_usec < 0) {
delayval.tv_usec += MILLION;
delayval.tv_sec--;
}
}
void PingServer(void)
{
snapshot;
pendingpings++;
gettimeofday(&pingval, NULL);
WriteServer("PING :%s", servername);
}
/* --- OnError ---------------------------------------------------- */
void OnError(char *from, char *line)
{
char buffer[BIGBUFFER];
snapshot;
#ifdef HAVE_LIBFPL
if (!runfpl(RUN_ERROR, line, FPLRUN_PRE))
runfpl(RUN_ERROR, line, FPLRUN_POST);
#endif
StrFormatMax(buffer, sizeof(buffer), "ERROR: (%s) from %s",
(':' == line[0]) ? &line[1] : line, from);
Log(LOG, buffer);
DisconnectServ(buffer);
connected = FALSE;
SeenInsertAll(SEENERROR, line, NULL);
DeleteGuests();
BanDisable();
restart = cleanup = TRUE;
}
/* --- OnKill ----------------------------------------------------- */
#if 0
void OnKill(char *from, char *line)
{
snapshot;
Logf(LOGKILL, "(%s) by %s", line, from);
#ifdef HAVE_LIBFPL
if (!runfpl(RUN_KILL, line, FPLRUN_PRE))
runfpl(RUN_KILL, line, FPLRUN_POST);
#endif
DisconnectServ(line);
connected = FALSE;
SeenInsertAll(SEENKILL, from, line);
DeleteGuests();
BanDisable();
restart = cleanup = TRUE;
}
#endif
#ifdef NICKSERV
extern char nickpasswd[];
#endif
#ifdef UNDERNET
extern char unetpasswd[];
extern char unetnick[];
extern char unetserv[];
#endif
/* --- OnNumeric -------------------------------------------------- */
void OnNumeric(char *from, int num, char *line)
{
char buffer[BIGBUFFER];
char number[22];
snapshot;
StrFormatMax(number, sizeof(number), "%d", num);
runfpl(RUN_NUMERIC, number, FPLRUN_PRE);
switch (num) {
case RPL_WELCOME: /* Welcomming message */
connected = TRUE;
StrCopyMax(servername, MIDBUFFER, from);
StrScan(line, "%"NICKLENTXT"s", nickname);
#ifndef DBUG
WriteServer("MODE %s +is", nickname);
#else
WriteServer("MODE %s +i", nickname);
#endif
#ifdef NICKSERV
WriteServer("NICKSERV identify %s", nickpasswd);
#endif
#ifdef UNDERNET
WriteServer("PRIVMSG %s :LOGIN %s %s", unetserv, unetnick, unetpasswd);
#endif
WriteServer("JOIN %s %s", channel, chankey);
gettimeofday(&pongval, NULL);
retry = 0;
maxmodeparams = MINMODEPARAMS;
break;
case ERR_NICKNAMEINUSE: /* Nickname already in use */
case ERR_ERRONEUSNICKNAME: /* or wrong nickname */
case ERR_UNAVAILRESOURCE: /* Nick/channel is temporarily unavailable */
if (!nickflag)
nickflag = TRUE;
else {
NewNick();
WriteServer("NICK %s", nickname);
}
break;
case ERR_NICKCOLLISION: /* Nickname collision */
Log(LOG, "Nickname collision");
restart = cleanup = TRUE;
break;
case ERR_NOTREGISTERED: /* You have not registered */
Hello(NULL);
break;
case RPL_WHOREPLY: /* Users on channel at join time */
NewGuest_Who(line);
break;
case RPL_TOPIC: /* The topic on join */
topictime = now;
topic[0] = (char)0;
StrScan(line, "%*s %*s :%"BIGBUFFERTXT"[^\n]", topic);
StrCopyMax(topicwho, sizeof(topicwho), from);
break;
case RPL_NOTOPIC:
topictime = 0;
break;
case RPL_CHANNELMODEIS:
chanmodes[0] = (char)0;
StrScan(line, "%*s %*s +%15s", chanmodes);
break;
case RPL_BANLIST: /* get 3rd word */
if (1 == StrScan(line, "%*s %*s %"BIGBUFFERTXT"s", buffer)) {
/* fprintf(stderr, "BANLIST ENTRY: '%s'\n", buffer); */
AddToBanList(BAN_ACTUAL, NULL, NULL, buffer, -1, NULL);
}
break;
case RPL_ENDOFBANLIST:
/* we're at the end of banlist */
bantimeouts = TRUE; /* enable timeouts now */
if (!CountBans(BAN_ACTUAL)) {
/* no actual bans in the channel */
possiblyfresh = TRUE; /* restarted server? */
/* fprintf(stderr, "### no bans in channel! ###\n"); */
}
break;
case ERR_NOSUCHNICK: /* Flush messages if target has disappeared */
case ERR_NOSUCHCHANNEL:
if (1 == StrScan(line, "%*s %"NICKLENTXT"s", buffer))
MessageReaper(buffer);
break;
case ERR_CANNOTSENDTOCHAN: /* Check for channel desync */
case ERR_CHANOPRIVSNEEDED:
if (!StrEqual(from, servername)) {
StrFormatMax(buffer, sizeof(buffer), "Desync: Server %s rejected request", from);
Log(LOG, buffer);
Multicast(REPORTCAST, buffer);
}
break;
case RPL_ENDOFNAMES: /* End of /NAMES list */
if (*askforops && !botop)
Action(askforops);
break;
case ERR_CHANNELISFULL:
StrFormatMax(buffer, sizeof(buffer), "Channel %s is full", channel);
Log(LOG, buffer);
Multicast(REPORTCAST, buffer);
break;
case ERR_INVITEONLYCHAN:
StrFormatMax(buffer, sizeof(buffer), "Channel %s is invite-only", channel);
Log(LOG, buffer);
Multicast(REPORTCAST, buffer);
break;
case ERR_BANNEDFROMCHAN:
StrFormatMax(buffer, sizeof(buffer), "Banned from channel %s", channel);
Log(LOG, buffer);
Multicast(REPORTCAST, buffer);
break;
case ERR_BADCHANNELKEY:
StrFormatMax(buffer, sizeof(buffer), "Bad channel key for %s", channel);
Log(LOG, buffer);
Multicast(REPORTCAST, buffer);
break;
case ERR_NOPERMFORHOST: /* Not I-lined */
Logf(LOG, "No permission to connect to server %s", servername);
break;
case ERR_YOUREBANNEDCREEP: /* K-lined */
Logf(LOG, "Banned from server %s", servername);
break;
#if 0
/* Messages we definetely ignore: */
/* MOTD junk */
case RPL_MOTDSTART:
case RPL_MOTD:
case RPL_ENDOFMOTD:
/* Inital crap info: */
case RPL_YOURHOST: /* 'Darxide :Your host is irc.ludd.luth.se, running
version 2.9.2+Cr8' */
case RPL_CREATED: /* 'Darxide :This server was created Sun Jan 26
1997 at 16:54:21 MET' */
case RPL_MYINFO: /* 'Darxide irc.ludd.luth.se 2.9.2+Cr8 oirw abiklmnopqstv' */
/* Luser statistics: */
case RPL_LUSERCLIENT: /* 'Darxide :There are 11523 users and 0 services
on 61 servers' */
case RPL_LUSEROP: /* 'Darxide 88 :operators online' */
case RPL_LUSERUNKNOWN: /* 'Darxide 2 :unknown connections' */
case RPL_LUSERCHANNELS: /* 'Darxide 4319 :channels formed' */
case RPL_LUSERME: /* 'Darxide :I have 309 clients, 0 services and 3
servers' */
case RPL_NAMREPLY: /* the name list we get automatically when we join a
channel */
case RPL_ENDOFWHO: /* End of /WHO list */
#endif
default: /* Recommended ending of a switch() */
/* case ERR_RESTRICTED: we're restricted here, could try to change to a
different server */
if (num >= ERR_NOSUCHNICK) {
/* Log errors only */
Logf(LOG, "Server message %d: '%s'", num, line);
}
break;
}
runfpl(RUN_NUMERIC, number, FPLRUN_POST);
}
syntax highlighted by Code2HTML, v. 0.9.1