anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
cs_log.cpp (12191B)
1 /* ChanServ 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 #include "modules/cs_log.h" 14 15 struct LogSettingImpl : LogSetting, Serializable 16 { 17 LogSettingImpl() : Serializable("LogSetting") 18 { 19 } 20 21 ~LogSettingImpl() 22 { 23 ChannelInfo *ci = ChannelInfo::Find(chan); 24 if (ci) 25 { 26 LogSettings *ls = ci->GetExt<LogSettings>("logsettings"); 27 if (ls) 28 { 29 LogSettings::iterator it = std::find((*ls)->begin(), (*ls)->end(), this); 30 if (it != (*ls)->end()) 31 (*ls)->erase(it); 32 } 33 } 34 } 35 36 void Serialize(Serialize::Data &data) const anope_override 37 { 38 data["ci"] << chan; 39 data["service_name"] << service_name; 40 data["command_service"] << command_service; 41 data["command_name"] << command_name; 42 data["method"] << method; 43 data["extra"] << extra; 44 data["creator"] << creator; 45 data.SetType("created", Serialize::Data::DT_INT); data["created"] << created; 46 } 47 48 static Serializable* Unserialize(Serializable *obj, Serialize::Data &data) 49 { 50 Anope::string sci; 51 data["ci"] >> sci; 52 53 ChannelInfo *ci = ChannelInfo::Find(sci); 54 if (ci == NULL) 55 return NULL; 56 57 LogSettingImpl *ls; 58 if (obj) 59 ls = anope_dynamic_static_cast<LogSettingImpl *>(obj); 60 else 61 { 62 LogSettings *lsettings = ci->Require<LogSettings>("logsettings"); 63 ls = new LogSettingImpl(); 64 (*lsettings)->push_back(ls); 65 } 66 67 ls->chan = ci->name; 68 data["service_name"] >> ls->service_name; 69 data["command_service"] >> ls->command_service; 70 data["command_name"] >> ls->command_name; 71 data["method"] >> ls->method; 72 data["extra"] >> ls->extra; 73 data["creator"] >> ls->creator; 74 data["created"] >> ls->created; 75 76 return ls; 77 } 78 }; 79 80 struct LogSettingsImpl : LogSettings 81 { 82 LogSettingsImpl(Extensible *) { } 83 84 ~LogSettingsImpl() 85 { 86 for (iterator it = (*this)->begin(); it != (*this)->end();) 87 { 88 LogSetting *ls = *it; 89 ++it; 90 delete ls; 91 } 92 } 93 94 LogSetting *Create() anope_override 95 { 96 return new LogSettingImpl(); 97 } 98 }; 99 100 class CommandCSLog : public Command 101 { 102 public: 103 CommandCSLog(Module *creator) : Command(creator, "chanserv/log", 1, 4) 104 { 105 this->SetDesc(_("Configures channel logging settings")); 106 this->SetSyntax(_("\037channel\037")); 107 this->SetSyntax(_("\037channel\037 \037command\037 \037method\037 [\037status\037]")); 108 } 109 110 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 111 { 112 const Anope::string &channel = params[0]; 113 114 ChannelInfo *ci = ChannelInfo::Find(channel); 115 if (ci == NULL) 116 source.Reply(CHAN_X_NOT_REGISTERED, channel.c_str()); 117 else if (!source.AccessFor(ci).HasPriv("SET") && !source.HasPriv("chanserv/administration")) 118 source.Reply(ACCESS_DENIED); 119 else if (params.size() == 1) 120 { 121 LogSettings *ls = ci->Require<LogSettings>("logsettings"); 122 if (!ls || (*ls)->empty()) 123 source.Reply(_("There currently are no logging configurations for %s."), ci->name.c_str()); 124 else 125 { 126 ListFormatter list(source.GetAccount()); 127 list.AddColumn(_("Number")).AddColumn(_("Service")).AddColumn(_("Command")).AddColumn(_("Method")).AddColumn(""); 128 129 for (unsigned i = 0; i < (*ls)->size(); ++i) 130 { 131 const LogSetting *log = (*ls)->at(i); 132 133 ListFormatter::ListEntry entry; 134 entry["Number"] = stringify(i + 1); 135 entry["Service"] = log->command_service; 136 entry["Command"] = !log->command_name.empty() ? log->command_name : log->service_name; 137 entry["Method"] = log->method; 138 entry[""] = log->extra; 139 list.AddEntry(entry); 140 } 141 142 source.Reply(_("Log list for %s:"), ci->name.c_str()); 143 144 std::vector<Anope::string> replies; 145 list.Process(replies); 146 147 for (unsigned i = 0; i < replies.size(); ++i) 148 source.Reply(replies[i]); 149 } 150 } 151 else if (params.size() > 2) 152 { 153 if (Anope::ReadOnly) 154 { 155 source.Reply(READ_ONLY_MODE); 156 return; 157 } 158 159 LogSettings *ls = ci->Require<LogSettings>("logsettings"); 160 const Anope::string &command = params[1]; 161 const Anope::string &method = params[2]; 162 const Anope::string &extra = params.size() > 3 ? params[3] : ""; 163 164 size_t sl = command.find('/'); 165 if (sl == Anope::string::npos) 166 { 167 source.Reply(_("%s is not a valid command."), command.c_str()); 168 return; 169 } 170 171 Anope::string service = command.substr(0, sl), 172 command_name = command.substr(sl + 1); 173 BotInfo *bi = BotInfo::Find(service, true); 174 175 Anope::string service_name; 176 177 /* Allow either a command name or a service name. */ 178 if (bi && bi->commands.count(command_name)) 179 { 180 /* Get service name from command */ 181 service_name = bi->commands[command_name].name; 182 } 183 else if (ServiceReference<Command>("Command", command.lower())) 184 { 185 /* This is the service name, don't use any specific command */ 186 service_name = command; 187 bi = NULL; 188 command_name.clear(); 189 } 190 else 191 { 192 source.Reply(_("%s is not a valid command."), command.c_str()); 193 return; 194 } 195 196 if (!method.equals_ci("MESSAGE") && !method.equals_ci("NOTICE") && !method.equals_ci("MEMO")) 197 { 198 source.Reply(_("%s is not a valid logging method."), method.c_str()); 199 return; 200 } 201 202 for (unsigned i = 0; i < extra.length(); ++i) 203 if (ModeManager::GetStatusChar(extra[i]) == 0) 204 { 205 source.Reply(_("%c is an unknown status mode."), extra[i]); 206 return; 207 } 208 209 bool override = !source.AccessFor(ci).HasPriv("SET"); 210 211 for (unsigned i = (*ls)->size(); i > 0; --i) 212 { 213 LogSetting *log = (*ls)->at(i - 1); 214 215 if (log->service_name == service_name && log->method.equals_ci(method) && command_name.equals_ci(log->command_name)) 216 { 217 if (log->extra == extra) 218 { 219 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to remove logging for " << command << " with method " << method << (extra == "" ? "" : " ") << extra; 220 source.Reply(_("Logging for command %s on %s with log method %s%s%s has been removed."), !log->command_name.empty() ? log->command_name.c_str() : log->service_name.c_str(), !log->command_service.empty() ? log->command_service.c_str() : "any service", method.c_str(), extra.empty() ? "" : " ", extra.empty() ? "" : extra.c_str()); 221 delete log; 222 } 223 else 224 { 225 log->extra = extra; 226 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to change logging for " << command << " to method " << method << (extra == "" ? "" : " ") << extra; 227 source.Reply(_("Logging changed for command %s on %s, now using log method %s%s%s."), !log->command_name.empty() ? log->command_name.c_str() : log->service_name.c_str(), !log->command_service.empty() ? log->command_service.c_str() : "any service", method.c_str(), extra.empty() ? "" : " ", extra.empty() ? "" : extra.c_str()); 228 } 229 return; 230 } 231 } 232 233 LogSetting *log = new LogSettingImpl(); 234 log->chan = ci->name; 235 log->service_name = service_name; 236 if (bi) 237 log->command_service = bi->nick; 238 log->command_name = command_name; 239 log->method = method; 240 log->extra = extra; 241 log->created = Anope::CurTime; 242 log->creator = source.GetNick(); 243 244 (*ls)->push_back(log); 245 246 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to log " << command << " with method " << method << (extra == "" ? "" : " ") << extra; 247 248 source.Reply(_("Logging is now active for command %s on %s, using log method %s%s%s."), !command_name.empty() ? command_name.c_str() : service_name.c_str(), bi ? bi->nick.c_str() : "any service", method.c_str(), extra.empty() ? "" : " ", extra.empty() ? "" : extra.c_str()); 249 } 250 else 251 this->OnSyntaxError(source, ""); 252 } 253 254 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override 255 { 256 this->SendSyntax(source); 257 source.Reply(" "); 258 source.Reply(_("The %s command allows users to configure logging settings\n" 259 "for their channel. If no parameters are given this command\n" 260 "lists the current logging methods in place for this channel.\n" 261 " \n" 262 "Otherwise, \037command\037 must be a command name, and \037method\037\n" 263 "is one of the following logging methods:\n" 264 " \n" 265 " MESSAGE [status], NOTICE [status], MEMO\n" 266 " \n" 267 "Which are used to message, notice, and memo the channel respectively.\n" 268 "With MESSAGE or NOTICE you must have a service bot assigned to and joined\n" 269 "to your channel. Status may be a channel status such as @ or +.\n" 270 " \n" 271 "To remove a logging method use the same syntax as you would to add it.\n" 272 " \n" 273 "Example:\n" 274 " %s #anope chanserv/access MESSAGE @\n" 275 " Would message any channel operators whenever someone used the\n" 276 " ACCESS command on ChanServ on the channel."), 277 source.command.upper().c_str(), source.command.upper().c_str()); 278 return true; 279 } 280 }; 281 282 class CSLog : public Module 283 { 284 ServiceReference<MemoServService> MSService; 285 CommandCSLog commandcslog; 286 ExtensibleItem<LogSettingsImpl> logsettings; 287 Serialize::Type logsetting_type; 288 289 struct LogDefault 290 { 291 Anope::string service, command, method; 292 }; 293 294 std::vector<LogDefault> defaults; 295 296 public: 297 CSLog(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), 298 MSService("MemoServService", "MemoServ"), commandcslog(this), 299 logsettings(this, "logsettings"), logsetting_type("LogSetting", LogSettingImpl::Unserialize) 300 { 301 302 } 303 304 void OnReload(Configuration::Conf *conf) anope_override 305 { 306 Configuration::Block *block = conf->GetModule(this); 307 defaults.clear(); 308 309 for (int i = 0; i < block->CountBlock("default"); ++i) 310 { 311 Configuration::Block *def = block->GetBlock("default", i); 312 313 LogDefault ld; 314 315 ld.service = def->Get<const Anope::string>("service"); 316 ld.command = def->Get<const Anope::string>("command"); 317 ld.method = def->Get<const Anope::string>("method"); 318 319 defaults.push_back(ld); 320 } 321 } 322 323 void OnChanRegistered(ChannelInfo *ci) anope_override 324 { 325 if (defaults.empty()) 326 return; 327 328 LogSettings *ls = logsettings.Require(ci); 329 for (unsigned i = 0; i < defaults.size(); ++i) 330 { 331 LogDefault &d = defaults[i]; 332 333 LogSetting *log = new LogSettingImpl(); 334 log->chan = ci->name; 335 336 if (!d.service.empty()) 337 { 338 log->service_name = d.service.lower() + "/" + d.command.lower(); 339 log->command_service = d.service; 340 log->command_name = d.command; 341 } 342 else 343 log->service_name = d.command; 344 345 spacesepstream sep(d.method); 346 sep.GetToken(log->method); 347 log->extra = sep.GetRemaining(); 348 349 log->created = Anope::CurTime; 350 log->creator = ci->GetFounder() ? ci->GetFounder()->display : "(default)"; 351 352 (*ls)->push_back(log); 353 } 354 } 355 356 void OnLog(Log *l) anope_override 357 { 358 if (l->type != LOG_COMMAND || l->u == NULL || l->c == NULL || l->ci == NULL || !Me || !Me->IsSynced()) 359 return; 360 361 LogSettings *ls = logsettings.Get(l->ci); 362 if (ls) 363 for (unsigned i = 0; i < (*ls)->size(); ++i) 364 { 365 const LogSetting *log = (*ls)->at(i); 366 367 /* wrong command */ 368 if (log->service_name != l->c->name) 369 continue; 370 371 /* if a command name is given check the service and the command */ 372 if (!log->command_name.empty()) 373 { 374 /* wrong service (only check if not a fantasy command, though) */ 375 if (!l->source->c && log->command_service != l->source->service->nick) 376 continue; 377 378 if (!log->command_name.equals_ci(l->source->command)) 379 continue; 380 } 381 382 Anope::string buffer = l->u->nick + " used " + l->source->command.upper() + " " + l->buf.str(); 383 384 if (log->method.equals_ci("MEMO") && MSService && l->ci->WhoSends() != NULL) 385 MSService->Send(l->ci->WhoSends()->nick, l->ci->name, buffer, true); 386 else if (l->source->c) 387 /* Sending a channel message or notice in response to a fantasy command */; 388 else if (log->method.equals_ci("MESSAGE") && l->ci->c) 389 { 390 IRCD->SendPrivmsg(l->ci->WhoSends(), log->extra + l->ci->c->name, "%s", buffer.c_str()); 391 l->ci->WhoSends()->lastmsg = Anope::CurTime; 392 } 393 else if (log->method.equals_ci("NOTICE") && l->ci->c) 394 IRCD->SendNotice(l->ci->WhoSends(), log->extra + l->ci->c->name, "%s", buffer.c_str()); 395 } 396 } 397 }; 398 399 MODULE_INIT(CSLog)