bs_badwords.cpp

Go to the documentation of this file.
00001 /* BotServ core functions
00002  *
00003  * (C) 2003-2013 Anope Team
00004  * Contact us at team@anope.org
00005  *
00006  * Please read COPYING and README for further details.
00007  *
00008  * Based on the original code of Epona by Lara.
00009  * Based on the original code of Services by Andy Church.
00010  */
00011 
00012 /*************************************************************************/
00013 
00014 #include "module.h"
00015 
00016 
00017 class BadwordsDelCallback : public NumberList
00018 {
00019         CommandSource &source;
00020         ChannelInfo *ci;
00021         Command *c;
00022         unsigned deleted;
00023         bool override;
00024  public:
00025         BadwordsDelCallback(CommandSource &_source, ChannelInfo *_ci, Command *_c, const Anope::string &list) : NumberList(list, true), source(_source), ci(_ci), c(_c), deleted(0), override(false)
00026         {
00027                 if (!source.AccessFor(ci).HasPriv("BADWORDS") && source.HasPriv("botserv/administration"))
00028                         this->override = true;
00029         }
00030 
00031         ~BadwordsDelCallback()
00032         {
00033                 if (!deleted)
00034                         source.Reply(_("No matching entries on %s bad words list."), ci->name.c_str());
00035                 else if (deleted == 1)
00036                         source.Reply(_("Deleted 1 entry from %s bad words list."), ci->name.c_str());
00037                 else
00038                         source.Reply(_("Deleted %d entries from %s bad words list."), deleted, ci->name.c_str());
00039         }
00040 
00041         void HandleNumber(unsigned Number) anope_override
00042         {
00043                 if (!Number || Number > ci->GetBadWordCount())
00044                         return;
00045 
00046                 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, c, ci) << "DEL " << ci->GetBadWord(Number - 1)->word;
00047                 ++deleted;
00048                 ci->EraseBadWord(Number - 1);
00049         }
00050 };
00051 
00052 class CommandBSBadwords : public Command
00053 {
00054  private:
00055         void DoList(CommandSource &source, ChannelInfo *ci, const Anope::string &word)
00056         {
00057                 bool override = !source.AccessFor(ci).HasPriv("BADWORDS");
00058                 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "LIST";
00059                 ListFormatter list;
00060 
00061                 list.AddColumn("Number").AddColumn("Word").AddColumn("Type");
00062 
00063                 if (!ci->GetBadWordCount())
00064                 {
00065                         source.Reply(_("%s bad words list is empty."), ci->name.c_str());
00066                         return;
00067                 }
00068                 else if (!word.empty() && word.find_first_not_of("1234567890,-") == Anope::string::npos)
00069                 {
00070                         class BadwordsListCallback : public NumberList
00071                         {
00072                                 ListFormatter &list;
00073                                 ChannelInfo *ci;
00074                          public:
00075                                 BadwordsListCallback(ListFormatter &_list, ChannelInfo *_ci, const Anope::string &numlist) : NumberList(numlist, false), list(_list), ci(_ci)
00076                                 {
00077                                 }
00078 
00079                                 void HandleNumber(unsigned Number) anope_override
00080                                 {
00081                                         if (!Number || Number > ci->GetBadWordCount())
00082                                                 return;
00083 
00084                                         const BadWord *bw = ci->GetBadWord(Number - 1);
00085                                         ListFormatter::ListEntry entry;
00086                                         entry["Number"] = stringify(Number);
00087                                         entry["Word"] = bw->word;
00088                                         entry["Type"] = bw->type == BW_SINGLE ? "(SINGLE)" : (bw->type == BW_START ? "(START)" : (bw->type == BW_END ? "(END)" : ""));
00089                                         this->list.AddEntry(entry);
00090                                 }
00091                         }
00092                         nl_list(list, ci, word);
00093                         nl_list.Process();
00094                 }
00095                 else
00096                 {
00097                         for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i)
00098                         {
00099                                 const BadWord *bw = ci->GetBadWord(i);
00100 
00101                                 if (!word.empty() && !Anope::Match(bw->word, word))
00102                                         continue;
00103 
00104                                 ListFormatter::ListEntry entry;
00105                                 entry["Number"] = stringify(i + 1);
00106                                 entry["Word"] = bw->word;
00107                                 entry["Type"] = bw->type == BW_SINGLE ? "(SINGLE)" : (bw->type == BW_START ? "(START)" : (bw->type == BW_END ? "(END)" : ""));
00108                                 list.AddEntry(entry);
00109                         }
00110                 }
00111 
00112                 if (list.IsEmpty())
00113                         source.Reply(_("No matching entries on %s bad words list."), ci->name.c_str());
00114                 else
00115                 {
00116                         std::vector<Anope::string> replies;
00117                         list.Process(replies);
00118 
00119                         source.Reply(_("Bad words list for %s:"), ci->name.c_str());
00120 
00121                         for (unsigned i = 0; i < replies.size(); ++i)
00122                                 source.Reply(replies[i]);
00123 
00124                         source.Reply(_("End of bad words list."));
00125                 }
00126         }
00127 
00128         void DoAdd(CommandSource &source, ChannelInfo *ci, const Anope::string &word)
00129         {
00130                 size_t pos = word.rfind(' ');
00131                 BadWordType bwtype = BW_ANY;
00132                 Anope::string realword = word;
00133 
00134                 if (pos != Anope::string::npos)
00135                 {
00136                         Anope::string opt = word.substr(pos + 1);
00137                         if (!opt.empty())
00138                         {
00139                                 if (opt.equals_ci("SINGLE"))
00140                                         bwtype = BW_SINGLE;
00141                                 else if (opt.equals_ci("START"))
00142                                         bwtype = BW_START;
00143                                 else if (opt.equals_ci("END"))
00144                                         bwtype = BW_END;
00145                         }
00146                         realword = word.substr(0, pos);
00147                 }
00148 
00149                 if (ci->GetBadWordCount() >= Config->BSBadWordsMax)
00150                 {
00151                         source.Reply(_("Sorry, you can only have %d bad words entries on a channel."), Config->BSBadWordsMax);
00152                         return;
00153                 }
00154 
00155                 for (unsigned i = 0, end = ci->GetBadWordCount(); i < end; ++i)
00156                 {
00157                         const BadWord *bw = ci->GetBadWord(i);
00158 
00159                         if (!bw->word.empty() && ((Config->BSCaseSensitive && realword.equals_cs(bw->word)) || (!Config->BSCaseSensitive && realword.equals_ci(bw->word))))
00160                         {
00161                                 source.Reply(_("\002%s\002 already exists in %s bad words list."), bw->word.c_str(), ci->name.c_str());
00162                                 return;
00163                         }
00164                 }
00165 
00166                 bool override = !source.AccessFor(ci).HasPriv("BADWORDS");
00167                 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "ADD " << realword;
00168                 ci->AddBadWord(realword, bwtype);
00169 
00170                 source.Reply(_("\002%s\002 added to %s bad words list."), realword.c_str(), ci->name.c_str());
00171 
00172                 return;
00173         }
00174 
00175         void DoDelete(CommandSource &source, ChannelInfo *ci, const Anope::string &word)
00176         {
00177                 /* Special case: is it a number/list?  Only do search if it isn't. */
00178                 if (!word.empty() && isdigit(word[0]) && word.find_first_not_of("1234567890,-") == Anope::string::npos)
00179                 {
00180                         BadwordsDelCallback list(source, ci, this, word);
00181                         list.Process();
00182                 }
00183                 else
00184                 {
00185                         unsigned i, end;
00186                         const BadWord *badword;
00187 
00188                         for (i = 0, end = ci->GetBadWordCount(); i < end; ++i)
00189                         {
00190                                 badword = ci->GetBadWord(i);
00191 
00192                                 if (word.equals_ci(badword->word))
00193                                         break;
00194                         }
00195 
00196                         if (i == end)
00197                         {
00198                                 source.Reply(_("\002%s\002 not found on %s bad words list."), word.c_str(), ci->name.c_str());
00199                                 return;
00200                         }
00201 
00202                         bool override = !source.AccessFor(ci).HasPriv("BADWORDS");
00203                         Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "DEL " << badword->word;
00204 
00205                         source.Reply(_("\002%s\002 deleted from %s bad words list."), badword->word.c_str(), ci->name.c_str());
00206 
00207                         ci->EraseBadWord(i);
00208                 }
00209 
00210                 return;
00211         }
00212 
00213         void DoClear(CommandSource &source, ChannelInfo *ci)
00214         {
00215                 bool override = !source.AccessFor(ci).HasPriv("BADWORDS");
00216                 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "CLEAR";
00217 
00218                 ci->ClearBadWords();
00219                 source.Reply(_("Bad words list is now empty."));
00220                 return;
00221         }
00222  public:
00223         CommandBSBadwords(Module *creator) : Command(creator, "botserv/badwords", 2, 3)
00224         {
00225                 this->SetDesc(_("Maintains the bad words list"));
00226                 this->SetSyntax(_("\037channel\037 ADD \037word\037 [\037SINGLE\037 | \037START\037 | \037END\037]"));
00227                 this->SetSyntax(_("\037channel\037 DEL {\037word\037 | \037entry-num\037 | \037list\037}"));
00228                 this->SetSyntax(_("\037channel\037 LIST [\037mask\037 | \037list\037]"));
00229                 this->SetSyntax(_("\037channel\037 CLEAR"));
00230         }
00231 
00232         void Execute(CommandSource &source, const std::vector<Anope::string> &params) anope_override
00233         {
00234                 const Anope::string &cmd = params[1];
00235                 const Anope::string &word = params.size() > 2 ? params[2] : "";
00236                 bool need_args = cmd.equals_ci("LIST") || cmd.equals_ci("CLEAR");
00237 
00238                 if (!need_args && word.empty())
00239                 {
00240                         this->OnSyntaxError(source, cmd);
00241                         return;
00242                 }
00243 
00244                 ChannelInfo *ci = ChannelInfo::Find(params[0]);
00245                 if (ci == NULL)
00246                 {
00247                         source.Reply(CHAN_X_NOT_REGISTERED, params[0].c_str());
00248                         return;
00249                 }
00250 
00251                 if (!source.AccessFor(ci).HasPriv("BADWORDS") && (!need_args || !source.HasPriv("botserv/administration")))
00252                 {
00253                         source.Reply(ACCESS_DENIED);
00254                         return;
00255                 }
00256 
00257                 if (Anope::ReadOnly)
00258                 {
00259                         source.Reply(_("Sorry, channel bad words list modification is temporarily disabled."));
00260                         return;
00261                 }
00262 
00263                 if (cmd.equals_ci("ADD"))
00264                         return this->DoAdd(source, ci, word);
00265                 else if (cmd.equals_ci("DEL"))
00266                         return this->DoDelete(source, ci, word);
00267                 else if (cmd.equals_ci("LIST"))
00268                         return this->DoList(source, ci, word);
00269                 else if (cmd.equals_ci("CLEAR"))
00270                         return this->DoClear(source, ci);
00271                 else
00272                         this->OnSyntaxError(source, "");
00273         }
00274 
00275         bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
00276         {
00277                 this->SendSyntax(source);
00278                 source.Reply(" ");
00279                 source.Reply(_("Maintains the \002bad words list\002 for a channel. The bad\n"
00280                                 "words list determines which words are to be kicked\n"
00281                                 "when the bad words kicker is enabled. For more information,\n"
00282                                 "type \002%s%s HELP KICK %s\002.\n"
00283                                 " \n"
00284                                 "The \002ADD\002 command adds the given word to the\n"
00285                                 "bad words list. If SINGLE is specified, a kick will be\n"
00286                                 "done only if a user says the entire word. If START is\n"
00287                                 "specified, a kick will be done if a user says a word\n"
00288                                 "that starts with \037word\037. If END is specified, a kick\n"
00289                                 "will be done if a user says a word that ends with\n"
00290                                 "\037word\037. If you don't specify anything, a kick will\n"
00291                                 "be issued every time \037word\037 is said by a user.\n"
00292                                 " \n"), Config->UseStrictPrivMsgString.c_str(), source.service->nick.c_str(), source.command.c_str());
00293                 source.Reply(_("The \002DEL\002 command removes the given word from the\n"
00294                                 "bad words list.  If a list of entry numbers is given, those\n"
00295                                 "entries are deleted.  (See the example for LIST below.)\n"
00296                                 " \n"
00297                                 "The \002LIST\002 command displays the bad words list.  If\n"
00298                                 "a wildcard mask is given, only those entries matching the\n"
00299                                 "mask are displayed.  If a list of entry numbers is given,\n"
00300                                 "only those entries are shown; for example:\n"
00301                                 "   \002#channel LIST 2-5,7-9\002\n"
00302                                 "      Lists bad words entries numbered 2 through 5 and\n"
00303                                 "      7 through 9.\n"
00304                                 " \n"
00305                                 "The \002CLEAR\002 command clears all entries of the\n"
00306                                 "bad words list."));
00307                 return true;
00308         }
00309 };
00310 
00311 class BSBadwords : public Module
00312 {
00313         CommandBSBadwords commandbsbadwords;
00314 
00315  public:
00316         BSBadwords(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
00317                 commandbsbadwords(this)
00318         {
00319                 this->SetAuthor("Anope");
00320 
00321         }
00322 };
00323 
00324 MODULE_INIT(BSBadwords)