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_messageflood.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 mode +f (message flood protection) */
00017 
00020 class floodsettings : public classbase
00021 {
00022  private:
00023         InspIRCd *ServerInstance;
00024  public:
00025         bool ban;
00026         int secs;
00027         int lines;
00028         time_t reset;
00029         std::map<User*,int> counters;
00030 
00031         floodsettings(InspIRCd *Instance) : ServerInstance(Instance), ban(0), secs(0), lines(0) {};
00032         floodsettings(InspIRCd *Instance, bool a, int b, int c) : ServerInstance(Instance), ban(a), secs(b), lines(c)
00033         {
00034                 reset = ServerInstance->Time() + secs;
00035         };
00036 
00037         void addmessage(User* who)
00038         {
00039                 std::map<User*,int>::iterator iter = counters.find(who);
00040                 if (iter != counters.end())
00041                 {
00042                         iter->second++;
00043                 }
00044                 else
00045                 {
00046                         counters[who] = 1;
00047                 }
00048                 if (ServerInstance->Time() > reset)
00049                 {
00050                         counters.clear();
00051                         reset = ServerInstance->Time() + secs;
00052                 }
00053         }
00054 
00055         bool shouldkick(User* who)
00056         {
00057                 std::map<User*,int>::iterator iter = counters.find(who);
00058                 if (iter != counters.end())
00059                 {
00060                         return (iter->second >= this->lines);
00061                 }
00062                 else return false;
00063         }
00064 
00065         void clear(User* who)
00066         {
00067                 std::map<User*,int>::iterator iter = counters.find(who);
00068                 if (iter != counters.end())
00069                 {
00070                         counters.erase(iter);
00071                 }
00072         }
00073 };
00074 
00077 class MsgFlood : public ModeHandler
00078 {
00079  public:
00080         MsgFlood(InspIRCd* Instance) : ModeHandler(Instance, 'f', 1, 0, false, MODETYPE_CHANNEL, false) { }
00081 
00082         ModePair ModeSet(User* source, User* dest, Channel* channel, const std::string &parameter)
00083         {
00084                 floodsettings* x;
00085                 if (channel->GetExt("flood",x))
00086                         return std::make_pair(true, (x->ban ? "*" : "")+ConvToStr(x->lines)+":"+ConvToStr(x->secs));
00087                 else
00088                         return std::make_pair(false, parameter);
00089         }
00090 
00091         bool CheckTimeStamp(time_t theirs, time_t ours, const std::string &their_param, const std::string &our_param, Channel* channel)
00092         {
00093                 /* When TS is equal, the alphabetically later one wins */
00094                 return (their_param < our_param);
00095         }
00096 
00097         ModeAction OnModeChange(User* source, User* dest, Channel* channel, std::string &parameter, bool adding, bool)
00098         {
00099                 floodsettings *f;
00100 
00101                 if (adding)
00102                 {
00103                         char ndata[MAXBUF];
00104                         char* data = ndata;
00105                         strlcpy(ndata,parameter.c_str(),MAXBUF);
00106                         char* lines = data;
00107                         char* secs = NULL;
00108                         bool ban = false;
00109                         if (*data == '*')
00110                         {
00111                                 ban = true;
00112                                 lines++;
00113                         }
00114                         else
00115                         {
00116                                 ban = false;
00117                         }
00118                         while (*data)
00119                         {
00120                                 if (*data == ':')
00121                                 {
00122                                         *data = 0;
00123                                         data++;
00124                                         secs = data;
00125                                         break;
00126                                 }
00127                                 else data++;
00128                         }
00129                         if (secs)
00130                         {
00131                                 /* Set up the flood parameters for this channel */
00132                                 int nlines = atoi(lines);
00133                                 int nsecs = atoi(secs);
00134                                 if ((nlines<1) || (nsecs<1))
00135                                 {
00136                                         source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
00137                                         parameter.clear();
00138                                         return MODEACTION_DENY;
00139                                 }
00140                                 else
00141                                 {
00142                                         if (!channel->GetExt("flood", f))
00143                                         {
00144                                                 parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" +ConvToStr(nsecs);
00145                                                 floodsettings *fs = new floodsettings(ServerInstance,ban,nsecs,nlines);
00146                                                 channel->Extend("flood",fs);
00147                                                 channel->SetMode('f', true);
00148                                                 channel->SetModeParam('f', parameter.c_str(), true);
00149                                                 return MODEACTION_ALLOW;
00150                                         }
00151                                         else
00152                                         {
00153                                                 std::string cur_param = channel->GetModeParameter('f');
00154                                                 parameter = std::string(ban ? "*" : "") + ConvToStr(nlines) + ":" +ConvToStr(nsecs);
00155                                                 if (cur_param == parameter)
00156                                                 {
00157                                                         // mode params match
00158                                                         return MODEACTION_DENY;
00159                                                 }
00160                                                 else
00161                                                 {
00162                                                         if ((((nlines != f->lines) || (nsecs != f->secs) || (ban != f->ban))) && (((nsecs > 0) && (nlines > 0))))
00163                                                         {
00164                                                                 delete f;
00165                                                                 floodsettings *fs = new floodsettings(ServerInstance,ban,nsecs,nlines);
00166                                                                 channel->Shrink("flood");
00167                                                                 channel->Extend("flood",fs);
00168                                                                 channel->SetModeParam('f', cur_param.c_str(), false);
00169                                                                 channel->SetModeParam('f', parameter.c_str(), true);
00170                                                                 return MODEACTION_ALLOW;
00171                                                         }
00172                                                         else
00173                                                         {
00174                                                                 return MODEACTION_DENY;
00175                                                         }
00176                                                 }
00177                                         }
00178                                 }
00179                         }
00180                         else
00181                         {
00182                                 source->WriteNumeric(608, "%s %s :Invalid flood parameter",source->nick.c_str(),channel->name.c_str());
00183                                 parameter.clear();
00184                                 return MODEACTION_DENY;
00185                         }
00186                 }
00187                 else
00188                 {
00189                         if (channel->GetExt("flood", f))
00190                         {
00191                                 delete f;
00192                                 channel->Shrink("flood");
00193                                 channel->SetMode('f', false);
00194                                 return MODEACTION_ALLOW;
00195                         }
00196                 }
00197 
00198                 return MODEACTION_DENY;
00199         }
00200 };
00201 
00202 class ModuleMsgFlood : public Module
00203 {
00204 
00205         MsgFlood* mf;
00206 
00207  public:
00208 
00209         ModuleMsgFlood(InspIRCd* Me)
00210                 : Module(Me)
00211         {
00212 
00213                 mf = new MsgFlood(ServerInstance);
00214                 if (!ServerInstance->Modes->AddMode(mf))
00215                         throw ModuleException("Could not add new modes!");
00216                 Implementation eventlist[] = { I_OnChannelDelete, I_OnUserPreNotice, I_OnUserPreMessage };
00217                 ServerInstance->Modules->Attach(eventlist, this, 3);
00218         }
00219 
00220         int ProcessMessages(User* user,Channel* dest, const std::string &text)
00221         {
00222                 if (!IS_LOCAL(user) || (CHANOPS_EXEMPT(ServerInstance, 'f') && dest->GetStatus(user) == STATUS_OP))
00223                 {
00224                         return 0;
00225                 }
00226 
00227                 floodsettings *f;
00228                 if (dest->GetExt("flood", f))
00229                 {
00230                         f->addmessage(user);
00231                         if (f->shouldkick(user))
00232                         {
00233                                 /* Youre outttta here! */
00234                                 f->clear(user);
00235                                 if (f->ban)
00236                                 {
00237                                         std::vector<std::string> parameters;
00238                                         parameters.push_back(dest->name);
00239                                         parameters.push_back("+b");
00240                                         parameters.push_back(user->MakeWildHost());
00241                                         ServerInstance->SendMode(parameters, ServerInstance->FakeClient);
00242 
00243                                         ServerInstance->PI->SendModeStr(dest->name, std::string("+b ") + user->MakeWildHost());
00244                                 }
00245 
00246                                 char kickmessage[MAXBUF];
00247                                 snprintf(kickmessage, MAXBUF, "Channel flood triggered (limit is %d lines in %d secs)", f->lines, f->secs);
00248 
00249                                 if (!dest->ServerKickUser(user, kickmessage, true))
00250                                 {
00251                                         delete dest;
00252                                 }
00253 
00254                                 return 1;
00255                         }
00256                 }
00257 
00258                 return 0;
00259         }
00260 
00261         virtual int OnUserPreMessage(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
00262         {
00263                 if (target_type == TYPE_CHANNEL)
00264                         return ProcessMessages(user,(Channel*)dest,text);
00265 
00266                 return 0;
00267         }
00268 
00269         virtual int OnUserPreNotice(User *user, void *dest, int target_type, std::string &text, char status, CUList &exempt_list)
00270         {
00271                 if (target_type == TYPE_CHANNEL)
00272                         return ProcessMessages(user,(Channel*)dest,text);
00273 
00274                 return 0;
00275         }
00276 
00277         void OnChannelDelete(Channel* chan)
00278         {
00279                 floodsettings* f;
00280                 if (chan->GetExt("flood", f))
00281                 {
00282                         delete f;
00283                         chan->Shrink("flood");
00284                 }
00285         }
00286 
00287 
00288         virtual ~ModuleMsgFlood()
00289         {
00290                 ServerInstance->Modes->DelMode(mf);
00291                 delete mf;
00292         }
00293 
00294         virtual Version GetVersion()
00295         {
00296                 return Version("$Id: m_messageflood.cpp 10783 2008-11-01 23:02:23Z w00t $", VF_COMMON | VF_VENDOR, API_VERSION);
00297         }
00298 };
00299 
00300 MODULE_INIT(ModuleMsgFlood)