Files
undernet-cservice/Sources/modes.c
2015-01-25 18:26:44 -06:00

971 lines
21 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* @(#)$Id: modes.c,v 1.10 1999/04/04 17:00:11 seks Exp $ */
/* Undernet Channel Service (X)
* Copyright (C) 1995-2002 Robin Thellend
*
* 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
*
* The author can be contact by email at <csfeedback@robin.pfft.net>
*
* Please note that this software is unsupported and mostly
* obsolete. It was replaced by GNUworld/CMaster. See
* http://gnuworld.sourceforge.net/ for more information.
*/
#include "h.h"
static void AddFlag(char *, char, char *);
static void RemFlag(char *, char);
void setchanmode(char *line)
{
register char *mode;
register achannel *chan;
if ((mode = strchr(line, ' ')) != NULL)
*(mode++) = 0;
chan = ToChannel(line);
if (!chan)
return;
strcpy(chan->mode, mode + 1); /* why +1 ???? coz I don't want the + */
}
void AddFlag(char *string, char flag, char *arg)
{
char newmode[15];
char newarg[200];
if (strchr(string, flag))
RemFlag(string, flag);
GetWord(0, string, newmode);
*(newmode + strlen(newmode) + 1) = 0;
*(newmode + strlen(newmode)) = flag;
strcpy(newarg, ToWord(1, string));
if (arg)
{
strcat(newarg, arg);
strcat(newarg, " ");
}
sprintf(string, "%s %s", newmode, newarg);
}
void RemFlag(char *string, char flag)
{
char newmode[15];
char newarg[200];
register char *ptr;
register char *curr = newmode;
register int count = 0;
GetWord(0, string, newmode);
strcpy(newarg, ToWord(1, string));
while (*curr)
{
ptr = strchr("lk", *curr);
if (*curr == flag)
{
strcpy(curr, curr + 1);
if (ptr)
strcpy(ToWord(count, newarg), ToWord(count + 1, newarg));
}
else
{
if (ptr)
count++;
curr++;
}
}
sprintf(string, "%s %s", newmode, newarg);
}
int IsSet(char *channel, char flag, char *param)
{
register achannel *chan;
register char *setmode;
char setflags[20];
char setparam[80];
register int out;
register int i, j;
chan = ToChannel(channel);
if (chan == NULL)
return 0;
setmode = chan->mode;
GetWord(0, setmode, setflags);
j = 0;
for (i = 0; setflags[i] != '\0' && setflags[i] != flag; i++)
{
if (setflags[i] == 'l' || setflags[i] == 'k')
j++;
}
if (setflags[i] != '\0')
{
if (flag == 'l' || flag == 'k')
{
GetWord(j + 1, setmode, setparam);
if (!strcmp(setparam, param))
out = 1;
else
out = 0;
}
else
{
out = 1;
}
}
else
{
out = 0;
}
#ifdef DEBUG
printf("IsSet(): +%c %s on %s (%d)\n", flag, param, channel, out);
#endif
return (out);
}
void bounce(char *channel, char *change, time_t TS)
{
char buffer[200];
char arg[200];
register char sign = '+';
register auser *user;
register achannel *chan;
register int pos = 1;
register int i;
chan = ToChannel(channel);
for (i = 0; change[i] != '\0' && change[i] != ' '; i++)
{
if (change[i] == '+')
sign = '+';
else if (change[i] == '-')
sign = '-';
else if (change[i] == 'o')
{
GetWord(pos++, change, arg);
user = ToUser(channel, arg);
if (user == NULL)
continue;
if (sign == '+')
{
if (!user->chanop)
{
changemode(channel, "-o", arg, 1);
user->chanop = 0;
}
}
else
{
if (user->chanop)
{
/*
changemode(channel,"+o",arg,1);
user->chanop=1;
*/
/* don't bounce -o */
user->chanop = 0;
}
}
}
else if (change[i] == 'v')
{
GetWord(pos++, change, arg);
if (sign == '+')
changemode(channel, "-v", arg, 1);
else
changemode(channel, "+v", arg, 1);
}
else if (change[i] == 'k')
{
GetWord(pos++, change, arg);
if (sign == '+')
{
if (!IsSet(channel, 'k', arg))
changemode(channel, "-k", arg, 1);
}
else
{
if (IsSet(channel, 'k', arg))
changemode(channel, "+k", arg, 1);
}
}
else if (change[i] == 'l')
{
pos++;
if (sign == '+' && !IsSet(channel, 'l', arg))
changemode(channel, "-l", "", 1);
}
else if ((IsSet(channel, change[i], "") && sign == '-') ||
(!IsSet(channel, change[i], "") && sign == '+'))
{
sprintf(buffer, "%c%c",
(sign == '+') ? '-' : '+',
change[i]);
changemode(channel, buffer, "", 1);
}
}
AddEvent(EVENT_FLUSHMODEBUFF, now + MODE_DELAY, channel);
}
void ModeChange(char *source, char *channel, char *change)
{
register char sign = '+';
char arg[200];
register char *ptr;
register achannel *chan;
register aluser *u;
register int count = 1;
int desync = 0;
#ifdef DEBUG
printf("ModeChange(%s, %s, %s)\n", source, channel, change);
#endif
chan = ToChannel(channel);
/* if chan == NULL at this point.. we're dealing with a
* user mode change
*/
if (chan != NULL)
chan->lastact = now;
u = ToLuser(source);
if (chan == NULL && u == NULL)
{
return; /* probably a lost MODE */
}
if (u == NULL)
{
/* This is a mode change from a server.. if it is not
* a +b, then the last argument is a TS
*/
for (ptr = change; *ptr != 'b' && *ptr != ' '; ptr++);
if (*ptr != 'b' && strcasecmp(UWORLD_SERVER, source)
#ifdef UWORLD2
&& strcasecmp(UWORLD2_SERVER, source)
#endif
#ifdef FAKE_UWORLD
&& strcasecmp(UFAKE_SERVER, source)
#endif
)
{
ptr = change + strlen(change);
while (*ptr != ' ')
ptr--;
*(ptr++) = '\0';
if (atol(ptr) > chan->TS && atol(ptr) != 0 && chan->on)
{
log(source);
log(channel);
log(change);
log(ptr);
bounce(channel, change, chan->TS);
return;
}
if (atol(ptr) != 0)
chan->TS = atol(ptr);
}
}
while (*change && *change != ' ')
{
if (*change == '+')
{
sign = '+';
}
else if (*change == '-')
{
sign = '-';
}
else
{
if (sign == '+')
{
if (chan != NULL)
{
ptr = strchr("blkov", *change);
if (ptr)
{
GetWord(count, change, arg);
if (strchr("lk", *change) && *arg)
AddFlag(chan->mode, *change, arg);
count++;
}
else
{
if (strchr("imntps", *change))
AddFlag(chan->mode, *change, NULL);
}
}
else
{
/* user mode change (+) */
if (*change == 'o')
{
u->mode |= LFL_ISOPER;
#ifdef DEBUG
printf("%s is now IRCOP\n", source);
#endif
}
}
}
else
{
if (chan != NULL)
{
ptr = strchr("bkov", *change);
if (strchr("ilkmntps", *change))
RemFlag(chan->mode, *change);
if (ptr)
{
GetWord(count, change, arg);
count++;
}
}
else
{
/* user mode change (-) */
if (*change == 'o')
{
u->mode &= ~LFL_ISOPER;
#ifdef DEBUG
printf("%s is no longer IRCOP\n", source);
#endif
}
}
}
/** other tests should go here **/
if (chan != NULL)
{
if (*change == 'o')
{
if (sign == '+')
onop(source, channel, arg);
else
ondeop(source, channel, arg, &desync);
}
else if (*change == 'b')
{
if (sign == '+')
onban(source, channel, arg);
else
onunban(source, channel, arg);
}
else if (*change == 'l')
{
if (sign == '+' && atoi(arg) < 2)
{
if (chan->on && chan->AmChanOp)
{
changemode(channel, "-l", "", 0);
RemFlag(chan->mode, *change);
}
}
}
}
}
change++;
if (*change == ' ')
{
change = ToWord(count, change);
count = 1;
}
}
if (chan != NULL)
{
if (desync)
{
part("", channel, "");
join("", channel, "");
}
CheckFlood(source, channel, 80);
flushmode(channel);
}
}
void onop(char *source, char *channel, char *target)
{
register auser *user;
register aluser *luser;
register achannel *chan;
char buffer[200];
chan = ToChannel(channel);
luser = ToLuser(source);
user = ToUser(channel, target);
if (!strcasecmp(mynick, target))
{
if (!chan)
{
log("ERROR: onop() channel not found!");
return;
}
chan->AmChanOp = 1;
}
else
{
#ifdef DEBUG
printf("OP for %s on %s\n", target, channel);
#endif
if (!user)
{
sprintf(buffer, "ERROR: onop() user not found (%s)",
target);
log(buffer);
return;
}
user->chanop = 1;
sprintf(buffer, "%s!%s@%s", user->N->nick, user->N->username, user->N->site);
if (luser && chan->on && (chan->flags & CFL_NOOP))
{
sprintf(buffer, "NoOp MODE! deopping %s and %s",
target, source);
log(buffer);
notice(target, replies[RPL_NOOP][chan->lang]);
notice(source, replies[RPL_NOOP][chan->lang]);
changemode(channel, "-o", target, 0);
changemode(channel, "-o", source, 0);
user->chanop = 0;
user = ToUser(channel, source);
if (user != NULL)
{
user->chanop = 0;
}
}
else if (luser && chan->on && (chan->flags & CFL_STRICTOP) &&
Access(channel, target) < OP_LEVEL)
{
sprintf(buffer, "StrictOp MODE! deopping %s and %s",
target, source);
log(buffer);
notice(target, "Only authenticated users may be op in StrictOp mode");
notice(source, "Only authenticated users may be op in StrictOp mode");
changemode(channel, "-o", target, 0);
user->chanop = 0;
if (Access(channel, source) < OP_LEVEL)
{
changemode(channel, "-o", source, 0);
user = ToUser(channel, source);
if (user != NULL)
{
user->chanop = 0;
}
}
}
else if (luser && chan->on && IsShit(channel, buffer, NULL, NULL) >= NO_OP_SHIT_LEVEL)
{
if (Access(channel, source) < ACCESS_BAN_PRIORITY)
{
sprintf(buffer, "%s is shitlisted (NO-OP LEVEL)!"
"Deopping %s and %s", target, target, source);
log(buffer);
notice(target, replies[RPL_CANTBEOP][chan->lang]);
notice(source, replies[RPL_CANTBEOPPED][chan->lang]);
changemode(channel, "-o", target, 0);
changemode(channel, "-o", source, 0);
user->chanop = 0;
user = ToUser(channel, source);
if (user != NULL)
user->chanop = 0;
sprintf(buffer, "%s %d", source, SUSPEND_TIME_FOR_OPPING_A_SHITLISTED_USER);
suspend("", channel, buffer);
}
else
{
unban(source, channel, target);
}
}
}
/** further tests should be put here **/
#ifdef NICKSERV
nserv_onop(channel, user);
#endif
}
void ondeop(char *source, char *channel, char *target, int *desync)
{
char buffer[200];
register auser *user1, *user2;
register achannel *chan;
register adeop *curr, *prec;
register int i;
chan = ToChannel(channel);
if (!chan)
{
log("ERROR: ondeop() channel not found!");
return;
}
user1 = ToUser(channel, source);
user2 = ToUser(channel, target);
if (chan->on && user1 == NULL)
{ /* desync! */
if (user2 != NULL)
{
user2->chanop = 0;
}
sprintf(buffer, "DESYNC detected! (%s deopped %s on %s)",
source, target, channel);
log(buffer);
/* part("",channel,"");
join("",channel,""); now doing this in ChangeMode */
*desync = 1;
return;
}
if (!strcasecmp(mynick, target))
{
if ((chan->flags & CFL_ALWAYSOP) &&
Access(channel, source) >= ALWAYSOP_OVERRIDE_LEVEL)
{
chan->flags &= ~CFL_ALWAYSOP;
sprintf(buffer, "AlwaysOp is turned off on %s (deopped by %s!%s@%s)",
channel, user1->N->nick, user1->N->username, user1->N->site);
log(buffer);
notice(source, replies[RPL_ALWAYSOPWASACTIVE][chan->lang]);
}
if (chan->flags & CFL_ALWAYSOP)
{
if (user1 != NULL)
{
sprintf(buffer, "DEOPPED by %s!%s@%s (%d) while AlwaysOp active on %s",
user1->N->nick, user1->N->username, user1->N->site,
Access(channel, source), channel);
log(buffer);
broadcast(buffer, 0);
i = IsShit(channel, source, NULL, NULL);
notice(source, replies[RPL_ALWAYSOP][chan->lang]);
switch (i)
{
case 0:
case 1:
case 2:
case 3:
case 4:
log("First warning");
notice(source, replies[RPL_DEOPPED1ST][chan->lang]);
changemode(channel, "-o", source, 0);
flushmode(channel);
user1->chanop = 0;
break;
case 5:
case 6:
case 7:
case 8:
case 9:
log("Second warning");
notice(source, replies[RPL_DEOPPED2ND][chan->lang]);
kick("", channel, source);
break;
default:
notice(source, "I warned you!");
changemode(channel, "-o", source, 0);
flushmode(channel);
user1->chanop = 0;
sprintf(buffer, "%s!%s@%s %d",
user1->N->nick, user1->N->username,
user1->N->site,
DEOPME_SUSPEND_TIME);
suspend("", channel, buffer);
sprintf(buffer, "Suspended %s!%s@%s on %s for repeatedly deopping me",
user1->N->nick, user1->N->username, user1->N->site, channel);
SpecLog(buffer);
broadcast(buffer, 0);
}
sprintf(buffer, "%s %d %d *** DEOP WHILE ALWAYSOP ACTIVE ***",
source, DEOP_SHITLIST_TIME,
(i < 10) ? i + 5 :
DEOP_SHITLIST_LEVEL);
AddToShitList("", channel, buffer, 0);
chan->AmChanOp = 0; /* to remove if I reop myself */
GetOps(channel);
}
}
else
{
chan->AmChanOp = 0;
if (IsOpless(channel))
onopless(channel);
}
}
/* massdeop protection does NOT apply to the bot itself!
if(!strcasecmp(source,mynick)) return; obsolete */
/* No protection against server deop :/
Hey! I'm not gonna /squit it just for doing a deop!! ;) */
if (!user1)
return;
if (user2)
user2->chanop = 0;
chan = ToChannel(channel);
if (!chan)
{
log("ERROR: ondeop() channel not found!");
return;
}
/* If not on channel.. do NOT check for massdeop
*/
if (!chan->on)
return;
curr = user1->deophist;
prec = NULL;
/* ok.. let's free the *old* deops from history */
while (curr)
{
if (curr->time < (now - 15) || !strcasecmp(curr->nick, target))
{
if (prec)
{
prec->next = curr->next;
TTLALLOCMEM -= sizeof(adeop);
free(curr);
curr = prec->next;
}
else
{
user1->deophist = curr->next;
TTLALLOCMEM -= sizeof(adeop);
free(curr);
curr = user1->deophist;
}
}
else
{
prec = curr;
curr = curr->next;
}
}
/* now store the deop in the user's deop history */
curr = (adeop *) MALLOC(sizeof(adeop));
strcpy(curr->nick, target);
curr->time = now;
curr->next = user1->deophist;
user1->deophist = curr;
/* now we count the number of deop in the user's history
if it's equal to the max number of deops per 15 seconds
interval.. activate the massdeop protection */
for (curr = user1->deophist, i = 0; curr; curr = curr->next, i++);
if (i == chan->MassDeopPro && chan->MassDeopPro != 0 && chan->on)
{
sprintf(buffer, "MASSDEOP from %s on %s", source, channel);
log(buffer);
notice(source, "### MASSDEOP PROTECTION ACTIVATED ###");
if (Access(channel, source) >= MASSDEOP_IMMUNE_LEVEL)
{
notice(source, "You are lucky your access is so high :P");
}
else
{
sprintf(buffer, "%s %d", source, MASSDEOP_SUSPEND_TIME);
suspend("", channel, buffer);
sprintf(buffer, "%s %d %d *** MASSDEOP ***", source,
MASSDEOP_SHITLIST_TIME,
MASSDEOP_SHITLIST_LEVEL);
AddToShitList("", channel, buffer, 0);
sprintf(buffer, "%s ### MASSDEOP PROTECTION ###", source);
kick("", channel, buffer);
}
}
}
void onban(char *nick, char *channel, char *pattern)
{
#ifdef DEBUG
printf("Wah! BAN FROM: %s\nON CHANNEL %s\nTARGET: %s\n",
nick, channel, pattern);
#endif
AddBan(channel, pattern);
}
void onunban(char *source, char *channel, char *pattern)
{
/*register achannel *chan; */
RemBan(channel, pattern);
/* mode -b no longer removes the ban from X's banlist
** it has to be removed with the 'unban' command.
chan=ToChannel(channel);
if(chan!=NULL && chan->on)
RemShitList(source,channel,pattern,1);
* */
}
void AddBan(char *channel, char *pattern)
{
register achannel *chan;
register aban *theban;
char buffer[200];
chan = ToChannel(channel);
if (chan == NULL)
{
sprintf(buffer, "ERROR: AddBan(): can't find %s", channel);
log(buffer);
return;
}
/* check if ban isn't already there */
for (theban = chan->bans; theban != NULL; theban = theban->next)
{
if (!strncasecmp(theban->pattern, pattern, 79))
{
return;
}
}
theban = (aban *) MALLOC(sizeof(aban));
strncpy(theban->pattern, pattern, 79);
theban->pattern[79] = '\0';
theban->next = chan->bans;
chan->bans = theban;
}
void RemBan(char *channel, char *pattern)
{
register achannel *chan;
register aban **b, *theban = NULL;
chan = ToChannel(channel);
b = &chan->bans;
while (*b != NULL)
{
if (!strncasecmp((*b)->pattern, pattern, 79))
{
theban = *b;
*b = theban->next;
TTLALLOCMEM -= sizeof(aban);
free(theban);
}
else
{
b = &(*b)->next;
}
}
#ifdef DEBUG
if (theban == NULL)
{
printf("WARNING: RemBan(): pattern NOT found!!\n");
}
#endif
}
void changemode(char *channel, char *flag, char *arg, int AsServer)
{
register achannel *chan;
register modequeue *mode, *curr;
#ifdef DEBUG
printf("Queueing mode change for channel %s %s %s %d\n", channel, flag, arg, AsServer);
#endif
chan = ToChannel(channel);
if (!AsServer && (!chan || !chan->on))
return; /* not on the channel.. ignore */
/* first, cancel previous contradicting mode changes..
ex.. mode #test +o-o+o-o+o .. the bot won't do that.. */
mode = chan->modebuff;
while (mode)
{
if (!strcmp(arg, mode->arg) && flag[1] == mode->flag[1] &&
((flag[0] == '+' && mode->flag[0] == '-') ||
(flag[0] == '-' && mode->flag[0] == '+')))
{
if (mode->prev)
mode->prev->next = mode->next;
else
chan->modebuff = mode->next;
if (mode->next)
mode->next->prev = mode->prev;
curr = mode;
TTLALLOCMEM -= sizeof(modequeue);
free(curr);
}
else if (!strcmp(arg, mode->arg) && flag[1] == mode->flag[1] &&
flag[0] == mode->flag[0])
return;
mode = mode->next;
}
curr = (modequeue *) MALLOC(sizeof(modequeue));
strcpy(curr->arg, arg);
strcpy(curr->flag, flag);
curr->AsServer = AsServer;
curr->next = NULL;
for (mode = chan->modebuff; mode && mode->next; mode = mode->next);
if (mode)
{
curr->prev = mode;
mode->next = curr;
}
else
{
chan->modebuff = curr;
curr->prev = NULL;
}
}
void flushmode(char *channel)
{
char buffer[500];
register achannel *chan;
register modequeue *mode, *tmp;
char flags[20] = "";
char args[500] = "";
register char lastsign;
register int AsServer;
register int count = 0;
#ifdef DEBUG
printf("Flushing mode change buffer.. for channel %s\n", channel);
#endif
/* gotta pass thru the list twice:
* the first time, check for Server mode changes
* the second time, check for user mode changes
*/
chan = ToChannel(channel);
if (chan == NULL)
return;
#ifdef FAKE_UWORLD
#define ASSERVERMAX 2
#else
#define ASSERVERMAX 1
#endif
for (AsServer = ASSERVERMAX; AsServer >= 0; AsServer--)
{
#undef ASSERVERMAX
lastsign = '\0';
mode = chan->modebuff;
while (mode != NULL)
{
if (!strcmp(mode->flag, "-b"))
{
RemBan(channel, mode->arg);
}
else if (!strcmp(mode->flag, "+b"))
{
AddBan(channel, mode->arg);
}
if (AsServer == mode->AsServer)
{
if (mode->arg[0] != '\0')
{
count++;
strcat(args, " ");
strcat(args, mode->arg);
}
if (lastsign != mode->flag[0])
{
strcat(flags, mode->flag);
lastsign = mode->flag[0];
}
else
{
strcat(flags, mode->flag + 1);
}
if (mode->prev)
mode->prev->next = mode->next;
else
chan->modebuff = mode->next;
if (mode->next)
mode->next->prev = mode->prev;
tmp = mode;
mode = mode->next;
TTLALLOCMEM -= sizeof(modequeue);
free(tmp);
}
else
{
mode = mode->next;
}
if (count == MAX_MODE_PER_LINE || !mode)
{
if (AsServer == 0 && chan->AmChanOp && *flags != '\0')
{
sprintf(buffer, ":%s MODE %s %s%s\n",
mynick, channel, flags, args);
sendtoserv(buffer);
}
else if (AsServer == 1 && *flags != '\0')
{
sprintf(buffer, ":%s MODE %s %s%s %ld\n",
SERVERNAME, channel,
flags, args, chan->TS);
sendtoserv(buffer);
}
#ifdef FAKE_UWORLD
else if (AsServer == 2 && Uworld_status == 1 && *flags != '\0')
{
sprintf(buffer, ":%s MODE %s %s%s\n",
UFAKE_SERVER, channel,
flags, args);
sendtoserv(buffer);
}
#endif
count = *args = *flags = 0;
lastsign = '+';
}
}
}
}