The InspIRCd Project
Home | Developers | Wiki | Forums | Bug Tracker | SVN | Download
Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

m_chanprotect.cpp

Go to the documentation of this file.
00001 /*       +------------------------------------+
00002  *       | Inspire Internet Relay Chat Daemon |
00003  *       +------------------------------------+
00004  *
00005  *  InspIRCd: (C) 2002-2008 InspIRCd Development Team
00006  * See: http://www.inspircd.org/wiki/index.php/Credits
00007  *
00008  * This program is free but copyrighted software; see
00009  *            the file COPYING for details.
00010  *
00011  * ---------------------------------------------------
00012  */
00013 
00014 #include "inspircd.h"
00015 
00016 /* $ModDesc: Provides channel modes +a and +q */
00017 
00018 #define PROTECT_VALUE 40000
00019 #define FOUNDER_VALUE 50000
00020 
00023 class FounderProtectBase
00024 {
00025  private:
00026         InspIRCd* MyInstance;
00027         std::string extend;
00028         std::string type;
00029         int list;
00030         int end;
00031  protected:
00032         bool& remove_own_privs;
00033         bool& remove_other_privs;
00034  public:
00035         FounderProtectBase(InspIRCd* Instance, const std::string &ext, const std::string &mtype, int l, int e, bool &remove_own, bool &remove_others) :
00036                 MyInstance(Instance), extend(ext), type(mtype), list(l), end(e), remove_own_privs(remove_own), remove_other_privs(remove_others)
00037         {
00038         }
00039 
00040         ModePair ModeSet(User* source, User* dest, Channel* channel, const std::string &parameter)
00041         {
00042                 User* x = MyInstance->FindNick(parameter);
00043                 if (x)
00044                 {
00045                         if (!channel->HasUser(x))
00046                         {
00047                                 return std::make_pair(false, parameter);
00048                         }
00049                         else
00050                         {
00051                                 std::string item = extend+std::string(channel->name);
00052                                 if (x->GetExt(item))
00053                                 {
00054                                         return std::make_pair(true, x->nick);
00055                                 }
00056                                 else
00057                                 {
00058                                         return std::make_pair(false, parameter);
00059                                 }
00060                         }
00061                 }
00062                 return std::make_pair(false, parameter);
00063         }
00064 
00065         void RemoveMode(Channel* channel, char mc, irc::modestacker* stack)
00066         {
00067                 CUList* cl = channel->GetUsers();
00068                 std::string item = extend + std::string(channel->name);
00069                 std::vector<std::string> mode_junk;
00070                 mode_junk.push_back(channel->name);
00071                 irc::modestacker modestack(MyInstance, false);
00072                 std::deque<std::string> stackresult;
00073 
00074                 for (CUList::iterator i = cl->begin(); i != cl->end(); i++)
00075                 {
00076                         if (i->first->GetExt(item))
00077                         {
00078                                 if (stack)
00079                                         stack->Push(mc, i->first->nick);
00080                                 else
00081                                         modestack.Push(mc, i->first->nick);
00082                         }
00083                 }
00084 
00085                 if (stack)
00086                         return;
00087 
00088                 while (modestack.GetStackedLine(stackresult))
00089                 {
00090                         for (size_t j = 0; j < stackresult.size(); j++)
00091                         {
00092                                 mode_junk.push_back(stackresult[j]);
00093                         }
00094                         MyInstance->SendMode(mode_junk, MyInstance->FakeClient);
00095                 }
00096         }
00097 
00098         void DisplayList(User* user, Channel* channel)
00099         {
00100                 CUList* cl = channel->GetUsers();
00101                 std::string item = extend+std::string(channel->name);
00102                 for (CUList::reverse_iterator i = cl->rbegin(); i != cl->rend(); ++i)
00103                 {
00104                         if (i->first->GetExt(item))
00105                         {
00106                                 user->WriteServ("%d %s %s %s", list, user->nick.c_str(), channel->name.c_str(), i->first->nick.c_str());
00107                         }
00108                 }
00109                 user->WriteServ("%d %s %s :End of channel %s list", end, user->nick.c_str(), channel->name.c_str(), type.c_str());
00110         }
00111 
00112         User* FindAndVerify(std::string &parameter, Channel* channel)
00113         {
00114                 User* theuser = MyInstance->FindNick(parameter);
00115                 if ((!theuser) || (!channel->HasUser(theuser)))
00116                 {
00117                         parameter.clear();
00118                         return NULL;
00119                 }
00120                 return theuser;
00121         }
00122 
00123         bool CanRemoveOthers(User* u1, User* u2, Channel* c)
00124         {
00125                 std::string item = extend+std::string(c->name);
00126                 return (remove_other_privs && u1->GetExt(item) && u2->GetExt(item));
00127         }
00128 
00129         ModeAction HandleChange(User* source, User* theuser, bool adding, Channel* channel, std::string &parameter)
00130         {
00131                 std::string item = extend+std::string(channel->name);
00132 
00133                 if (adding)
00134                 {
00135                         if (!theuser->GetExt(item))
00136                         {
00137                                 theuser->Extend(item);
00138                                 parameter = theuser->nick;
00139                                 return MODEACTION_ALLOW;
00140                         }
00141                 }
00142                 else
00143                 {
00144                         if (theuser->GetExt(item))
00145                         {
00146                                 theuser->Shrink(item);
00147                                 parameter = theuser->nick;
00148                                 return MODEACTION_ALLOW;
00149                         }
00150                 }
00151                 return MODEACTION_DENY;
00152         }
00153 };
00154 
00157 class ChanFounder : public ModeHandler, public FounderProtectBase
00158 {
00159  public:
00160         ChanFounder(InspIRCd* Instance, char my_prefix, bool &depriv_self, bool &depriv_others)
00161                 : ModeHandler(Instance, 'q', 1, 1, true, MODETYPE_CHANNEL, false, my_prefix, 0),
00162                   FounderProtectBase(Instance, "cm_founder_", "founder", 386, 387, depriv_self, depriv_others) { }
00163 
00164         unsigned int GetPrefixRank()
00165         {
00166                 return FOUNDER_VALUE;
00167         }
00168 
00169         ModePair ModeSet(User* source, User* dest, Channel* channel, const std::string &parameter)
00170         {
00171                 return FounderProtectBase::ModeSet(source, dest, channel, parameter);
00172         }
00173 
00174         void RemoveMode(Channel* channel, irc::modestacker* stack)
00175         {
00176                 FounderProtectBase::RemoveMode(channel, this->GetModeChar(), stack);
00177         }
00178 
00179         void RemoveMode(User* user, irc::modestacker* stack)
00180         {
00181         }
00182 
00183         ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding, bool)
00184         {
00185                 User* theuser = FounderProtectBase::FindAndVerify(parameter, channel);
00186 
00187                 if (!theuser)
00188                 {
00189                         return MODEACTION_DENY;
00190                 }
00191 
00192                 if ((!adding) && FounderProtectBase::CanRemoveOthers(source, theuser, channel))
00193                 {
00194                         return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter);
00195                 }
00196                  // source is a server, or ulined, we'll let them +-q the user.
00197                 if (source == ServerInstance->FakeClient ||
00198                                 ((source == theuser) && (!adding) && (FounderProtectBase::remove_own_privs)) ||
00199                                 (ServerInstance->ULine(source->nick.c_str())) ||
00200                                 (ServerInstance->ULine(source->server)) ||
00201                                 (!*source->server) ||
00202                                 (!IS_LOCAL(source)))
00203                 {
00204                         return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter);
00205                 }
00206                 else
00207                 {
00208                         // whoops, someones being naughty!
00209                         source->WriteNumeric(468, "%s %s :Only servers may set channel mode +q", source->nick.c_str(), channel->name.c_str());
00210                         parameter.clear();
00211                         return MODEACTION_DENY;
00212                 }
00213         }
00214 
00215         void DisplayList(User* user, Channel* channel)
00216         {
00217                 FounderProtectBase::DisplayList(user,channel);
00218         }
00219 };
00220 
00223 class ChanProtect : public ModeHandler, public FounderProtectBase
00224 {
00225  public:
00226         ChanProtect(InspIRCd* Instance, char my_prefix, bool &depriv_self, bool &depriv_others)
00227                 : ModeHandler(Instance, 'a', 1, 1, true, MODETYPE_CHANNEL, false, my_prefix, 0),
00228                   FounderProtectBase(Instance,"cm_protect_","protected user", 388, 389, depriv_self, depriv_others) { }
00229 
00230         unsigned int GetPrefixRank()
00231         {
00232                 return PROTECT_VALUE;
00233         }
00234 
00235         ModePair ModeSet(User* source, User* dest, Channel* channel, const std::string &parameter)
00236         {
00237                 return FounderProtectBase::ModeSet(source, dest, channel, parameter);
00238         }
00239 
00240         void RemoveMode(Channel* channel, irc::modestacker* stack)
00241         {
00242                 FounderProtectBase::RemoveMode(channel, this->GetModeChar(), stack);
00243         }
00244 
00245         void RemoveMode(User* user, irc::modestacker* stack)
00246         {
00247         }
00248 
00249         ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding, bool)
00250         {
00251                 User* theuser = FounderProtectBase::FindAndVerify(parameter, channel);
00252 
00253                 if (!theuser)
00254                         return MODEACTION_DENY;
00255 
00256                 std::string founder = "cm_founder_"+std::string(channel->name);
00257 
00258                 if ((!adding) && FounderProtectBase::CanRemoveOthers(source, theuser, channel))
00259                 {
00260                         return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter);
00261                 }
00262                 // source has +q, is a server, or ulined, we'll let them +-a the user.
00263                 if (source == ServerInstance->FakeClient ||
00264                         ((source == theuser) && (!adding) && (FounderProtectBase::remove_own_privs)) ||
00265                         (ServerInstance->ULine(source->nick.c_str())) ||
00266                         (ServerInstance->ULine(source->server)) ||
00267                         (!*source->server) ||
00268                         (source->GetExt(founder)) ||
00269                         (!IS_LOCAL(source)))
00270                 {
00271                         return FounderProtectBase::HandleChange(source, theuser, adding, channel, parameter);
00272                 }
00273                 else
00274                 {
00275                         // bzzzt, wrong answer!
00276                         source->WriteNumeric(482, "%s %s :You are not a channel founder", source->nick.c_str(), channel->name.c_str());
00277                         return MODEACTION_DENY;
00278                 }
00279         }
00280 
00281         virtual void DisplayList(User* user, Channel* channel)
00282         {
00283                 FounderProtectBase::DisplayList(user, channel);
00284         }
00285 
00286 };
00287 
00288 class ModuleChanProtect : public Module
00289 {
00290 
00291         bool FirstInGetsFounder;
00292         char QPrefix;
00293         char APrefix;
00294         bool DeprivSelf;
00295         bool DeprivOthers;
00296         bool booting;
00297         ChanProtect* cp;
00298         ChanFounder* cf;
00299 
00300  public:
00301 
00302         ModuleChanProtect(InspIRCd* Me)
00303                 : Module(Me), FirstInGetsFounder(false), QPrefix(0), APrefix(0), DeprivSelf(false), DeprivOthers(false), booting(true), cp(NULL), cf(NULL)
00304         {
00305                 /* Load config stuff */
00306                 LoadSettings();
00307                 booting = false;
00308 
00309                 /* Initialise module variables */
00310 
00311                 cp = new ChanProtect(ServerInstance, APrefix, DeprivSelf, DeprivOthers);
00312                 cf = new ChanFounder(ServerInstance, QPrefix, DeprivSelf, DeprivOthers);
00313 
00314                 if (!ServerInstance->Modes->AddMode(cp) || !ServerInstance->Modes->AddMode(cf))
00315                 {
00316                         delete cp;
00317                         delete cf;
00318                         throw ModuleException("Could not add new modes!");
00319                 }
00320 
00321                 Implementation eventlist[] = { I_OnUserKick, I_OnUserPart, I_OnUserPreJoin, I_OnPostJoin, I_OnAccessCheck };
00322                 ServerInstance->Modules->Attach(eventlist, this, 5);
00323         }
00324 
00325         virtual void OnUserKick(User* source, User* user, Channel* chan, const std::string &reason, bool &silent)
00326         {
00327                 // FIX: when someone gets kicked from a channel we must remove their Extensibles!
00328                 user->Shrink("cm_founder_"+std::string(chan->name));
00329                 user->Shrink("cm_protect_"+std::string(chan->name));
00330         }
00331 
00332         virtual void OnUserPart(User* user, Channel* channel, std::string &partreason, bool &silent)
00333         {
00334                 // FIX: when someone parts a channel we must remove their Extensibles!
00335                 user->Shrink("cm_founder_"+std::string(channel->name));
00336                 user->Shrink("cm_protect_"+std::string(channel->name));
00337         }
00338 
00339         void LoadSettings()
00340         {
00341                 ConfigReader Conf(ServerInstance);
00342 
00343                 FirstInGetsFounder = Conf.ReadFlag("chanprotect", "noservices", 0);
00344 
00345                 std::string qpre = Conf.ReadValue("chanprotect", "qprefix", 0);
00346                 QPrefix = qpre.empty() ? 0 : qpre[0];
00347 
00348                 std::string apre = Conf.ReadValue("chanprotect", "aprefix", 0);
00349                 APrefix = apre.empty() ? 0 : apre[0];
00350 
00351                 if ((APrefix && QPrefix) && APrefix == QPrefix)
00352                         throw ModuleException("What the smeg, why are both your +q and +a prefixes the same character?");
00353 
00354                 if (cp && ServerInstance->Modes->FindPrefix(APrefix) == cp)
00355                         throw ModuleException("Looks like the +a prefix you picked for m_chanprotect is already in use. Pick another.");
00356 
00357                 if (cf && ServerInstance->Modes->FindPrefix(QPrefix) == cf)
00358                         throw ModuleException("Looks like the +q prefix you picked for m_chanprotect is already in use. Pick another.");
00359 
00360                 DeprivSelf = Conf.ReadFlag("chanprotect","deprotectself", "yes", 0);
00361                 DeprivOthers = Conf.ReadFlag("chanprotect","deprotectothers", "yes", 0);
00362         }
00363 
00364         virtual int OnUserPreJoin(User *user, Channel *chan, const char *cname, std::string &privs, const std::string &keygiven)
00365         {
00366                 // if the user is the first user into the channel, mark them as the founder, but only if
00367                 // the config option for it is set
00368 
00369                 if (FirstInGetsFounder && !chan)
00370                         privs = std::string(1, QPrefix) + "@";
00371 
00372                 return 0;
00373         }
00374 
00375         virtual void OnPostJoin(User *user, Channel *channel)
00376         {
00377                 // This *must* be in PostJoin, not UserJoin - the former will make it appear to happen
00378                 // before the client is in the channel
00379 
00380                 // This notice was here originally because it was all done prior to the creation of
00381                 // privs in OnUserPreJoin. I've left it because it might still be wanted, but i'm
00382                 // not sure it really should be here - ops don't get shown, obviously, and the prefix
00383                 // will appear in the names list for the user.. remove if desired -Special
00384 
00385                 if (FirstInGetsFounder && channel->GetUserCounter() == 1)
00386                         user->WriteServ("MODE %s +q %s", channel->name.c_str(), user->nick.c_str());
00387         }
00388 
00389         virtual int OnAccessCheck(User* source,User* dest,Channel* channel,int access_type)
00390         {
00391                 // here we perform access checks, this is the important bit that actually stops kicking/deopping
00392                 // etc of protected users. There are many types of access check, we're going to handle
00393                 // a relatively small number of them relevent to our module using a switch statement.
00394                 // don't allow action if:
00395                 // (A) Theyre founder (no matter what)
00396                 // (B) Theyre protected, unless you're founder or are protected and DeprivOthers is enabled
00397                 // always allow the action if:
00398                 // (A) The source is ulined
00399 
00400                 // firstly, if a ulined nick, or a server, is setting the mode, then allow them to set the mode
00401                 // without any access checks, we're not worthy :p
00402                 if ((ServerInstance->ULine(source->nick.c_str())) || (ServerInstance->ULine(source->server)) || (!*source->server))
00403                         return ACR_ALLOW;
00404 
00405                 std::string founder("cm_founder_"+channel->name);
00406                 std::string protect("cm_protect_"+channel->name);
00407 
00408                 // Can do anything to yourself if deprotectself is enabled.
00409                 if (DeprivSelf && source == dest)
00410                         return ACR_DEFAULT;
00411 
00412                 bool candepriv_founder = (DeprivOthers && source->GetExt(founder));
00413                 bool candepriv_protected = (source->GetExt(founder) || (DeprivOthers && source->GetExt(protect))); // Can the source remove +a?
00414 
00415                 switch (access_type)
00416                 {
00417                         // a user has been deopped. Do we let them? hmmm...
00418                         case AC_DEOP:
00419                                 if (dest->GetExt(founder) && !candepriv_founder)
00420                                 {
00421                                         source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't deop "+dest->nick+" as they're a channel founder");
00422                                         return ACR_DENY;
00423                                 }
00424                                 if ((dest->GetExt(protect)) && !candepriv_protected)
00425                                 {
00426                                         source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't deop "+dest->nick+" as they're protected (+a)");
00427                                         return ACR_DENY;
00428                                 }
00429                         break;
00430 
00431                         // a user is being kicked. do we chop off the end of the army boot?
00432                         case AC_KICK:
00433                                 if (dest->GetExt(founder) && !candepriv_founder)
00434                                 {
00435                                         source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't kick "+dest->nick+" as they're a channel founder");
00436                                         return ACR_DENY;
00437                                 }
00438                                 if ((dest->GetExt(protect)) && !candepriv_protected)
00439                                 {
00440                                         source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't kick "+dest->nick+" as they're protected (+a)");
00441                                         return ACR_DENY;
00442                                 }
00443                         break;
00444 
00445                         // a user is being dehalfopped. Yes, we do disallow -h of a +ha user
00446                         case AC_DEHALFOP:
00447                                 if (dest->GetExt(founder) && !candepriv_founder)
00448                                 {
00449                                         source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't de-halfop "+dest->nick+" as they're a channel founder");
00450                                         return ACR_DENY;
00451                                 }
00452                                 if ((dest->GetExt(protect)) && !candepriv_protected)
00453                                 {
00454                                         source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't de-halfop "+dest->nick+" as they're protected (+a)");
00455                                         return ACR_DENY;
00456                                 }
00457                         break;
00458 
00459                         // same with devoice.
00460                         case AC_DEVOICE:
00461                                 if (dest->GetExt(founder) && !candepriv_founder)
00462                                 {
00463                                         source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't devoice "+dest->nick+" as they're a channel founder");
00464                                         return ACR_DENY;
00465                                 }
00466                                 if ((dest->GetExt(protect)) && !candepriv_protected)
00467                                 {
00468                                         source->WriteNumeric(484, source->nick+" "+channel->name+" :Can't devoice "+dest->nick+" as they're protected (+a)");
00469                                         return ACR_DENY;
00470                                 }
00471                         break;
00472                 }
00473 
00474                 // we dont know what this access check is, or dont care. just carry on, nothing to see here.
00475                 return ACR_DEFAULT;
00476         }
00477 
00478         virtual ~ModuleChanProtect()
00479         {
00480                 ServerInstance->Modes->DelMode(cp);
00481                 ServerInstance->Modes->DelMode(cf);
00482                 delete cp;
00483                 delete cf;
00484         }
00485 
00486         virtual Version GetVersion()
00487         {
00488                 return Version("$Id: m_chanprotect.cpp 10752 2008-10-30 20:47:13Z w00t $", VF_COMMON | VF_VENDOR, API_VERSION);
00489         }
00490 };
00491 
00492 MODULE_INIT(ModuleChanProtect)