X-Git-Url: https://git.netwichtig.de/gitweb/?a=blobdiff_plain;f=src%2Fcommands.cpp;h=0797c3f19d08edac492e5fbec7b0c525daa93cb8;hb=cef3e32fae0132d51b2fcca16ef05907b174e2a3;hp=82a6a2237670992be8a0569cbd3bcad37790aa1c;hpb=5a331966832a930d4770752f5e60338581830887;p=user%2Fhenk%2Fcode%2Finspircd.git diff --git a/src/commands.cpp b/src/commands.cpp index 82a6a2237..0797c3f19 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -50,6 +50,9 @@ using namespace std; #include #include #include +#ifdef THREADED_DNS +#include +#endif #ifndef RUSAGE_SELF #define RUSAGE_SELF 0 #define RUSAGE_CHILDREN -1 @@ -61,20 +64,16 @@ using namespace std; #include "dynamic.h" #include "wildcard.h" #include "message.h" +#include "commands.h" #include "mode.h" #include "xline.h" #include "inspstring.h" #include "dnsqueue.h" #include "helperfuncs.h" #include "hashcomp.h" +#include "socketengine.h" -#ifdef USE_KQUEUE -extern int kq; -#endif - -#ifdef USE_EPOLL -int ep; -#endif +extern SocketEngine* SE; extern int MODCOUNT; extern std::vector modules; @@ -203,7 +202,7 @@ void handle_kick(char **parameters, int pcnt, userrec *user) return; } - if (!has_channel(user,Ptr)) + if ((!has_channel(user,Ptr)) && (!is_uline(user->server))) { WriteServ(user->fd,"442 %s %s :You're not on that channel!",user->nick, parameters[0]); return; @@ -472,7 +471,7 @@ void handle_topic(char **parameters, int pcnt, userrec *user) { if (((Ptr) && (!has_channel(user,Ptr))) && (Ptr->binarymodes & CM_SECRET)) { - WriteServ(user->fd,"442 %s %s :You're not on that channel!",user->nick, Ptr->name); + WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, Ptr->name); return; } if (Ptr->topicset) @@ -559,7 +558,7 @@ void handle_names(char **parameters, int pcnt, userrec *user) { if (((c) && (!has_channel(user,c))) && (c->binarymodes & CM_SECRET)) { - WriteServ(user->fd,"442 %s %s :You're not on that channel!",user->nick, c->name); + WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, c->name); return; } userlist(user,c); @@ -580,7 +579,18 @@ void handle_privmsg(char **parameters, int pcnt, userrec *user) if (loop_call(handle_privmsg,parameters,pcnt,user,0,pcnt-2,0)) return; - if (parameters[0][0] == '#') + if (parameters[0][0] == '$') + { + // notice to server mask + char* servermask = parameters[0]; + servermask++; + if (match(ServerName,servermask)) + { + ServerPrivmsgAll("%s",parameters[1]); + } + return; + } + else if (parameters[0][0] == '#') { chan = FindChan(parameters[0]); if (chan) @@ -667,7 +677,18 @@ void handle_notice(char **parameters, int pcnt, userrec *user) if (loop_call(handle_notice,parameters,pcnt,user,0,pcnt-2,0)) return; - if (parameters[0][0] == '#') + if (parameters[0][0] == '$') + { + // notice to server mask + char* servermask = parameters[0]; + servermask++; + if (match(ServerName,servermask)) + { + NoticeAll(user, true, "%s",parameters[1]); + } + return; + } + else if (parameters[0][0] == '#') { chan = FindChan(parameters[0]); if (chan) @@ -756,6 +777,7 @@ void handle_info(char **parameters, int pcnt, userrec *user) WriteServ(user->fd,"371 %s : Jazza",user->nick); WriteServ(user->fd,"371 %s : ",user->nick); WriteServ(user->fd,"371 %s :Testers: CC",user->nick); + WriteServ(user->fd,"371 %s : Om",user->nick); WriteServ(user->fd,"371 %s : Piggles",user->nick); WriteServ(user->fd,"371 %s : Foamy",user->nick); WriteServ(user->fd,"371 %s : Hart",user->nick); @@ -771,6 +793,7 @@ void handle_info(char **parameters, int pcnt, userrec *user) WriteServ(user->fd,"371 %s : Rob",user->nick); WriteServ(user->fd,"371 %s : angelic",user->nick); WriteServ(user->fd,"371 %s : Jason",user->nick); + WriteServ(user->fd,"371 %s : ThaPrince",user->nick); WriteServ(user->fd,"371 %s : ",user->nick); WriteServ(user->fd,"371 %s :Thanks to irc-junkie and searchirc",user->nick); WriteServ(user->fd,"371 %s :for the nice comments and the help",user->nick); @@ -796,55 +819,72 @@ void handle_time(char **parameters, int pcnt, userrec *user) void handle_whois(char **parameters, int pcnt, userrec *user) { userrec *dest; - - if (loop_call(handle_whois,parameters,pcnt,user,0,pcnt-1,0)) - return; + if (loop_call(handle_whois,parameters,pcnt,user,0,pcnt-1,0)) + return; dest = Find(parameters[0]); if (dest) { - // bug found by phidjit - were able to whois an incomplete connection if it had sent a NICK or USER - if (dest->registered == 7) + do_whois(user,dest,0,0,parameters[0]); + } + else + { + /* no such nick/channel */ + WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]); + WriteServ(user->fd,"318 %s %s :End of /WHOIS list.",user->nick, parameters[0]); + } +} + +void do_whois(userrec* user, userrec* dest,unsigned long signon, unsigned long idle, char* nick) +{ + // bug found by phidjit - were able to whois an incomplete connection if it had sent a NICK or USER + if (dest->registered == 7) + { + WriteServ(user->fd,"311 %s %s %s %s * :%s",user->nick, dest->nick, dest->ident, dest->dhost, dest->fullname); + if ((user == dest) || (strchr(user->modes,'o'))) { - WriteServ(user->fd,"311 %s %s %s %s * :%s",user->nick, dest->nick, dest->ident, dest->dhost, dest->fullname); - if ((user == dest) || (strchr(user->modes,'o'))) - { - WriteServ(user->fd,"378 %s %s :is connecting from *@%s %s",user->nick, dest->nick, dest->host, dest->ip); - } - char* cl = chlist(dest,user); - if (strcmp(cl,"")) - { - WriteServ(user->fd,"319 %s %s :%s",user->nick, dest->nick, cl); - } - WriteServ(user->fd,"312 %s %s %s :%s",user->nick, dest->nick, dest->server, GetServerDescription(dest->server).c_str()); - if (strcmp(dest->awaymsg,"")) + WriteServ(user->fd,"378 %s %s :is connecting from *@%s %s",user->nick, dest->nick, dest->host, dest->ip); + } + char* cl = chlist(dest,user); + if (*cl) + { + WriteServ(user->fd,"319 %s %s :%s",user->nick, dest->nick, cl); + } + WriteServ(user->fd,"312 %s %s %s :%s",user->nick, dest->nick, dest->server, GetServerDescription(dest->server).c_str()); + if (*dest->awaymsg) + { + WriteServ(user->fd,"301 %s %s :%s",user->nick, dest->nick, dest->awaymsg); + } + if (strchr(dest->modes,'o')) + { + if (*dest->oper) { - WriteServ(user->fd,"301 %s %s :%s",user->nick, dest->nick, dest->awaymsg); + WriteServ(user->fd,"313 %s %s :is %s %s on %s",user->nick, dest->nick, (strchr("aeiou",dest->oper[0]) ? "an" : "a"),dest->oper, Network); } - if ((strchr(dest->modes,'o')) && (strcmp(dest->oper,""))) + else { - WriteServ(user->fd,"313 %s %s :is %s %s on %s",user->nick, dest->nick, - (strchr("aeiou",dest->oper[0]) ? "an" : "a"),dest->oper, Network); + WriteServ(user->fd,"313 %s %s :is opered but has an unknown type",user->nick, dest->nick); } + } + if ((!signon) && (!idle)) + { FOREACH_MOD OnWhois(user,dest); - if (!strcasecmp(user->server,dest->server)) - { - // idle time and signon line can only be sent if youre on the same server (according to RFC) - WriteServ(user->fd,"317 %s %s %d %d :seconds idle, signon time",user->nick, dest->nick, abs((dest->idle_lastmsg)-TIME), dest->signon); - } - - WriteServ(user->fd,"318 %s %s :End of /WHOIS list.",user->nick, dest->nick); + } + if (!strcasecmp(user->server,dest->server)) + { + // idle time and signon line can only be sent if youre on the same server (according to RFC) + WriteServ(user->fd,"317 %s %s %d %d :seconds idle, signon time",user->nick, dest->nick, abs((dest->idle_lastmsg)-TIME), dest->signon); } else { - WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]); - WriteServ(user->fd,"318 %s %s :End of /WHOIS list.",user->nick, parameters[0]); + if ((idle) || (signon)) + WriteServ(user->fd,"317 %s %s %d %d :seconds idle, signon time",user->nick, dest->nick, idle, signon); } + WriteServ(user->fd,"318 %s %s :End of /WHOIS list.",user->nick, dest->nick); } else { - /* no such nick/channel */ - WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, parameters[0]); - WriteServ(user->fd,"318 %s %s :End of /WHOIS list.",user->nick, parameters[0]); + WriteServ(user->fd,"401 %s %s :No such nick/channel",user->nick, nick); + WriteServ(user->fd,"318 %s %s :End of /WHOIS list.",user->nick, nick); } } @@ -869,9 +909,20 @@ void handle_quit(char **parameters, int pcnt, userrec *user) reason[MAXQUIT-1] = '\0'; } - Write(user->fd,"ERROR :Closing link (%s@%s) [%s%s]",user->ident,user->host,PrefixQuit,parameters[0]); - WriteOpers("*** Client exiting: %s!%s@%s [%s%s]",user->nick,user->ident,user->host,PrefixQuit,parameters[0]); - WriteCommonExcept(user,"QUIT :%s%s",PrefixQuit,parameters[0]); + /* We should only prefix the quit for a local user. Remote users have + * already been prefixed, where neccessary, by the upstream server. + */ + if (!strcasecmp(user->server,ServerName)) + { + Write(user->fd,"ERROR :Closing link (%s@%s) [%s%s]",user->ident,user->host,PrefixQuit,parameters[0]); + WriteOpers("*** Client exiting: %s!%s@%s [%s%s]",user->nick,user->ident,user->host,PrefixQuit,parameters[0]); + WriteCommonExcept(user,"QUIT :%s%s",PrefixQuit,parameters[0]); + } + else + { + WriteOpers("*** Client exiting at %s: %s!%s@%s [%s]",user->server,user->nick,user->ident,user->host,parameters[0]); + WriteCommonExcept(user,"QUIT :%s",parameters[0]); + } FOREACH_MOD OnUserQuit(user,std::string(PrefixQuit)+std::string(parameters[0])); } @@ -891,25 +942,7 @@ void handle_quit(char **parameters, int pcnt, userrec *user) /* push the socket on a stack of sockets due to be closed at the next opportunity */ if (user->fd > -1) { -#ifdef USE_KQUEUE - struct kevent ke; - EV_SET(&ke, user->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); - int i = kevent(kq, &ke, 1, 0, 0, NULL); - if (i == -1) - { - log(DEBUG,"kqueue: Failed to remove user from queue!"); - } -#endif -#ifdef USE_EPOLL - struct epoll_event ev; - ev.events = EPOLLIN | EPOLLET; - ev.data.fd = user->fd; - int i = epoll_ctl(ep, EPOLL_CTL_DEL, user->fd, &ev); - if (i < 0) - { - log(DEBUG,"epoll: List deletion failure!"); - } -#endif + SE->DelFd(user->fd); user->CloseSocket(); } @@ -1055,6 +1088,7 @@ void handle_who(char **parameters, int pcnt, userrec *user) void handle_wallops(char **parameters, int pcnt, userrec *user) { WriteWallOps(user,false,"%s",parameters[0]); + FOREACH_MOD OnWallops(user,parameters[0]); } void handle_list(char **parameters, int pcnt, userrec *user) @@ -1075,21 +1109,28 @@ void handle_list(char **parameters, int pcnt, userrec *user) void handle_rehash(char **parameters, int pcnt, userrec *user) { WriteServ(user->fd,"382 %s %s :Rehashing",user->nick,CleanFilename(CONFIG_FILE)); - ReadConfig(false,user); std::string parameter = ""; if (pcnt) + { parameter = parameters[0]; + } + else + { + WriteOpers("%s is rehashing config file %s",user->nick,CleanFilename(CONFIG_FILE)); + ReadConfig(false,user); + } FOREACH_MOD OnRehash(parameter); - WriteOpers("%s is rehashing config file %s",user->nick,CleanFilename(CONFIG_FILE)); } void handle_lusers(char **parameters, int pcnt, userrec *user) { - WriteServ(user->fd,"251 %s :There are %d users and %d invisible on %d servers",user->nick,usercnt()-usercount_invisible(),usercount_invisible(),servercount()); + // this lusers command shows one server at all times because + // a protocol module must override it to show those stats. + WriteServ(user->fd,"251 %s :There are %d users and %d invisible on 1 server",user->nick,usercnt()-usercount_invisible(),usercount_invisible()); WriteServ(user->fd,"252 %s %d :operator(s) online",user->nick,usercount_opers()); WriteServ(user->fd,"253 %s %d :unknown connections",user->nick,usercount_unknown()); WriteServ(user->fd,"254 %s %d :channels formed",user->nick,chancount()); - WriteServ(user->fd,"254 %s :I have %d clients and %d servers",user->nick,local_count(),count_servs()); + WriteServ(user->fd,"254 %s :I have %d clients and 0 servers",user->nick,local_count()); } void handle_admin(char **parameters, int pcnt, userrec *user) @@ -1294,7 +1335,6 @@ void handle_modules(char **parameters, int pcnt, userrec *user) void handle_stats(char **parameters, int pcnt, userrec *user) { - char Link_ServerName[MAXBUF],Link_IPAddr[MAXBUF],Link_Port[MAXBUF]; if (pcnt != 1) { return; @@ -1310,14 +1350,7 @@ void handle_stats(char **parameters, int pcnt, userrec *user) if (*parameters[0] == 'c') { - for (int i = 0; i < ConfValueEnum("link",&config_f); i++) - { - ConfValue("link","name",i,Link_ServerName,&config_f); - ConfValue("link","ipaddr",i,Link_IPAddr,&config_f); - ConfValue("link","port",i,Link_Port,&config_f); - WriteServ(user->fd,"213 %s C *@%s * %s %s 0 M",user->nick,Link_IPAddr,Link_ServerName,Link_Port); - WriteServ(user->fd,"244 %s H * * %s",user->nick,Link_ServerName); - } + /* This stats symbol must be handled by a linking module */ } if (*parameters[0] == 'i') @@ -1342,10 +1375,11 @@ void handle_stats(char **parameters, int pcnt, userrec *user) if (*parameters[0] == 'U') { + char ulined[MAXBUF]; for (int i = 0; i < ConfValueEnum("uline",&config_f); i++) { - ConfValue("uline","server",i,Link_ServerName,&config_f); - WriteServ(user->fd,"248 %s U %s",user->nick,Link_ServerName); + ConfValue("uline","server",i,ulined,&config_f); + WriteServ(user->fd,"248 %s U %s",user->nick,ulined); } } @@ -1512,8 +1546,10 @@ void handle_links(char **parameters, int pcnt, userrec *user) void handle_map(char **parameters, int pcnt, userrec *user) { - char line[MAXBUF]; - snprintf(line,MAXBUF,"006 %s :%s",user->nick,ServerName); + // as with /LUSERS this does nothing without a linking + // module to override its behaviour and display something + // better. + WriteServ(user->fd,"006 %s :%s",user->nick,ServerName); WriteServ(user->fd,"007 %s :End of /MAP",user->nick); } @@ -1521,8 +1557,9 @@ bool is_uline(const char* server) { char ServName[MAXBUF]; - /* fix, by w00t - per nenolod. I don't see how we can want '""' as a uline. */ - if (!server || !(*server)) + if (!server) + return false; + if (!(*server)) return true; for (int i = 0; i < ConfValueEnum("uline",&config_f); i++) @@ -1713,9 +1750,22 @@ void handle_nick(char **parameters, int pcnt, userrec *user) user->registered = (user->registered | 2); // dont attempt to look up the dns until they pick a nick... because otherwise their pointer WILL change // and unless we're lucky we'll get a duff one later on. + //user->dns_done = (!lookup_dns(user->nick)); + //if (user->dns_done) + // log(DEBUG,"Aborting dns lookup of %s because dns server experienced a failure.",user->nick); + +#ifdef THREADED_DNS + // initialize their dns lookup thread + if (pthread_create(&user->dnsthread, NULL, dns_task, (void *)user) != 0) + { + log(DEBUG,"Failed to create DNS lookup thread for user %s",user->nick); + } +#else user->dns_done = (!lookup_dns(user->nick)); if (user->dns_done) log(DEBUG,"Aborting dns lookup of %s because dns server experienced a failure.",user->nick); +#endif + } if (user->registered == 3) { @@ -1735,6 +1785,13 @@ long duration(const char* str) long total = 0; const char* str_end = str + strlen(str); n_field[0] = 0; + + if ((!strchr(str,'s')) && (!strchr(str,'m')) && (!strchr(str,'h')) && (!strchr(str,'d')) && (!strchr(str,'w')) && (!strchr(str,'y'))) + { + std::string n = str; + n = n + "s"; + return duration(n.c_str()); + } for (char* i = (char*)str; i < str_end; i++) { @@ -1782,12 +1839,95 @@ long duration(const char* str) return total; } +/* All other ircds when doing this check usually just look for a string of *@* or *. We're smarter than that, though. */ + +bool host_matches_everyone(std::string mask, userrec* user) +{ + char insanemasks[MAXBUF]; + char buffer[MAXBUF]; + char itrigger[MAXBUF]; + ConfValue("insane","hostmasks",0,insanemasks,&config_f); + ConfValue("insane","trigger",0,itrigger,&config_f); + if (*itrigger == 0) + strlcpy(itrigger,"95.5",MAXBUF); + if ((*insanemasks == 'y') || (*insanemasks == 't') || (*insanemasks == '1')) + return false; + long matches = 0; + for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++) + { + strlcpy(buffer,u->second->ident,MAXBUF); + strlcat(buffer,"@",MAXBUF); + strlcat(buffer,u->second->host,MAXBUF); + if (match(buffer,mask.c_str())) + matches++; + } + float percent = ((float)matches / (float)clientlist.size()) * 100; + if (percent > (float)atof(itrigger)) + { + WriteOpers("*** \2WARNING\2: %s tried to set a G/K/E line mask of %s, which covers %.2f%% of the network!",user->nick,mask.c_str(),percent); + return true; + } + return false; +} + +bool ip_matches_everyone(std::string ip, userrec* user) +{ + char insanemasks[MAXBUF]; + char itrigger[MAXBUF]; + ConfValue("insane","ipmasks",0,insanemasks,&config_f); + ConfValue("insane","trigger",0,itrigger,&config_f); + if (*itrigger == 0) + strlcpy(itrigger,"95.5",MAXBUF); + if ((*insanemasks == 'y') || (*insanemasks == 't') || (*insanemasks == '1')) + return false; + long matches = 0; + for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++) + { + if (match(u->second->ip,ip.c_str())) + matches++; + } + float percent = ((float)matches / (float)clientlist.size()) * 100; + if (percent > (float)atof(itrigger)) + { + WriteOpers("*** \2WARNING\2: %s tried to set a Z line mask of %s, which covers %.2f%% of the network!",user->nick,ip.c_str(),percent); + return true; + } + return false; +} + +bool nick_matches_everyone(std::string nick, userrec* user) +{ + char insanemasks[MAXBUF]; + char itrigger[MAXBUF]; + ConfValue("insane","nickmasks",0,insanemasks,&config_f); + ConfValue("insane","trigger",0,itrigger,&config_f); + if (*itrigger == 0) + strlcpy(itrigger,"95.5",MAXBUF); + if ((*insanemasks == 'y') || (*insanemasks == 't') || (*insanemasks == '1')) + return false; + long matches = 0; + for (user_hash::iterator u = clientlist.begin(); u != clientlist.end(); u++) + { + if (match(u->second->nick,nick.c_str())) + matches++; + } + float percent = ((float)matches / (float)clientlist.size()) * 100; + if (percent > (float)atof(itrigger)) + { + WriteOpers("*** \2WARNING\2: %s tried to set a Q line mask of %s, which covers %.2f%% of the network!",user->nick,nick.c_str(),percent); + return true; + } + return false; +} void handle_kline(char **parameters, int pcnt, userrec *user) { if (pcnt >= 3) { + if (host_matches_everyone(parameters[0],user)) + return; add_kline(duration(parameters[1]),user->nick,parameters[2],parameters[0]); + FOREACH_MOD OnAddKLine(duration(parameters[1]), user, parameters[2], parameters[0]); if (!duration(parameters[1])) { WriteOpers("*** %s added permenant K-line for %s.",user->nick,parameters[0]); @@ -1801,6 +1941,7 @@ void handle_kline(char **parameters, int pcnt, userrec *user) { if (del_kline(parameters[0])) { + FOREACH_MOD OnDelKLine(user, parameters[0]); WriteOpers("*** %s Removed K-line on %s.",user->nick,parameters[0]); } else @@ -1815,7 +1956,10 @@ void handle_eline(char **parameters, int pcnt, userrec *user) { if (pcnt >= 3) { + if (host_matches_everyone(parameters[0],user)) + return; add_eline(duration(parameters[1]),user->nick,parameters[2],parameters[0]); + FOREACH_MOD OnAddELine(duration(parameters[1]), user, parameters[2], parameters[0]); if (!duration(parameters[1])) { WriteOpers("*** %s added permenant E-line for %s.",user->nick,parameters[0]); @@ -1829,6 +1973,7 @@ void handle_eline(char **parameters, int pcnt, userrec *user) { if (del_eline(parameters[0])) { + FOREACH_MOD OnDelELine(user, parameters[0]); WriteOpers("*** %s Removed E-line on %s.",user->nick,parameters[0]); } else @@ -1841,10 +1986,12 @@ void handle_eline(char **parameters, int pcnt, userrec *user) void handle_gline(char **parameters, int pcnt, userrec *user) { - char netdata[MAXBUF]; if (pcnt >= 3) { + if (host_matches_everyone(parameters[0],user)) + return; add_gline(duration(parameters[1]),user->nick,parameters[2],parameters[0]); + FOREACH_MOD OnAddGLine(duration(parameters[1]), user, parameters[2], parameters[0]); if (!duration(parameters[1])) { WriteOpers("*** %s added permenant G-line for %s.",user->nick,parameters[0]); @@ -1858,8 +2005,7 @@ void handle_gline(char **parameters, int pcnt, userrec *user) { if (del_gline(parameters[0])) { - // . - snprintf(netdata,MAXBUF,". %s %s",parameters[0],user->nick); + FOREACH_MOD OnDelGLine(user, parameters[0]); WriteOpers("*** %s Removed G-line on %s.",user->nick,parameters[0]); } else @@ -1874,7 +2020,10 @@ void handle_zline(char **parameters, int pcnt, userrec *user) { if (pcnt >= 3) { + if (ip_matches_everyone(parameters[0],user)) + return; add_zline(duration(parameters[1]),user->nick,parameters[2],parameters[0]); + FOREACH_MOD OnAddZLine(duration(parameters[1]), user, parameters[2], parameters[0]); if (!duration(parameters[1])) { WriteOpers("*** %s added permenant Z-line for %s.",user->nick,parameters[0]); @@ -1888,6 +2037,7 @@ void handle_zline(char **parameters, int pcnt, userrec *user) { if (del_zline(parameters[0])) { + FOREACH_MOD OnDelZLine(user, parameters[0]); WriteOpers("*** %s Removed Z-line on %s.",user->nick,parameters[0]); } else @@ -1902,7 +2052,10 @@ void handle_qline(char **parameters, int pcnt, userrec *user) { if (pcnt >= 3) { + if (nick_matches_everyone(parameters[0],user)) + return; add_qline(duration(parameters[1]),user->nick,parameters[2],parameters[0]); + FOREACH_MOD OnAddQLine(duration(parameters[1]), user, parameters[2], parameters[0]); if (!duration(parameters[1])) { WriteOpers("*** %s added permenant Q-line for %s.",user->nick,parameters[0]); @@ -1916,6 +2069,7 @@ void handle_qline(char **parameters, int pcnt, userrec *user) { if (del_qline(parameters[0])) { + FOREACH_MOD OnDelQLine(user, parameters[0]); WriteOpers("*** %s Removed Q-line on %s.",user->nick,parameters[0]); } else