00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #include "services.h"
00014 #include "modules.h"
00015 #include "commands.h"
00016 #include "channels.h"
00017 #include "users.h"
00018 #include "logger.h"
00019 #include "config.h"
00020 #include "bots.h"
00021 #include "servers.h"
00022 #include "uplink.h"
00023 #include "protocol.h"
00024 #include "global.h"
00025
00026 #ifndef _WIN32
00027 #include <sys/time.h>
00028 #include <unistd.h>
00029 #endif
00030
00031 static Anope::string GetTimeStamp()
00032 {
00033 char tbuf[256];
00034 time_t t;
00035
00036 if (time(&t) < 0)
00037 t = Anope::CurTime;
00038
00039 tm tm = *localtime(&t);
00040 if (Anope::Debug)
00041 {
00042 char *s;
00043 struct timeval tv;
00044 gettimeofday(&tv, NULL);
00045 strftime(tbuf, sizeof(tbuf) - 1, "[%b %d %H:%M:%S", &tm);
00046 s = tbuf + strlen(tbuf);
00047 s += snprintf(s, sizeof(tbuf) - (s - tbuf), ".%06d", static_cast<int>(tv.tv_usec));
00048 strftime(s, sizeof(tbuf) - (s - tbuf) - 1, " %Y]", &tm);
00049 }
00050 else
00051 strftime(tbuf, sizeof(tbuf) - 1, "[%b %d %H:%M:%S %Y]", &tm);
00052
00053 return tbuf;
00054 }
00055
00056 static inline Anope::string CreateLogName(const Anope::string &file, time_t t = Anope::CurTime)
00057 {
00058 char timestamp[32];
00059
00060 tm *tm = localtime(&t);
00061
00062 strftime(timestamp, sizeof(timestamp), "%Y%m%d", tm);
00063
00064 return Anope::LogDir + "/" + file + "." + timestamp;
00065 }
00066
00067 LogFile::LogFile(const Anope::string &name) : filename(name), stream(name.c_str(), std::ios_base::out | std::ios_base::app)
00068 {
00069 }
00070
00071 Anope::string LogFile::GetName() const
00072 {
00073 return this->filename;
00074 }
00075
00076 Log::Log(LogType t, const Anope::string &cat, const BotInfo *b) : bi(b), u(NULL), nc(NULL), c(NULL), chan(NULL), ci(NULL), s(NULL), type(t), category(cat)
00077 {
00078 if (!bi && Config)
00079 bi = Global;
00080 if (bi)
00081 this->sources.push_back(bi->nick);
00082 }
00083
00084 Log::Log(LogType t, CommandSource &source, Command *_c, const ChannelInfo *_ci) : nick(source.GetNick()), u(source.GetUser()), nc(source.nc), c(_c), chan(NULL), ci(_ci), s(NULL), m(NULL), type(t)
00085 {
00086 if (!c)
00087 throw CoreException("Invalid pointers passed to Log::Log");
00088
00089 if (type != LOG_COMMAND && type != LOG_OVERRIDE && type != LOG_ADMIN)
00090 throw CoreException("This constructor does not support this log type");
00091
00092 size_t sl = c->name.find('/');
00093 this->bi = NULL;
00094 if (sl != Anope::string::npos)
00095 this->bi = BotInfo::Find(c->name.substr(0, sl));
00096 if (this->bi == NULL && Config)
00097 this->bi = Global;
00098 this->category = c->name;
00099 if (this->bi)
00100 this->sources.push_back(this->bi->nick);
00101 if (u)
00102 this->sources.push_back(u->nick);
00103 this->sources.push_back(c->name);
00104 if (ci)
00105 this->sources.push_back(ci->name);
00106 }
00107
00108 Log::Log(const User *_u, Channel *ch, const Anope::string &cat) : bi(NULL), u(_u), nc(NULL), c(NULL), chan(ch), ci(chan ? *chan->ci : NULL), s(NULL), m(NULL), type(LOG_CHANNEL), category(cat)
00109 {
00110 if (!chan)
00111 throw CoreException("Invalid pointers passed to Log::Log");
00112
00113 if (Config)
00114 this->bi = ChanServ;
00115 if (this->bi)
00116 this->sources.push_back(this->bi->nick);
00117 if (u)
00118 this->sources.push_back(u->nick);
00119 this->sources.push_back(chan->name);
00120 }
00121
00122 Log::Log(const User *_u, const Anope::string &cat, const BotInfo *_bi) : bi(_bi), u(_u), nc(NULL), c(NULL), chan(NULL), ci(NULL), s(NULL), m(NULL), type(LOG_USER), category(cat)
00123 {
00124 if (!u)
00125 throw CoreException("Invalid pointers passed to Log::Log");
00126
00127 if (!this->bi && Config)
00128 this->bi = Global;
00129 if (this->bi)
00130 this->sources.push_back(this->bi->nick);
00131 this->sources.push_back(u->nick);
00132 }
00133
00134 Log::Log(Server *serv, const Anope::string &cat, const BotInfo *_bi) : bi(_bi), u(NULL), nc(NULL), c(NULL), chan(NULL), ci(NULL), s(serv), m(NULL), type(LOG_SERVER), category(cat)
00135 {
00136 if (!s)
00137 throw CoreException("Invalid pointer passed to Log::Log");
00138
00139 if (!this->bi && Config)
00140 this->bi = OperServ;
00141 if (!this->bi && Config)
00142 this->bi = Global;
00143 if (this->bi)
00144 this->sources.push_back(this->bi->nick);
00145 this->sources.push_back(s->GetName());
00146 }
00147
00148 Log::Log(const BotInfo *b, const Anope::string &cat) : bi(b), u(NULL), nc(NULL), c(NULL), chan(NULL), ci(NULL), s(NULL), m(NULL), type(LOG_NORMAL), category(cat)
00149 {
00150 if (!this->bi && Config)
00151 this->bi = Global;
00152 if (this->bi)
00153 this->sources.push_back(bi->nick);
00154 }
00155
00156 Log::Log(Module *mod, const Anope::string &cat) : bi(NULL), u(NULL), nc(NULL), c(NULL), chan(NULL), ci(NULL), s(NULL), m(mod), type(LOG_MODULE), category(cat)
00157 {
00158 if (m)
00159 this->sources.push_back(m->name);
00160 }
00161
00162 Log::~Log()
00163 {
00164 if (Anope::NoFork && Anope::Debug && this->type >= LOG_NORMAL && this->type <= LOG_DEBUG + Anope::Debug - 1)
00165 std::cout << GetTimeStamp() << " Debug: " << this->BuildPrefix() << this->buf.str() << std::endl;
00166 else if (Anope::NoFork && this->type <= LOG_TERMINAL)
00167 std::cout << GetTimeStamp() << " " << this->BuildPrefix() << this->buf.str() << std::endl;
00168 else if (this->type == LOG_TERMINAL)
00169 std::cout << this->BuildPrefix() << this->buf.str() << std::endl;
00170 for (unsigned i = 0; Config && i < Config->LogInfos.size(); ++i)
00171 {
00172 LogInfo *l = Config->LogInfos[i];
00173 l->ProcessMessage(this);
00174 }
00175 FOREACH_MOD(I_OnLog, OnLog(this));
00176 }
00177
00178 Anope::string Log::BuildPrefix() const
00179 {
00180 Anope::string buffer;
00181
00182 switch (this->type)
00183 {
00184 case LOG_ADMIN:
00185 {
00186 if (!this->c && !(this->u || this->nc))
00187 break;
00188 buffer += "ADMIN: ";
00189 size_t sl = this->c->name.find('/');
00190 Anope::string cname = sl != Anope::string::npos ? this->c->name.substr(sl + 1) : this->c->name;
00191 if (this->u)
00192 buffer += this->u->GetMask() + " used " + cname + " ";
00193 else if (this->nc)
00194 buffer += this->nc->display + " used " + cname + " ";
00195 if (this->ci)
00196 buffer += "on " + this->ci->name + " ";
00197 break;
00198 }
00199 case LOG_OVERRIDE:
00200 {
00201 if (!this->c && !(this->u || this->nc))
00202 break;
00203 buffer += "OVERRIDE: ";
00204 size_t sl = this->c->name.find('/');
00205 Anope::string cname = sl != Anope::string::npos ? this->c->name.substr(sl + 1) : this->c->name;
00206 if (this->u)
00207 buffer += this->u->GetMask() + " used " + cname + " ";
00208 else if (this->nc)
00209 buffer += this->nc->display + " used " + cname + " ";
00210 if (this->ci)
00211 buffer += "on " + this->ci->name + " ";
00212 break;
00213 }
00214 case LOG_COMMAND:
00215 {
00216 if (!this->c)
00217 break;
00218 buffer += "COMMAND: ";
00219 size_t sl = this->c->name.find('/');
00220 Anope::string cname = sl != Anope::string::npos ? this->c->name.substr(sl + 1) : this->c->name;
00221 if (this->u)
00222 buffer += this->u->GetMask() + " used " + cname + " ";
00223 else if (this->nc)
00224 buffer += this->nc->display + " used " + cname + " ";
00225 else
00226 buffer += this->nick + " used " + cname + " ";
00227 if (this->ci)
00228 buffer += "on " + this->ci->name + " ";
00229 break;
00230 }
00231 case LOG_CHANNEL:
00232 {
00233 if (!this->chan)
00234 break;
00235 buffer += "CHANNEL: ";
00236 if (this->u)
00237 buffer += this->u->GetMask() + " " + this->category + " " + this->chan->name + " ";
00238 else
00239 buffer += this->category + " " + this->chan->name + " ";
00240 break;
00241 }
00242 case LOG_USER:
00243 {
00244 if (this->u)
00245 buffer += "USERS: " + this->u->GetMask() + " ";
00246 break;
00247 }
00248 case LOG_SERVER:
00249 {
00250 if (this->s)
00251 buffer += "SERVER: " + this->s->GetName() + " (" + this->s->GetDescription() + ") ";
00252 break;
00253 }
00254 case LOG_MODULE:
00255 {
00256 if (this->m)
00257 buffer += this->m->name.upper() + ": ";
00258 break;
00259 }
00260 default:
00261 break;
00262 }
00263
00264 return buffer;
00265 }
00266
00267 LogInfo::LogInfo(int la, bool rio, bool ldebug) : log_age(la), raw_io(rio), debug(ldebug)
00268 {
00269 }
00270
00271 LogInfo::~LogInfo()
00272 {
00273 for (std::map<Anope::string, LogFile *>::iterator it = this->logfiles.begin(), it_end = this->logfiles.end(); it != it_end; ++it)
00274 {
00275 LogFile *f = it->second;
00276
00277 if (f && f->stream.is_open())
00278 f->stream.close();
00279 delete f;
00280 }
00281 this->logfiles.clear();
00282 }
00283
00284 void LogInfo::AddType(std::list<Anope::string> &list, const Anope::string &type)
00285 {
00286 for (std::list<Anope::string>::iterator it = list.begin(), it_end = list.end(); it != it_end; ++it)
00287 {
00288 if (Anope::Match(type, *it))
00289 {
00290 Log() << "Log: Type " << type << " is already covered by " << *it;
00291 return;
00292 }
00293 }
00294
00295 list.push_back(type);
00296 }
00297
00298 bool LogInfo::HasType(LogType ltype, const Anope::string &type) const
00299 {
00300 const std::list<Anope::string> *list = NULL;
00301 switch (ltype)
00302 {
00303 case LOG_ADMIN:
00304 list = &this->admin;
00305 break;
00306 case LOG_OVERRIDE:
00307 list = &this->override;
00308 break;
00309 case LOG_COMMAND:
00310 list = &this->commands;
00311 break;
00312 case LOG_SERVER:
00313 list = &this->servers;
00314 break;
00315 case LOG_CHANNEL:
00316 list = &this->channels;
00317 break;
00318 case LOG_USER:
00319 list = &this->users;
00320 break;
00321 case LOG_TERMINAL:
00322 return true;
00323 case LOG_RAWIO:
00324 return (Anope::Debug || this->debug) ? true : this->raw_io;
00325 case LOG_DEBUG:
00326 return Anope::Debug ? true : this->debug;
00327 case LOG_DEBUG_2:
00328 case LOG_DEBUG_3:
00329 case LOG_DEBUG_4:
00330 break;
00331 case LOG_MODULE:
00332 case LOG_NORMAL:
00333 default:
00334 list = &this->normal;
00335 break;
00336 }
00337
00338 if (list == NULL)
00339 return false;
00340
00341 for (std::list<Anope::string>::const_iterator it = list->begin(), it_end = list->end(); it != it_end; ++it)
00342 {
00343 Anope::string cat = *it;
00344 bool inverse = false;
00345 if (cat[0] == '~')
00346 {
00347 cat.erase(cat.begin());
00348 inverse = true;
00349 }
00350 if (Anope::Match(type, cat))
00351 {
00352 return !inverse;
00353 }
00354 }
00355
00356 return false;
00357 }
00358
00359 void LogInfo::ProcessMessage(const Log *l)
00360 {
00361 static time_t lastwarn = Anope::CurTime;
00362
00363 if (!l)
00364 throw CoreException("Bad values passed to LogInfo::ProcessMessages");
00365 else if (!this->HasType(l->type, l->category))
00366 return;
00367
00368 if (!this->sources.empty())
00369 {
00370 bool log = false;
00371 for (std::list<Anope::string>::const_iterator it = this->sources.begin(), it_end = this->sources.end(); it != it_end; ++it)
00372 {
00373 if (std::find(l->sources.begin(), l->sources.end(), *it) != l->sources.end())
00374 {
00375 log = true;
00376 break;
00377 }
00378 }
00379 if (!log)
00380 return;
00381 }
00382
00383 for (std::list<Anope::string>::iterator it = this->targets.begin(), it_end = this->targets.end(); it != it_end; ++it)
00384 {
00385 const Anope::string &target = *it;
00386 Anope::string buffer = l->BuildPrefix() + l->buf.str();
00387
00388 if (target[0] == '#')
00389 {
00390 if (UplinkSock && l->type <= LOG_NORMAL && Me && Me->IsSynced())
00391 {
00392 Channel *c = Channel::Find(target);
00393 if (!c || !l->bi)
00394 continue;
00395 IRCD->SendPrivmsg(l->bi, c->name, "%s", buffer.c_str());
00396 }
00397 }
00398 else if (target == "globops")
00399 {
00400 if (UplinkSock && l->bi && l->type <= LOG_NORMAL && Me && Me->IsSynced())
00401 {
00402 IRCD->SendGlobops(l->bi, "%s", buffer.c_str());
00403 }
00404 }
00405 else
00406 {
00407 LogFile *log = NULL;
00408 std::map<Anope::string, LogFile *>::iterator lit = this->logfiles.find(target);
00409 if (lit != this->logfiles.end())
00410 {
00411 log = lit->second;
00412 if (log && log->GetName() != CreateLogName(target))
00413 {
00414 delete log;
00415 this->logfiles.erase(lit);
00416 log = new LogFile(CreateLogName(target));
00417 this->logfiles[target] = log;
00418
00419 if (this->log_age)
00420 {
00421 Anope::string oldlog = CreateLogName(target, Anope::CurTime - 86400 * this->log_age);
00422 if (IsFile(oldlog))
00423 {
00424 unlink(oldlog.c_str());
00425 Log(LOG_DEBUG) << "Deleted old logfile " << oldlog;
00426 }
00427 }
00428 }
00429 if (!log || !log->stream.is_open())
00430 {
00431 if (log && lastwarn + 300 < Anope::CurTime)
00432 {
00433 lastwarn = Anope::CurTime;
00434 Log() << "Unable to open logfile " << log->GetName();
00435 }
00436 delete log;
00437 this->logfiles.erase(lit);
00438 log = NULL;
00439 }
00440 }
00441 else if (lit == this->logfiles.end())
00442 {
00443 log = new LogFile(CreateLogName(target));
00444
00445 if (!log->stream.is_open())
00446 {
00447 if (lastwarn + 300 < Anope::CurTime)
00448 {
00449 lastwarn = Anope::CurTime;
00450 Log() << "Unable to open logfile " << log->GetName();
00451 }
00452 delete log;
00453 log = NULL;
00454 }
00455 else
00456 this->logfiles[target] = log;
00457 }
00458
00459 if (log)
00460 log->stream << GetTimeStamp() << " " << buffer << std::endl;
00461 }
00462 }
00463 }
00464