00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014 #include "module.h"
00015
00016 class CommandCSMode : public Command
00017 {
00018 bool CanSet(CommandSource &source, ChannelInfo *ci, ChannelMode *cm, bool self)
00019 {
00020 if (!ci || !cm || cm->type != MODE_STATUS)
00021 return false;
00022
00023 const Anope::string accesses[] = { "VOICE", "HALFOP", "OPDEOP", "PROTECT", "OWNER", "" },
00024 accesses_self[] = { "VOICEME", "HALFOPME", "OPDEOPME", "PROTECTME", "OWNERME", "" };
00025 const Anope::string modes[] = { "VOICE", "HALFOP", "OP", "PROTECT", "OWNER" };
00026 ChannelModeStatus *cms = anope_dynamic_static_cast<ChannelModeStatus *>(cm);
00027 AccessGroup access = source.AccessFor(ci);
00028 short u_level = -1;
00029
00030 for (int i = 0; !accesses[i].empty(); ++i)
00031 if (access.HasPriv(self ? accesses_self[i] : accesses[i]))
00032 {
00033 ChannelMode *cm2 = ModeManager::FindChannelModeByName(modes[i]);
00034 if (cm2 == NULL || cm2->type != MODE_STATUS)
00035 continue;
00036 ChannelModeStatus *cms2 = anope_dynamic_static_cast<ChannelModeStatus *>(cm2);
00037 if (cms2->level > u_level)
00038 u_level = cms2->level;
00039 }
00040
00041 return u_level >= cms->level;
00042 }
00043
00044 void DoLock(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
00045 {
00046 User *u = source.GetUser();
00047 const Anope::string &subcommand = params[2];
00048 const Anope::string ¶m = params.size() > 3 ? params[3] : "";
00049
00050 bool override = !source.AccessFor(ci).HasPriv("MODE");
00051
00052 if ((subcommand.equals_ci("ADD") || subcommand.equals_ci("SET")) && !param.empty())
00053 {
00054
00055 if (subcommand.equals_ci("SET"))
00056 {
00057 const ChannelInfo::ModeList &mlocks = ci->GetMLock();
00058 for (ChannelInfo::ModeList::const_iterator it = mlocks.begin(), it_next; it != mlocks.end(); it = it_next)
00059 {
00060 const ModeLock *ml = it->second;
00061 ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name);
00062 it_next = it;
00063 ++it_next;
00064 if (cm && cm->CanSet(source.GetUser()))
00065 ci->RemoveMLock(cm, ml->set, ml->param);
00066 }
00067 }
00068
00069 spacesepstream sep(param);
00070 Anope::string modes;
00071
00072 sep.GetToken(modes);
00073
00074 Anope::string pos = "+", neg = "-", pos_params, neg_params;
00075
00076 int adding = -1;
00077 for (size_t i = 0; i < modes.length(); ++i)
00078 {
00079 switch (modes[i])
00080 {
00081 case '+':
00082 adding = 1;
00083 break;
00084 case '-':
00085 adding = 0;
00086 break;
00087 default:
00088 if (adding == -1)
00089 break;
00090 ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]);
00091 if (!cm)
00092 {
00093 source.Reply(_("Unknown mode character %c ignored."), modes[i]);
00094 break;
00095 }
00096 else if (u && !cm->CanSet(u))
00097 {
00098 source.Reply(_("You may not (un)lock mode %c."), modes[i]);
00099 break;
00100 }
00101
00102 Anope::string mode_param;
00103 if (((cm->type == MODE_STATUS || cm->type == MODE_LIST) && !sep.GetToken(mode_param)) || (cm->type == MODE_PARAM && adding && !sep.GetToken(mode_param)))
00104 source.Reply(_("Missing parameter for mode %c."), cm->mchar);
00105 else
00106 {
00107 ci->SetMLock(cm, adding, mode_param, source.GetNick());
00108
00109 if (adding)
00110 {
00111 pos += cm->mchar;
00112 if (!mode_param.empty())
00113 pos_params += " " + mode_param;
00114 }
00115 else
00116 {
00117 neg += cm->mchar;
00118 if (!mode_param.empty())
00119 neg_params += " " + mode_param;
00120 }
00121 }
00122 }
00123 }
00124
00125 if (pos == "+")
00126 pos.clear();
00127 if (neg == "-")
00128 neg.clear();
00129 Anope::string reply = pos + neg + pos_params + neg_params;
00130
00131 source.Reply(_("%s locked on %s."), ci->GetMLockAsString(true).c_str(), ci->name.c_str());
00132 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to lock " << ci->GetMLockAsString(true);
00133
00134 if (ci->c)
00135 ci->c->CheckModes();
00136 }
00137 else if (subcommand.equals_ci("DEL") && !param.empty())
00138 {
00139 spacesepstream sep(param);
00140 Anope::string modes;
00141
00142 sep.GetToken(modes);
00143
00144 int adding = -1;
00145 for (size_t i = 0; i < modes.length(); ++i)
00146 {
00147 switch (modes[i])
00148 {
00149 case '+':
00150 adding = 1;
00151 break;
00152 case '-':
00153 adding = 0;
00154 break;
00155 default:
00156 if (adding == -1)
00157 break;
00158 ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]);
00159 if (!cm)
00160 {
00161 source.Reply(_("Unknown mode character %c ignored."), modes[i]);
00162 break;
00163 }
00164 else if (u && !cm->CanSet(u))
00165 {
00166 source.Reply(_("You may not (un)lock mode %c."), modes[i]);
00167 break;
00168 }
00169
00170 Anope::string mode_param;
00171 if (!cm->type == MODE_REGULAR && !sep.GetToken(mode_param))
00172 source.Reply(_("Missing parameter for mode %c."), cm->mchar);
00173 else
00174 {
00175 if (ci->RemoveMLock(cm, adding, mode_param))
00176 {
00177 if (!mode_param.empty())
00178 mode_param = " " + mode_param;
00179 source.Reply(_("%c%c%s has been unlocked from %s."), adding == 1 ? '+' : '-', cm->mchar, mode_param.c_str(), ci->name.c_str());
00180 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to unlock " << (adding ? '+' : '-') << cm->mchar << mode_param;
00181 }
00182 else
00183 source.Reply(_("%c%c is not locked on %s."), adding == 1 ? '+' : '-', cm->mchar, ci->name.c_str());
00184 }
00185 }
00186 }
00187 }
00188 else if (subcommand.equals_ci("LIST"))
00189 {
00190 const ChannelInfo::ModeList &mlocks = ci->GetMLock();
00191 if (mlocks.empty())
00192 {
00193 source.Reply(_("Channel %s has no mode locks."), ci->name.c_str());
00194 }
00195 else
00196 {
00197 ListFormatter list;
00198 list.AddColumn("Mode").AddColumn("Param").AddColumn("Creator").AddColumn("Created");
00199
00200 for (ChannelInfo::ModeList::const_iterator it = mlocks.begin(), it_end = mlocks.end(); it != it_end; ++it)
00201 {
00202 const ModeLock *ml = it->second;
00203 ChannelMode *cm = ModeManager::FindChannelModeByName(ml->name);
00204 if (!cm)
00205 continue;
00206
00207 ListFormatter::ListEntry entry;
00208 entry["Mode"] = Anope::printf("%c%c", ml->set ? '+' : '-', cm->mchar);
00209 entry["Param"] = ml->param;
00210 entry["Creator"] = ml->setter;
00211 entry["Created"] = Anope::strftime(ml->created, source.nc, false);
00212 list.AddEntry(entry);
00213 }
00214
00215 source.Reply(_("Mode locks for %s:"), ci->name.c_str());
00216
00217 std::vector<Anope::string> replies;
00218 list.Process(replies);
00219
00220 for (unsigned i = 0; i < replies.size(); ++i)
00221 source.Reply(replies[i]);
00222 }
00223 }
00224 else
00225 this->OnSyntaxError(source, subcommand);
00226 }
00227
00228 void DoSet(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
00229 {
00230 User *u = source.GetUser();
00231
00232 bool has_access = source.AccessFor(ci).HasPriv("MODE") || source.HasPriv("chanserv/set");
00233
00234 spacesepstream sep(params.size() > 3 ? params[3] : "");
00235 Anope::string modes = params[2], param;
00236
00237 bool override = !source.AccessFor(ci).HasPriv("MODE") && source.HasPriv("chanserv/set");
00238 Log(override ? LOG_OVERRIDE : LOG_COMMAND, source, this, ci) << "to set " << params[2];
00239
00240 int adding = -1;
00241 for (size_t i = 0; i < modes.length(); ++i)
00242 {
00243 switch (modes[i])
00244 {
00245 case '+':
00246 adding = 1;
00247 break;
00248 case '-':
00249 adding = 0;
00250 break;
00251 case '*':
00252 if (adding == -1 || !has_access)
00253 break;
00254 for (unsigned j = 0; j < ModeManager::ChannelModes.size(); ++j)
00255 {
00256 ChannelMode *cm = ModeManager::ChannelModes[j];
00257 if (!u || cm->CanSet(u))
00258 {
00259 if (cm->type == MODE_REGULAR || (!adding && cm->type == MODE_PARAM))
00260 {
00261 if (adding)
00262 ci->c->SetMode(NULL, cm);
00263 else
00264 ci->c->RemoveMode(NULL, cm);
00265 }
00266 }
00267 }
00268 break;
00269 default:
00270 if (adding == -1)
00271 break;
00272 ChannelMode *cm = ModeManager::FindChannelModeByChar(modes[i]);
00273 if (!cm || (u && !cm->CanSet(u)))
00274 continue;
00275 switch (cm->type)
00276 {
00277 case MODE_REGULAR:
00278 if (!has_access)
00279 break;
00280 if (adding)
00281 ci->c->SetMode(NULL, cm);
00282 else
00283 ci->c->RemoveMode(NULL, cm);
00284 break;
00285 case MODE_PARAM:
00286 if (!has_access)
00287 break;
00288 if (adding && !sep.GetToken(param))
00289 break;
00290 if (adding)
00291 ci->c->SetMode(NULL, cm, param);
00292 else
00293 ci->c->RemoveMode(NULL, cm);
00294 break;
00295 case MODE_STATUS:
00296 {
00297 if (!sep.GetToken(param))
00298 param = source.GetNick();
00299
00300 AccessGroup u_access = source.AccessFor(ci);
00301
00302 if (param.find_first_of("*?") != Anope::string::npos)
00303 {
00304 if (!this->CanSet(source, ci, cm, false))
00305 {
00306 source.Reply(_("You do not have access to set mode %c."), cm->mchar);
00307 break;
00308 }
00309
00310 for (Channel::ChanUserList::const_iterator it = ci->c->users.begin(), it_end = ci->c->users.end(); it != it_end; ++it)
00311 {
00312 ChanUserContainer *uc = *it;
00313
00314 AccessGroup targ_access = ci->AccessFor(uc->user);
00315
00316 if (uc->user->IsProtected() || targ_access > u_access)
00317 {
00318 source.Reply(_("You do not have the access to change %s's modes."), uc->user->nick.c_str());
00319 continue;
00320 }
00321
00322 if (Anope::Match(uc->user->GetMask(), param))
00323 {
00324 if (adding)
00325 ci->c->SetMode(NULL, cm, uc->user->GetUID());
00326 else
00327 ci->c->RemoveMode(NULL, cm, uc->user->GetUID());
00328 }
00329 }
00330 }
00331 else
00332 {
00333 User *target = User::Find(param, true);
00334 if (target == NULL)
00335 {
00336 source.Reply(NICK_X_NOT_IN_USE, param.c_str());
00337 break;
00338 }
00339
00340 if (!this->CanSet(source, ci, cm, source.GetUser() == target))
00341 {
00342 source.Reply(_("You do not have access to set mode %c."), cm->mchar);
00343 break;
00344 }
00345
00346 if (source.GetUser() != target)
00347 {
00348 AccessGroup targ_access = ci->AccessFor(target);
00349 if (targ_access > u_access)
00350 {
00351 source.Reply(_("You do not have the access to change %s's modes."), target->nick.c_str());
00352 break;
00353 }
00354 else if (target->IsProtected())
00355 {
00356 source.Reply(ACCESS_DENIED);
00357 break;
00358 }
00359 }
00360
00361 if (adding)
00362 ci->c->SetMode(NULL, cm, target->GetUID());
00363 else
00364 ci->c->RemoveMode(NULL, cm, target->GetUID());
00365 }
00366 break;
00367 }
00368 case MODE_LIST:
00369 if (!has_access)
00370 break;
00371 if (!sep.GetToken(param))
00372 break;
00373 if (adding)
00374 ci->c->SetMode(NULL, cm, param);
00375 else
00376 {
00377 std::pair<Channel::ModeList::iterator, Channel::ModeList::iterator> its = ci->c->GetModeList(cm->name);
00378 for (; its.first != its.second;)
00379 {
00380 const Anope::string &mask = its.first->second;
00381 ++its.first;
00382
00383 if (Anope::Match(mask, param))
00384 ci->c->RemoveMode(NULL, cm, mask);
00385 }
00386 }
00387 }
00388 }
00389 }
00390 }
00391
00392 void DoClear(CommandSource &source, ChannelInfo *ci, const std::vector<Anope::string> ¶ms)
00393 {
00394 const Anope::string ¶m = params.size() > 2 ? params[2] : "";
00395
00396 if (param.empty())
00397 {
00398 std::vector<Anope::string> new_params;
00399 new_params.push_back(params[0]);
00400 new_params.push_back("SET");
00401 new_params.push_back("-*");
00402 this->DoSet(source, ci, new_params);
00403 }
00404 else if (param.equals_ci("BANS") || param.equals_ci("EXCEPTS") || param.equals_ci("INVITEOVERRIDES") || param.equals_ci("VOICES") || param.equals_ci("HALFOPS") || param.equals_ci("OPS"))
00405 {
00406 const Anope::string &mname = param.upper().substr(0, param.length() - 1);
00407 ChannelMode *cm = ModeManager::FindChannelModeByName(mname);
00408 if (cm == NULL)
00409 {
00410 source.Reply(_("Your IRCD does not support %s."), mname.upper().c_str());
00411 return;
00412 }
00413
00414 std::vector<Anope::string> new_params;
00415 new_params.push_back(params[0]);
00416 new_params.push_back("SET");
00417 new_params.push_back("-" + stringify(cm->mchar));
00418 new_params.push_back("*");
00419 this->DoSet(source, ci, new_params);
00420 }
00421 else
00422 this->SendSyntax(source);
00423 }
00424
00425 public:
00426 CommandCSMode(Module *creator) : Command(creator, "chanserv/mode", 2, 4)
00427 {
00428 this->SetDesc(_("Control modes and mode locks on a channel"));
00429 this->SetSyntax(_("\037channel\037 LOCK {ADD|DEL|SET|LIST} [\037what\037]"));
00430 this->SetSyntax(_("\037channel\037 SET \037modes\037"));
00431 this->SetSyntax(_("\037channel\037 CLEAR [\037what\037]"));
00432 }
00433
00434 void Execute(CommandSource &source, const std::vector<Anope::string> ¶ms) anope_override
00435 {
00436 const Anope::string &subcommand = params[1];
00437
00438 ChannelInfo *ci = ChannelInfo::Find(params[0]);
00439
00440 if (!ci || !ci->c)
00441 source.Reply(CHAN_X_NOT_IN_USE, params[0].c_str());
00442 else if (subcommand.equals_ci("LOCK") && params.size() > 2)
00443 {
00444 if (!source.AccessFor(ci).HasPriv("MODE") && !source.HasPriv("chanserv/set"))
00445 source.Reply(ACCESS_DENIED);
00446 else
00447 this->DoLock(source, ci, params);
00448 }
00449 else if (subcommand.equals_ci("SET") && params.size() > 2)
00450 this->DoSet(source, ci, params);
00451 else if (subcommand.equals_ci("CLEAR"))
00452 {
00453 if (!source.AccessFor(ci).HasPriv("MODE") && !source.HasPriv("chanserv/set"))
00454 source.Reply(ACCESS_DENIED);
00455 else
00456 this->DoClear(source, ci, params);
00457 }
00458 else
00459 this->OnSyntaxError(source, "");
00460 }
00461
00462 bool OnHelp(CommandSource &source, const Anope::string &subcommand) anope_override
00463 {
00464 this->SendSyntax(source);
00465 source.Reply(" ");
00466 source.Reply(_("Mainly controls mode locks and mode access (which is different from channel access)\n"
00467 "on a channel.\n"
00468 " \n"
00469 "The \002%s LOCK\002 command allows you to add, delete, and view mode locks on a channel.\n"
00470 "If a mode is locked on or off, services will not allow that mode to be changed. The \2SET\2\n"
00471 "command will clear all existing mode locks and set the new one given, while \2ADD\2 and \2DEL\2\n"
00472 "modify the existing mode lock.\n"
00473 "Example:\n"
00474 " \002MODE #channel LOCK ADD +bmnt *!*@*aol*\002\n"
00475 " \n"
00476 "The \002%s SET\002 command allows you to set modes through services. Wildcards * and ? may\n"
00477 "be given as parameters for list and status modes.\n"
00478 "Example:\n"
00479 " \002MODE #channel SET +v *\002\n"
00480 " Sets voice status to all users in the channel.\n"
00481 " \n"
00482 " \002MODE #channel SET -b ~c:*\n"
00483 " Clears all extended bans that start with ~c:\n"
00484 " \n"
00485 "The \002%s CLEAR\002 command is an easy way to clear modes on a channel. \037what\037 may be\n"
00486 "one of bans, exempts, inviteoverrides, ops, halfops, or voices. If \037what\037 is not given then all\n"
00487 "basic modes are removed."),
00488 source.command.upper().c_str(), source.command.upper().c_str(), source.command.upper().c_str());
00489 return true;
00490 }
00491 };
00492
00493 class CSMode : public Module
00494 {
00495 CommandCSMode commandcsmode;
00496
00497 public:
00498 CSMode(const Anope::string &modname, const Anope::string &creator) : Module(modname, creator, CORE),
00499 commandcsmode(this)
00500 {
00501 this->SetAuthor("Anope");
00502
00503 }
00504 };
00505
00506 MODULE_INIT(CSMode)