anope

- supernets anope source code & configuration
git clone git://git.acid.vegas/anope.git
Log | Files | Refs | Archive | README

os_logsearch.cpp (5142B)

      1 /* OperServ core functions
      2  *
      3  * (C) 2003-2022 Anope Team
      4  * Contact us at team@anope.org
      5  *
      6  * Please read COPYING and README for further details.
      7  *
      8  * Based on the original code of Epona by Lara.
      9  * Based on the original code of Services by Andy Church.
     10  */
     11 
     12 #include "module.h"
     13 
     14 static unsigned int HARDMAX = 65536;
     15 
     16 class CommandOSLogSearch : public Command
     17 {
     18 	static inline Anope::string CreateLogName(const Anope::string &file, time_t t = Anope::CurTime)
     19 	{
     20 		char timestamp[32];
     21 
     22 		tm *tm = localtime(&t);
     23 
     24 		strftime(timestamp, sizeof(timestamp), "%Y%m%d", tm);
     25 
     26 		return Anope::LogDir + "/" + file + "." + timestamp;
     27 	}
     28 
     29  public:
     30 	CommandOSLogSearch(Module *creator) : Command(creator, "operserv/logsearch", 1, 3)
     31 	{
     32 		this->SetDesc(_("Searches logs for a matching pattern"));
     33 		this->SetSyntax(_("[+\037days\037d] [+\037limit\037l] \037pattern\037"));
     34 	}
     35 
     36 	void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
     37 	{
     38 		int days = 7, replies = 50;
     39 
     40 		unsigned i;
     41 		for (i = 0; i < params.size() && params[i][0] == '+'; ++i)
     42 		{
     43 			switch (params[i][params[i].length() - 1])
     44 			{
     45 				case 'd':
     46 					if (params[i].length() > 2)
     47 					{
     48 						Anope::string dur = params[i].substr(1, params[i].length() - 2);
     49 						try
     50 						{
     51 							days = convertTo<int>(dur);
     52 							if (days <= 0)
     53 								throw ConvertException();
     54 						}
     55 						catch (const ConvertException &)
     56 						{
     57 							source.Reply(_("Invalid duration %s, using %d days."), dur.c_str(), days);
     58 						}
     59 					}
     60 					break;
     61 				case 'l':
     62 					if (params[i].length() > 2)
     63 					{
     64 						Anope::string dur = params[i].substr(1, params[i].length() - 2);
     65 						try
     66 						{
     67 							replies = convertTo<int>(dur);
     68 							if (replies <= 0)
     69 								throw ConvertException();
     70 						}
     71 						catch (const ConvertException &)
     72 						{
     73 							source.Reply(_("Invalid limit %s, using %d."), dur.c_str(), replies);
     74 						}
     75 					}
     76 					break;
     77 				default:
     78 					source.Reply(_("Unknown parameter: %s"), params[i].c_str());
     79 			}
     80 		}
     81 
     82 		if (i >= params.size())
     83 		{
     84 			this->OnSyntaxError(source, "");
     85 			return;
     86 		}
     87 
     88 		Anope::string search_string = params[i++];
     89 		for (; i < params.size(); ++i)
     90 			search_string += " " + params[i];
     91 
     92 		Log(LOG_ADMIN, source, this) << "for " << search_string;
     93 
     94 		bool wildcard = search_string.find_first_of("?*") != Anope::string::npos;
     95 		bool regex = search_string.empty() == false && search_string[0] == '/' && search_string[search_string.length() - 1] == '/';
     96 
     97 		const Anope::string &logfile_name = Config->GetModule(this->owner)->Get<const Anope::string>("logname");
     98 		std::vector<Anope::string> matches;
     99 		for (int d = days - 1; d >= 0; --d)
    100 		{
    101 			Anope::string lf_name = CreateLogName(logfile_name, Anope::CurTime - (d * 86400));
    102 			Log(LOG_DEBUG) << "Searching " << lf_name;
    103 			std::fstream fd(lf_name.c_str(), std::ios_base::in);
    104 			if (!fd.is_open())
    105 				continue;
    106 
    107 			for (Anope::string buf, token; std::getline(fd, buf.str());)
    108 			{
    109 				bool match = false;
    110 
    111 				if (regex)
    112 					match = Anope::Match(buf, search_string, false, true);
    113 				else if (wildcard)
    114 					match = Anope::Match(buf, "*" + search_string + "*");
    115 				else
    116 					match = buf.find_ci(search_string) != Anope::string::npos;
    117 
    118 				if (match)
    119 				{
    120 					matches.push_back(buf);
    121 
    122 					if (matches.size() >= HARDMAX)
    123 						break;
    124 				}
    125 			}
    126 
    127 			fd.close();
    128 		}
    129 
    130 		unsigned int found = matches.size();
    131 		if (!found)
    132 		{
    133 			source.Reply(_("No matches for \002%s\002 found."), search_string.c_str());
    134 			return;
    135 		}
    136 
    137 		if (matches.size() >= HARDMAX)
    138 		{
    139 			source.Reply(_("Too many results for \002%s\002."), search_string.c_str());
    140 			return;
    141 		}
    142 
    143 		if (matches.size() > static_cast<unsigned int>(replies))
    144 		{
    145 			matches.erase(matches.begin(), matches.begin() + (matches.size() - static_cast<unsigned int>(replies)));
    146 		}
    147 
    148 		source.Reply(_("Matches for \002%s\002:"), search_string.c_str());
    149 		unsigned int count = 0;
    150 		for (std::vector<Anope::string>::iterator it = matches.begin(), it_end = matches.end(); it != it_end; ++it)
    151 			source.Reply("#%d: %s", ++count, it->c_str());
    152 		source.Reply(_("Showed %d/%d matches for \002%s\002."), matches.size(), found, search_string.c_str());
    153 	}
    154 
    155 	bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
    156 	{
    157 		this->SendSyntax(source);
    158 		source.Reply(" ");
    159 		source.Reply(_("This command searches the Services logfiles for messages\n"
    160 				"that match the given pattern. The day and limit argument\n"
    161 				"may be used to specify how many days of logs to search\n"
    162 				"and the number of replies to limit to. By default this\n"
    163 				"command searches one week of logs, and limits replies\n"
    164 				"to 50.\n"
    165 				" \n"
    166 				"For example:\n"
    167 				"    \002LOGSEARCH +21d +500l Anope\002\n"
    168 				"      Searches the last 21 days worth of logs for messages\n"
    169 				"      containing Anope and lists the most recent 500 of them."));
    170 		return true;
    171 	}
    172 };
    173 
    174 class OSLogSearch : public Module
    175 {
    176 	CommandOSLogSearch commandoslogsearch;
    177 
    178  public:
    179 	OSLogSearch(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR),
    180 		commandoslogsearch(this)
    181 	{
    182 	}
    183 };
    184 
    185 MODULE_INIT(OSLogSearch)