mirror of
https://github.com/anope/anope.git
synced 2026-06-26 05:56:39 +02:00
854 lines
20 KiB
C++
854 lines
20 KiB
C++
/* BotServ functions
|
|
*
|
|
* (C) 2003-2010 Anope Team
|
|
* Contact us at team@anope.org
|
|
*
|
|
* Please read COPYING and README for further details.
|
|
*
|
|
* Based on the original code of Epona by Lara.
|
|
* Based on the original code of Services by Andy Church.
|
|
*
|
|
*
|
|
*/
|
|
|
|
/*************************************************************************/
|
|
|
|
#include "services.h"
|
|
#include "modules.h"
|
|
#include "language.h"
|
|
|
|
static UserData *get_user_data(Channel * c, User * u);
|
|
|
|
static void check_ban(ChannelInfo * ci, User * u, int ttbtype);
|
|
static void bot_kick(ChannelInfo * ci, User * u, int message, ...);
|
|
|
|
E void moduleAddBotServCmds();
|
|
|
|
/*************************************************************************/
|
|
/* *INDENT-OFF* */
|
|
void moduleAddBotServCmds() {
|
|
ModuleManager::LoadModuleList(Config.BotServCoreModules);
|
|
}
|
|
/* *INDENT-ON* */
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
/* Return information on memory use. Assumes pointers are valid. */
|
|
|
|
void get_botserv_stats(long *nrec, long *memuse)
|
|
{
|
|
long count = 0, mem = 0;
|
|
|
|
for (botinfo_map::const_iterator it = BotList.begin(); it != BotList.end(); ++it)
|
|
{
|
|
BotInfo *bi = it->second;
|
|
|
|
count++;
|
|
mem += sizeof(*bi);
|
|
mem += bi->nick.size() + 1;
|
|
mem += bi->user.size() + 1;
|
|
mem += bi->host.size() + 1;
|
|
mem += bi->real.size() + 1;
|
|
}
|
|
|
|
*nrec = count;
|
|
*memuse = mem;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/*************************************************************************/
|
|
|
|
/* BotServ initialization. */
|
|
|
|
void bs_init()
|
|
{
|
|
if (Config.s_BotServ) {
|
|
moduleAddBotServCmds();
|
|
}
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Main BotServ routine. */
|
|
|
|
void botserv(User * u, char *buf)
|
|
{
|
|
char *cmd, *s;
|
|
|
|
cmd = strtok(buf, " ");
|
|
|
|
if (!cmd) {
|
|
return;
|
|
} else if (stricmp(cmd, "\1PING") == 0) {
|
|
if (!(s = strtok(NULL, ""))) {
|
|
*s = 0;
|
|
}
|
|
ircdproto->SendCTCP(BotServ, u->nick.c_str(), "PING %s", s);
|
|
} else {
|
|
mod_run_cmd(BotServ, u, cmd);
|
|
}
|
|
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Handles all messages sent to bots. (Currently only answers to pings ;) */
|
|
|
|
void botmsgs(User * u, BotInfo * bi, char *buf)
|
|
{
|
|
char *cmd = strtok(buf, " ");
|
|
char *s;
|
|
|
|
if (!cmd || !u || !bi)
|
|
return;
|
|
|
|
if (!stricmp(cmd, "\1PING")) {
|
|
if (!(s = strtok(NULL, ""))) {
|
|
*s = 0;
|
|
}
|
|
ircdproto->SendCTCP(bi, u->nick.c_str(), "PING %s", s);
|
|
}
|
|
else if (cmd && !bi->Commands.empty())
|
|
{
|
|
mod_run_cmd(bi, u, cmd);
|
|
}
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Handles all messages that are sent to registered channels where a
|
|
* bot is on.
|
|
*
|
|
*/
|
|
|
|
void botchanmsgs(User * u, ChannelInfo * ci, char *buf)
|
|
{
|
|
int c;
|
|
char *cmd;
|
|
UserData *ud;
|
|
bool was_action = false;
|
|
std::string bbuf;
|
|
|
|
if (!u || !buf || !ci || !ci->c)
|
|
return;
|
|
|
|
/* Answer to ping if needed, without breaking the buffer. */
|
|
if (!strnicmp(buf, "\1PING", 5)) {
|
|
ircdproto->SendCTCP(ci->bi, u->nick.c_str(), "PING %s", buf);
|
|
}
|
|
|
|
/* If it's a /me, cut the CTCP part at the beginning (not
|
|
* at the end, because one character just doesn't matter,
|
|
* but the ACTION may create strange behaviours with the
|
|
* caps or badwords kickers */
|
|
if (!strnicmp(buf, "\1ACTION ", 8))
|
|
{
|
|
buf += 8;
|
|
was_action = true;
|
|
}
|
|
|
|
/* Now we can make kicker stuff. We try to order the checks
|
|
* from the fastest one to the slowest one, since there's
|
|
* no need to process other kickers if an user is kicked before
|
|
* the last kicker check.
|
|
*
|
|
* But FIRST we check whether the user is protected in any
|
|
* way.
|
|
*/
|
|
|
|
bool Allow = false;
|
|
if (!ci->botflags.HasFlag(BS_DONTKICKOPS) && !ci->botflags.HasFlag(BS_DONTKICKVOICES))
|
|
Allow = true;
|
|
else if (ci->botflags.HasFlag(BS_DONTKICKOPS) && (ci->c->HasUserStatus(u, CMODE_HALFOP) || ci->c->HasUserStatus(u, CMODE_OP) || ci->c->HasUserStatus(u, CMODE_PROTECT) || ci->c->HasUserStatus(u, CMODE_OWNER)))
|
|
Allow = true;
|
|
else if (ci->botflags.HasFlag(BS_DONTKICKVOICES) && ci->c->HasUserStatus(u, CMODE_VOICE))
|
|
Allow = true;
|
|
|
|
if (buf && !check_access(u, ci, CA_NOKICK) && Allow)
|
|
{
|
|
/* Bolds kicker */
|
|
if (ci->botflags.HasFlag(BS_KICK_BOLDS) && strchr(buf, 2)) {
|
|
check_ban(ci, u, TTB_BOLDS);
|
|
bot_kick(ci, u, BOT_REASON_BOLD);
|
|
return;
|
|
}
|
|
|
|
/* Color kicker */
|
|
if (ci->botflags.HasFlag(BS_KICK_COLORS) && strchr(buf, 3)) {
|
|
check_ban(ci, u, TTB_COLORS);
|
|
bot_kick(ci, u, BOT_REASON_COLOR);
|
|
return;
|
|
}
|
|
|
|
/* Reverses kicker */
|
|
if (ci->botflags.HasFlag(BS_KICK_REVERSES) && strchr(buf, 22)) {
|
|
check_ban(ci, u, TTB_REVERSES);
|
|
bot_kick(ci, u, BOT_REASON_REVERSE);
|
|
return;
|
|
}
|
|
|
|
/* Underlines kicker */
|
|
if (ci->botflags.HasFlag(BS_KICK_UNDERLINES) && strchr(buf, 31)) {
|
|
check_ban(ci, u, TTB_UNDERLINES);
|
|
bot_kick(ci, u, BOT_REASON_UNDERLINE);
|
|
return;
|
|
}
|
|
|
|
/* Caps kicker */
|
|
if (ci->botflags.HasFlag(BS_KICK_CAPS)
|
|
&& ((c = strlen(buf)) >= ci->capsmin)) {
|
|
int i = 0;
|
|
int l = 0;
|
|
char *s = buf;
|
|
|
|
do {
|
|
if (isupper(*s))
|
|
i++;
|
|
else if (islower(*s))
|
|
l++;
|
|
} while (*s++);
|
|
|
|
/* i counts uppercase chars, l counts lowercase chars. Only
|
|
* alphabetic chars (so islower || isupper) qualify for the
|
|
* percentage of caps to kick for; the rest is ignored. -GD
|
|
*/
|
|
|
|
if (i && l && i >= ci->capsmin && i * 100 / (i + l) >= ci->capspercent) {
|
|
check_ban(ci, u, TTB_CAPS);
|
|
bot_kick(ci, u, BOT_REASON_CAPS);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Bad words kicker */
|
|
if (ci->botflags.HasFlag(BS_KICK_BADWORDS)) {
|
|
int mustkick = 0;
|
|
char *nbuf;
|
|
BadWord *bw;
|
|
|
|
/* Normalize the buffer */
|
|
nbuf = normalizeBuffer(buf);
|
|
|
|
for (unsigned i = 0; i < ci->GetBadWordCount(); ++i)
|
|
{
|
|
bw = ci->GetBadWord(i);
|
|
|
|
if (bw->type == BW_ANY
|
|
&& ((Config.BSCaseSensitive && strstr(nbuf, bw->word.c_str()))
|
|
|| (!Config.BSCaseSensitive && stristr(nbuf, bw->word.c_str())))) {
|
|
mustkick = 1;
|
|
} else if (bw->type == BW_SINGLE) {
|
|
int len = bw->word.length();
|
|
|
|
if ((Config.BSCaseSensitive && nbuf == bw->word)
|
|
|| (!Config.BSCaseSensitive
|
|
&& (!stricmp(nbuf, bw->word.c_str())))) {
|
|
mustkick = 1;
|
|
/* two next if are quite odd isn't it? =) */
|
|
} else if ((strchr(nbuf, ' ') == nbuf + len)
|
|
&&
|
|
((Config.BSCaseSensitive && nbuf == bw->word)
|
|
|| (!Config.BSCaseSensitive
|
|
&& (stristr(nbuf, bw->word.c_str()) ==
|
|
nbuf)))) {
|
|
mustkick = 1;
|
|
} else {
|
|
if ((strrchr(nbuf, ' ') ==
|
|
nbuf + strlen(nbuf) - len - 1)
|
|
&&
|
|
((Config.BSCaseSensitive
|
|
&& (strstr(nbuf, bw->word.c_str()) ==
|
|
nbuf + strlen(nbuf) - len))
|
|
|| (!Config.BSCaseSensitive
|
|
&& (stristr(nbuf, bw->word.c_str()) ==
|
|
nbuf + strlen(nbuf) - len)))) {
|
|
mustkick = 1;
|
|
} else {
|
|
char *wordbuf = new char[len + 3];
|
|
|
|
wordbuf[0] = ' ';
|
|
wordbuf[len + 1] = ' ';
|
|
wordbuf[len + 2] = '\0';
|
|
memcpy(wordbuf + 1, bw->word.c_str(), len);
|
|
|
|
if ((Config.BSCaseSensitive
|
|
&& (strstr(nbuf, wordbuf)))
|
|
|| (!Config.BSCaseSensitive
|
|
&& (stristr(nbuf, wordbuf)))) {
|
|
mustkick = 1;
|
|
}
|
|
|
|
/* free previous (sc)allocated memory (#850) */
|
|
delete [] wordbuf;
|
|
}
|
|
}
|
|
} else if (bw->type == BW_START) {
|
|
int len = bw->word.length();
|
|
|
|
if ((Config.BSCaseSensitive
|
|
&& (!strncmp(nbuf, bw->word.c_str(), len)))
|
|
|| (!Config.BSCaseSensitive
|
|
&& (!strnicmp(nbuf, bw->word.c_str(), len)))) {
|
|
mustkick = 1;
|
|
} else {
|
|
char *wordbuf = new char[len + 2];
|
|
|
|
memcpy(wordbuf + 1, bw->word.c_str(), len);
|
|
wordbuf[0] = ' ';
|
|
wordbuf[len + 1] = '\0';
|
|
|
|
if ((Config.BSCaseSensitive && (strstr(nbuf, wordbuf)))
|
|
|| (!Config.BSCaseSensitive
|
|
&& (stristr(nbuf, wordbuf))))
|
|
mustkick = 1;
|
|
|
|
delete [] wordbuf;
|
|
}
|
|
} else if (bw->type == BW_END) {
|
|
int len = bw->word.length();
|
|
|
|
if ((Config.BSCaseSensitive
|
|
&&
|
|
(!strncmp
|
|
(nbuf + strlen(nbuf) - len, bw->word.c_str(), len)))
|
|
|| (!Config.BSCaseSensitive
|
|
&&
|
|
(!strnicmp
|
|
(nbuf + strlen(nbuf) - len, bw->word.c_str(),
|
|
len)))) {
|
|
mustkick = 1;
|
|
} else {
|
|
char *wordbuf = new char[len + 2];
|
|
|
|
memcpy(wordbuf, bw->word.c_str(), len);
|
|
wordbuf[len] = ' ';
|
|
wordbuf[len + 1] = '\0';
|
|
|
|
if ((Config.BSCaseSensitive && (strstr(nbuf, wordbuf)))
|
|
|| (!Config.BSCaseSensitive
|
|
&& (stristr(nbuf, wordbuf))))
|
|
mustkick = 1;
|
|
|
|
delete [] wordbuf;
|
|
}
|
|
}
|
|
|
|
if (mustkick) {
|
|
check_ban(ci, u, TTB_BADWORDS);
|
|
if (Config.BSGentleBWReason)
|
|
bot_kick(ci, u, BOT_REASON_BADWORD_GENTLE);
|
|
else
|
|
bot_kick(ci, u, BOT_REASON_BADWORD, bw->word.c_str());
|
|
|
|
/* free the normalized buffer before return (#850) */
|
|
delete [] nbuf;
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Free the normalized buffer */
|
|
delete [] nbuf;
|
|
}
|
|
|
|
/* Flood kicker */
|
|
if (ci->botflags.HasFlag(BS_KICK_FLOOD)) {
|
|
time_t now = time(NULL);
|
|
|
|
ud = get_user_data(ci->c, u);
|
|
if (!ud) {
|
|
return;
|
|
}
|
|
|
|
if (now - ud->last_start > ci->floodsecs) {
|
|
ud->last_start = time(NULL);
|
|
ud->lines = 0;
|
|
}
|
|
|
|
ud->lines++;
|
|
if (ud->lines >= ci->floodlines) {
|
|
check_ban(ci, u, TTB_FLOOD);
|
|
bot_kick(ci, u, BOT_REASON_FLOOD);
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* Repeat kicker */
|
|
if (ci->botflags.HasFlag(BS_KICK_REPEAT)) {
|
|
ud = get_user_data(ci->c, u);
|
|
if (!ud) {
|
|
return;
|
|
}
|
|
if (ud->lastline && stricmp(ud->lastline, buf)) {
|
|
delete [] ud->lastline;
|
|
ud->lastline = sstrdup(buf);
|
|
ud->times = 0;
|
|
} else {
|
|
if (!ud->lastline)
|
|
ud->lastline = sstrdup(buf);
|
|
ud->times++;
|
|
}
|
|
|
|
if (ud->times >= ci->repeattimes) {
|
|
check_ban(ci, u, TTB_REPEAT);
|
|
bot_kick(ci, u, BOT_REASON_REPEAT);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* return if the user is on the ignore list */
|
|
if (get_ignore(u->nick.c_str()) != NULL) {
|
|
return;
|
|
}
|
|
|
|
/* Fantaisist commands */
|
|
|
|
if (buf && ci->botflags.HasFlag(BS_FANTASY) && *buf == *Config.BSFantasyCharacter && !was_action) {
|
|
cmd = strtok(buf, " ");
|
|
|
|
if (cmd && (cmd[0] == *Config.BSFantasyCharacter)) {
|
|
char *params = strtok(NULL, "");
|
|
|
|
/* Strip off the fantasy character */
|
|
cmd++;
|
|
|
|
if (check_access(u, ci, CA_FANTASIA))
|
|
{
|
|
Command *command = FindCommand(ChanServ, cmd);
|
|
|
|
/* Command exists and can not be called by fantasy */
|
|
if (command && !command->HasFlag(CFLAG_DISABLE_FANTASY))
|
|
{
|
|
bbuf = std::string(cmd);
|
|
|
|
/* Some commands don't need the channel name added.. eg !help */
|
|
if (!command->HasFlag(CFLAG_STRIP_CHANNEL))
|
|
{
|
|
bbuf += " ";
|
|
bbuf += ci->name;
|
|
}
|
|
|
|
if (params)
|
|
{
|
|
bbuf += " ";
|
|
bbuf += params;
|
|
}
|
|
|
|
chanserv(u, const_cast<char *>(bbuf.c_str())); // XXX Unsafe cast, this needs reviewing -- CyberBotX
|
|
}
|
|
|
|
FOREACH_MOD(I_OnBotFantasy, OnBotFantasy(cmd, u, ci, params));
|
|
}
|
|
else
|
|
{
|
|
FOREACH_MOD(I_OnBotNoFantasyAccess, OnBotNoFantasyAccess(cmd, u, ci, params));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
BotInfo *findbot(const char *nick)
|
|
{
|
|
return findbot(ci::string(nick));
|
|
}
|
|
|
|
BotInfo *findbot(const std::string &nick)
|
|
{
|
|
return findbot(ci::string(nick.c_str()));
|
|
}
|
|
|
|
BotInfo *findbot(const ci::string &nick)
|
|
{
|
|
botinfo_map::const_iterator it = BotList.find(nick);
|
|
|
|
if (it != BotList.end())
|
|
return it->second;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Returns ban data associated with an user if it exists, allocates it
|
|
otherwise. */
|
|
|
|
static BanData *get_ban_data(Channel * c, User * u)
|
|
{
|
|
char mask[BUFSIZE];
|
|
BanData *bd, *next;
|
|
time_t now = time(NULL);
|
|
|
|
if (!c || !u)
|
|
return NULL;
|
|
|
|
snprintf(mask, sizeof(mask), "%s@%s", u->GetIdent().c_str(),
|
|
u->GetDisplayedHost().c_str());
|
|
|
|
for (bd = c->bd; bd; bd = next) {
|
|
if (now - bd->last_use > Config.BSKeepData) {
|
|
if (bd->next)
|
|
bd->next->prev = bd->prev;
|
|
if (bd->prev)
|
|
bd->prev->next = bd->next;
|
|
else
|
|
c->bd = bd->next;
|
|
if (bd->mask)
|
|
delete [] bd->mask;
|
|
next = bd->next;
|
|
delete bd;
|
|
continue;
|
|
}
|
|
if (!stricmp(bd->mask, mask)) {
|
|
bd->last_use = now;
|
|
return bd;
|
|
}
|
|
next = bd->next;
|
|
}
|
|
|
|
/* If we fall here it is that we haven't found the record */
|
|
bd = new BanData;
|
|
bd->mask = sstrdup(mask);
|
|
bd->last_use = now;
|
|
for (int x = 0; x < TTB_SIZE; ++x)
|
|
bd->ttb[x] = 0;
|
|
|
|
bd->prev = NULL;
|
|
bd->next = c->bd;
|
|
if (bd->next)
|
|
bd->next->prev = bd;
|
|
c->bd = bd;
|
|
|
|
return bd;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Returns BotServ data associated with an user on a given channel.
|
|
* Allocates it if necessary.
|
|
*/
|
|
|
|
static UserData *get_user_data(Channel *c, User *u)
|
|
{
|
|
if (!c || !u)
|
|
return NULL;
|
|
|
|
for (CUserList::iterator it = c->users.begin(); it != c->users.end(); ++it)
|
|
{
|
|
UserContainer *uc = *it;
|
|
|
|
if (uc->user == u)
|
|
{
|
|
time_t now = time(NULL);
|
|
|
|
/* Checks whether data is obsolete */
|
|
if (now - uc->ud.last_use > Config.BSKeepData)
|
|
{
|
|
if (uc->ud.lastline)
|
|
delete [] uc->ud.lastline;
|
|
/* We should not free and realloc, but reset to 0
|
|
instead. */
|
|
memset(&uc->ud, 0, sizeof(UserData));
|
|
uc->ud.last_use = now;
|
|
}
|
|
|
|
return &uc->ud;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Makes the bot join a channel and op himself. */
|
|
|
|
void bot_join(ChannelInfo * ci)
|
|
{
|
|
if (!ci || !ci->c || !ci->bi)
|
|
return;
|
|
|
|
if (Config.BSSmartJoin)
|
|
{
|
|
/* We check for bans */
|
|
if (ci->c->bans && ci->c->bans->count)
|
|
{
|
|
Entry *ban, *next;
|
|
|
|
for (ban = ci->c->bans->entries; ban; ban = next)
|
|
{
|
|
next = ban->next;
|
|
|
|
if (entry_match(ban, ci->bi->nick.c_str(), ci->bi->user.c_str(), ci->bi->host.c_str(), 0))
|
|
{
|
|
ci->c->RemoveMode(NULL, CMODE_BAN, ban->mask);
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string Limit;
|
|
int limit = 0;
|
|
if (ci->c->GetParam(CMODE_LIMIT, Limit))
|
|
{
|
|
limit = atoi(Limit.c_str());
|
|
}
|
|
|
|
/* Should we be invited? */
|
|
if (ci->c->HasMode(CMODE_INVITE)
|
|
|| (limit && ci->c->users.size() >= limit))
|
|
ircdproto->SendNoticeChanops(ci->bi, ci->c,
|
|
"%s invited %s into the channel.",
|
|
ci->bi->nick.c_str(), ci->bi->nick.c_str());
|
|
}
|
|
ircdproto->SendJoin(ci->bi, ci->c->name.c_str(), ci->c->creation_time);
|
|
for (std::list<ChannelModeStatus *>::iterator it = BotModes.begin(); it != BotModes.end(); ++it)
|
|
{
|
|
ci->c->SetMode(ci->bi, *it, ci->bi->nick, false);
|
|
}
|
|
FOREACH_MOD(I_OnBotJoin, OnBotJoin(ci, ci->bi));
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/** Check if a user should be banned by botserv
|
|
* @param ci The channel the user is on
|
|
* @param u The user
|
|
* @param ttbtype The type of bot kicker the user should be checked against
|
|
*/
|
|
static void check_ban(ChannelInfo *ci, User *u, int ttbtype)
|
|
{
|
|
BanData *bd = get_ban_data(ci->c, u);
|
|
|
|
if (!bd)
|
|
return;
|
|
|
|
/* Don't ban ulines */
|
|
if (u->server->IsULined())
|
|
return;
|
|
|
|
bd->ttb[ttbtype]++;
|
|
if (ci->ttb[ttbtype] && bd->ttb[ttbtype] >= ci->ttb[ttbtype])
|
|
{
|
|
/* Should not use == here because bd->ttb[ttbtype] could possibly be > ci->ttb[ttbtype]
|
|
* if the TTB was changed after it was not set (0) before and the user had already been
|
|
* kicked a few times. Bug #1056 - Adam */
|
|
char mask[BUFSIZE];
|
|
|
|
bd->ttb[ttbtype] = 0;
|
|
|
|
get_idealban(ci, u, mask, sizeof(mask));
|
|
|
|
if (ci->c)
|
|
ci->c->SetMode(NULL, CMODE_BAN, mask);
|
|
FOREACH_MOD(I_OnBotBan, OnBotBan(u, ci, mask));
|
|
}
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* This makes a bot kick an user. Works somewhat like notice_lang in fact ;) */
|
|
|
|
static void bot_kick(ChannelInfo * ci, User * u, int message, ...)
|
|
{
|
|
va_list args;
|
|
char buf[1024];
|
|
const char *fmt;
|
|
|
|
if (!ci || !ci->bi || !ci->c || !u)
|
|
return;
|
|
|
|
va_start(args, message);
|
|
fmt = getstring(u, message);
|
|
if (!fmt)
|
|
return;
|
|
vsnprintf(buf, sizeof(buf), fmt, args);
|
|
va_end(args);
|
|
|
|
ci->c->Kick(ci->bi, u, "%s", buf);
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Makes a simple ban and kicks the target */
|
|
|
|
void bot_raw_ban(User * requester, ChannelInfo * ci, char *nick, const char *reason)
|
|
{
|
|
char mask[BUFSIZE];
|
|
User *u = finduser(nick);
|
|
|
|
if (!u)
|
|
return;
|
|
|
|
if ((ModeManager::FindUserModeByName(UMODE_PROTECTED)))
|
|
{
|
|
if (u->IsProtected() && (requester != u)) {
|
|
ircdproto->SendPrivmsg(ci->bi, ci->name.c_str(), "%s", getstring(ACCESS_DENIED));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ci->HasFlag(CI_PEACE) && stricmp(requester->nick.c_str(), nick) && (get_access(u, ci) >= get_access(requester, ci)))
|
|
return;
|
|
|
|
if (ModeManager::FindChannelModeByName(CMODE_EXCEPT))
|
|
{
|
|
if (is_excepted(ci, u) == 1) {
|
|
ircdproto->SendPrivmsg(ci->bi, ci->name.c_str(), "%s", getstring(BOT_EXCEPT));
|
|
return;
|
|
}
|
|
}
|
|
|
|
get_idealban(ci, u, mask, sizeof(mask));
|
|
|
|
ci->c->SetMode(NULL, CMODE_BAN, mask);
|
|
|
|
/* Check if we need to do a signkick or not -GD */
|
|
if (ci->HasFlag(CI_SIGNKICK) || (ci->HasFlag(CI_SIGNKICK_LEVEL) && !check_access(requester, ci, CA_SIGNKICK)))
|
|
ci->c->Kick(ci->bi, u, "%s (%s)", reason ? reason : ci->bi->nick.c_str(), requester->nick.c_str());
|
|
else
|
|
ci->c->Kick(ci->bi, u, "%s", reason ? reason : ci->bi->nick.c_str());
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Makes a kick with a "dynamic" reason ;) */
|
|
|
|
void bot_raw_kick(User * requester, ChannelInfo * ci, char *nick, const char *reason)
|
|
{
|
|
User *u = finduser(nick);
|
|
|
|
if (!u || !ci->c->FindUser(u))
|
|
return;
|
|
|
|
if ((ModeManager::FindUserModeByName(UMODE_PROTECTED)))
|
|
{
|
|
if (u->IsProtected() && (requester != u)) {
|
|
ircdproto->SendPrivmsg(ci->bi, ci->name.c_str(), "%s", getstring(ACCESS_DENIED));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (ci->HasFlag(CI_PEACE) && stricmp(requester->nick.c_str(), nick) && (get_access(u, ci) >= get_access(requester, ci)))
|
|
return;
|
|
|
|
if (ci->HasFlag(CI_SIGNKICK) || (ci->HasFlag(CI_SIGNKICK_LEVEL) && !check_access(requester, ci, CA_SIGNKICK)))
|
|
ci->c->Kick(ci->bi, u, "%s (%s)", reason ? reason : ci->bi->nick.c_str(), requester->nick.c_str());
|
|
else
|
|
ci->c->Kick(ci->bi, u, "%s", reason ? reason : ci->bi->nick.c_str());
|
|
}
|
|
|
|
/*************************************************************************/
|
|
|
|
/* Makes a mode operation on a channel for a nick */
|
|
|
|
void bot_raw_mode(User * requester, ChannelInfo * ci, const char *mode, char *nick)
|
|
{
|
|
char buf[BUFSIZE];
|
|
User *u;
|
|
|
|
*buf = '\0';
|
|
u = finduser(nick);
|
|
|
|
if (!u || !ci->c->FindUser(u))
|
|
return;
|
|
|
|
snprintf(buf, BUFSIZE - 1, "%ld", static_cast<long>(time(NULL)));
|
|
|
|
if ((ModeManager::FindUserModeByName(UMODE_PROTECTED))) {
|
|
if (u->IsProtected() && *mode == '-' && (requester != u)) {
|
|
ircdproto->SendPrivmsg(ci->bi, ci->name.c_str(), "%s", getstring(ACCESS_DENIED));
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (*mode == '-' && ci->HasFlag(CI_PEACE)
|
|
&& stricmp(requester->nick.c_str(), nick) && (get_access(u, ci) >= get_access(requester, ci)))
|
|
return;
|
|
|
|
ci->c->SetModes(NULL, "%s %s", mode, nick);
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/**
|
|
* Normalize buffer stripping control characters and colors
|
|
* @param A string to be parsed for control and color codes
|
|
* @return A string stripped of control and color codes
|
|
*/
|
|
char *normalizeBuffer(const char *buf)
|
|
{
|
|
char *newbuf;
|
|
int i, len, j = 0;
|
|
|
|
len = strlen(buf);
|
|
newbuf = new char[len + 1];
|
|
|
|
for (i = 0; i < len; i++) {
|
|
switch (buf[i]) {
|
|
/* ctrl char */
|
|
case 1:
|
|
break;
|
|
/* Bold ctrl char */
|
|
case 2:
|
|
break;
|
|
/* Color ctrl char */
|
|
case 3:
|
|
/* If the next character is a digit, its also removed */
|
|
if (isdigit(buf[i + 1])) {
|
|
i++;
|
|
|
|
/* not the best way to remove colors
|
|
* which are two digit but no worse then
|
|
* how the Unreal does with +S - TSL
|
|
*/
|
|
if (isdigit(buf[i + 1])) {
|
|
i++;
|
|
}
|
|
|
|
/* Check for background color code
|
|
* and remove it as well
|
|
*/
|
|
if (buf[i + 1] == ',') {
|
|
i++;
|
|
|
|
if (isdigit(buf[i + 1])) {
|
|
i++;
|
|
}
|
|
/* not the best way to remove colors
|
|
* which are two digit but no worse then
|
|
* how the Unreal does with +S - TSL
|
|
*/
|
|
if (isdigit(buf[i + 1])) {
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
/* line feed char */
|
|
case 10:
|
|
break;
|
|
/* carriage returns char */
|
|
case 13:
|
|
break;
|
|
/* Reverse ctrl char */
|
|
case 22:
|
|
break;
|
|
/* Underline ctrl char */
|
|
case 31:
|
|
break;
|
|
/* A valid char gets copied into the new buffer */
|
|
default:
|
|
newbuf[j] = buf[i];
|
|
j++;
|
|
}
|
|
}
|
|
|
|
/* Terminate the string */
|
|
newbuf[j] = 0;
|
|
|
|
return (newbuf);
|
|
}
|