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

m_silence_ext.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 #include "users.h"
00016 #include "channels.h"
00017 #include "modules.h"
00018 #include "hashcomp.h"
00019 #include "wildcard.h"
00020 
00021 /* $ModDesc: Provides support for the /SILENCE command */
00022 
00023 /* Improved drop-in replacement for the /SILENCE command
00024  * syntax: /SILENCE [+|-]<mask> <p|c|i|n|t|a|x> as in <privatemessage|channelmessage|invites|privatenotice|channelnotice|all|exclude>
00025  *
00026  * example that blocks all except private messages
00027  *  /SILENCE +*!*@* a
00028  *  /SILENCE +*!*@* px
00029  *
00030  * example that blocks all invites except from channel services
00031  *  /SILENCE +*!*@* i
00032  *  /SILENCE +chanserv!services@chatters.net ix
00033  *
00034  * example that blocks some bad dude from private, notice and inviting you
00035  *  /SILENCE +*!kiddie@lamerz.net pin
00036  *
00037  * TODO: possibly have add and remove check for existing host and only modify flags according to
00038  *       what's been changed instead of having to remove first, then add if you want to change
00039  *       an entry.
00040  */
00041 
00042 // pair of hostmask and flags
00043 typedef std::pair<std::string, int> silenceset;
00044 
00045 // deque list of pairs
00046 typedef std::deque<silenceset> silencelist;
00047 
00048 // intmasks for flags
00049 static int SILENCE_PRIVATE      = 0x0001; /* p  private messages      */
00050 static int SILENCE_CHANNEL      = 0x0002; /* c  channel messages      */
00051 static int SILENCE_INVITE       = 0x0004; /* i  invites               */
00052 static int SILENCE_NOTICE       = 0x0008; /* n  notices               */
00053 static int SILENCE_CNOTICE      = 0x0010; /* t  channel notices       */
00054 static int SILENCE_ALL          = 0x0020; /* a  all, (pcint)          */
00055 static int SILENCE_EXCLUDE      = 0x0040; /* x  exclude this pattern  */
00056 
00057 
00058 class cmd_silence : public command_t
00059 {
00060         unsigned int& maxsilence;
00061  public:
00062         cmd_silence (InspIRCd* Instance, unsigned int &max) : command_t(Instance,"SILENCE", 0, 0), maxsilence(max)
00063         {
00064                 this->source = "m_silence_ext.so";
00065                 syntax = "{[+|-]<mask> <p|c|i|n|t|a|x>}";
00066         }
00067 
00068         CmdResult Handle (const char** parameters, int pcnt, userrec *user)
00069         {
00070                 if (!pcnt)
00071                 {
00072                         // no parameters, show the current silence list.
00073                         // Use Extensible::GetExt to fetch the silence list
00074                         silencelist* sl;
00075                         user->GetExt("silence_list", sl);
00076                         // if the user has a silence list associated with their user record, show it
00077                         if (sl)
00078                         {
00079                                 for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
00080                                 {
00081                                         user->WriteServ("271 %s %s %s %s",user->nick, user->nick,c->first.c_str(), DecompPattern(c->second).c_str());
00082                                 }
00083                         }
00084                         user->WriteServ("272 %s :End of Silence List",user->nick);
00085 
00086                         return CMD_LOCALONLY;
00087                 }
00088                 else if (pcnt > 0)
00089                 {
00090                         // one or more parameters, add or delete entry from the list (only the first parameter is used)
00091                         std::string mask = parameters[0] + 1;
00092                         char action = *parameters[0];
00093                         // Default is private and notice so clients do not break
00094                         int pattern = CompilePattern("pn");
00095 
00096                         // if pattern supplied, use it
00097                         if (pcnt > 1) {
00098                                 pattern = CompilePattern(parameters[1]);
00099                         }
00100                         
00101                         if (!mask.length())
00102                         {
00103                                 // 'SILENCE +' or 'SILENCE -', assume *!*@*
00104                                 mask = "*!*@*";
00105                         }
00106                         
00107                         ModeParser::CleanMask(mask);
00108 
00109                         if (action == '-')
00110                         {
00111                                 // fetch their silence list
00112                                 silencelist* sl;
00113                                 user->GetExt("silence_list", sl);
00114                                 // does it contain any entries and does it exist?
00115                                 if (sl)
00116                                 {
00117                                         for (silencelist::iterator i = sl->begin(); i != sl->end(); i++)
00118                                         {
00119                                                 // search through for the item
00120                                                 irc::string listitem = i->first.c_str();
00121                                                 if (listitem == mask && i->second == pattern)
00122                                                 {
00123                                                         sl->erase(i);
00124                                                         user->WriteServ("950 %s %s :Removed %s %s from silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
00125                                                         if (!sl->size())
00126                                                         {
00127                                                                 DELETE(sl);
00128                                                                 user->Shrink("silence_list");
00129                                                         }
00130                                                         return CMD_SUCCESS;
00131                                                 }
00132                                         }
00133                                 }
00134                                 user->WriteServ("952 %s %s :%s %s does not exist on your silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
00135                         }
00136                         else if (action == '+')
00137                         {
00138                                 // fetch the user's current silence list
00139                                 silencelist* sl;
00140                                 user->GetExt("silence_list", sl);
00141                                 // what, they dont have one??? WE'RE ALL GONNA DIE! ...no, we just create an empty one.
00142                                 if (!sl)
00143                                 {
00144                                         sl = new silencelist;
00145                                         user->Extend("silence_list", sl);
00146                                 }
00147                                 if (sl->size() > maxsilence)
00148                                 {
00149                                         user->WriteServ("952 %s %s :Your silence list is full",user->nick, user->nick);
00150                                         return CMD_FAILURE;
00151                                 }
00152                                 for (silencelist::iterator n = sl->begin(); n != sl->end();  n++)
00153                                 {
00154                                         irc::string listitem = n->first.c_str();
00155                                         if (listitem == mask && n->second == pattern)
00156                                         {
00157                                                 user->WriteServ("952 %s %s :%s %s is already on your silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
00158                                                 return CMD_FAILURE;
00159                                         }
00160                                 }
00161                                 if (((pattern & SILENCE_EXCLUDE) > 0))
00162                                 {
00163                                         sl->push_front(silenceset(mask,pattern));
00164                                 }
00165                                 else
00166                                 {
00167                                         sl->push_back(silenceset(mask,pattern));
00168                                 }
00169                                 user->WriteServ("951 %s %s :Added %s %s to silence list",user->nick, user->nick, mask.c_str(), DecompPattern(pattern).c_str());
00170                                 return CMD_SUCCESS;
00171                         }
00172                 }
00173                 return CMD_LOCALONLY;
00174         }
00175 
00176         /* turn the nice human readable pattern into a mask */
00177         int CompilePattern(const char* pattern)
00178         {
00179                 int p = 0;
00180                 for (const char* n = pattern; *n; n++)
00181                 {
00182                         switch (*n)
00183                         {
00184                                 case 'p':
00185                                         p |= SILENCE_PRIVATE;
00186                                         break;
00187                                 case 'c':
00188                                         p |= SILENCE_CHANNEL;
00189                                         break;
00190                                 case 'i': 
00191                                         p |= SILENCE_INVITE;
00192                                         break;
00193                                 case 'n':
00194                                         p |= SILENCE_NOTICE;
00195                                         break;
00196                                 case 't':
00197                                         p |= SILENCE_CNOTICE;
00198                                         break;
00199                                 case 'a':
00200                                         p |= SILENCE_ALL;
00201                                         break;
00202                                 case 'x':
00203                                         p |= SILENCE_EXCLUDE;
00204                                         break;
00205                                 default:
00206                                         break;
00207                         }
00208                 }
00209                 return p;
00210         }
00211 
00212         /* turn the mask into a nice human readable format */
00213         std::string DecompPattern (const int pattern)
00214         {
00215                 std::string out;
00216                 if ((pattern & SILENCE_PRIVATE) > 0)
00217                         out += ",privatemessages";
00218                 if ((pattern & SILENCE_CHANNEL) > 0)
00219                         out += ",channelmessages";
00220                 if ((pattern & SILENCE_INVITE) > 0)
00221                         out += ",invites";
00222                 if ((pattern & SILENCE_NOTICE) > 0)
00223                         out += ",privatenotices";
00224                 if ((pattern & SILENCE_CNOTICE) > 0)
00225                         out += ",channelnotices";
00226                 if ((pattern & SILENCE_ALL) > 0)
00227                         out = ",all";
00228                 if ((pattern & SILENCE_EXCLUDE) > 0)
00229                         out += ",exclude";
00230                 return "<" + out.substr(1) + ">";
00231         }
00232 
00233 };
00234 
00235 class ModuleSilence : public Module
00236 {
00237         cmd_silence* mycommand;
00238         unsigned int maxsilence;
00239  public:
00240  
00241         ModuleSilence(InspIRCd* Me)
00242                 : Module(Me), maxsilence(32)
00243         {
00244                 OnRehash(NULL, "");
00245                 mycommand = new cmd_silence(ServerInstance,maxsilence);
00246                 ServerInstance->AddCommand(mycommand);
00247         }
00248 
00249         virtual void OnRehash(userrec* user, const std::string &parameter)
00250         {
00251                 ConfigReader Conf(ServerInstance);
00252                 maxsilence = Conf.ReadInteger("silence", "maxentries", 0, true);
00253                 if (!maxsilence)
00254                         maxsilence = 32;
00255         }
00256 
00257         void Implements(char* List)
00258         {
00259                 List[I_OnRehash] = List[I_OnBuildExemptList] = List[I_OnUserQuit] = List[I_On005Numeric] = List[I_OnUserPreNotice] = List[I_OnUserPreMessage] = List[I_OnUserPreInvite] = 1;
00260         }
00261 
00262         virtual void OnUserQuit(userrec* user, const std::string &reason, const std::string &oper_message)
00263         {
00264                 // when the user quits tidy up any silence list they might have just to keep things tidy
00265                 silencelist* sl;
00266                 user->GetExt("silence_list", sl);
00267                 if (sl)
00268                 {
00269                         DELETE(sl);
00270                         user->Shrink("silence_list");
00271                 }
00272         }
00273 
00274         virtual void On005Numeric(std::string &output)
00275         {
00276                 // we don't really have a limit...
00277                 output = output + " ESILENCE SILENCE=" + ConvToStr(maxsilence);
00278         }
00279 
00280         virtual void OnBuildExemptList(MessageType message_type, chanrec* chan, userrec* sender, char status, CUList &exempt_list)
00281         {
00282                 int public_silence = (message_type == MSG_PRIVMSG ? SILENCE_CHANNEL : SILENCE_CNOTICE);
00283                 CUList *ulist;
00284                 switch (status)
00285                 {
00286                         case '@':
00287                                 ulist = chan->GetOppedUsers();
00288                                 break;
00289                         case '%':
00290                                 ulist = chan->GetHalfoppedUsers();
00291                                 break;
00292                         case '+':
00293                                 ulist = chan->GetVoicedUsers();
00294                                 break;
00295                         default:
00296                                 ulist = chan->GetUsers();
00297                                 break;
00298                 }
00299 
00300                 for (CUList::iterator i = ulist->begin(); i != ulist->end(); i++)
00301                 {
00302                         if (IS_LOCAL(i->first))
00303                         {
00304                                 if (MatchPattern(i->first, sender, public_silence) == 1)
00305                                 {
00306                                         exempt_list[i->first] = i->first->nick;
00307                                 }
00308                         }
00309                 }
00310         }
00311 
00312         virtual int PreText(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list, int silence_type)
00313         {
00314                 if (target_type == TYPE_USER && IS_LOCAL(((userrec*)dest)))
00315                 {
00316                         return MatchPattern((userrec*)dest, user, silence_type);
00317                 }
00318                 else if (target_type == TYPE_CHANNEL)
00319                 {
00320                         chanrec* chan = (chanrec*)dest;
00321                         if (chan)
00322                         {
00323                                 this->OnBuildExemptList((silence_type == SILENCE_PRIVATE ? MSG_PRIVMSG : MSG_NOTICE), chan, user, status, exempt_list);
00324                         }
00325                 }
00326                 return 0;
00327         }
00328 
00329         virtual int OnUserPreMessage(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
00330         {
00331                 return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_PRIVATE);
00332         }
00333 
00334         virtual int OnUserPreNotice(userrec* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
00335         {
00336                 return PreText(user, dest, target_type, text, status, exempt_list, SILENCE_NOTICE);
00337         }
00338 
00339         virtual int OnUserPreInvite(userrec* source,userrec* dest,chanrec* channel)
00340         {
00341                 return MatchPattern(dest, source, SILENCE_INVITE);
00342         }
00343 
00344         int MatchPattern(userrec* dest, userrec* source, int pattern)
00345         {
00346                 silencelist* sl;
00347                 dest->GetExt("silence_list", sl);
00348                 if (sl)
00349                 {
00350                         for (silencelist::const_iterator c = sl->begin(); c != sl->end(); c++)
00351                         {
00352                                 if (((((c->second & pattern) > 0)) || ((c->second & SILENCE_ALL) > 0)) && (ServerInstance->MatchText(source->GetFullHost(), c->first)))
00353                                         return !(((c->second & SILENCE_EXCLUDE) > 0));
00354                         }
00355                 }
00356                 return 0;
00357         }
00358 
00359         virtual ~ModuleSilence()
00360         {
00361         }
00362         
00363         virtual Version GetVersion()
00364         {
00365                 return Version(1, 1, 0, 1, VF_COMMON | VF_VENDOR, API_VERSION);
00366         }
00367 };
00368 
00369 MODULE_INIT(ModuleSilence)