cs_mode.cpp

Go to the documentation of this file.
00001 /* ChanServ 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 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> &params)
00045         {
00046                 User *u = source.GetUser();
00047                 const Anope::string &subcommand = params[2];
00048                 const Anope::string &param = 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                         /* If setting, remove the existing locks */
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> &params)
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> &params)
00393         {
00394                 const Anope::string &param = 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> &params) 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)