00001
00002
00003
00004
00005
00006
00007
00008
00009
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
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> ¶ms) 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)