mirror of
https://github.com/anope/anope.git
synced 2026-06-28 19:36:39 +02:00
Moved the core pseudo clients out into their own modules
This commit is contained in:
-554
@@ -14,351 +14,6 @@
|
||||
#include "services.h"
|
||||
#include "modules.h"
|
||||
|
||||
static UserData *get_user_data(Channel *c, User *u);
|
||||
|
||||
E void moduleAddBotServCmds();
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
void moduleAddBotServCmds()
|
||||
{
|
||||
ModuleManager::LoadModuleList(Config->BotServCoreModules);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
/*************************************************************************/
|
||||
|
||||
/* Return information on memory use. Assumes pointers are valid. */
|
||||
|
||||
void get_botserv_stats(long *nrec, long *memuse)
|
||||
{
|
||||
long count = 0, mem = 0;
|
||||
|
||||
for (Anope::insensitive_map<BotInfo *>::const_iterator it = BotListByNick.begin(), it_end = BotListByNick.end(); it != it_end; ++it)
|
||||
{
|
||||
BotInfo *bi = it->second;
|
||||
|
||||
++count;
|
||||
mem += sizeof(*bi);
|
||||
mem += bi->nick.length() + 1;
|
||||
mem += bi->GetIdent().length() + 1;
|
||||
mem += bi->host.length() + 1;
|
||||
mem += bi->realname.length() + 1;
|
||||
}
|
||||
|
||||
*nrec = count;
|
||||
*memuse = mem;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
/*************************************************************************/
|
||||
|
||||
/* BotServ initialization. */
|
||||
|
||||
void bs_init()
|
||||
{
|
||||
if (!Config->s_BotServ.empty())
|
||||
moduleAddBotServCmds();
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/* Handles all messages that are sent to registered channels where a
|
||||
* bot is on.
|
||||
*/
|
||||
|
||||
void botchanmsgs(User *u, ChannelInfo *ci, const Anope::string &buf)
|
||||
{
|
||||
if (!u || !ci || !ci->c || buf.empty())
|
||||
return;
|
||||
|
||||
/* Answer to ping if needed */
|
||||
if (buf.substr(0, 6).equals_ci("\1PING ") && buf[buf.length() - 1] == '\1')
|
||||
{
|
||||
Anope::string ctcp = buf;
|
||||
ctcp.erase(ctcp.begin());
|
||||
ctcp.erase(ctcp.length() - 1);
|
||||
ircdproto->SendCTCP(ci->bi, u->nick, "%s", ctcp.c_str());
|
||||
}
|
||||
|
||||
bool was_action = false;
|
||||
Anope::string realbuf = buf;
|
||||
|
||||
/* If it's a /me, cut the CTCP part because the ACTION will cause
|
||||
* problems with the caps or badwords kicker
|
||||
*/
|
||||
if (realbuf.substr(0, 8).equals_ci("\1ACTION ") && realbuf[realbuf.length() - 1] == '\1')
|
||||
{
|
||||
realbuf.erase(0, 8);
|
||||
realbuf.erase(realbuf.length() - 1);
|
||||
was_action = true;
|
||||
}
|
||||
|
||||
if (realbuf.empty())
|
||||
return;
|
||||
|
||||
/* 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 a user is kicked before
|
||||
* the last kicker check.
|
||||
*
|
||||
* But FIRST we check whether the user is protected in any
|
||||
* way.
|
||||
*/
|
||||
|
||||
bool Allow = true;
|
||||
if (check_access(u, ci, CA_NOKICK))
|
||||
Allow = false;
|
||||
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 = false;
|
||||
else if (ci->botflags.HasFlag(BS_DONTKICKVOICES) && ci->c->HasUserStatus(u, CMODE_VOICE))
|
||||
Allow = false;
|
||||
|
||||
EventReturn MOD_RESULT;
|
||||
FOREACH_RESULT(I_OnPrivmsg, OnPrivmsg(u, ci, realbuf, Allow));
|
||||
if (MOD_RESULT == EVENT_STOP)
|
||||
return;
|
||||
|
||||
if (Allow)
|
||||
{
|
||||
/* Bolds kicker */
|
||||
if (ci->botflags.HasFlag(BS_KICK_BOLDS) && realbuf.find(2) != Anope::string::npos)
|
||||
{
|
||||
check_ban(ci, u, TTB_BOLDS);
|
||||
bot_kick(ci, u, _("Don't use bolds on this channel!"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Color kicker */
|
||||
if (ci->botflags.HasFlag(BS_KICK_COLORS) && realbuf.find(3) != Anope::string::npos)
|
||||
{
|
||||
check_ban(ci, u, TTB_COLORS);
|
||||
bot_kick(ci, u, _("Don't use colors on this channel!"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Reverses kicker */
|
||||
if (ci->botflags.HasFlag(BS_KICK_REVERSES) && realbuf.find(22) != Anope::string::npos)
|
||||
{
|
||||
check_ban(ci, u, TTB_REVERSES);
|
||||
bot_kick(ci, u, _("Don't use reverses on this channel!"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Italics kicker */
|
||||
if (ci->botflags.HasFlag(BS_KICK_ITALICS) && realbuf.find(29) != Anope::string::npos)
|
||||
{
|
||||
check_ban(ci, u, TTB_ITALICS);
|
||||
bot_kick(ci, u, _("Don't use italics on this channel!"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Underlines kicker */
|
||||
if (ci->botflags.HasFlag(BS_KICK_UNDERLINES) && realbuf.find(31) != Anope::string::npos)
|
||||
{
|
||||
check_ban(ci, u, TTB_UNDERLINES);
|
||||
bot_kick(ci, u, _("Don't use underlines on this channel!"));
|
||||
return;
|
||||
}
|
||||
|
||||
/* Caps kicker */
|
||||
if (ci->botflags.HasFlag(BS_KICK_CAPS) && realbuf.length() >= ci->capsmin)
|
||||
{
|
||||
int i = 0, l = 0;
|
||||
|
||||
for (unsigned j = 0, end = realbuf.length(); j < end; ++j)
|
||||
{
|
||||
if (isupper(realbuf[j]))
|
||||
++i;
|
||||
else if (islower(realbuf[j]))
|
||||
++l;
|
||||
}
|
||||
|
||||
/* 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, _("Turn caps lock OFF!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Bad words kicker */
|
||||
if (ci->botflags.HasFlag(BS_KICK_BADWORDS))
|
||||
{
|
||||
bool mustkick = false;
|
||||
|
||||
/* Normalize the buffer */
|
||||
Anope::string nbuf = normalizeBuffer(realbuf);
|
||||
|
||||
for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i)
|
||||
{
|
||||
BadWord *bw = ci->GetBadWord(i);
|
||||
|
||||
if (bw->type == BW_ANY && ((Config->BSCaseSensitive && nbuf.find(bw->word) != Anope::string::npos) || (!Config->BSCaseSensitive && nbuf.find_ci(bw->word) != Anope::string::npos)))
|
||||
mustkick = true;
|
||||
else if (bw->type == BW_SINGLE)
|
||||
{
|
||||
size_t len = bw->word.length();
|
||||
|
||||
if ((Config->BSCaseSensitive && bw->word.equals_cs(nbuf)) || (!Config->BSCaseSensitive && bw->word.equals_ci(nbuf)))
|
||||
mustkick = true;
|
||||
else if (nbuf.find(' ') == len && ((Config->BSCaseSensitive && bw->word.equals_cs(nbuf)) || (!Config->BSCaseSensitive && bw->word.equals_ci(nbuf))))
|
||||
mustkick = true;
|
||||
else
|
||||
{
|
||||
if (nbuf.rfind(' ') == nbuf.length() - len - 1 && ((Config->BSCaseSensitive && nbuf.find(bw->word) == nbuf.length() - len) || (!Config->BSCaseSensitive && nbuf.find_ci(bw->word) == nbuf.length() - len)))
|
||||
mustkick = true;
|
||||
else
|
||||
{
|
||||
Anope::string wordbuf = " " + bw->word + " ";
|
||||
|
||||
if ((Config->BSCaseSensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!Config->BSCaseSensitive && nbuf.find_ci(wordbuf) != Anope::string::npos))
|
||||
mustkick = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (bw->type == BW_START)
|
||||
{
|
||||
size_t len = bw->word.length();
|
||||
|
||||
if ((Config->BSCaseSensitive && nbuf.substr(0, len).equals_cs(bw->word)) || (!Config->BSCaseSensitive && nbuf.substr(0, len).equals_ci(bw->word)))
|
||||
mustkick = true;
|
||||
else
|
||||
{
|
||||
Anope::string wordbuf = " " + bw->word;
|
||||
|
||||
if ((Config->BSCaseSensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!Config->BSCaseSensitive && nbuf.find_ci(wordbuf) != Anope::string::npos))
|
||||
mustkick = true;
|
||||
}
|
||||
}
|
||||
else if (bw->type == BW_END)
|
||||
{
|
||||
size_t len = bw->word.length();
|
||||
|
||||
if ((Config->BSCaseSensitive && nbuf.substr(nbuf.length() - len).equals_cs(bw->word)) || (!Config->BSCaseSensitive && nbuf.substr(nbuf.length() - len).equals_ci(bw->word)))
|
||||
mustkick = true;
|
||||
else
|
||||
{
|
||||
Anope::string wordbuf = bw->word + " ";
|
||||
|
||||
if ((Config->BSCaseSensitive && nbuf.find(wordbuf) != Anope::string::npos) || (!Config->BSCaseSensitive && nbuf.find_ci(wordbuf) != Anope::string::npos))
|
||||
mustkick = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (mustkick)
|
||||
{
|
||||
check_ban(ci, u, TTB_BADWORDS);
|
||||
if (Config->BSGentleBWReason)
|
||||
bot_kick(ci, u, _("Watch your language!"));
|
||||
else
|
||||
bot_kick(ci, u, _("Don't use the word \"%s\" on this channel!"), bw->word.c_str());
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Flood kicker */
|
||||
if (ci->botflags.HasFlag(BS_KICK_FLOOD))
|
||||
{
|
||||
UserData *ud = get_user_data(ci->c, u);
|
||||
if (!ud)
|
||||
return;
|
||||
|
||||
if (Anope::CurTime - ud->last_start > ci->floodsecs)
|
||||
{
|
||||
ud->last_start = Anope::CurTime;
|
||||
ud->lines = 0;
|
||||
}
|
||||
|
||||
++ud->lines;
|
||||
if (ud->lines >= ci->floodlines)
|
||||
{
|
||||
check_ban(ci, u, TTB_FLOOD);
|
||||
bot_kick(ci, u, _("Stop flooding!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Repeat kicker */
|
||||
if (ci->botflags.HasFlag(BS_KICK_REPEAT))
|
||||
{
|
||||
UserData *ud = get_user_data(ci->c, u);
|
||||
if (!ud)
|
||||
return;
|
||||
|
||||
if (!ud->lastline.empty() && !ud->lastline.equals_ci(buf))
|
||||
{
|
||||
ud->lastline = buf;
|
||||
ud->times = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ud->lastline.empty())
|
||||
ud->lastline = buf;
|
||||
++ud->times;
|
||||
}
|
||||
|
||||
if (ud->times >= ci->repeattimes)
|
||||
{
|
||||
check_ban(ci, u, TTB_REPEAT);
|
||||
bot_kick(ci, u, _("Stop repeating yourself!"));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Fantaisist commands */
|
||||
if (ci->botflags.HasFlag(BS_FANTASY) && buf[0] == Config->BSFantasyCharacter[0] && !was_action)
|
||||
{
|
||||
Anope::string message = buf;
|
||||
/* Strip off the fantasy character */
|
||||
message.erase(message.begin());
|
||||
|
||||
size_t space = message.find(' ');
|
||||
Anope::string command, rest;
|
||||
if (space == Anope::string::npos)
|
||||
command = message;
|
||||
else
|
||||
{
|
||||
command = message.substr(0, space);
|
||||
rest = message.substr(space + 1);
|
||||
}
|
||||
|
||||
if (check_access(u, ci, CA_FANTASIA))
|
||||
{
|
||||
|
||||
Command *cmd = FindCommand(ChanServ, command);
|
||||
|
||||
/* Command exists and can be called by fantasy */
|
||||
if (cmd && !cmd->HasFlag(CFLAG_DISABLE_FANTASY))
|
||||
{
|
||||
Anope::string params = rest;
|
||||
/* Some commands don't need the channel name added.. eg !help */
|
||||
if (!cmd->HasFlag(CFLAG_STRIP_CHANNEL))
|
||||
params = ci->name + " " + params;
|
||||
params = command + " " + params;
|
||||
|
||||
mod_run_cmd(ChanServ, u, ci, params);
|
||||
}
|
||||
|
||||
FOREACH_MOD(I_OnBotFantasy, OnBotFantasy(command, u, ci, rest));
|
||||
}
|
||||
else
|
||||
{
|
||||
FOREACH_MOD(I_OnBotNoFantasyAccess, OnBotNoFantasyAccess(command, u, ci, rest));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
BotInfo *findbot(const Anope::string &nick)
|
||||
{
|
||||
BotInfo *bi = NULL;
|
||||
@@ -380,138 +35,6 @@ BotInfo *findbot(const Anope::string &nick)
|
||||
return bi;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/* Returns ban data associated with a user if it exists, allocates it
|
||||
otherwise. */
|
||||
|
||||
static BanData *get_ban_data(Channel *c, User *u)
|
||||
{
|
||||
if (!c || !u)
|
||||
return NULL;
|
||||
|
||||
Anope::string mask = u->GetIdent() + "@" + u->GetDisplayedHost();
|
||||
/* XXX This should really be on some sort of timer/garbage collector, and use std::map */
|
||||
for (std::list<BanData *>::iterator it = c->bd.begin(), it_end = c->bd.end(), it_next; it != it_end; it = it_next)
|
||||
{
|
||||
it_next = it;
|
||||
++it_next;
|
||||
|
||||
if (Anope::CurTime - (*it)->last_use > Config->BSKeepData)
|
||||
{
|
||||
delete *it;
|
||||
c->bd.erase(it);
|
||||
continue;
|
||||
}
|
||||
if ((*it)->mask.equals_ci(mask))
|
||||
{
|
||||
(*it)->last_use = Anope::CurTime;
|
||||
return *it;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we fall here it is that we haven't found the record */
|
||||
BanData *bd = new BanData();
|
||||
bd->mask = mask;
|
||||
bd->last_use = Anope::CurTime;
|
||||
for (int x = 0; x < TTB_SIZE; ++x)
|
||||
bd->ttb[x] = 0;
|
||||
|
||||
c->bd.push_front(bd);
|
||||
|
||||
return bd;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/* Returns BotServ data associated with a 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_end = c->users.end(); it != it_end; ++it)
|
||||
{
|
||||
UserContainer *uc = *it;
|
||||
|
||||
if (uc->user == u)
|
||||
{
|
||||
/* Checks whether data is obsolete */
|
||||
if (Anope::CurTime - uc->ud.last_use > Config->BSKeepData)
|
||||
{
|
||||
/* We should not free and realloc, but reset to 0
|
||||
instead. */
|
||||
uc->ud.Clear();
|
||||
uc->ud.last_use = Anope::CurTime;
|
||||
}
|
||||
|
||||
return &uc->ud;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/** 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
|
||||
*/
|
||||
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 */
|
||||
Anope::string mask;
|
||||
|
||||
bd->ttb[ttbtype] = 0;
|
||||
|
||||
get_idealban(ci, u, mask);
|
||||
|
||||
if (ci->c)
|
||||
ci->c->SetMode(NULL, CMODE_BAN, mask);
|
||||
FOREACH_MOD(I_OnBotBan, OnBotBan(u, ci, mask));
|
||||
}
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/* This makes a bot kick a user. Works somewhat like notice_lang in fact ;) */
|
||||
|
||||
void bot_kick(ChannelInfo *ci, User *u, const char *message, ...)
|
||||
{
|
||||
va_list args;
|
||||
char buf[1024];
|
||||
|
||||
if (!ci || !ci->bi || !ci->c || !u)
|
||||
return;
|
||||
|
||||
Anope::string fmt = GetString(u->Account(), message);
|
||||
va_start(args, message);
|
||||
if (fmt.empty())
|
||||
return;
|
||||
vsnprintf(buf, sizeof(buf), fmt.c_str(), args);
|
||||
va_end(args);
|
||||
|
||||
ci->c->Kick(ci->bi, u, "%s", buf);
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
@@ -585,80 +108,3 @@ void bot_raw_kick(User *requester, ChannelInfo *ci, User *u, const Anope::string
|
||||
ci->c->Kick(ci->bi, u, "%s", !reason.empty() ? reason.c_str() : ci->bi->nick.c_str());
|
||||
}
|
||||
|
||||
/*************************************************************************/
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
Anope::string normalizeBuffer(const Anope::string &buf)
|
||||
{
|
||||
Anope::string newbuf;
|
||||
|
||||
for (unsigned i = 0, end = buf.length(); i < end; ++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;
|
||||
/* Italic ctrl char */
|
||||
case 29:
|
||||
break;
|
||||
/* A valid char gets copied into the new buffer */
|
||||
default:
|
||||
newbuf += buf[i];
|
||||
}
|
||||
}
|
||||
|
||||
return newbuf;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user