anope- supernets anope source code & configuration |
git clone git://git.acid.vegas/anope.git |
Log | Files | Refs | Archive | README |
os_sxline.cpp (22591B)
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 class SXLineDelCallback : public NumberList 15 { 16 XLineManager *xlm; 17 Command *command; 18 CommandSource &source; 19 unsigned deleted; 20 public: 21 SXLineDelCallback(XLineManager *x, Command *c, CommandSource &_source, const Anope::string &numlist) : NumberList(numlist, true), xlm(x), command(c), source(_source), deleted(0) 22 { 23 } 24 25 ~SXLineDelCallback() 26 { 27 if (!deleted) 28 source.Reply(_("No matching entries on the %s list."), source.command.c_str()); 29 else if (deleted == 1) 30 source.Reply(_("Deleted 1 entry from the %s list."), source.command.c_str()); 31 else 32 source.Reply(_("Deleted %d entries from the %s list."), deleted, source.command.c_str()); 33 } 34 35 void HandleNumber(unsigned number) anope_override 36 { 37 if (!number) 38 return; 39 40 XLine *x = this->xlm->GetEntry(number - 1); 41 42 if (!x) 43 return; 44 45 Log(LOG_ADMIN, source, command) << "to remove " << x->mask << " from the list"; 46 47 ++deleted; 48 DoDel(this->xlm, source, x); 49 } 50 51 static void DoDel(XLineManager *xlm, CommandSource &source, XLine *x) 52 { 53 xlm->DelXLine(x); 54 } 55 }; 56 57 class CommandOSSXLineBase : public Command 58 { 59 private: 60 virtual XLineManager* xlm() = 0; 61 62 virtual void OnAdd(CommandSource &source, const std::vector<Anope::string> ¶ms) = 0; 63 64 void OnDel(CommandSource &source, const std::vector<Anope::string> ¶ms) 65 { 66 67 if (!this->xlm() || this->xlm()->GetList().empty()) 68 { 69 source.Reply(_("%s list is empty."), source.command.c_str()); 70 return; 71 } 72 73 const Anope::string &mask = params.size() > 1 ? params[1] : ""; 74 75 if (mask.empty()) 76 { 77 this->OnSyntaxError(source, "DEL"); 78 return; 79 } 80 81 if (isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos) 82 { 83 SXLineDelCallback list(this->xlm(), this, source, mask); 84 list.Process(); 85 } 86 else 87 { 88 XLine *x = this->xlm()->HasEntry(mask); 89 90 if (!x) 91 { 92 source.Reply(_("\002%s\002 not found on the %s list."), mask.c_str(), source.command.c_str()); 93 return; 94 } 95 96 FOREACH_MOD(OnDelXLine, (source, x, this->xlm())); 97 98 SXLineDelCallback::DoDel(this->xlm(), source, x); 99 source.Reply(_("\002%s\002 deleted from the %s list."), mask.c_str(), source.command.c_str()); 100 Log(LOG_ADMIN, source, this) << "to remove " << mask << " from the list"; 101 } 102 103 if (Anope::ReadOnly) 104 source.Reply(READ_ONLY_MODE); 105 106 return; 107 } 108 109 void ProcessList(CommandSource &source, const std::vector<Anope::string> ¶ms, ListFormatter &list) 110 { 111 if (!this->xlm() || this->xlm()->GetList().empty()) 112 { 113 source.Reply(_("%s list is empty."), source.command.c_str()); 114 return; 115 } 116 117 const Anope::string &mask = params.size() > 1 ? params[1] : ""; 118 119 if (!mask.empty() && isdigit(mask[0]) && mask.find_first_not_of("1234567890,-") == Anope::string::npos) 120 { 121 class SXLineListCallback : public NumberList 122 { 123 XLineManager *xlm; 124 CommandSource &source; 125 ListFormatter &list; 126 public: 127 SXLineListCallback(XLineManager *x, CommandSource &_source, ListFormatter &_list, const Anope::string &numlist) : NumberList(numlist, false), xlm(x), source(_source), list(_list) 128 { 129 } 130 131 void HandleNumber(unsigned number) anope_override 132 { 133 if (!number) 134 return; 135 136 const XLine *x = this->xlm->GetEntry(number - 1); 137 138 if (!x) 139 return; 140 141 ListFormatter::ListEntry entry; 142 entry["Number"] = stringify(number); 143 entry["Mask"] = x->mask; 144 entry["By"] = x->by; 145 entry["Created"] = Anope::strftime(x->created, NULL, true); 146 entry["Expires"] = Anope::Expires(x->expires, source.nc); 147 entry["ID"] = x->id; 148 entry["Reason"] = x->reason; 149 list.AddEntry(entry); 150 } 151 } 152 sl_list(this->xlm(), source, list, mask); 153 sl_list.Process(); 154 } 155 else 156 { 157 for (unsigned i = 0, end = this->xlm()->GetCount(); i < end; ++i) 158 { 159 const XLine *x = this->xlm()->GetEntry(i); 160 161 if (mask.empty() || mask.equals_ci(x->mask) || mask == x->id || Anope::Match(x->mask, mask, false, true)) 162 { 163 ListFormatter::ListEntry entry; 164 entry["Number"] = stringify(i + 1); 165 entry["Mask"] = x->mask; 166 entry["By"] = x->by; 167 entry["Created"] = Anope::strftime(x->created, NULL, true); 168 entry["Expires"] = Anope::Expires(x->expires, source.nc); 169 entry["ID"] = x->id; 170 entry["Reason"] = x->reason; 171 list.AddEntry(entry); 172 } 173 } 174 } 175 176 if (list.IsEmpty()) 177 source.Reply(_("No matching entries on the %s list."), source.command.c_str()); 178 else 179 { 180 source.Reply(_("Current %s list:"), source.command.c_str()); 181 182 std::vector<Anope::string> replies; 183 list.Process(replies); 184 185 for (unsigned i = 0; i < replies.size(); ++i) 186 source.Reply(replies[i]); 187 } 188 } 189 190 void OnList(CommandSource &source, const std::vector<Anope::string> ¶ms) 191 { 192 ListFormatter list(source.GetAccount()); 193 list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("Reason")); 194 195 this->ProcessList(source, params, list); 196 } 197 198 void OnView(CommandSource &source, const std::vector<Anope::string> ¶ms) 199 { 200 ListFormatter list(source.GetAccount()); 201 list.AddColumn(_("Number")).AddColumn(_("Mask")).AddColumn(_("By")).AddColumn(_("Created")).AddColumn(_("Expires")); 202 if (Config->GetModule("operserv")->Get<bool>("akillids")) 203 list.AddColumn(_("ID")); 204 list.AddColumn(_("Reason")); 205 206 this->ProcessList(source, params, list); 207 } 208 209 void OnClear(CommandSource &source) 210 { 211 FOREACH_MOD(OnDelXLine, (source, NULL, this->xlm())); 212 213 for (unsigned i = this->xlm()->GetCount(); i > 0; --i) 214 { 215 XLine *x = this->xlm()->GetEntry(i - 1); 216 this->xlm()->DelXLine(x); 217 } 218 219 Log(LOG_ADMIN, source, this) << "to CLEAR the list"; 220 source.Reply(_("The %s list has been cleared."), source.command.c_str()); 221 if (Anope::ReadOnly) 222 source.Reply(READ_ONLY_MODE); 223 224 return; 225 } 226 public: 227 CommandOSSXLineBase(Module *creator, const Anope::string &cmd) : Command(creator, cmd, 1, 4) 228 { 229 } 230 231 const Anope::string GetDesc(CommandSource &source) const anope_override 232 { 233 return Anope::printf(Language::Translate(source.GetAccount(), _("Manipulate the %s list")), source.command.upper().c_str()); 234 } 235 236 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 237 { 238 const Anope::string &cmd = params[0]; 239 240 if (cmd.equals_ci("ADD")) 241 return this->OnAdd(source, params); 242 else if (cmd.equals_ci("DEL")) 243 return this->OnDel(source, params); 244 else if (cmd.equals_ci("LIST")) 245 return this->OnList(source, params); 246 else if (cmd.equals_ci("VIEW")) 247 return this->OnView(source, params); 248 else if (cmd.equals_ci("CLEAR")) 249 return this->OnClear(source); 250 else 251 this->OnSyntaxError(source, ""); 252 253 return; 254 } 255 256 virtual bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override = 0; 257 }; 258 259 class CommandOSSNLine : public CommandOSSXLineBase 260 { 261 XLineManager *xlm() anope_override 262 { 263 return this->snlines; 264 } 265 266 void OnAdd(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 267 { 268 if (!this->xlm()) 269 return; 270 271 unsigned last_param = 2; 272 Anope::string param, expiry; 273 274 param = params.size() > 1 ? params[1] : ""; 275 if (!param.empty() && param[0] == '+') 276 { 277 expiry = param; 278 param = params.size() > 2 ? params[2] : ""; 279 last_param = 3; 280 } 281 282 time_t expires = !expiry.empty() ? Anope::DoTime(expiry) : Config->GetModule("operserv")->Get<time_t>("snlineexpiry", "30d"); 283 /* If the expiry given does not contain a final letter, it's in days, 284 * said the doc. Ah well. 285 */ 286 if (!expiry.empty() && isdigit(expiry[expiry.length() - 1])) 287 expires *= 86400; 288 /* Do not allow less than a minute expiry time */ 289 if (expires && expires < 60) 290 { 291 source.Reply(BAD_EXPIRY_TIME); 292 return; 293 } 294 else if (expires > 0) 295 expires += Anope::CurTime; 296 297 if (param.empty()) 298 { 299 this->OnSyntaxError(source, "ADD"); 300 return; 301 } 302 303 Anope::string rest = param; 304 if (params.size() > last_param) 305 rest += " " + params[last_param]; 306 307 if (rest.find(':') == Anope::string::npos) 308 { 309 this->OnSyntaxError(source, "ADD"); 310 return; 311 } 312 313 sepstream sep(rest, ':'); 314 Anope::string mask; 315 sep.GetToken(mask); 316 Anope::string reason = sep.GetRemaining(); 317 318 if (mask.empty() || reason.empty()) 319 { 320 this->OnSyntaxError(source, "ADD"); 321 return; 322 } 323 324 if (mask[0] == '/' && mask[mask.length() - 1] == '/') 325 { 326 const Anope::string ®exengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine"); 327 328 if (regexengine.empty()) 329 { 330 source.Reply(_("Regex is disabled.")); 331 return; 332 } 333 334 ServiceReference<RegexProvider> provider("Regex", regexengine); 335 if (!provider) 336 { 337 source.Reply(_("Unable to find regex engine %s."), regexengine.c_str()); 338 return; 339 } 340 341 try 342 { 343 Anope::string stripped_mask = mask.substr(1, mask.length() - 2); 344 delete provider->Compile(stripped_mask); 345 } 346 catch (const RegexException &ex) 347 { 348 source.Reply("%s", ex.GetReason().c_str()); 349 return; 350 } 351 } 352 353 /* Clean up the last character of the mask if it is a space 354 * See bug #761 355 */ 356 unsigned masklen = mask.length(); 357 if (mask[masklen - 1] == ' ') 358 mask.erase(masklen - 1); 359 360 if (Config->GetModule("operserv")->Get<bool>("addakiller", "yes") && !source.GetNick().empty()) 361 reason = "[" + source.GetNick() + "] " + reason; 362 363 if (mask.find_first_not_of("/.*?") == Anope::string::npos) 364 { 365 source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); 366 return; 367 } 368 369 XLine *x = new XLine(mask, source.GetNick(), expires, reason); 370 if (Config->GetModule("operserv")->Get<bool>("akillids")) 371 x->id = XLineManager::GenerateUID(); 372 373 unsigned int affected = 0; 374 for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) 375 if (this->xlm()->Check(it->second, x)) 376 ++affected; 377 float percent = static_cast<float>(affected) / static_cast<float>(UserListByNick.size()) * 100.0; 378 379 if (percent > 95) 380 { 381 source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); 382 Log(LOG_ADMIN, source, this) << "tried to " << source.command << " " << percent << "% of the network (" << affected << " users)"; 383 delete x; 384 return; 385 } 386 387 if (!this->xlm()->CanAdd(source, mask, expires, reason)) 388 return; 389 390 EventReturn MOD_RESULT; 391 FOREACH_RESULT(OnAddXLine, MOD_RESULT, (source, x, this->xlm())); 392 if (MOD_RESULT == EVENT_STOP) 393 { 394 delete x; 395 return; 396 } 397 398 this->xlm()->AddXLine(x); 399 400 if (Config->GetModule("operserv")->Get<bool>("killonsnline", "yes")) 401 { 402 Anope::string rreason = "G-Lined: " + reason; 403 404 for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) 405 { 406 User *user = it->second; 407 408 if (!user->HasMode("OPER") && user->server != Me && this->xlm()->Check(user, x)) 409 user->Kill(Me, rreason); 410 } 411 412 this->xlm()->Send(NULL, x); 413 } 414 415 source.Reply(_("\002%s\002 added to the %s list."), mask.c_str(), source.command.c_str()); 416 Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]"; 417 if (Anope::ReadOnly) 418 source.Reply(READ_ONLY_MODE); 419 } 420 421 ServiceReference<XLineManager> snlines; 422 public: 423 CommandOSSNLine(Module *creator) : CommandOSSXLineBase(creator, "operserv/snline"), snlines("XLineManager", "xlinemanager/snline") 424 { 425 this->SetSyntax(_("ADD [+\037expiry\037] \037mask\037:\037reason\037")); 426 this->SetSyntax(_("DEL {\037mask\037 | \037entry-num\037 | \037list\037 | \037id\037}")); 427 this->SetSyntax(_("LIST [\037mask\037 | \037list\037 | \037id\037]")); 428 this->SetSyntax(_("VIEW [\037mask\037 | \037list\037 | \037id\037]")); 429 this->SetSyntax("CLEAR"); 430 } 431 432 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override 433 { 434 this->SendSyntax(source); 435 source.Reply(" "); 436 source.Reply(_("Allows Services Operators to manipulate the SNLINE list. If\n" 437 "a user with a realname matching an SNLINE mask attempts to\n" 438 "connect, Services will not allow it to pursue his IRC\n" 439 "session.")); 440 source.Reply(_(" \n" 441 "\002SNLINE ADD\002 adds the given realname mask to the SNLINE\n" 442 "list for the given reason (which \002must\002 be given).\n" 443 "\037expiry\037 is specified as an integer followed by one of \037d\037\n" 444 "(days), \037h\037 (hours), or \037m\037 (minutes). Combinations (such as\n" 445 "\0371h30m\037) are not permitted. If a unit specifier is not\n" 446 "included, the default is days (so \037+30\037 by itself means 30\n" 447 "days). To add an SNLINE which does not expire, use \037+0\037. If the\n" 448 "realname mask to be added starts with a \037+\037, an expiry time must\n" 449 "be given, even if it is the same as the default. The\n" 450 "current SNLINE default expiry time can be found with the\n" 451 "\002STATS AKILL\002 command.\n" 452 " \n" 453 "\002Note\002: because the realname mask may contain spaces, the\n" 454 "separator between it and the reason is a colon.")); 455 const Anope::string ®exengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine"); 456 if (!regexengine.empty()) 457 { 458 source.Reply(" "); 459 source.Reply(_("Regex matches are also supported using the %s engine.\n" 460 "Enclose your mask in // if this is desired."), regexengine.c_str()); 461 } 462 source.Reply(_(" \n" 463 "The \002SNLINE DEL\002 command removes the given mask from the\n" 464 "SNLINE list if it is present. If a list of entry numbers is\n" 465 "given, those entries are deleted. (See the example for LIST\n" 466 "below.)\n" 467 " \n" 468 "The \002SNLINE LIST\002 command displays the SNLINE list.\n" 469 "If a wildcard mask is given, only those entries matching the\n" 470 "mask are displayed. If a list of entry numbers is given,\n" 471 "only those entries are shown; for example:\n" 472 " \002SNLINE LIST 2-5,7-9\002\n" 473 " Lists SNLINE entries numbered 2 through 5 and 7\n" 474 " through 9.\n" 475 " \n" 476 "\002SNLINE VIEW\002 is a more verbose version of \002SNLINE LIST\002, and\n" 477 "will show who added an SNLINE, the date it was added, and when\n" 478 "it expires, as well as the realname mask and reason.\n" 479 " \n" 480 "\002SNLINE CLEAR\002 clears all entries of the SNLINE list.")); 481 return true; 482 } 483 }; 484 485 class CommandOSSQLine : public CommandOSSXLineBase 486 { 487 XLineManager *xlm() anope_override 488 { 489 return this->sqlines; 490 } 491 492 void OnAdd(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override 493 { 494 if (!this->xlm()) 495 return; 496 497 unsigned last_param = 2; 498 Anope::string expiry, mask; 499 500 mask = params.size() > 1 ? params[1] : ""; 501 if (!mask.empty() && mask[0] == '+') 502 { 503 expiry = mask; 504 mask = params.size() > 2 ? params[2] : ""; 505 last_param = 3; 506 } 507 508 time_t expires = !expiry.empty() ? Anope::DoTime(expiry) : Config->GetModule("operserv")->Get<time_t>("sqlineexpiry", "30d"); 509 /* If the expiry given does not contain a final letter, it's in days, 510 * said the doc. Ah well. 511 */ 512 if (!expiry.empty() && isdigit(expiry[expiry.length() - 1])) 513 expires *= 86400; 514 /* Do not allow less than a minute expiry time */ 515 if (expires && expires < 60) 516 { 517 source.Reply(BAD_EXPIRY_TIME); 518 return; 519 } 520 else if (expires > 0) 521 expires += Anope::CurTime; 522 523 if (params.size() <= last_param) 524 { 525 this->OnSyntaxError(source, "ADD"); 526 return; 527 } 528 529 Anope::string reason = params[last_param]; 530 if (last_param == 2 && params.size() > 3) 531 reason += " " + params[3]; 532 533 if (mask.empty() || reason.empty()) 534 { 535 this->OnSyntaxError(source, "ADD"); 536 return; 537 } 538 539 if (mask[0] == '/' && mask[mask.length() - 1] == '/') 540 { 541 const Anope::string ®exengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine"); 542 543 if (regexengine.empty()) 544 { 545 source.Reply(_("Regex is disabled.")); 546 return; 547 } 548 549 ServiceReference<RegexProvider> provider("Regex", regexengine); 550 if (!provider) 551 { 552 source.Reply(_("Unable to find regex engine %s."), regexengine.c_str()); 553 return; 554 } 555 556 try 557 { 558 Anope::string stripped_mask = mask.substr(1, mask.length() - 2); 559 delete provider->Compile(stripped_mask); 560 } 561 catch (const RegexException &ex) 562 { 563 source.Reply("%s", ex.GetReason().c_str()); 564 return; 565 } 566 } 567 568 if (Config->GetModule("operserv")->Get<bool>("addakiller", "yes") && !source.GetNick().empty()) 569 reason = "[" + source.GetNick() + "] " + reason; 570 571 if (mask.find_first_not_of("./?*") == Anope::string::npos) 572 { 573 source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); 574 return; 575 } 576 577 XLine *x = new XLine(mask, source.GetNick(), expires, reason); 578 if (Config->GetModule("operserv")->Get<bool>("akillids")) 579 x->id = XLineManager::GenerateUID(); 580 581 unsigned int affected = 0; 582 for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) 583 if (this->xlm()->Check(it->second, x)) 584 ++affected; 585 float percent = static_cast<float>(affected) / static_cast<float>(UserListByNick.size()) * 100.0; 586 587 if (percent > 95) 588 { 589 source.Reply(USERHOST_MASK_TOO_WIDE, mask.c_str()); 590 Log(LOG_ADMIN, source, this) << "tried to SQLine " << percent << "% of the network (" << affected << " users)"; 591 delete x; 592 return; 593 } 594 595 if (!this->sqlines->CanAdd(source, mask, expires, reason)) 596 return; 597 598 EventReturn MOD_RESULT; 599 FOREACH_RESULT(OnAddXLine, MOD_RESULT, (source, x, this->xlm())); 600 if (MOD_RESULT == EVENT_STOP) 601 { 602 delete x; 603 return; 604 } 605 606 this->xlm()->AddXLine(x); 607 608 if (Config->GetModule("operserv")->Get<bool>("killonsqline", "yes")) 609 { 610 Anope::string rreason = "Q-Lined: " + reason; 611 612 if (mask[0] == '#') 613 { 614 for (channel_map::const_iterator cit = ChannelList.begin(), cit_end = ChannelList.end(); cit != cit_end; ++cit) 615 { 616 Channel *c = cit->second; 617 618 if (!Anope::Match(c->name, mask, false, true)) 619 continue; 620 621 std::vector<User *> users; 622 for (Channel::ChanUserList::iterator it = c->users.begin(), it_end = c->users.end(); it != it_end; ++it) 623 { 624 ChanUserContainer *uc = it->second; 625 User *user = uc->user; 626 627 if (!user->HasMode("OPER") && user->server != Me) 628 users.push_back(user); 629 } 630 631 for (unsigned i = 0; i < users.size(); ++i) 632 c->Kick(NULL, users[i], "%s", reason.c_str()); 633 } 634 } 635 else 636 { 637 for (user_map::const_iterator it = UserListByNick.begin(); it != UserListByNick.end(); ++it) 638 { 639 User *user = it->second; 640 641 if (!user->HasMode("OPER") && user->server != Me && this->xlm()->Check(user, x)) 642 user->Kill(Me, rreason); 643 } 644 } 645 646 this->xlm()->Send(NULL, x); 647 } 648 649 source.Reply(_("\002%s\002 added to the %s list."), mask.c_str(), source.command.c_str()); 650 Log(LOG_ADMIN, source, this) << "on " << mask << " (" << reason << "), expires in " << (expires ? Anope::Duration(expires - Anope::CurTime) : "never") << " [affects " << affected << " user(s) (" << percent << "%)]"; 651 if (Anope::ReadOnly) 652 source.Reply(READ_ONLY_MODE); 653 } 654 655 ServiceReference<XLineManager> sqlines; 656 public: 657 CommandOSSQLine(Module *creator) : CommandOSSXLineBase(creator, "operserv/sqline"), sqlines("XLineManager", "xlinemanager/sqline") 658 { 659 this->SetSyntax(_("ADD [+\037expiry\037] \037mask\037 \037reason\037")); 660 this->SetSyntax(_("DEL {\037mask\037 | \037entry-num\037 | \037list\037 | \037id\037}")); 661 this->SetSyntax(_("LIST [\037mask\037 | \037list\037 | \037id\037]")); 662 this->SetSyntax(_("VIEW [\037mask\037 | \037list\037 | \037id\037]")); 663 this->SetSyntax("CLEAR"); 664 } 665 666 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override 667 { 668 this->SendSyntax(source); 669 source.Reply(" "); 670 source.Reply(_("Allows Services Operators to manipulate the SQLINE list. If\n" 671 "a user with a nick matching an SQLINE mask attempts to\n" 672 "connect, Services will not allow it to pursue his IRC\n" 673 "session.\n" 674 "If the first character of the mask is #, services will\n" 675 "prevent the use of matching channels. If the mask is a\n" 676 "regular expression, the expression will be matched against\n" 677 "channels too.")); 678 source.Reply(_(" \n" 679 "\002SQLINE ADD\002 adds the given (nick's) mask to the SQLINE\n" 680 "list for the given reason (which \002must\002 be given).\n" 681 "\037expiry\037 is specified as an integer followed by one of \037d\037\n" 682 "(days), \037h\037 (hours), or \037m\037 (minutes). Combinations (such as\n" 683 "\0371h30m\037) are not permitted. If a unit specifier is not\n" 684 "included, the default is days (so \037+30\037 by itself means 30\n" 685 "days). To add an SQLINE which does not expire, use \037+0\037.\n" 686 "If the mask to be added starts with a \037+\037, an expiry time\n" 687 "must be given, even if it is the same as the default. The\n" 688 "current SQLINE default expiry time can be found with the\n" 689 "\002STATS AKILL\002 command.")); 690 const Anope::string ®exengine = Config->GetBlock("options")->Get<const Anope::string>("regexengine"); 691 if (!regexengine.empty()) 692 { 693 source.Reply(" "); 694 source.Reply(_("Regex matches are also supported using the %s engine.\n" 695 "Enclose your mask in // if this is desired."), regexengine.c_str()); 696 } 697 source.Reply(_(" \n" 698 "The \002SQLINE DEL\002 command removes the given mask from the\n" 699 "SQLINE list if it is present. If a list of entry numbers is\n" 700 "given, those entries are deleted. (See the example for LIST\n" 701 "below.)\n" 702 " \n" 703 "The \002SQLINE LIST\002 command displays the SQLINE list.\n" 704 "If a wildcard mask is given, only those entries matching the\n" 705 "mask are displayed. If a list of entry numbers is given,\n" 706 "only those entries are shown; for example:\n" 707 " \002SQLINE LIST 2-5,7-9\002\n" 708 " Lists SQLINE entries numbered 2 through 5 and 7\n" 709 " through 9.\n" 710 " \n" 711 "\002SQLINE VIEW\002 is a more verbose version of \002SQLINE LIST\002, and\n" 712 "will show who added an SQLINE, the date it was added, and when\n" 713 "it expires, as well as the mask and reason.\n" 714 " \n" 715 "\002SQLINE CLEAR\002 clears all entries of the SQLINE list.")); 716 return true; 717 } 718 }; 719 720 class OSSXLine : public Module 721 { 722 CommandOSSNLine commandossnline; 723 CommandOSSQLine commandossqline; 724 725 public: 726 OSSXLine(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, VENDOR), 727 commandossnline(this), commandossqline(this) 728 { 729 } 730 }; 731 732 MODULE_INIT(OSSXLine)