mirror of
https://github.com/anope/anope.git
synced 2026-06-25 19:36:38 +02:00
cc23f6dc01
If a command is named e.g. nickserv/wibble it previously tried to look up the nickserv service and if this service didn't exist then it would fall back to sending from the first available service. This caused problems if the NickServ service had been renamed to something else. Reported by @AndrioCelos on IRC.
419 lines
9.7 KiB
C++
419 lines
9.7 KiB
C++
/* Logging routines.
|
|
*
|
|
* (C) 2003-2023 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 "commands.h"
|
|
#include "channels.h"
|
|
#include "users.h"
|
|
#include "logger.h"
|
|
#include "config.h"
|
|
#include "bots.h"
|
|
#include "servers.h"
|
|
#include "uplink.h"
|
|
#include "protocol.h"
|
|
|
|
#ifndef _WIN32
|
|
#include <sys/time.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
static Anope::string GetTimeStamp()
|
|
{
|
|
char tbuf[256];
|
|
time_t t;
|
|
|
|
if (time(&t) < 0)
|
|
t = Anope::CurTime;
|
|
|
|
tm tm = *localtime(&t);
|
|
if (Anope::Debug)
|
|
{
|
|
char *s;
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
strftime(tbuf, sizeof(tbuf) - 1, "[%b %d %H:%M:%S", &tm);
|
|
s = tbuf + strlen(tbuf);
|
|
s += snprintf(s, sizeof(tbuf) - (s - tbuf), ".%06d", static_cast<int>(tv.tv_usec));
|
|
strftime(s, sizeof(tbuf) - (s - tbuf) - 1, " %Y]", &tm);
|
|
}
|
|
else
|
|
strftime(tbuf, sizeof(tbuf) - 1, "[%b %d %H:%M:%S %Y]", &tm);
|
|
|
|
return tbuf;
|
|
}
|
|
|
|
static inline Anope::string CreateLogName(const Anope::string &file, time_t t = Anope::CurTime)
|
|
{
|
|
char timestamp[32];
|
|
tm *tm = localtime(&t);
|
|
strftime(timestamp, sizeof(timestamp), "%Y%m%d", tm);
|
|
|
|
return Anope::LogDir + "/" + file + "." + timestamp;
|
|
}
|
|
|
|
LogFile::LogFile(const Anope::string &name) : filename(name), stream(name.c_str(), std::ios_base::out | std::ios_base::app)
|
|
{
|
|
}
|
|
|
|
LogFile::~LogFile()
|
|
{
|
|
this->stream.close();
|
|
}
|
|
|
|
const Anope::string &LogFile::GetName() const
|
|
{
|
|
return this->filename;
|
|
}
|
|
|
|
Log::Log(LogType t, const Anope::string &cat, BotInfo *b) : bi(b), u(NULL), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(NULL), m(NULL), type(t), category(cat)
|
|
{
|
|
}
|
|
|
|
Log::Log(LogType t, CommandSource &src, Command *_c, ChannelInfo *_ci) : u(src.GetUser()), nc(src.nc), c(_c), source(&src), chan(NULL), ci(_ci), s(NULL), m(NULL), type(t)
|
|
{
|
|
if (!c)
|
|
throw CoreException("Invalid pointers passed to Log::Log");
|
|
|
|
if (type != LOG_COMMAND && type != LOG_OVERRIDE && type != LOG_ADMIN)
|
|
throw CoreException("This constructor does not support this log type");
|
|
|
|
size_t sl = c->name.find('/');
|
|
this->bi = NULL;
|
|
if (sl != Anope::string::npos)
|
|
this->bi = Config->GetClient(c->name.substr(0, sl));
|
|
this->category = c->name;
|
|
}
|
|
|
|
Log::Log(User *_u, Channel *ch, const Anope::string &cat) : bi(NULL), u(_u), nc(NULL), c(NULL), source(NULL), chan(ch), ci(chan ? *chan->ci : NULL), s(NULL), m(NULL), type(LOG_CHANNEL), category(cat)
|
|
{
|
|
if (!chan)
|
|
throw CoreException("Invalid pointers passed to Log::Log");
|
|
}
|
|
|
|
Log::Log(User *_u, const Anope::string &cat, BotInfo *_bi) : bi(_bi), u(_u), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(NULL), m(NULL), type(LOG_USER), category(cat)
|
|
{
|
|
if (!u)
|
|
throw CoreException("Invalid pointers passed to Log::Log");
|
|
}
|
|
|
|
Log::Log(Server *serv, const Anope::string &cat, BotInfo *_bi) : bi(_bi), u(NULL), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(serv), m(NULL), type(LOG_SERVER), category(cat)
|
|
{
|
|
if (!s)
|
|
throw CoreException("Invalid pointer passed to Log::Log");
|
|
}
|
|
|
|
Log::Log(BotInfo *b, const Anope::string &cat) : bi(b), u(NULL), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(NULL), m(NULL), type(LOG_NORMAL), category(cat)
|
|
{
|
|
}
|
|
|
|
Log::Log(Module *mod, const Anope::string &cat, BotInfo *_bi) : bi(_bi), u(NULL), nc(NULL), c(NULL), source(NULL), chan(NULL), ci(NULL), s(NULL), m(mod), type(LOG_MODULE), category(cat)
|
|
{
|
|
}
|
|
|
|
Log::~Log()
|
|
{
|
|
if (Anope::NoFork && Anope::Debug && this->type >= LOG_NORMAL && this->type <= LOG_DEBUG + Anope::Debug - 1)
|
|
std::cout << GetTimeStamp() << " Debug: " << this->BuildPrefix() << this->buf.str() << std::endl;
|
|
else if (Anope::NoFork && this->type <= LOG_TERMINAL)
|
|
std::cout << GetTimeStamp() << " " << this->BuildPrefix() << this->buf.str() << std::endl;
|
|
else if (this->type == LOG_TERMINAL)
|
|
std::cout << this->BuildPrefix() << this->buf.str() << std::endl;
|
|
|
|
FOREACH_MOD(OnLog, (this));
|
|
|
|
if (Config)
|
|
for (unsigned i = 0; i < Config->LogInfos.size(); ++i)
|
|
if (Config->LogInfos[i].HasType(this->type, this->category))
|
|
Config->LogInfos[i].ProcessMessage(this);
|
|
}
|
|
|
|
Anope::string Log::FormatSource() const
|
|
{
|
|
if (u)
|
|
if (nc)
|
|
return this->u->GetMask() + " (" + this->nc->display + ")";
|
|
else
|
|
return this->u->GetMask();
|
|
else if (nc)
|
|
return nc->display;
|
|
else if (source)
|
|
{
|
|
Anope::string nickbuf = source->GetNick();
|
|
if (!nickbuf.empty() && !source->ip.empty())
|
|
nickbuf += " (" + source->ip + ")";
|
|
return nickbuf;
|
|
}
|
|
return "";
|
|
}
|
|
|
|
Anope::string Log::FormatCommand() const
|
|
{
|
|
Anope::string buffer = FormatSource() + " used " + (source != NULL && !source->command.empty() ? source->command : this->c->name) + " ";
|
|
if (this->ci)
|
|
buffer += "on " + this->ci->name + " ";
|
|
|
|
return buffer;
|
|
}
|
|
|
|
Anope::string Log::BuildPrefix() const
|
|
{
|
|
Anope::string buffer;
|
|
|
|
switch (this->type)
|
|
{
|
|
case LOG_ADMIN:
|
|
{
|
|
if (!this->c)
|
|
break;
|
|
buffer += "ADMIN: " + FormatCommand();
|
|
break;
|
|
}
|
|
case LOG_OVERRIDE:
|
|
{
|
|
if (!this->c)
|
|
break;
|
|
buffer += "OVERRIDE: " + FormatCommand();
|
|
break;
|
|
}
|
|
case LOG_COMMAND:
|
|
{
|
|
if (!this->c)
|
|
break;
|
|
buffer += "COMMAND: " + FormatCommand();
|
|
break;
|
|
}
|
|
case LOG_CHANNEL:
|
|
{
|
|
if (!this->chan)
|
|
break;
|
|
buffer += "CHANNEL: ";
|
|
Anope::string src = FormatSource();
|
|
if (!src.empty())
|
|
buffer += src + " ";
|
|
buffer += this->category + " " + this->chan->name + " ";
|
|
break;
|
|
}
|
|
case LOG_USER:
|
|
{
|
|
if (this->u)
|
|
buffer += "USERS: " + FormatSource() + " ";
|
|
break;
|
|
}
|
|
case LOG_SERVER:
|
|
{
|
|
if (this->s)
|
|
buffer += "SERVER: " + this->s->GetName() + " (" + this->s->GetDescription() + ") ";
|
|
break;
|
|
}
|
|
case LOG_MODULE:
|
|
{
|
|
if (this->m)
|
|
buffer += this->m->name.upper() + ": ";
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
LogInfo::LogInfo(int la, bool rio, bool ldebug) : bot(NULL), last_day(0), log_age(la), raw_io(rio), debug(ldebug)
|
|
{
|
|
}
|
|
|
|
LogInfo::~LogInfo()
|
|
{
|
|
for (unsigned i = 0; i < this->logfiles.size(); ++i)
|
|
delete this->logfiles[i];
|
|
this->logfiles.clear();
|
|
}
|
|
|
|
bool LogInfo::HasType(LogType ltype, const Anope::string &type) const
|
|
{
|
|
const std::vector<Anope::string> *list = NULL;
|
|
switch (ltype)
|
|
{
|
|
case LOG_ADMIN:
|
|
list = &this->admin;
|
|
break;
|
|
case LOG_OVERRIDE:
|
|
list = &this->override;
|
|
break;
|
|
case LOG_COMMAND:
|
|
list = &this->commands;
|
|
break;
|
|
case LOG_SERVER:
|
|
list = &this->servers;
|
|
break;
|
|
case LOG_CHANNEL:
|
|
list = &this->channels;
|
|
break;
|
|
case LOG_USER:
|
|
list = &this->users;
|
|
break;
|
|
case LOG_TERMINAL:
|
|
return true;
|
|
case LOG_RAWIO:
|
|
return (Anope::Debug || this->debug) ? true : this->raw_io;
|
|
case LOG_DEBUG:
|
|
return Anope::Debug ? true : this->debug;
|
|
case LOG_DEBUG_2:
|
|
case LOG_DEBUG_3:
|
|
case LOG_DEBUG_4:
|
|
break;
|
|
case LOG_MODULE:
|
|
case LOG_NORMAL:
|
|
default:
|
|
list = &this->normal;
|
|
break;
|
|
}
|
|
|
|
if (list == NULL)
|
|
return false;
|
|
|
|
for (unsigned i = 0; i < list->size(); ++i)
|
|
{
|
|
Anope::string cat = list->at(i);
|
|
bool inverse = false;
|
|
if (cat[0] == '~')
|
|
{
|
|
cat.erase(cat.begin());
|
|
inverse = true;
|
|
}
|
|
if (Anope::Match(type, cat))
|
|
{
|
|
return !inverse;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void LogInfo::OpenLogFiles()
|
|
{
|
|
for (unsigned i = 0; i < this->logfiles.size(); ++i)
|
|
delete this->logfiles[i];
|
|
this->logfiles.clear();
|
|
|
|
for (unsigned i = 0; i < this->targets.size(); ++i)
|
|
{
|
|
const Anope::string &target = this->targets[i];
|
|
|
|
if (target.empty() || target[0] == '#' || target == "globops" || target.find(":") != Anope::string::npos)
|
|
continue;
|
|
|
|
LogFile *lf = new LogFile(CreateLogName(target));
|
|
if (!lf->stream.is_open())
|
|
{
|
|
Log() << "Unable to open logfile " << lf->GetName();
|
|
delete lf;
|
|
}
|
|
else
|
|
this->logfiles.push_back(lf);
|
|
}
|
|
}
|
|
|
|
void LogInfo::ProcessMessage(const Log *l)
|
|
{
|
|
if (!this->sources.empty())
|
|
{
|
|
bool log = false;
|
|
for (unsigned i = 0; i < this->sources.size() && !log; ++i)
|
|
{
|
|
const Anope::string &src = this->sources[i];
|
|
|
|
if (l->bi && src == l->bi->nick)
|
|
log = true;
|
|
else if (l->u && src == l->u->nick)
|
|
log = true;
|
|
else if (l->nc && src == l->nc->display)
|
|
log = true;
|
|
else if (l->ci && src == l->ci->name)
|
|
log = true;
|
|
else if (l->m && src == l->m->name)
|
|
log = true;
|
|
else if (l->s && src == l->s->GetName())
|
|
log = true;
|
|
}
|
|
if (!log)
|
|
return;
|
|
}
|
|
|
|
const Anope::string &buffer = l->BuildPrefix() + l->buf.str();
|
|
|
|
FOREACH_MOD(OnLogMessage, (this, l, buffer));
|
|
|
|
for (unsigned i = 0; i < this->targets.size(); ++i)
|
|
{
|
|
const Anope::string &target = this->targets[i];
|
|
|
|
if (!target.empty() && target[0] == '#')
|
|
{
|
|
if (UplinkSock && l->type <= LOG_NORMAL && Me && Me->IsSynced())
|
|
{
|
|
Channel *c = Channel::Find(target);
|
|
if (!c)
|
|
continue;
|
|
|
|
BotInfo *bi = l->bi;
|
|
if (!bi)
|
|
bi = this->bot;
|
|
if (!bi)
|
|
bi = c->WhoSends();
|
|
if (bi)
|
|
IRCD->SendPrivmsg(bi, c->name, "%s", buffer.c_str());
|
|
}
|
|
}
|
|
else if (target == "globops")
|
|
{
|
|
if (UplinkSock && l->type <= LOG_NORMAL && Me && Me->IsSynced())
|
|
{
|
|
BotInfo *bi = l->bi;
|
|
if (!bi)
|
|
bi = this->bot;
|
|
if (bi)
|
|
IRCD->SendGlobops(bi, "%s", buffer.c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
tm *tm = localtime(&Anope::CurTime);
|
|
if (tm->tm_mday != this->last_day)
|
|
{
|
|
this->last_day = tm->tm_mday;
|
|
this->OpenLogFiles();
|
|
|
|
if (this->log_age)
|
|
for (unsigned i = 0; i < this->targets.size(); ++i)
|
|
{
|
|
const Anope::string &target = this->targets[i];
|
|
|
|
if (target.empty() || target[0] == '#' || target == "globops" || target.find(":") != Anope::string::npos)
|
|
continue;
|
|
|
|
Anope::string oldlog = CreateLogName(target, Anope::CurTime - 86400 * this->log_age);
|
|
if (IsFile(oldlog))
|
|
{
|
|
unlink(oldlog.c_str());
|
|
Log(LOG_DEBUG) << "Deleted old logfile " << oldlog;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (unsigned i = 0; i < this->logfiles.size(); ++i)
|
|
{
|
|
LogFile *lf = this->logfiles[i];
|
|
lf->stream << GetTimeStamp() << " " << buffer << std::endl;
|
|
}
|
|
}
|