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_filter.h

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 "xline.h"
00015 
00016 enum FilterFlags
00017 {
00018         FLAG_PART = 2,
00019         FLAG_QUIT = 4,
00020         FLAG_PRIVMSG = 8,
00021         FLAG_NOTICE = 16
00022 };
00023 
00024 class FilterResult : public classbase
00025 {
00026  public:
00027         std::string freeform;
00028         std::string reason;
00029         std::string action;
00030         long gline_time;
00031         std::string flags;
00032 
00033         bool flag_no_opers;
00034         bool flag_part_message;
00035         bool flag_quit_message;
00036         bool flag_privmsg;
00037         bool flag_notice;
00038 
00039         FilterResult(const std::string free, const std::string &rea, const std::string &act, long gt, const std::string &fla) :
00040                         freeform(free), reason(rea), action(act), gline_time(gt), flags(fla)
00041         {
00042                 this->FillFlags(fla);
00043         }
00044 
00045         int FillFlags(const std::string &fl)
00046         {
00047                 flags = fl;
00048                 flag_no_opers = flag_part_message = flag_quit_message = flag_privmsg = flag_notice = false;
00049                 size_t x = 0;
00050 
00051                 for (std::string::const_iterator n = flags.begin(); n != flags.end(); ++n, ++x)
00052                 {
00053                         switch (*n)
00054                         {
00055                                 case 'o':
00056                                         flag_no_opers = true;
00057                                 break;
00058                                 case 'P':
00059                                         flag_part_message = true;
00060                                 break;
00061                                 case 'q':
00062                                         flag_quit_message = true;
00063                                 break;
00064                                 case 'p':
00065                                         flag_privmsg = true;
00066                                 break;
00067                                 case 'n':
00068                                         flag_notice = true;
00069                                 break;
00070                                 case '*':
00071                                         flag_no_opers = flag_part_message = flag_quit_message =
00072                                                 flag_privmsg = flag_notice = true;
00073                                 break;
00074                                 default:
00075                                         return x;
00076                                 break;
00077                         }
00078                 }
00079                 return 0;
00080         }
00081 
00082         FilterResult()
00083         {
00084         }
00085 
00086         virtual ~FilterResult()
00087         {
00088         }
00089 };
00090 
00091 class CommandFilter;
00092 
00093 class FilterBase : public Module
00094 {
00095         CommandFilter* filtcommand;
00096         int flags;
00097 protected:
00098         std::vector<std::string> exemptfromfilter; // List of channel names excluded from filtering.
00099  public:
00100         FilterBase(InspIRCd* Me, const std::string &source);
00101         virtual ~FilterBase();
00102         virtual int OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list);
00103         virtual FilterResult* FilterMatch(User* user, const std::string &text, int flags) = 0;
00104         virtual bool DeleteFilter(const std::string &freeform) = 0;
00105         virtual void SyncFilters(Module* proto, void* opaque) = 0;
00106         virtual void SendFilter(Module* proto, void* opaque, FilterResult* iter);
00107         virtual std::pair<bool, std::string> AddFilter(const std::string &freeform, const std::string &type, const std::string &reason, long duration, const std::string &flags) = 0;
00108         virtual int OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list);
00109         virtual void OnRehash(User* user, const std::string &parameter);
00110         virtual Version GetVersion();
00111         std::string EncodeFilter(FilterResult* filter);
00112         FilterResult DecodeFilter(const std::string &data);
00113         virtual void OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable = false);
00114         virtual void OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata);
00115         virtual int OnStats(char symbol, User* user, string_list &results) = 0;
00116         virtual int OnPreCommand(std::string &command, std::vector<std::string> &parameters, User *user, bool validated, const std::string &original_line);
00117         bool AppliesToMe(User* user, FilterResult* filter, int flags);
00118 };
00119 
00120 class CommandFilter : public Command
00121 {
00122         FilterBase* Base;
00123  public:
00124         CommandFilter(FilterBase* f, InspIRCd* Me, const std::string &ssource) : Command(Me, "FILTER", "o", 1), Base(f)
00125         {
00126                 this->source = ssource;
00127                 this->syntax = "<filter-definition> <type> <flags> [<gline-duration>] :<reason>";
00128         }
00129 
00130         CmdResult Handle(const std::vector<std::string> &parameters, User *user)
00131         {
00132                 if (parameters.size() == 1)
00133                 {
00134                         /* Deleting a filter */
00135                         if (Base->DeleteFilter(parameters[0]))
00136                         {
00137                                 user->WriteServ("NOTICE %s :*** Deleted filter '%s'", user->nick.c_str(), parameters[0].c_str());
00138                                 return CMD_SUCCESS;
00139                         }
00140                         else
00141                         {
00142                                 user->WriteServ("NOTICE %s :*** Filter '%s' not found on list.", user->nick.c_str(), parameters[0].c_str());
00143                                 return CMD_FAILURE;
00144                         }
00145                 }
00146                 else
00147                 {
00148                         /* Adding a filter */
00149                         if (parameters.size() >= 4)
00150                         {
00151                                 std::string freeform = parameters[0];
00152                                 std::string type = parameters[1];
00153                                 std::string flags = parameters[2];
00154                                 std::string reason;
00155                                 long duration = 0;
00156 
00157 
00158                                 if ((type != "gline") && (type != "none") && (type != "block") && (type != "kill") && (type != "silent"))
00159                                 {
00160                                         user->WriteServ("NOTICE %s :*** Invalid filter type '%s'. Supported types are 'gline', 'none', 'block', 'silent' and 'kill'.", user->nick.c_str(), freeform.c_str());
00161                                         return CMD_FAILURE;
00162                                 }
00163 
00164                                 if (type == "gline")
00165                                 {
00166                                         if (parameters.size() >= 5)
00167                                         {
00168                                                 duration = ServerInstance->Duration(parameters[3]);
00169                                                 reason = parameters[4];
00170                                         }
00171                                         else
00172                                         {
00173                                                 this->TooFewParams(user, " When setting a gline type filter, a gline duration must be specified as the third parameter.");
00174                                                 return CMD_FAILURE;
00175                                         }
00176                                 }
00177                                 else
00178                                 {
00179                                         reason = parameters[3];
00180                                 }
00181                                 std::pair<bool, std::string> result = Base->AddFilter(freeform, type, reason, duration, flags);
00182                                 if (result.first)
00183                                 {
00184                                         user->WriteServ("NOTICE %s :*** Added filter '%s', type '%s'%s%s, flags '%s', reason: '%s'", user->nick.c_str(), freeform.c_str(),
00185                                                         type.c_str(), (duration ? " duration: " : ""), (duration ? parameters[3].c_str() : ""),
00186                                                         flags.c_str(), reason.c_str());
00187                                         return CMD_SUCCESS;
00188                                 }
00189                                 else
00190                                 {
00191                                         user->WriteServ("NOTICE %s :*** Filter '%s' could not be added: %s", user->nick.c_str(), freeform.c_str(), result.second.c_str());
00192                                         return CMD_FAILURE;
00193                                 }
00194                         }
00195                         else
00196                         {
00197                                 this->TooFewParams(user, ".");
00198                                 return CMD_FAILURE;
00199                         }
00200 
00201                 }
00202         }
00203 
00204         void TooFewParams(User* user, const std::string &extra_text)
00205         {
00206                 user->WriteServ("NOTICE %s :*** Not enough parameters%s", user->nick.c_str(), extra_text.c_str());
00207         }
00208 };
00209 
00210 bool FilterBase::AppliesToMe(User* user, FilterResult* filter, int iflags)
00211 {
00212         if ((filter->flag_no_opers) && IS_OPER(user))
00213                 return false;
00214         if ((iflags & FLAG_PRIVMSG) && (!filter->flag_privmsg))
00215                 return false;
00216         if ((iflags & FLAG_NOTICE) && (!filter->flag_notice))
00217                 return false;
00218         if ((iflags & FLAG_QUIT)   && (!filter->flag_quit_message))
00219                 return false;
00220         if ((iflags & FLAG_PART)   && (!filter->flag_part_message))
00221                 return false;
00222         return true;
00223 }
00224 
00225 FilterBase::FilterBase(InspIRCd* Me, const std::string &source) : Module(Me)
00226 {
00227         filtcommand = new CommandFilter(this, Me, source);
00228         ServerInstance->AddCommand(filtcommand);
00229         Implementation eventlist[] = { I_OnPreCommand, I_OnStats, I_OnSyncOtherMetaData, I_OnDecodeMetaData, I_OnUserPreMessage, I_OnUserPreNotice, I_OnRehash };
00230         ServerInstance->Modules->Attach(eventlist, this, 7);
00231 }
00232 
00233 FilterBase::~FilterBase()
00234 {
00235 }
00236 
00237 int FilterBase::OnUserPreMessage(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
00238 {
00239         flags = FLAG_PRIVMSG;
00240         return OnUserPreNotice(user,dest,target_type,text,status,exempt_list);
00241 }
00242 
00243 int FilterBase::OnUserPreNotice(User* user,void* dest,int target_type, std::string &text, char status, CUList &exempt_list)
00244 {
00245         if (!flags)
00246                 flags = FLAG_NOTICE;
00247 
00248         /* Leave ulines alone */
00249         if ((ServerInstance->ULine(user->server)) || (!IS_LOCAL(user)))
00250                 return 0;
00251 
00252         FilterResult* f = this->FilterMatch(user, text, flags);
00253         if (f)
00254         {
00255                 std::string target = "";
00256                 if (target_type == TYPE_USER)
00257                 {
00258                         User* t = (User*)dest;
00259                         target = std::string(t->nick);
00260                 }
00261                 else if (target_type == TYPE_CHANNEL)
00262                 {
00263                         Channel* t = (Channel*)dest;
00264                         target = std::string(t->name);
00265                         std::vector<std::string>::iterator i = find(exemptfromfilter.begin(), exemptfromfilter.end(), target);
00266                         if (i != exemptfromfilter.end()) return 0;
00267                 }
00268                 if (f->action == "block")
00269                 {       
00270                         ServerInstance->SNO->WriteToSnoMask('A', std::string("FILTER: ")+user->nick+" had their message filtered, target was "+target+": "+f->reason);
00271                         user->WriteServ("NOTICE "+std::string(user->nick)+" :Your message has been filtered and opers notified: "+f->reason);
00272                 }
00273                 if (f->action == "silent")
00274                 {
00275                         user->WriteServ("NOTICE "+std::string(user->nick)+" :Your message has been filtered: "+f->reason);
00276                 }
00277                 if (f->action == "kill")
00278                 {
00279                         ServerInstance->Users->QuitUser(user, "Filtered: " + f->reason);
00280                 }
00281                 if (f->action == "gline")
00282                 {
00283                         GLine* gl = new GLine(ServerInstance, ServerInstance->Time(), f->gline_time, ServerInstance->Config->ServerName, f->reason.c_str(), "*", user->GetIPString());
00284                         if (ServerInstance->XLines->AddLine(gl,NULL))
00285                         {
00286                                 ServerInstance->XLines->ApplyLines();
00287                         }
00288                         else
00289                                 delete gl;
00290                 }
00291 
00292                 ServerInstance->Logs->Log("FILTER",DEFAULT,"FILTER: "+ user->nick + " had their message filtered, target was " + target + ": " + f->reason + " Action: " + f->action);
00293                 return 1;
00294         }
00295         return 0;
00296 }
00297 
00298 int FilterBase::OnPreCommand(std::string &command, std::vector<std::string> &parameters, User *user, bool validated, const std::string &original_line)
00299 {
00300         flags = 0;
00301         if (validated && IS_LOCAL(user))
00302         {
00303                 std::string checkline;
00304                 int replacepoint = 0;
00305                 bool parting = false;
00306         
00307                 if (command == "QUIT")
00308                 {
00309                         /* QUIT with no reason: nothing to do */
00310                         if (parameters.size() < 1)
00311                                 return 0;
00312 
00313                         checkline = parameters[0];
00314                         replacepoint = 0;
00315                         parting = false;
00316                         flags = FLAG_QUIT;
00317                 }
00318                 else if (command == "PART")
00319                 {
00320                         /* PART with no reason: nothing to do */
00321                         if (parameters.size() < 2)
00322                                 return 0;
00323 
00324                         std::vector<std::string>::iterator i = find(exemptfromfilter.begin(), exemptfromfilter.end(), parameters[0]);
00325                         if (i != exemptfromfilter.end()) return 0;
00326                         checkline = parameters[1];
00327                         replacepoint = 1;
00328                         parting = true;
00329                         flags = FLAG_PART;
00330                 }
00331                 else
00332                         /* We're only messing with PART and QUIT */
00333                         return 0;
00334 
00335                 FilterResult* f = NULL;
00336                 
00337                 if (flags)
00338                         f = this->FilterMatch(user, checkline, flags);
00339 
00340                 if (!f)
00341                         /* PART or QUIT reason doesnt match a filter */
00342                         return 0;
00343 
00344                 /* We cant block a part or quit, so instead we change the reason to 'Reason filtered' */
00345                 Command* c = ServerInstance->Parser->GetHandler(command);
00346                 if (c)
00347                 {
00348                         std::vector<std::string> params;
00349                         for (int item = 0; item < (int)parameters.size(); item++)
00350                                 params.push_back(parameters[item]);
00351                         params[replacepoint] = "Reason filtered";
00352 
00353                         /* We're blocking, OR theyre quitting and its a KILL action
00354                          * (we cant kill someone whos already quitting, so filter them anyway)
00355                          */
00356                         if ((f->action == "block") || (((!parting) && (f->action == "kill"))) || (f->action == "silent"))
00357                         {
00358                                 c->Handle(params, user);
00359                                 return 1;
00360                         }
00361                         else
00362                         {
00363                                 /* Are they parting, if so, kill is applicable */
00364                                 if ((parting) && (f->action == "kill"))
00365                                 {
00366                                         user->WriteServ("NOTICE %s :*** Your PART message was filtered: %s", user->nick.c_str(), f->reason.c_str());
00367                                         ServerInstance->Users->QuitUser(user, "Filtered: " + f->reason);
00368                                 }
00369                                 if (f->action == "gline")
00370                                 {
00371                                         /* Note: We gline *@IP so that if their host doesnt resolve the gline still applies. */
00372                                         GLine* gl = new GLine(ServerInstance, ServerInstance->Time(), f->gline_time, ServerInstance->Config->ServerName, f->reason.c_str(), "*", user->GetIPString());
00373                                         if (ServerInstance->XLines->AddLine(gl,NULL))
00374                                         {
00375                                                 ServerInstance->XLines->ApplyLines();
00376                                         }
00377                                         else
00378                                                 delete gl;
00379                                 }
00380                                 return 1;
00381                         }
00382                 }
00383                 return 0;
00384         }
00385         return 0;
00386 }
00387 
00388 void FilterBase::OnRehash(User* user, const std::string &parameter)
00389 {
00390         ConfigReader* MyConf = new ConfigReader(ServerInstance);
00391         std::vector<std::string>().swap(exemptfromfilter);
00392         for (int index = 0; index < MyConf->Enumerate("exemptfromfilter"); ++index)
00393         {
00394                 std::string chan = MyConf->ReadValue("exemptfromfilter", "channel", index);
00395                 if (!chan.empty()) {
00396                         exemptfromfilter.push_back(chan);
00397                 }
00398         }
00399         delete MyConf;
00400 }
00401 
00402 Version FilterBase::GetVersion()
00403 {
00404         return Version("$Id: m_filter.h 10295 2008-08-25 20:50:11Z w00t $", VF_VENDOR | VF_COMMON, API_VERSION);
00405 }
00406 
00407 
00408 std::string FilterBase::EncodeFilter(FilterResult* filter)
00409 {
00410         std::ostringstream stream;
00411         std::string x = filter->freeform;
00412 
00413         /* Hax to allow spaces in the freeform without changing the design of the irc protocol */
00414         for (std::string::iterator n = x.begin(); n != x.end(); n++)
00415                 if (*n == ' ')
00416                         *n = '\7';
00417 
00418         stream << x << " " << filter->action << " " << (filter->flags.empty() ? "-" : filter->flags) << " " << filter->gline_time << " :" << filter->reason;
00419         return stream.str();
00420 }
00421 
00422 FilterResult FilterBase::DecodeFilter(const std::string &data)
00423 {
00424         FilterResult res;
00425         irc::tokenstream tokens(data);
00426         tokens.GetToken(res.freeform);
00427         tokens.GetToken(res.action);
00428         tokens.GetToken(res.flags);
00429         if (res.flags == "-")
00430                 res.flags = "";
00431         res.FillFlags(res.flags);
00432         tokens.GetToken(res.gline_time);
00433         tokens.GetToken(res.reason);
00434 
00435         /* Hax to allow spaces in the freeform without changing the design of the irc protocol */
00436         for (std::string::iterator n = res.freeform.begin(); n != res.freeform.end(); n++)
00437                 if (*n == '\7')
00438                         *n = ' ';
00439 
00440         return res;
00441 }
00442 
00443 void FilterBase::OnSyncOtherMetaData(Module* proto, void* opaque, bool displayable)
00444 {
00445         this->SyncFilters(proto, opaque);
00446 }
00447 
00448 void FilterBase::SendFilter(Module* proto, void* opaque, FilterResult* iter)
00449 {
00450         proto->ProtoSendMetaData(opaque, TYPE_OTHER, NULL, "filter", EncodeFilter(iter));
00451 }
00452 
00453 void FilterBase::OnDecodeMetaData(int target_type, void* target, const std::string &extname, const std::string &extdata)
00454 {
00455         if ((target_type == TYPE_OTHER) && (extname == "filter"))
00456         {
00457                 FilterResult data = DecodeFilter(extdata);
00458                 this->AddFilter(data.freeform, data.action, data.reason, data.gline_time, data.flags);
00459         }
00460 }
00461