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_safelist.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 
00018 class ListData : public classbase
00019 {
00020  public:
00021         long list_start;
00022         long list_position;
00023         bool list_ended;
00024         const std::string glob;
00025         int minusers;
00026         int maxusers;
00027 
00028         ListData() : list_start(0), list_position(0), list_ended(false) {};
00029         ListData(long pos, time_t t, const std::string &pattern, int mi, int ma) : list_start(t), list_position(pos), list_ended(false), glob(pattern), minusers(mi), maxusers(ma) {};
00030 };
00031 
00032 /* $ModDesc: A module overriding /list, and making it safe - stop those sendq problems. */
00033 
00034 class ModuleSafeList : public Module
00035 {
00036         time_t ThrottleSecs;
00037         size_t ServerNameSize;
00038         int global_listing;
00039         int LimitList;
00040  public:
00041         ModuleSafeList(InspIRCd* Me) : Module(Me)
00042         {
00043                 OnRehash(NULL, "");
00044                 Implementation eventlist[] = { I_OnBufferFlushed, I_OnPreCommand, I_OnCleanup, I_OnUserQuit, I_On005Numeric, I_OnRehash };
00045                 ServerInstance->Modules->Attach(eventlist, this, 6);
00046         }
00047 
00048         virtual ~ModuleSafeList()
00049         {
00050         }
00051 
00052         virtual void OnRehash(User* user, const std::string &parameter)
00053         {
00054                 ConfigReader MyConf(ServerInstance);
00055                 ThrottleSecs = MyConf.ReadInteger("safelist", "throttle", "60", 0, true);
00056                 LimitList = MyConf.ReadInteger("safelist", "maxlisters", "50", 0, true);
00057                 ServerNameSize = strlen(ServerInstance->Config->ServerName) + 4;
00058                 global_listing = 0;
00059         }
00060 
00061         virtual Version GetVersion()
00062         {
00063                 return Version("$Id: m_safelist.cpp 10666 2008-10-18 16:52:53Z w00t $",VF_VENDOR,API_VERSION);
00064         }
00065 
00066 
00067         /*
00068          * OnPreCommand()
00069          *   Intercept the LIST command.
00070          */
00071         virtual int OnPreCommand(std::string &command, std::vector<std::string> &parameters, User *user, bool validated, const std::string &original_line)
00072         {
00073                 /* If the command doesnt appear to be valid, we dont want to mess with it. */
00074                 if (!validated)
00075                         return 0;
00076 
00077                 if (command == "LIST")
00078                 {
00079                         return this->HandleList(parameters, user);
00080                 }
00081                 return 0;
00082         }
00083 
00084         /*
00085          * HandleList()
00086          *   Handle (override) the LIST command.
00087          */
00088         int HandleList(const std::vector<std::string> &parameters, User* user)
00089         {
00090                 int pcnt = parameters.size();
00091                 int minusers = 0, maxusers = 0;
00092 
00093                 if (global_listing >= LimitList && !IS_OPER(user))
00094                 {
00095                         user->WriteServ("NOTICE %s :*** Server load is currently too heavy. Please try again later.", user->nick.c_str());
00096                         user->WriteNumeric(321, "%s Channel :Users Name",user->nick.c_str());
00097                         user->WriteNumeric(323, "%s :End of channel list.",user->nick.c_str());
00098                         return 1;
00099                 }
00100 
00101                 /* First, let's check if the user is currently /list'ing */
00102                 ListData *ld;
00103                 user->GetExt("safelist_cache", ld);
00104 
00105                 if (ld)
00106                 {
00107                         /* user is already /list'ing, we don't want to do shit. */
00108                         return 1;
00109                 }
00110 
00111                 /* Work around mIRC suckyness. YOU SUCK, KHALED! */
00112                 if (pcnt == 1)
00113                 {
00114                         if (parameters[0][0] == '<')
00115                         {
00116                                 maxusers = atoi(parameters[0].c_str()+1);
00117                                 ServerInstance->Logs->Log("m_safelist",DEBUG,"Max users: %d", maxusers);
00118                                 pcnt = 0;
00119                         }
00120                         else if (parameters[0][0] == '>')
00121                         {
00122                                 minusers = atoi(parameters[0].c_str()+1);
00123                                 ServerInstance->Logs->Log("m_safelist",DEBUG,"Min users: %d", minusers);
00124                                 pcnt = 0;
00125                         }
00126                 }
00127 
00128                 time_t* last_list_time;
00129                 user->GetExt("safelist_last", last_list_time);
00130                 if (last_list_time)
00131                 {
00132                         if (ServerInstance->Time() < (*last_list_time)+ThrottleSecs)
00133                         {
00134                                 user->WriteServ("NOTICE %s :*** Woah there, slow down a little, you can't /LIST so often!",user->nick.c_str());
00135                                 user->WriteNumeric(321, "%s Channel :Users Name",user->nick.c_str());
00136                                 user->WriteNumeric(323, "%s :End of channel list.",user->nick.c_str());
00137                                 return 1;
00138                         }
00139 
00140                         delete last_list_time;
00141                         user->Shrink("safelist_last");
00142                 }
00143 
00144 
00145                 /*
00146                  * start at channel 0! ;)
00147                  */
00148                 ld = new ListData(0,ServerInstance->Time(), (pcnt && (parameters[0][0] != '<' && parameters[0][0] != '>')) ? parameters[0] : "*", minusers, maxusers);
00149                 user->Extend("safelist_cache", ld);
00150 
00151                 time_t* llt = new time_t;
00152                 *llt = ServerInstance->Time();
00153                 user->Extend("safelist_last", llt);
00154 
00155                 user->WriteNumeric(321, "%s Channel :Users Name",user->nick.c_str());
00156 
00157                 global_listing++;
00158 
00159                 return 1;
00160         }
00161 
00162         virtual void OnBufferFlushed(User* user)
00163         {
00164                 char buffer[MAXBUF];
00165                 ListData* ld;
00166                 if (user->GetExt("safelist_cache", ld))
00167                 {
00168                         Channel* chan = NULL;
00169                         unsigned long amount_sent = 0;
00170                         do
00171                         {
00172                                 chan = ServerInstance->GetChannelIndex(ld->list_position);
00173                                 bool is_special = (chan && (chan->HasUser(user) || user->HasPrivPermission("channels/auspex")));
00174                                 long users = chan ? chan->GetUserCounter() : 0;
00175 
00176                                 bool too_few = (ld->minusers && (users <= ld->minusers));
00177                                 bool too_many = (ld->maxusers && (users >= ld->maxusers));
00178 
00179                                 if (chan && (too_many || too_few))
00180                                 {
00181                                         ld->list_position++;
00182                                         continue;
00183                                 }
00184 
00185                                 if (chan)
00186                                 {
00187                                         bool display = (InspIRCd::Match(chan->name, ld->glob) || (!chan->topic.empty() && InspIRCd::Match(chan->topic, ld->glob)));
00188 
00189                                         if (!users || !display)
00190                                         {
00191                                                 ld->list_position++;
00192                                                 continue;
00193                                         }
00194 
00195                                         /* +s, not in chan / not got channels/auspex */
00196                                         if (chan->IsModeSet('s') && !is_special)
00197                                         {
00198                                                 ld->list_position++;
00199                                                 continue;
00200                                         }
00201 
00202                                         if (chan->IsModeSet('p') && !is_special)
00203                                         {
00204                                                 /* Channel is +p and user is outside/not privileged */
00205                                                 int counter = snprintf(buffer, MAXBUF, "322 %s * %ld :", user->nick.c_str(), users);
00206                                                 amount_sent += counter + ServerNameSize;
00207                                                 user->WriteServ(std::string(buffer));
00208                                         }
00209                                         else
00210                                         {
00211                                                 /* User is in the channel/privileged, channel is not +s */
00212                                                 int counter = snprintf(buffer, MAXBUF, "322 %s %s %ld :[+%s] %s", user->nick.c_str(), chan->name.c_str(), users, chan->ChanModes(is_special), chan->topic.c_str());
00213                                                 amount_sent += counter + ServerNameSize;
00214                                                 user->WriteServ(std::string(buffer));
00215                                         }
00216                                 }
00217                                 else
00218                                 {
00219                                         if (!ld->list_ended)
00220                                         {
00221                                                 ld->list_ended = true;
00222                                                 user->WriteNumeric(323, "%s :End of channel list.",user->nick.c_str());
00223                                         }
00224                                 }
00225 
00226                                 ld->list_position++;
00227                         }
00228                         while ((chan != NULL) && (amount_sent < (user->MyClass->GetSendqMax() / 4)));
00229                         if (ld->list_ended)
00230                         {
00231                                 user->Shrink("safelist_cache");
00232                                 delete ld;
00233                                 global_listing--;
00234                         }
00235                 }
00236         }
00237 
00238         virtual void OnCleanup(int target_type, void* item)
00239         {
00240                 if(target_type == TYPE_USER)
00241                 {
00242                         User* u = (User*)item;
00243                         ListData* ld;
00244                         u->GetExt("safelist_cache", ld);
00245                         if (ld)
00246                         {
00247                                 u->Shrink("safelist_cache");
00248                                 delete ld;
00249                                 global_listing--;
00250                         }
00251                         time_t* last_list_time;
00252                         u->GetExt("safelist_last", last_list_time);
00253                         if (last_list_time)
00254                         {
00255                                 delete last_list_time;
00256                                 u->Shrink("safelist_last");
00257                         }
00258                 }
00259         }
00260 
00261         virtual void On005Numeric(std::string &output)
00262         {
00263                 output.append(" SAFELIST");
00264         }
00265 
00266         virtual void OnUserQuit(User* user, const std::string &message, const std::string &oper_message)
00267         {
00268                 this->OnCleanup(TYPE_USER,user);
00269         }
00270 
00271 };
00272 
00273 MODULE_INIT(ModuleSafeList)