00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "module.h"
00015
00016 class SXLineDelCallback : public NumberList
00017 {
00018 XLineManager *xlm;
00019 Command *command;
00020 CommandSource &source;
00021 unsigned deleted;
00022 public:
00023 SXLineDelCallback(XLineManager *x, Command *c, CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, true), xlm(x), command(c), source(_source), deleted(0)
00024 {
00025 }
00026
00027 ~SXLineDelCallback()
00028 {
00029 if (!deleted)
00030 source.Reply(_("No matching entries on the %s list."), this->command->name.c_str());
00031 else if (deleted == 1)
00032 source.Reply(_("Deleted 1 entry from the %s list."), this->command->name.c_str());
00033 else
00034 source.Reply(_("Deleted %d entries from the %s list."), deleted, this->command->name.c_str());
00035 }
00036
00037 void HandleNumber(unsigned number) anope_override
00038 {
00039 if (!number)
00040 return;
00041
00042 XLine *x = this->xlm->GetEntry(number - 1);
00043
00044 if (!x)
00045 return;
00046
00047 ++deleted;
00048 DoDel(this->xlm, source, x);
00049 }
00050
00051 static void DoDel(XLineManager *xlm, CommandSource &source, XLine *x)
00052 {
00053 xlm->DelXLine(x);
00054 }
00055 };
00056
00057 class CommandOSSXLineBase : public Command
00058 {
00059 private:
00060 virtual XLineManager* xlm() = 0;
00061
00062 virtual void OnAdd(CommandSource &source, const std::vector<Anope::string> ¶ms) = 0;
00063
00064 void OnDel(CommandSource &source, const std::vector<Anope::string> ¶ms)
00065 {
00066
00067 if (!this->xlm() || this->xlm()->GetList().empty())
00068 {
00069 source.Reply(_("%s list is empty."), source.command.c_str());
00070 return;
00071 }
00072
00073 const Anope::string &mask = params.size() > 1 ? params[1] : "";
00074
00075 if (mask.empty())
00076 {
00077 this->OnSyntaxError(source, "DEL");
00078 return;
00079 }
00080
00081 if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
00082 {
00083 SXLineDelCallback list(this->xlm(), this, source, mask);
00084 list.Process();
00085 }
00086 else
00087 {
00088 XLine *x = this->xlm()->HasEntry(mask);
00089
00090 if (!x)
00091 {
00092 source.Reply(_("\002%s\002 not found on the %s list."), mask.c_str(), source.command.c_str());
00093 return;
00094 }
00095
00096 FOREACH_MOD(I_OnDelXLine, OnDelXLine(source, x, this->xlm()));
00097
00098 SXLineDelCallback::DoDel(this->xlm(), source, x);
00099 source.Reply(_("\002%s\002 deleted from the %s list."), mask.c_str(), source.command.c_str());
00100 }
00101
00102 if (Anope::ReadOnly)
00103 source.Reply(READ_ONLY_MODE);
00104
00105 return;
00106 }
00107
00108 void ProcessList(CommandSource &source, const std::vector<Anope::string> ¶ms, ListFormatter &list)
00109 {
00110 if (!this->xlm() || this->xlm()->GetList().empty())
00111 {
00112 source.Reply(_("%s list is empty."), source.command.c_str());
00113 return;
00114 }
00115
00116 const Anope::string &mask = params.size() > 1 ? params[1] : "";
00117
00118 if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos)
00119 {
00120 class SXLineListCallback : public NumberList
00121 {
00122 XLineManager *xlm;
00123 ListFormatter &list;
00124 public:
00125 SXLineListCallback(XLineManager *x, ListFormatter &_list, const Anope::string &numlist) : NumberList(numlist, false), xlm(x), list(_list)
00126 {
00127 }
00128
00129 void HandleNumber(unsigned number) anope_override
00130 {
00131 if (!number)
00132 return;
00133
00134 const XLine *x = this->xlm->GetEntry(number - 1);
00135
00136 if (!x)
00137 return;
00138
00139 ListFormatter::ListEntry entry;
00140 entry["Number"] = stringify(number);
00141 entry["Mask"] = x->mask;
00142 entry["By"] = x->by;
00143 entry["Created"] = Anope::strftime(x->created, NULL, true);
00144 entry["Expires"] = Anope::Expires(x->expires);
00145 entry["Reason"] = x->reason;
00146 list.AddEntry(entry);
00147 }
00148 }
00149 sl_list(this->xlm(), list, mask);
00150 sl_list.Process();
00151 }
00152 else
00153 {
00154 for (unsigned i = 0, end = this->xlm()->GetCount(); i < end; ++i)
00155 {
00156 const XLine *x = this->xlm()->GetEntry(i);
00157
00158 if (mask.empty() || mask.equals_ci(x->mask) || mask == x->id || Anope::Match(x->mask, mask, false, true))
00159 {
00160 ListFormatter::ListEntry entry;
00161 entry["Number"] = stringify(i + 1);
00162 entry["Mask"] = x->mask;
00163 entry["By"] = x->by;
00164 entry["Created"] = Anope::strftime(x->created, NULL, true);
00165 entry["Expires"] = Anope::Expires(x->expires, source.nc);
00166 entry["Reason"] = x->reason;
00167 list.AddEntry(entry);
00168 }
00169 }
00170 }
00171
00172 if (list.IsEmpty())
00173 source.Reply(_("No matching entries on the %s list."), source.command.c_str());
00174 else
00175 {
00176 source.Reply(_("Current %s list:"), source.command.c_str());
00177
00178 std::vector<Anope::string> replies;
00179 list.Process(replies);
00180
00181 for (unsigned i = 0; i < replies.size(); ++i)
00182 source.Reply(replies[i]);
00183 }
00184 }
00185
00186 void OnList(CommandSource &source, const std::vector<Anope::string> ¶ms)
00187 {
00188 ListFormatter list;
00189 list.AddColumn("Number").AddColumn("Mask").AddColumn("Reason");
00190
00191 this->ProcessList(source, params, list);
00192 }
00193
00194 void OnView(CommandSource &source, const std::vector<Anope::string> ¶ms)
00195 {
00196 ListFormatter list;
00197 list.AddColumn("Number").AddColumn("Mask").AddColumn("By").AddColumn("Created").AddColumn("Expires").AddColumn("Reason");
00198 this->ProcessList(source, params, list);
00199 }
00200
00201 void OnClear(CommandSource &source)
00202 {
00203 FOREACH_MOD(I_OnDelXLine, OnDelXLine(source, NULL, this->xlm()));
00204
00205 for (unsigned i = this->xlm()->GetCount(); i > 0; --i)
00206 {
00207 XLine *x = this->xlm()->GetEntry(i - 1);
00208 this->xlm()->DelXLine(x);
00209 }
00210
00211 source.Reply(_("The %s list has been cleared."), source.command.c_str());
00212
00213 return;
00214 }
00215 public:
00216 CommandOSSXLineBase(Module *creator, const Anope::string &cmd) : Command(creator, cmd, 1, 4)
00217 {
00218 this->SetDesc(Anope::printf(_("Manipulate the %s list"), cmd.c_str()));
00219 }
00220
00221 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
00222 {
00223 const Anope::string &cmd = params[0];
00224
00225 if (cmd.equals_ci("ADD"))
00226 return this->OnAdd(source, params);
00227 else if (cmd.equals_ci("DEL"))
00228 return this->OnDel(source, params);
00229 else if (cmd.equals_ci("LIST"))
00230 return this->OnList(source, params);
00231 else if (cmd.equals_ci("VIEW"))
00232 return this->OnView(source, params);
00233 else if (cmd.equals_ci("CLEAR"))
00234 return this->OnClear(source);
00235 else
00236 this->OnSyntaxError(source, "");
00237
00238 return;
00239 }
00240
00241 virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand) = 0;
00242 };
00243
00244 class CommandOSSNLine : public CommandOSSXLineBase
00245 {
00246 XLineManager *xlm()
00247 {
00248 return this->snlines;
00249 }
00250
00251 void OnAdd(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
00252 {
00253 if (!this->xlm() || !IRCD->CanSNLine)
00254 {
00255 source.Reply(_("Your IRCd does not support SNLINE."));
00256 return;
00257 }
00258
00259 unsigned last_param = 2;
00260 Anope::string param, expiry;
00261 time_t expires;
00262
00263 param = params.size() > 1 ? params[1] : "";
00264 if (!param.empty() && param[0] == '+')
00265 {
00266 expiry = param;
00267 param = params.size() > 2 ? params[2] : "";
00268 last_param = 3;
00269 }
00270
00271 expires = !expiry.empty() ? Anope::DoTime(expiry) : Config->SNLineExpiry;
00272
00273
00274
00275 if (!expiry.empty() && isdigit(expiry[expiry.length() - 1]))
00276 expires *= 86400;
00277
00278 if (expires && expires < 60)
00279 {
00280 source.Reply(BAD_EXPIRY_TIME);
00281 return;
00282 }
00283 else if (expires > 0)
00284 expires += Anope::CurTime;
00285
00286 if (param.empty())
00287 {
00288 this->OnSyntaxError(source, "ADD");
00289 return;
00290 }
00291
00292 Anope::string rest = param;
00293 if (params.size() > last_param)
00294 rest += " " + params[last_param];
00295
00296 if (rest.find(':') == Anope::string::npos)
00297 {
00298 this->OnSyntaxError(source, "ADD");
00299 return;
00300 }
00301
00302 sepstream sep(rest, ':');
00303 Anope::string mask;
00304 sep.GetToken(mask);
00305 Anope::string reason = sep.GetRemaining();
00306
00307 if (mask.empty() || reason.empty())
00308 {
00309 this->OnSyntaxError(source, "ADD");
00310 return;
00311 }
00312
00313 if (mask[0] == '/' && mask[mask.length() - 1] == '/')
00314 {
00315 if (Config->RegexEngine.empty())
00316 {
00317 source.Reply(_("Regex is enabled."));
00318 return;
00319 }
00320
00321 ServiceReference<RegexProvider> provider("Regex", Config->RegexEngine);
00322 if (!provider)
00323 {
00324 source.Reply(_("Unable to find regex engine %s."), Config->RegexEngine.c_str());
00325 return;
00326 }
00327
00328 try
00329 {
00330 Anope::string stripped_mask = mask.substr(1, mask.length() - 2);
00331 delete provider->Compile(stripped_mask);
00332 }
00333 catch (const RegexException &ex)
00334 {
00335 source.Reply("%s", ex.GetReason().c_str());
00336 return;
00337 }
00338 }
00339
00340 if (!this->xlm()->CanAdd(source, mask, expires, reason))
00341 return;
00342 else if (mask.find_first_not_of("/.*?") == Anope::string::npos)
00343 {
00344 source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str());
00345 return;
00346 }
00347
00348
00349
00350
00351 unsigned masklen = mask.length();
00352 if (mask[masklen - 1] == ' ')
00353 mask.erase(masklen - 1);
00354
00355 XLine *x = new XLine(mask, source.GetNick(), expires, reason);
00356 if (Config->AkillIds)
00357 x->id = XLineManager::GenerateUID();
00358
00359 unsigned int affected = 0;
00360 for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
00361 if (this->xlm()->Check(it->second, x))
00362 ++affected;
00363 float percent = static_cast<float>(affected) / static_cast<float>(UserListByNick.size()) * 100.0;
00364
00365 if (percent > 95)
00366 {
00367 source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str());
00368 Log(LOG_ADMIN, source, this) << "tried to " << source.command << " " << percent << "% of the network (" << affected << " users)";
00369 delete x;
00370 return;
00371 }
00372
00373 EventReturn MOD_RESULT;
00374 FOREACH_RESULT(I_OnAddXLine, OnAddXLine(source, x, this->xlm()));
00375 if (MOD_RESULT == EVENT_STOP)
00376 {
00377 delete x;
00378 return;
00379 }
00380
00381 this->xlm()->AddXLine(x);
00382
00383 if (Config->KillonSNline)
00384 {
00385 this->xlm()->Send(source.GetUser(), x);
00386
00387 Anope::string rreason = "G-Lined: " + reason;
00388
00389 for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
00390 {
00391 User *user = it->second;
00392 ++it;
00393
00394 if (!user->HasMode("OPER") && user->server != Me && Anope::Match(user->realname, x->mask, false, true))
00395 user->Kill(Config->ServerName, rreason);
00396 }
00397 }
00398
00399 source.Reply(_("\002%s\002 added to the %s list."), mask.c_str(), source.command.c_str());
00400 Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
00401 if (Anope::ReadOnly)
00402 source.Reply(READ_ONLY_MODE);
00403 }
00404
00405 ServiceReference<XLineManager> snlines;
00406 public:
00407 CommandOSSNLine(Module *creator) : CommandOSSXLineBase(creator, "operserv/snline"), snlines("XLineManager", "xlinemanager/snline")
00408 {
00409 this->SetSyntax(_("ADD [+\037expiry\037] \037mask\037:\037reason\037"));
00410 this->SetSyntax(_("DEL {\037mask\037 | \037entry-num\037 | \037list\037 | \037id\037}"));
00411 this->SetSyntax(_("LIST [\037mask\037 | \037list\037 | \037id\037]"));
00412 this->SetSyntax(_("VIEW [\037mask\037 | \037list\037 | \037id\037]"));
00413 this->SetSyntax(_("CLEAR"));
00414 }
00415
00416 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
00417 {
00418 this->SendSyntax(source);
00419 source.Reply(" ");
00420 source.Reply(_("Allows Services Operators to manipulate the SNLINE list. If\n"
00421 "a user with a realname matching an SNLINE mask attempts to\n"
00422 "connect, Services will not allow it to pursue his IRC\n"
00423 "session."));
00424 source.Reply(_(" \n"
00425 "\002SNLINE ADD\002 adds the given realname mask to the SNLINE\n"
00426 "list for the given reason (which \002must\002 be given).\n"
00427 "\037expiry\037 is specified as an integer followed by one of \037d\037\n"
00428 "(days), \037h\037 (hours), or \037m\037 (minutes). Combinations (such as\n"
00429 "\0371h30m\037) are not permitted. If a unit specifier is not\n"
00430 "included, the default is days (so \037+30\037 by itself means 30\n"
00431 "days). To add an SNLINE which does not expire, use \037+0\037. If the\n"
00432 "realname mask to be added starts with a \037+\037, an expiry time must\n"
00433 "be given, even if it is the same as the default. The\n"
00434 "current SNLINE default expiry time can be found with the\n"
00435 "\002STATS AKILL\002 command.\n"
00436 "Note: because the realname mask may contain spaces, the\n"
00437 "separator between it and the reason is a colon."));
00438 if (!Config->RegexEngine.empty())
00439 {
00440 source.Reply(" ");
00441 source.Reply(_("Regex matches are also supported using the %s engine.\n"
00442 "Enclose your mask in // if this is desired."), Config->RegexEngine.c_str());
00443 }
00444 source.Reply(_(" \n"
00445 "The \002SNLINE DEL\002 command removes the given mask from the\n"
00446 "SNLINE list if it is present. If a list of entry numbers is\n"
00447 "given, those entries are deleted. (See the example for LIST\n"
00448 "below.)\n"
00449 " \n"
00450 "The \002SNLINE LIST\002 command displays the SNLINE list.\n"
00451 "If a wildcard mask is given, only those entries matching the\n"
00452 "mask are displayed. If a list of entry numbers is given,\n"
00453 "only those entries are shown; for example:\n"
00454 " \002SNLINE LIST 2-5,7-9\002\n"
00455 " Lists SNLINE entries numbered 2 through 5 and 7\n"
00456 " through 9.\n"
00457 " \n"
00458 "\002SNLINE VIEW\002 is a more verbose version of \002SNLINE LIST\002, and\n"
00459 "will show who added an SNLINE, the date it was added, and when\n"
00460 "it expires, as well as the realname mask and reason.\n"
00461 " \n"
00462 "\002SNLINE CLEAR\002 clears all entries of the SNLINE list."));
00463 return true;
00464 }
00465 };
00466
00467 class CommandOSSQLine : public CommandOSSXLineBase
00468 {
00469 XLineManager *xlm()
00470 {
00471 return this->sqlines;
00472 }
00473
00474 void OnAdd(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
00475 {
00476 if (!this->xlm() || !IRCD->CanSQLine)
00477 {
00478 source.Reply(_("Your IRCd does not support SQLINE."));
00479 return;
00480 }
00481
00482 unsigned last_param = 2;
00483 Anope::string expiry, mask;
00484 time_t expires;
00485
00486 mask = params.size() > 1 ? params[1] : "";
00487 if (!mask.empty() && mask[0] == '+')
00488 {
00489 expiry = mask;
00490 mask = params.size() > 2 ? params[2] : "";
00491 last_param = 3;
00492 }
00493
00494 expires = !expiry.empty() ? Anope::DoTime(expiry) : Config->SQLineExpiry;
00495
00496
00497
00498 if (!expiry.empty() && isdigit(expiry[expiry.length() - 1]))
00499 expires *= 86400;
00500
00501 if (expires && expires < 60)
00502 {
00503 source.Reply(BAD_EXPIRY_TIME);
00504 return;
00505 }
00506 else if (expires > 0)
00507 expires += Anope::CurTime;
00508
00509 if (params.size() <= last_param)
00510 {
00511 this->OnSyntaxError(source, "ADD");
00512 return;
00513 }
00514
00515 Anope::string reason = params[last_param];
00516 if (last_param == 2 && params.size() > 3)
00517 reason += " " + params[3];
00518
00519 if (mask.empty() || reason.empty())
00520 {
00521 this->OnSyntaxError(source, "ADD");
00522 return;
00523 }
00524
00525 if (mask[0] == '/' && mask[mask.length() - 1] == '/')
00526 {
00527 if (Config->RegexEngine.empty())
00528 {
00529 source.Reply(_("Regex is enabled."));
00530 return;
00531 }
00532
00533 ServiceReference<RegexProvider> provider("Regex", Config->RegexEngine);
00534 if (!provider)
00535 {
00536 source.Reply(_("Unable to find regex engine %s."), Config->RegexEngine.c_str());
00537 return;
00538 }
00539
00540 try
00541 {
00542 Anope::string stripped_mask = mask.substr(1, mask.length() - 2);
00543 delete provider->Compile(stripped_mask);
00544 }
00545 catch (const RegexException &ex)
00546 {
00547 source.Reply("%s", ex.GetReason().c_str());
00548 return;
00549 }
00550 }
00551
00552 if (!this->sqlines->CanAdd(source, mask, expires, reason))
00553 return;
00554 else if (mask.find_first_not_of("./?*") == Anope::string::npos)
00555 {
00556 source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str());
00557 return;
00558 }
00559
00560 XLine *x = new XLine(mask, source.GetNick(), expires, reason);
00561 if (Config->AkillIds)
00562 x->id = XLineManager::GenerateUID();
00563
00564 unsigned int affected = 0;
00565 for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
00566 if (this->xlm()->Check(it->second, x))
00567 ++affected;
00568 float percent = static_cast<float>(affected) / static_cast<float>(UserListByNick.size()) * 100.0;
00569
00570 if (percent > 95)
00571 {
00572 source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str());
00573 Log(LOG_ADMIN, source, this) << "tried to SQLine " << percent << "% of the network (" << affected << " users)";
00574 delete x;
00575 return;
00576 }
00577
00578 EventReturn MOD_RESULT;
00579 FOREACH_RESULT(I_OnAddXLine, OnAddXLine(source, x, this->xlm()));
00580 if (MOD_RESULT == EVENT_STOP)
00581 {
00582 delete x;
00583 return;
00584 }
00585
00586 this->xlm()->AddXLine(x);
00587 if (Config->KillonSQline)
00588 {
00589 Anope::string rreason = "Q-Lined: " + reason;
00590
00591 if (mask[0] == '#')
00592 {
00593 for (channel_map::const_iterator cit = ChannelList.begin(), cit_end = ChannelList.end(); cit != cit_end; ++cit)
00594 {
00595 Channel *c = cit->second;
00596
00597 if (!Anope::Match(c->name, mask, false, true))
00598 continue;
00599
00600 std::vector<User *> users;
00601 for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it)
00602 {
00603 ChanUserContainer *uc = *it;
00604 User *user = uc->user;
00605
00606 if (!user->HasMode("OPER") && user->server != Me)
00607 users.push_back(user);
00608 }
00609
00610 for (unsigned i = 0; i < users.size(); ++i)
00611 c->Kick(NULL, users[i], "%s", reason.c_str());
00612 }
00613 }
00614 else
00615 {
00616 for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it)
00617 {
00618 User *user = it->second;
00619 ++it;
00620
00621 if (!user->HasMode("OPER") && user->server != Me && Anope::Match(user->nick, x->mask, false, true))
00622 user->Kill(Config->ServerName, rreason);
00623 }
00624 }
00625
00626 this->xlm()->Send(source.GetUser(), x);
00627 }
00628
00629 source.Reply(_("\002%s\002 added to the SQLINE list."), mask.c_str());
00630 Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << ") expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]";
00631
00632 if (Anope::ReadOnly)
00633 source.Reply(READ_ONLY_MODE);
00634 }
00635
00636 ServiceReference<XLineManager> sqlines;
00637 public:
00638 CommandOSSQLine(Module *creator) : CommandOSSXLineBase(creator, "operserv/sqline"), sqlines("XLineManager", "xlinemanager/sqline")
00639 {
00640 this->SetSyntax(_("ADD [+\037expiry\037] \037mask\037 \037reason\037"));
00641 this->SetSyntax(_("DEL {\037mask\037 | \037entry-num\037 | \037list\037 | \037id\037}"));
00642 this->SetSyntax(_("LIST [\037mask\037 | \037list\037 | \037id\037]"));
00643 this->SetSyntax(_("VIEW [\037mask\037 | \037list\037 | \037id\037]"));
00644 this->SetSyntax(_("CLEAR"));
00645 }
00646
00647 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
00648 {
00649 this->SendSyntax(source);
00650 source.Reply(" ");
00651 source.Reply(_("Allows Services Operators to manipulate the SQLINE list. If\n"
00652 "a user with a nick matching an SQLINE mask attempts to\n"
00653 "connect, Services will not allow it to pursue his IRC\n"
00654 "session.\n"
00655 "If the first character of the mask is #, services will\n"
00656 "prevent the use of matching channels."));
00657 source.Reply(_(" \n"
00658 "\002SQLINE ADD\002 adds the given (nick's) mask to the SQLINE\n"
00659 "list for the given reason (which \002must\002 be given).\n"
00660 "\037expiry\037 is specified as an integer followed by one of \037d\037\n"
00661 "(days), \037h\037 (hours), or \037m\037 (minutes). Combinations (such as\n"
00662 "\0371h30m\037) are not permitted. If a unit specifier is not\n"
00663 "included, the default is days (so \037+30\037 by itself means 30\n"
00664 "days). To add an SQLINE which does not expire, use \037+0\037.\n"
00665 "If the mask to be added starts with a \037+\037, an expiry time\n"
00666 "must be given, even if it is the same as the default. The\n"
00667 "current SQLINE default expiry time can be found with the\n"
00668 "\002STATS AKILL\002 command."));
00669 if (!Config->RegexEngine.empty())
00670 {
00671 source.Reply(" ");
00672 source.Reply(_("Regex matches are also supported using the %s engine.\n"
00673 "Enclose your mask in // if this is desired."), Config->RegexEngine.c_str());
00674 }
00675 source.Reply(_(" \n"
00676 "The \002SQLINE DEL\002 command removes the given mask from the\n"
00677 "SQLINE list if it is present. If a list of entry numbers is\n"
00678 "given, those entries are deleted. (See the example for LIST\n"
00679 "below.)\n"
00680 " \n"
00681 "The \002SQLINE LIST\002 command displays the SQLINE list.\n"
00682 "If a wildcard mask is given, only those entries matching the\n"
00683 "mask are displayed. If a list of entry numbers is given,\n"
00684 "only those entries are shown; for example:\n"
00685 " \002SQLINE LIST 2-5,7-9\002\n"
00686 " Lists SQLINE entries numbered 2 through 5 and 7\n"
00687 " through 9.\n"
00688 " \n"
00689 "\002SQLINE VIEW\002 is a more verbose version of \002SQLINE LIST\002, and\n"
00690 "will show who added an SQLINE, the date it was added, and when\n"
00691 "it expires, as well as the mask and reason.\n"
00692 " \n"
00693 "\002SQLINE CLEAR\002 clears all entries of the SQLINE list."));
00694 return true;
00695 }
00696 };
00697
00698 class OSSXLine : public Module
00699 {
00700 CommandOSSNLine commandossnline;
00701 CommandOSSQLine commandossqline;
00702
00703 public:
00704 OSSXLine(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
00705 commandossnline(this), commandossqline(this)
00706 {
00707 this->SetAuthor("Anope");
00708 }
00709 };
00710
00711 MODULE_INIT(OSSXLine)