From: Chris Porter Date: Fri, 30 Dec 2005 14:42:00 +0000 (+0000) Subject: Merged revisions 310,312-314,322-323,328-329 via svnmerge from X-Git-Url: https://jfr.im/git/irc/quakenet/newserv.git/commitdiff_plain/664aeff392777d5887852b6f91fda7449b098aab?hp=9f824c9581a915db12e154bd1c75a622641fbc97 Merged revisions 310,312-314,322-323,328-329 via svnmerge from https://svn.quakenet.org/svn/dev-com/N/trunk ........ r310 | cruicky | 2005-12-04 17:45:05 +0000 (Sun, 04 Dec 2005) | 2 lines Request from Henrik for an option to prevent users updating channel settings via authgate. Added channel flag +l for nterfacer to check whether the channel is locked. ........ r312 | strutsi | 2005-12-05 18:03:07 +0000 (Mon, 05 Dec 2005) | 2 lines G 2.15 mostly bugfixes ........ r313 | paul | 2005-12-10 15:51:21 +0000 (Sat, 10 Dec 2005) | 2 lines dont free(arg) else bad things happen i.e. a valid users nick gets nuked ........ r314 | paul | 2005-12-10 15:59:39 +0000 (Sat, 10 Dec 2005) | 2 lines fix searching by auth/host typo ........ r322 | cruicky | 2005-12-20 22:29:34 +0000 (Tue, 20 Dec 2005) | 11 lines Removed +p chanmode. Removed +@'s for both points and patterns in profiles. Changed the way in which the 'Points' part of the profile is used. Added AMSG detection. Fixed SQL injection in SPAMLOOKUP. Changed DB's queries so they don't use SELECT * The following changes were recommended by staff: SPAMLOOKUP without IPs/Hosts for helpers. Restricted SPAMLOOKUP to a maximum of 50 results. REJOIN lowered to helper level. ........ r323 | cruicky | 2005-12-21 00:32:09 +0000 (Wed, 21 Dec 2005) | 1 line Fixed a small issue where hostnames and IPs would be shown if a user had an oper or greater account, but not opered. ........ r328 | cruicky | 2005-12-21 21:47:53 +0000 (Wed, 21 Dec 2005) | 3 lines Fixed an error in string parser causing the repeat detector to fail. Added storage of strings so SPAMLOOKUP can show what was said. General code changes. ........ r329 | cruicky | 2005-12-23 20:40:46 +0000 (Fri, 23 Dec 2005) | 3 lines 2 minor fixes: Text would not be stored if regex matched. Fixed whitespace issue in string compacter. ........ --- diff --git a/carrot/carrot.c b/carrot/carrot.c index ce89788a..e39bef28 100644 --- a/carrot/carrot.c +++ b/carrot/carrot.c @@ -7,10 +7,8 @@ int ca_carrot(void *source, int cargc, char **cargv) { nick *victim; channel *cp; - if (cargc<1) { - controlreply(sender,"Usage: carrot channel and/or user"); - return CMD_ERROR; - } + if (cargc<1) + return CMD_USAGE; if ((victim=getnickbynick(cargv[0]))!=NULL) { controlreply(victim,"%cACTION ger %s en morot%c",1,victim->nick,1); @@ -25,14 +23,13 @@ int ca_carrot(void *source, int cargc, char **cargv) { controlreply(sender,"Used carrot in %s.",cp->index->name->content); } else { controlreply(sender,"Couldn't find %s.",cargv[0]); - return; } return CMD_OK; } void _init() { - registercontrolcmd("carrot",10,2,ca_carrot); + registercontrolhelpcmd("carrot",NO_OPERED,2,ca_carrot,"Usage: carrot <#channel|user> ?user?"); } void _fini() { diff --git a/chansearch/chansearch.c b/chansearch/chansearch.c index 734c884b..4adfcc7b 100644 --- a/chansearch/chansearch.c +++ b/chansearch/chansearch.c @@ -56,7 +56,7 @@ void _init() { regchansearchdisp("topic", cs_desctopic); regchansearchdisp("services", cs_descservices); - registercontrolcmd("chansearch",10,19,&dochansearch); + registercontrolhelpcmd("chansearch",NO_OPER,19,&dochansearch,"Usage: chansearch \nSearches for channels with specified criteria.\nSend chanstats with no arguments for more information."); } void _fini() { diff --git a/chanstats/chansearch.c b/chanstats/chansearch.c index bb0d3c1e..3f4564a2 100644 --- a/chanstats/chansearch.c +++ b/chanstats/chansearch.c @@ -46,7 +46,10 @@ void initchansearch() { regchansearchfunc("moderated", 0, cs_moderated); regchansearchfunc("modes", 1, cs_modes); - registercontrolcmd("chansearch",10,19,&dochansearch); + registercontrolhelpcmd("chansearch",NO_OPER,19,&dochansearch, + "Usage: chansearch \n" + " Valid search terms are: name, exists, size, nick, namelen, keyed, secret, invite\n" + " Terms can be inverted with !"); } void finichansearch() { @@ -73,12 +76,8 @@ int dochansearch(void *source, int cargc, char **cargv) { int offset; int res; - if (cargc<1) { - controlreply(sender,"Usage: chansearch (search terms)"); - controlreply(sender," Valid search terms are: name, exists, size, nick, namelen, keyed, secret, invite"); - controlreply(sender," Terms can be inverted with !"); - return CMD_OK; - } + if (cargc<1) + return CMD_USAGE; for (i=0;i #include -nick *mynick; +nick *hooknick; -nick *statsnick; +nick *mynick; -CommandTree *controlcmds; +CommandTree *controlcmds; +ControlMsg controlreply; +ControlWall controlwall; void handlemessages(nick *target, int messagetype, void **args); int controlstatus(void *sender, int cargc, char **cargv); @@ -39,32 +41,35 @@ int controlchannel(void *sender, int cargc, char **cargv); int relink(void *sender, int cargc, char **cargv); int die(void *sender, int cargc, char **cargv); int controlinsmod(void *sender, int cargc, char **cargv); -int controlrmmod(void *sender, int cargc, char **cargv); int controllsmod(void *sender, int cargc, char **cargv); int controlrehash(void *sender, int cargc, char **cargv); -int controlshowcommands(void *sender, int cargc, char **cargv); int controlreload(void *sender, int cargc, char **cargv); +int controlhelpcmd(void *sender, int cargc, char **cargv); +void controlnoticeopers(flag_t permissionlevel, flag_t noticelevel, char *format, ...); void _init() { controlcmds=newcommandtree(); - - addcommandtotree(controlcmds,"status",10,1,&controlstatus); - addcommandtotree(controlcmds,"whois",10,1,&controlwhois); - addcommandtotree(controlcmds,"channel",10,1,&controlchannel); - addcommandtotree(controlcmds,"relink",10,1,&relink); - addcommandtotree(controlcmds,"die",10,1,&die); - addcommandtotree(controlcmds,"insmod",10,1,&controlinsmod); - addcommandtotree(controlcmds,"rmmod",10,1,&controlrmmod); - addcommandtotree(controlcmds,"lsmod",10,1,&controllsmod); - addcommandtotree(controlcmds,"rehash",10,1,&controlrehash); - addcommandtotree(controlcmds,"showcommands",10,0,&controlshowcommands); - addcommandtotree(controlcmds,"reload",10,1,&controlreload); + controlreply=&controlmessage; + controlwall=&controlnoticeopers; + + registercontrolhelpcmd("status",NO_DEVELOPER,1,&controlstatus,"Usage: status ?level?\nDisplays status information, increasing level gives more verbose information."); + registercontrolhelpcmd("whois",NO_OPERED,1,&controlwhois,"Usage: whois \nDisplays lots of information about the specified nickname or numeric."); + registercontrolhelpcmd("channel",NO_OPER,1,&controlchannel,"Usage: channel <#channel>\nDisplays channel information."); + registercontrolhelpcmd("relink",NO_DEVELOPER,1,&relink,"Usage: relink\nRelinks service to the network."); + registercontrolhelpcmd("die",NO_DEVELOPER,1,&die,"Usage: die \nTerminates the service."); + registercontrolhelpcmd("insmod",NO_DEVELOPER,1,&controlinsmod,"Usage: insmod \nAdds a module to the running instance."); + registercontrolhelpcmd("rmmod",NO_DEVELOPER,1,&controlrmmod,"Usage: rmmod \nRemoves a module from the running instance."); + registercontrolhelpcmd("lsmod",NO_DEVELOPER,0,&controllsmod,"Usage: lsmod\nLists currently running modules."); + registercontrolhelpcmd("rehash",NO_DEVELOPER,1,&controlrehash,"Usage: rehash\nReloads configuration file."); + registercontrolhelpcmd("showcommands",NO_ACCOUNT,0,&controlshowcommands,"Usage: showcommands\nShows all registered commands."); + registercontrolhelpcmd("reload",NO_DEVELOPER,1,&controlreload,"Usage: reload \nReloads specified module."); + registercontrolhelpcmd("help",NO_ANYONE,1,&controlhelpcmd,"Usage: help \nShows help for specified command."); scheduleoneshot(time(NULL)+1,&controlconnect,NULL); } -void registercontrolcmd(const char *name, int level, int maxparams, CommandHandler handler) { - addcommandtotree(controlcmds,name,level,maxparams,handler); +void registercontrolhelpcmd(const char *name, int level, int maxparams, CommandHandler handler, char *help) { + addcommandhelptotree(controlcmds,name,level,maxparams,handler,help); } int deregistercontrolcmd(const char *name, CommandHandler handler) { @@ -82,6 +87,7 @@ void controlconnect(void *arg) { myauthname=getcopyconfigitem("control","authname","C",ACCOUNTLEN); mynick=registerlocaluser(cnick->content,myident->content,myhost->content,myrealname->content,myauthname->content,UMODE_SERVICE|UMODE_DEAF|UMODE_OPER|UMODE_ACCOUNT,&handlemessages); + triggerhook(HOOK_CONTROL_REGISTERED, mynick); cp=findchannel("#twilightzone"); if (!cp) { localcreatechannel(mynick,"#twilightzone"); @@ -98,13 +104,13 @@ void controlconnect(void *arg) { } void handlestats(int hooknum, void *arg) { - sendmessagetouser(mynick,statsnick,"%s",(char *)arg); + controlreply(hooknick,"%s",(char *)arg); } int controlstatus(void *sender, int cargc, char **cargv) { unsigned int level=5; - statsnick=(nick *)sender; + hooknick=(nick *)sender; if (cargc>0) { level=strtoul(cargv[0],NULL,10); @@ -125,55 +131,63 @@ int controlrehash(void *sender, int cargc, char **cargv) { triggerhook(HOOK_CORE_REHASH,(void *)0); return CMD_OK; } - + +void handlewhois(int hooknum, void *arg) { + controlreply(hooknick,"%s",(char *)arg); +} + int controlwhois(void *sender, int cargc, char **cargv) { nick *target; channel **channels; char buf[BUFSIZE]; int i; - if (cargc<1) { - sendmessagetouser(mynick,(nick *)sender,"Usage: whois "); - return CMD_ERROR; - } + if (cargc<1) + return CMD_USAGE; if (cargv[0][0]=='#') { if (!(target=getnickbynumericstr(cargv[0]+1))) { - sendmessagetouser(mynick,sender,"Sorry, couldn't find numeric %s",cargv[0]+1); + controlreply(sender,"Sorry, couldn't find numeric %s",cargv[0]+1); return CMD_ERROR; } } else { if ((target=getnickbynick(cargv[0]))==NULL) { - sendmessagetouser(mynick,(nick *)sender,"Sorry, couldn't find that user"); + controlreply((nick *)sender,"Sorry, couldn't find that user"); return CMD_ERROR; } } - sendmessagetouser(mynick,(nick *)sender,"Nick : %s",target->nick); - sendmessagetouser(mynick,(nick *)sender,"Numeric : %s",longtonumeric(target->numeric,5)); - sendmessagetouser(mynick,(nick *)sender,"User@Host : %s@%s (%d user(s) on this host)",target->ident,target->host->name->content,target->host->clonecount); + controlreply((nick *)sender,"Nick : %s",target->nick); + controlreply((nick *)sender,"Numeric : %s",longtonumeric(target->numeric,5)); + controlreply((nick *)sender,"User@Host : %s@%s (%d user(s) on this host)",target->ident,target->host->name->content,target->host->clonecount); if (IsSetHost(target)) { if (target->shident) { - sendmessagetouser(mynick,(nick *)sender,"Fakehost : %s@%s",target->shident->content, target->sethost->content); + controlreply((nick *)sender,"Fakehost : %s@%s",target->shident->content, target->sethost->content); } else { - sendmessagetouser(mynick,(nick *)sender,"Fakehost : %s",target->sethost->content); + controlreply((nick *)sender,"Fakehost : %s",target->sethost->content); } } - sendmessagetouser(mynick,(nick *)sender,"Timestamp : %lu",target->timestamp); - sendmessagetouser(mynick,(nick *)sender,"IP address: %d.%d.%d.%d",(target->ipaddress)>>24,(target->ipaddress>>16)&255, (target->ipaddress>>8)&255,target->ipaddress&255); - sendmessagetouser(mynick,(nick *)sender,"Realname : %s (%d user(s) have this realname)",target->realname->name->content,target->realname->usercount); + controlreply((nick *)sender,"Timestamp : %lu",target->timestamp); + controlreply((nick *)sender,"IP address: %d.%d.%d.%d",(target->ipaddress)>>24,(target->ipaddress>>16)&255, (target->ipaddress>>8)&255,target->ipaddress&255); + controlreply((nick *)sender,"Realname : %s (%d user(s) have this realname)",target->realname->name->content,target->realname->usercount); if (target->umodes) { - sendmessagetouser(mynick,(nick *)sender,"Umode(s) : %s",printflags(target->umodes,umodeflags)); + controlreply((nick *)sender,"Umode(s) : %s",printflags(target->umodes,umodeflags)); } if (IsAccount(target)) { - sendmessagetouser(mynick,(nick *)sender,"Account : %s",target->authname); + controlreply((nick *)sender,"Account : %s",target->authname); if (target->accountts) - sendmessagetouser(mynick,(nick *)sender,"AccountTS : %ld",target->accountts); + controlreply((nick *)sender,"AccountTS : %ld",target->accountts); } + + hooknick=(nick *)sender; + registerhook(HOOK_CONTROL_WHOISREPLY,&handlewhois); + triggerhook(HOOK_CONTROL_WHOISREQUEST,target); + deregisterhook(HOOK_CONTROL_WHOISREPLY,&handlewhois); + if (target->channels->cursi==0) { - sendmessagetouser(mynick,(nick *)sender,"Channels : none"); + controlreply((nick *)sender,"Channels : none"); } else if (target->channels->cursi>50) { - sendmessagetouser(mynick,(nick *)sender,"Channels : - (total: %d)",target->channels->cursi); + controlreply((nick *)sender,"Channels : - (total: %d)",target->channels->cursi); } else { buf[0]='\0'; channels=(channel **)target->channels->content; @@ -185,7 +199,7 @@ int controlwhois(void *sender, int cargc, char **cargv) { if (strlen(buf)==0) { break; } else { - sendmessagetouser(mynick,(nick *)sender,"Channels : %s",buf); + controlreply((nick *)sender,"Channels : %s",buf); buf[0]='\0'; i--; } @@ -197,47 +211,43 @@ int controlwhois(void *sender, int cargc, char **cargv) { } int controlinsmod(void *sender, int cargc, char **cargv) { - if (cargc<1) { - sendmessagetouser(mynick,(nick *)sender,"Usage: insmod "); - return CMD_ERROR; - } + if (cargc<1) + return CMD_USAGE; switch(insmod(cargv[0])) { case -1: - sendmessagetouser(mynick,(nick *)sender,"Unable to load module %s",cargv[0]); + controlreply((nick *)sender,"Unable to load module %s",cargv[0]); return CMD_ERROR; case 1: - sendmessagetouser(mynick,(nick *)sender,"Module %s already loaded, or name not valid",cargv[0]); + controlreply((nick *)sender,"Module %s already loaded, or name not valid",cargv[0]); return CMD_ERROR; case 0: - sendmessagetouser(mynick,(nick *)sender,"Module %s loaded.",cargv[0]); + controlreply((nick *)sender,"Module %s loaded.",cargv[0]); return CMD_OK; default: - sendmessagetouser(mynick,(nick *)sender,"An unknown error occured."); + controlreply((nick *)sender,"An unknown error occured."); return CMD_ERROR; } } int controlrmmod(void *sender, int cargc, char **cargv) { - if (cargc<1) { - sendmessagetouser(mynick,(nick *)sender,"Usage: rmmod "); - return CMD_ERROR; - } + if (cargc<1) + return CMD_USAGE; switch(rmmod(cargv[0])) { case 1: - sendmessagetouser(mynick,(nick *)sender,"Module %s is not loaded.",cargv[0]); + controlreply((nick *)sender,"Module %s is not loaded.",cargv[0]); return CMD_ERROR; case 0: - sendmessagetouser(mynick,(nick *)sender,"Module %s unloaded.",cargv[0]); + controlreply((nick *)sender,"Module %s unloaded.",cargv[0]); return CMD_OK; default: - sendmessagetouser(mynick,(nick *)sender,"An unknown error occured."); + controlreply((nick *)sender,"An unknown error occured."); return CMD_ERROR; } } @@ -248,23 +258,21 @@ int controllsmod(void *sender, int cargc, char **cargv) { if (cargc < 1) { /* list all loaded modules */ ptr = lsmod(i); - sendmessagetouser(mynick,(nick *)sender,"Module"); + controlreply((nick *)sender,"Module"); while (ptr != NULL) { - sendmessagetouser(mynick,(nick *)sender,"%s", ptr); + controlreply((nick *)sender,"%s", ptr); ptr = lsmod(++i); } } else { ptr = lsmod(getindex(cargv[0])); - sendmessagetouser(mynick,(nick *)sender,"Module \"%s\" %s", cargv[0], (ptr ? "is loaded." : "is NOT loaded.")); + controlreply((nick *)sender,"Module \"%s\" %s", cargv[0], (ptr ? "is loaded." : "is NOT loaded.")); } return CMD_OK; } int controlreload(void *sender, int cargc, char **cargv) { - if (cargc<1) { - sendmessagetouser(mynick,(nick *)sender,"Usage: reload "); - return CMD_ERROR; - } + if (cargc<1) + return CMD_USAGE; controlrmmod(sender, cargc, cargv); return controlinsmod(sender, cargc, cargv); @@ -272,8 +280,8 @@ int controlreload(void *sender, int cargc, char **cargv) { int relink(void *sender, int cargc, char **cargv) { if (cargc<1) { - sendmessagetouser(mynick,(nick *)sender,"You must give a reason."); - return CMD_ERROR; + controlreply((nick *)sender,"You must give a reason."); + return CMD_USAGE; } irc_send("%s SQ %s 0 :%s",mynumeric->content,myserver->content,cargv[0]); @@ -284,8 +292,8 @@ int relink(void *sender, int cargc, char **cargv) { int die(void *sender, int cargc, char **cargv) { if (cargc<1) { - sendmessagetouser(mynick,(nick *)sender,"You must give a reason."); - return CMD_ERROR; + controlreply((nick *)sender,"You must give a reason."); + return CMD_USAGE; } irc_send("%s SQ %s 0 :%s",mynumeric->content,myserver->content,cargv[0]); @@ -302,13 +310,11 @@ int controlchannel(void *sender, int cargc, char **cargv) { char buf2[12]; int i,j; - if (cargc<1) { - sendmessagetouser(mynick,(nick *)sender,"Usage: channel #chan"); - return CMD_ERROR; - } + if (cargc<1) + return CMD_USAGE; if ((cp=findchannel(cargv[0]))==NULL) { - sendmessagetouser(mynick,(nick *)sender,"Couldn't find channel: %s",cargv[0]); + controlreply((nick *)sender,"Couldn't find channel: %s",cargv[0]); return CMD_ERROR; } @@ -316,14 +322,14 @@ int controlchannel(void *sender, int cargc, char **cargv) { sprintf(buf2,"%d",cp->limit); } - sendmessagetouser(mynick,(nick *)sender,"Channel : %s",cp->index->name->content); + controlreply((nick *)sender,"Channel : %s",cp->index->name->content); if (cp->topic) { - sendmessagetouser(mynick,(nick *)sender,"Topic : %s",cp->topic->content); - sendmessagetouser(mynick,(nick *)sender,"T-time : %ld [%s]",cp->topictime,ctime(&(cp->topictime))); + controlreply((nick *)sender,"Topic : %s",cp->topic->content); + controlreply((nick *)sender,"T-time : %ld [%s]",cp->topictime,ctime(&(cp->topictime))); } - sendmessagetouser(mynick,(nick *)sender,"Mode(s) : %s %s%s%s",printflags(cp->flags,cmodeflags),IsLimit(cp)?buf2:"", + controlreply((nick *)sender,"Mode(s) : %s %s%s%s",printflags(cp->flags,cmodeflags),IsLimit(cp)?buf2:"", IsLimit(cp)?" ":"",IsKey(cp)?cp->key->content:""); - sendmessagetouser(mynick,(nick *)sender,"Users : %d (hash size %d, utilisation %.1f%%); %d unique hosts", + controlreply((nick *)sender,"Users : %d (hash size %d, utilisation %.1f%%); %d unique hosts", cp->users->totalusers,cp->users->hashsize,((float)(100*cp->users->totalusers)/cp->users->hashsize), countuniquehosts(cp)); i=0; @@ -332,7 +338,7 @@ int controlchannel(void *sender, int cargc, char **cargv) { for (j=0;j<=cp->users->hashsize;j++) { if (i==4 || j==cp->users->hashsize) { if(i>0) { - sendmessagetouser(mynick,(nick *)sender,"Users : %s",buf); + controlreply((nick *)sender,"Users : %s",buf); } i=0; memset(buf,' ',72); @@ -350,7 +356,7 @@ int controlchannel(void *sender, int cargc, char **cargv) { } for (cbp=cp->bans;cbp;cbp=cbp->next) { - sendmessagetouser(mynick,(nick *)sender,"Ban : %s",bantostringdebug(cbp)); + controlreply((nick *)sender,"Ban : %s",bantostringdebug(cbp)); } return CMD_OK; } @@ -365,7 +371,7 @@ int controlshowcommands(void *sender, int cargc, char **cargv) { controlreply(np,"The following commands are registered at present:"); for(i=0;icommand->content,cmdlist[i]->level); + controlreply(np,"%s",cmdlist[i]->command->content); } controlreply(np,"End of list."); @@ -396,12 +402,12 @@ void handlemessages(nick *target, int messagetype, void **args) { cmd=findcommandintree(controlcmds,cargv[0],1); if (cmd==NULL) { - sendmessagetouser(mynick,sender,"Unknown command."); + controlreply(sender,"Unknown command."); return; } - if (cmd->level>=10 && !IsOper(sender)) { - sendmessagetouser(mynick,sender,"You need to be opered to use this command."); + if (cmd->level>0 && !IsOper(sender)) { + controlreply(sender,"You need to be opered to use this command."); return; } @@ -414,7 +420,8 @@ void handlemessages(nick *target, int messagetype, void **args) { cargc=(cmd->maxparams)+1; } - (cmd->handler)((void *)sender,cargc-1,&(cargv[1])); + if((cmd->handler)((void *)sender,cargc-1,&(cargv[1])) == CMD_USAGE) + controlhelp(sender, cmd); break; case LU_KILLED: @@ -428,7 +435,7 @@ void handlemessages(nick *target, int messagetype, void **args) { } } -void controlreply(nick *target, char *message, ... ) { +void controlmessage(nick *target, char *message, ... ) { char buf[512]; va_list va; @@ -473,13 +480,78 @@ void controlnotice(nick *target, char *message, ... ) { sendnoticetouser(mynick,target,"%s",buf); } -/* Send a notice to all opers, O(n) and a notice otherwise we'll get infinite loops with services */ -void controlnoticeopers(char *format, ...) { +void controlspecialrmmod(void *arg) { + struct specialsched *a = (struct specialsched *)arg; + + a->schedule = NULL; + sstring *froo = a->modulename; + + rmmod(froo->content); + freesstring(froo); +} + +void controlspecialreloadmod(void *arg) { + struct specialsched *a = (struct specialsched *)arg; + + a->schedule = NULL; + sstring *froo = a->modulename; + + rmmod(froo->content); + insmod(froo->content); + freesstring(froo); +} + +void controlhelp(nick *np, Command *cmd) { + char *cp = cmd->help, *sp = cp; + if(!cp || !*cp) { + controlreply(np, "Sorry, no help for this command."); + } else { + int finished = 0; + for(;;cp++) { + if(*cp == '\0' || *cp == '\n') { + if(*cp == '\0') { + finished = 1; + } else { + *cp = '\0'; + } + + if(sp != cp) + controlreply(np, "%s", sp); + + if(finished) + break; + + *cp = '\n'; + + sp = cp + 1; + } + } + } +} + +int controlhelpcmd(void *sender, int cargc, char **cargv) { + Command *cmd; + nick *np = (nick *)sender; + + if (cargc<1) + return CMD_USAGE; + + cmd=findcommandintree(controlcmds,cargv[0],1); + if (cmd==NULL) { + controlreply(np,"Unknown command."); + return CMD_ERROR; + } + + controlhelp(np, cmd); + return CMD_OK; +} + +void controlnoticeopers(flag_t permissionlevel, flag_t noticelevel, char *format, ...) { int i; nick *np; - char broadcast[BUFSIZE]; + char broadcast[512]; va_list va; - + va_start(va, format); vsnprintf(broadcast, sizeof(broadcast), format, va); va_end(va); diff --git a/control/control.h b/control/control.h index bbc63c4d..248e8970 100644 --- a/control/control.h +++ b/control/control.h @@ -6,11 +6,84 @@ #include "../nick/nick.h" #include "../channel/channel.h" -void registercontrolcmd(const char *name, int level, int maxparams, CommandHandler handler); +void registercontrolhelpcmd(const char *name, int level, int maxparams, CommandHandler handler, char *help); int deregistercontrolcmd(const char *name, CommandHandler handler); -void controlreply(nick *target, char *message, ... ); +void controlmessage(nick *target, char *message, ... ); void controlchanmsg(channel *cp, char *message, ...); void controlnotice(nick *target, char *message, ...); -void controlnoticeopers(char *format, ...); +int controlshowcommands(void *sender, int cargc, char **cargv); +int controlrmmod(void *sender, int cargc, char **cargv); +void controlspecialrmmod(void *arg); +void controlspecialreloadmod(void *arg); +void controlhelp(nick *np, Command *cmd); + +#define registercontrolcmd(a, b, c, d) registercontrolhelpcmd(a, b, c, d, NULL) + +typedef void (*ControlMsg)(nick *, char *, ... ); +typedef void (*ControlWall)(flag_t, flag_t, char *, ...); + +extern ControlMsg controlreply; +extern ControlWall controlwall; + +extern nick *mynick; + +extern CommandTree *controlcmds; + +struct specialsched { + sstring *modulename; + void *schedule; +} specialsched; + +/* NEVER USE THE FOLLOWING IN COMMANDS, you'll end up missing bits off and users'll end up being able to gline people */ +#define __NO_ANYONE 0x000 +#define __NO_AUTHED 0x001 /* must be authed with the network, don't know what use this is really */ +#define __NO_OPERED 0x002 /* just means that they MUST be /oper'ed, for hello -- part of LEGACY */ +#define __NO_ACCOUNT 0x004 /* for hello, must have a user account */ +#define __NO_LEGACY 0x008 /* reserved for old newserv commands, their level is 10 */ +#define __NO_STAFF 0x010 /* +s */ +#define __NO_TRUST 0x020 /* +t */ +#define __NO_OPER 0x040 /* +O */ +#define __NO_SEC 0x080 /* +w */ +#define __NO_DEVELOPER 0x100 /* +d */ + +/* These are dangerous, they don't include requiring /OPER or STAFF status, be careful */ +#define NOD_ACCOUNT __NO_ACCOUNT | NO_AUTHED /* must contain authed else account won't be checked */ +#define NOD_STAFF __NO_STAFF | NOD_ACCOUNT +#define NOD_TRUST __NO_TRUST | NOD_ACCOUNT +#define NOD_OPER __NO_OPER | NOD_ACCOUNT +#define NOD_SEC __NO_SEC | NOD_ACCOUNT +#define NOD_DEVELOPER __NO_DEVELOPER | NOD_ACCOUNT + +/* These ones are safe to use */ +#define NO_ANYONE __NO_ANYONE /* don't have to be authed to Q, or us, or opered or anything */ +#define NO_AUTHED __NO_AUTHED /* must be authed to Q */ +#define NO_ACCOUNT NOD_ACCOUNT /* must have an account on the bot */ +#define NO_OPERED __NO_OPERED /* must be /opered */ +#define NO_STAFF NOD_STAFF /* must be authed to Q and have staff level on bot */ +#define NO_OPER NO_OPERED | NOD_OPER /* must be authed to Q, /opered, and have oper level on bot */ +#define NO_DEVELOPER NO_OPERED | NOD_DEVELOPER /* must be authed to Q, /opered, and have dev level on bot */ +#define NO_TRUST_STAFF NO_STAFF | NOD_TRUST /* must be authed to Q, and have staff and trust level on bot */ +#define NO_TRUST_OPER NO_OPER | NOD_TRUST /* must be authed to Q, /opered, and have trust and oper levels on bot */ +#define NO_SEC_STAFF NO_STAFF | NOD_SEC /* must be authed to Q, and have staff and sec level on bot */ +#define NO_SEC_OPER NO_OPER | NOD_SEC /* must be authed to Q, /opered, and have sec and oper levels on bot */ + +#define NO_ALL_FLAGS __NO_STAFF | __NO_TRUST | __NO_OPER | __NO_SEC | __NO_DEVELOPER +#define NO_OPER_FLAGS __NO_STAFF +#define NO_DEV_FLAGS NO_ALL_FLAGS + +#define NL_MANAGEMENT 0x0001 /* hello, password, userflags, noticeflags */ +#define NL_TRUSTS 0x0002 /* trust stuff... */ +#define NL_KICKS 0x0004 /* KICK command */ +#define NL_KILLS 0x0008 /* KILL command */ +#define NL_GLINES 0x0010 /* GLINE commands */ +#define NL_HITS 0x0020 /* Where a gline or kill is set automatically by the bot */ +#define NL_CLONING 0x0040 /* Clone detection */ +#define NL_CLEARCHAN 0x0080 /* When someone clearchans */ +#define NL_FAKEUSERS 0x0100 /* Fakeuser addition */ +#define NL_BROADCASTS 0x0200 /* Broadcast/mbroadcast/sbroadcast */ +#define NL_OPERATIONS 0x0400 /* insmod/rmmod/etc */ +#define NL_OPERING 0x0800 /* when someone opers */ +#define NL_NOTICES 0x1000 /* turn off to receive privmsgs instead of notices */ +#define NL_ALL_COMMANDS 0x2000 /* every single command sent */ #endif diff --git a/core/hooks.h b/core/hooks.h index 483574f7..1c256d39 100644 --- a/core/hooks.h +++ b/core/hooks.h @@ -27,6 +27,7 @@ #define HOOK_NICK_ACCOUNT 304 /* Argument is nick* */ #define HOOK_NICK_QUIT 305 /* Argument is void*[2] (nick, reason) */ #define HOOK_NICK_SETHOST 306 /* Argument is nick* */ +#define HOOK_NICK_MODEOPER 307 /* Argument is void*[2] (nick, modes) */ #define HOOK_CHANNEL_BURST 400 /* Argument is channel pointer */ #define HOOK_CHANNEL_CREATE 401 /* Argument is void*[2] (channel, nick) */ @@ -50,6 +51,10 @@ #define HOOK_CHANSERV_DBLOADED 500 /* No arg */ +#define HOOK_CONTROL_REGISTERED 600 /* Argument is nick* */ +#define HOOK_CONTROL_WHOISREQUEST 601 /* Argument is nick* */ +#define HOOK_CONTROL_WHOISREPLY 602 /* Argument is char* */ + typedef void (*HookCallback)(int, void *); void inithooks(); diff --git a/countusers/countusers.c b/countusers/countusers.c index 2c3e3ee7..31826ee7 100644 --- a/countusers/countusers.c +++ b/countusers/countusers.c @@ -7,13 +7,11 @@ int do_countusers(void *source, int cargc, char **cargv) { nick *sender=(nick *)source, *np; host *hp; - unsigned int i=0,count=0; + unsigned int count=0; char *chp,*host,*ident; - if (cargc<1) { - controlreply(sender, "Usage: countusers "); - return CMD_ERROR; - } + if (cargc<1) + return CMD_USAGE; for (chp=cargv[0]; ;chp++) { if (*chp=='@') { @@ -51,7 +49,7 @@ int do_countusers(void *source, int cargc, char **cargv) { } void _init() { - registercontrolcmd("countusers", 10, 1, do_countusers); + registercontrolhelpcmd("countusers", NO_OPER, 1, do_countusers, "Usage: countusers \nReturns users on specified hostmask."); } void _fini() { diff --git a/fsck/fsck.c b/fsck/fsck.c index 5cec1c91..1f5bda03 100644 --- a/fsck/fsck.c +++ b/fsck/fsck.c @@ -8,7 +8,7 @@ int dofsck(void *source, int cargc, char **cargv); void _init() { - registercontrolcmd("fsck",10,0,dofsck); + registercontrolhelpcmd("fsck",NO_DEVELOPER,0,dofsck, "Usage: fsck\nRuns internal structure check."); } void _fini() { diff --git a/horse/horse.c b/horse/horse.c index d3c347d6..67d72067 100644 --- a/horse/horse.c +++ b/horse/horse.c @@ -7,10 +7,8 @@ int ho_horse(void *source, int cargc, char **cargv) { nick *victim; channel *cp; - if (cargc<1) { - controlreply(sender,"Usage: horse "); - return CMD_ERROR; - } + if (cargc<1) + return CMD_USAGE; if ((victim=getnickbynick(cargv[0]))!=NULL) { controlreply(victim," _ ___,;;;^"); @@ -32,7 +30,7 @@ int ho_horse(void *source, int cargc, char **cargv) { } void _init() { - registercontrolcmd("horse",10,2,ho_horse); + registercontrolhelpcmd("horse",NO_OPERED,2,ho_horse,"Usage: horse \nSpams a horse at target."); } void _fini() { diff --git a/lameisp/lameisp.c b/lameisp/lameisp.c index 993f2d9e..92957d74 100644 --- a/lameisp/lameisp.c +++ b/lameisp/lameisp.c @@ -36,7 +36,7 @@ void _init() { li_killyoungest(hp->nicks); } while (hp->clonecount > LI_CLONEMAX); - registercontrolcmd("victims", 10, 0, li_stats); + registercontrolhelpcmd("victims", NO_OPER, 0, li_stats, "Usage: victims\nShows the amount of clients victimised by lameisp."); registerhook(HOOK_NICK_NEWNICK, &li_nick); } @@ -68,4 +68,4 @@ int li_stats(void *source, int cargc, char **cargv) { controlreply((nick *)source, "Victims: %d clients", li_victims); return CMD_OK; -} \ No newline at end of file +} diff --git a/lib/sha1.c b/lib/sha1.c index 52cde648..2172392f 100644 --- a/lib/sha1.c +++ b/lib/sha1.c @@ -48,6 +48,14 @@ static char rcsid[] = "$OpenBSD: sha1.c,v 1.12 2003/07/21 20:37:08 millert Exp $ #define R3(v,w,x,y,z,i) z+=(((w|x)&y)|(w&x))+blk(i)+0x8F1BBCDC+rol(v,5);w=rol(w,30); #define R4(v,w,x,y,z,i) z+=(w^x^y)+blk(i)+0xCA62C1D6+rol(v,5);w=rol(w,30); +#if !defined(u_char) +typedef u_int8_t u_char; +#endif + +#if !defined(u_int) +typedef u_int32_t u_int; +#endif + typedef union { u_char c[64]; u_int l[16]; diff --git a/newsearch/newsearch.c b/newsearch/newsearch.c index 466559ce..85dd2bd8 100644 --- a/newsearch/newsearch.c +++ b/newsearch/newsearch.c @@ -47,7 +47,7 @@ void _init() { /* Nickname / channel operations */ registersearchterm("modes",modes_parse); - registercontrolcmd("nicksearch",10,4,do_nicksearch); + registercontrolhelpcmd("nicksearch",NO_OPER,4,do_nicksearch, "Usage: nicksearch \nSearches for nicknames with the given criteria."); } void _fini() { @@ -78,10 +78,8 @@ int do_nicksearch(void *source, int cargc, char **cargv) { char *ch; int arg=0; - if (cargc<1) { - controlreply(sender,"Usage: nicksearch (criteria)"); - return CMD_ERROR; - } + if (cargc<1) + return CMD_USAGE; if (*cargv[0] == '-') { /* options */ @@ -92,7 +90,7 @@ int do_nicksearch(void *source, int cargc, char **cargv) { case 'l': if (cargcumodes; + if (strchr(cargv[1],'o')) { + void *harg[2]; + harg[0]=np; + harg[1]=cargv[1]; + triggerhook(HOOK_NICK_MODEOPER,harg); + } setflags(&(np->umodes),UMODE_ALL,cargv[1],umodeflags,REJECT_NONE); if (strchr(cargv[1],'h')) { /* Have to allow +h twice.. */ /* +-h: just the freesstring() calls for the -h case */ diff --git a/noperserv/Makefile b/noperserv/Makefile new file mode 100644 index 00000000..7d4b879f --- /dev/null +++ b/noperserv/Makefile @@ -0,0 +1,9 @@ + +.PHONY: all +all: noperserv.so noperserv_commands.so + +noperserv.so: noperserv.o noperserv_db.o noperserv_hooks.o noperserv_policy.o + ld -shared -Bdynamic ${LIBPGSQL} -o $@ $^ + +noperserv_commands.so: noperserv_commands.o + ld -shared -Bdynamic ${LIBPGSQL} -o $@ $^ diff --git a/noperserv/noperserv.c b/noperserv/noperserv.c new file mode 100644 index 00000000..43b16006 --- /dev/null +++ b/noperserv/noperserv.c @@ -0,0 +1,481 @@ +/* + * NOperserv v0.01 + * + * A replacement for Germania's ageing Operservice2 + * + * Copyright (C) 2005 Chris Porter. + */ + +#include "../localuser/localuser.h" +#include "../lib/irc_string.h" +#include "../lib/strlfunc.h" +#include "noperserv.h" +#include "noperserv_db.h" +#include "noperserv_hooks.h" +#include "noperserv_policy.h" + +#include +#include +#include + +#define FLAGBUFLEN 100 + +#define NO_FOUND_NICKNAME 1 +#define NO_FOUND_AUTHNAME 2 + +const flag no_commandflags[] = { + { 'o', __NO_OPER }, + { 't', __NO_TRUST }, + { 's', __NO_STAFF }, + { 'S', __NO_SEC }, + { 'd', __NO_DEVELOPER }, + { 'L', __NO_LEGACY }, + { 'O', __NO_OPERED }, + { 'r', __NO_AUTHED }, + { 'R', __NO_ACCOUNT }, + { '\0', 0 } + }; + +const flag no_userflags[] = { + { 'o', __NO_OPER }, + { 't', __NO_TRUST }, + { 's', __NO_STAFF }, + { 'S', __NO_SEC }, + { 'd', __NO_DEVELOPER }, + { '\0', 0 } + }; + +const flag no_noticeflags[] = { + { 'm', NL_MANAGEMENT }, /* hello, password, userflags, noticeflags */ + { 't', NL_TRUSTS }, /* trust stuff... */ + { 'k', NL_KICKS }, /* KICK command */ + { 'K', NL_KILLS }, /* KILL command */ + { 'g', NL_GLINES }, /* GLINE commands */ + { 'h', NL_HITS }, /* Where a gline or kill is set automatically by the bot */ + { 'c', NL_CLONING }, /* Clone detection */ + { 'C', NL_CLEARCHAN }, /* When someone clearchans */ + { 'f', NL_FAKEUSERS }, /* Fakeuser addition */ + { 'b', NL_BROADCASTS }, /* Broadcast/mbroadcast/sbroadcast */ + { 'o', NL_OPERATIONS }, /* insmod/rmmod/etc */ + { 'O', NL_OPERING }, /* when someone opers */ + { 'n', NL_NOTICES }, /* turn off to receive notices instead of privmsgs */ + { 'A', NL_ALL_COMMANDS }, /* all commands sent */ + { '\0', 0 } + }; + +int noperserv_hello(void *sender, int cargc, char **cargv); +int noperserv_noticeflags(void *sender, int cargc, char **cargv); +int noperserv_userflags(void *sender, int cargc, char **cargv); +int noperserv_deluser(void *sender, int cargc, char **cargv); +void noperserv_oper_detection(int hooknum, void *arg); +void noperserv_reply(nick *np, char *format, ...); + +int init = 0; + +void _init() { + if(!noperserv_load_db()) + return; + + noperserv_ext = registernickext("noperserv"); + + noperserv_setup_hooks(); + + registercontrolhelpcmd("hello", NO_OPERED | NO_AUTHED, 1, &noperserv_hello, "Syntax: HELLO ?nickname|#authname?\nCreates an account on the service for the specified nick, or if one isn't supplied, your nickname."); + registercontrolhelpcmd("userflags", NO_ACCOUNT, 2, &noperserv_userflags, "Syntax: USERFLAGS ?modifications?\nViews and modifies user permissions.\nIf no nickname or authname is supplied, you are substituted for it.\nIf no flags are supplied, flags are just displayed instead of modified."); + registercontrolhelpcmd("noticeflags", NO_ACCOUNT, 1, &noperserv_noticeflags, + "Syntax: NOTICEFLAGS ?(nickname|#authname)|flags?\n" + " This command can view and modify your own notice flags, and view that of other users.\n" + " Flags:\n" + " +m: Management (hello, password, userflags, noticeflags)\n" + " +t: Trusts\n" + " +k: KICK command\n" + " +K: KILL command\n" + " +g: GLINE commands\n" + " +h: Shows when glines are played automatically (hits)\n" + " +c: Clone information\n" + " +C: CLEARCHAN command\n" + " +f: FAKEUSER command\n" + " +b: BROADCAST commands\n" + " +o: Operation commands, such as insmod, rmmod, die, etc\n" + " +O: /OPER\n" + " +n: Sends notices instead of privmsgs\n" + " +A: Every single command sent to the service\n" + ); + + registercontrolhelpcmd("deluser", NO_OPERED | NO_ACCOUNT, 2, &noperserv_deluser, "Syntax: DELUSER \nDeletes the specified user."); + registerhook(HOOK_NICK_MODEOPER, &noperserv_oper_detection); + + init = 1; +} + +#ifdef BROKEN_DLCLOSE +void __fini() { +#else +void _fini() { +#endif + if(!init) + return; + + deregisterhook(HOOK_NICK_MODEOPER, &noperserv_oper_detection); + + deregistercontrolcmd("noticeflags", &noperserv_noticeflags); + deregistercontrolcmd("userflags", &noperserv_userflags); + deregistercontrolcmd("noticeflags", &noperserv_noticeflags); + + noperserv_cleanup_hooks(); + + noperserv_cleanup_db(); + + releasenickext(noperserv_ext); +} + +/* @test */ +int noperserv_hello(void *sender, int cargc, char **cargv) { + char *newaccount; + no_autheduser *au; + int i; + nick *np = (nick *)sender, *np2, *target = NULL; + + if(cargc == 0) { + newaccount = np->authname; + } else { + if(cargv[0][0] == '#') { + nick *np2; + for(i=0;inext) + if(IsAccount(np2) && !ircd_strcmp(cargv[0] + 1, np2->authname)) { + target = np2; + newaccount = target->authname; + break; + } + if(!target) { + controlreply(np, "Cannot find anyone with that authname on the network."); + return CMD_ERROR; + } + } else { + target = getnickbynick(cargv[0]); + if(!target) { + controlreply(np, "Supplied nickname is not on the network."); + return CMD_ERROR; + } + if(!IsAccount(target)) { + controlreply(np, "Supplied user is not authed with the network."); + return CMD_ERROR; + } + newaccount = target->authname; + } + } + au = noperserv_get_autheduser(newaccount); + if(au) { + controlreply(np, "Authname already registered."); + return CMD_ERROR; + } + + au = noperserv_new_autheduser(newaccount); + if(!au) { + controlreply(np, "Memory allocation error."); + return CMD_ERROR; + } + + if(noperserv_get_autheduser_count() == 1) { + au->authlevel = NO_FIRST_USER_LEVEL; + au->noticelevel = NO_FIRST_USER_DEFAULT_NOTICELEVEL; + } else { + au->authlevel = NO_DEFAULT_LEVEL; + au->noticelevel = NO_DEFAULT_NOTICELEVEL; + } + + au->id = noperserv_next_autheduser_id(); + noperserv_update_autheduser(au); + + for(i=0;inext) + if(IsAccount(np2) && !ircd_strcmp(newaccount, np2->authname)) { + noperserv_add_to_autheduser(np2, au); + controlreply(np2, "An account has been created for you (auth %s).", au->authname->content); + if(NOGetAuthLevel(au)) + controlreply(np2, "User flags: %s", printflags(NOGetAuthLevel(au), no_userflags)); + controlreply(np2, "Notice flags: %s", printflags(NOGetNoticeLevel(au), no_noticeflags)); + } + + if(ircd_strcmp(np->authname, newaccount)) { /* send a message to the person who HELLO'ed if we haven't already been told */ + controlreply(np, "Account created for auth %s.", au->authname->content); + if(NOGetAuthLevel(au)) + controlreply(np, "User flags: %s", printflags(NOGetAuthLevel(au), no_userflags)); + controlreply(np, "Notice flags: %s", printflags(NOGetNoticeLevel(au), no_noticeflags)); + controlreply(np, "Instructions sent to all authed users."); + } else if(au->nick && au->nick->next) { /* if we have already been told, tell the user it was sent to more than themselves */ + controlreply(np, "Instructions sent to all authed users."); + } + + controlwall(NO_OPERED, NL_MANAGEMENT, "%s/%s just HELLO'ed: %s", np->nick, np->authname, au->authname->content); + return CMD_OK; +} + +no_autheduser *noperserv_autheduser_from_command(nick *np, char *command, int *typefound, char **returned) { + no_autheduser *au; + if(command[0] == '#') { + au = noperserv_get_autheduser(command + 1); + if(!au) { + controlreply(np, "Authname not found."); + } else { + *typefound = NO_FOUND_AUTHNAME; + *returned = au->authname->content; + return au; + } + } else { + nick *np2 = getnickbynick(command); + if(!np2) { + controlreply(np, "Nickname not on the network."); + return CMD_OK; + } + if(!IsAccount(np2)) { + controlreply(np, "User is not authed with the network."); + return CMD_OK; + } + au = NOGetAuthedUser(np2); + if(!au) { + controlreply(np, "User does not have an account."); + } else { + *typefound = NO_FOUND_NICKNAME; + *returned = np2->nick; + return au; + } + } + + return NULL; +} + +int noperserv_noticeflags(void *sender, int cargc, char **cargv) { + nick *np = (nick *)sender; + no_autheduser *au; + + if(cargc == 1) { + if((cargv[0][0] == '+') || (cargv[0][0] == '-')) { + int ret; + au = NOGetAuthedUser(np); + flag_t fwas = NOGetNoticeLevel(au), permittedchanges = noperserv_policy_permitted_noticeflags(au); + + ret = setflags(&au->noticelevel, permittedchanges, cargv[0], no_noticeflags, REJECT_DISALLOWED | REJECT_UNKNOWN); + if(ret != REJECT_UNKNOWN) { + if(ret == REJECT_DISALLOWED) { + flag_t fnow = fwas; + setflags(&fnow, NL_ALL, cargv[0], no_noticeflags, REJECT_NONE); + if(fnow == fwas) { + controlreply(np, "No changes made to existing flags."); + } else { + char ourflags[FLAGBUFLEN], ournoticeflags[FLAGBUFLEN]; + controlreply(np, "Flag alterations denied."); + + strlcpy(ourflags, printflags(NOGetAuthLevel(au), no_userflags), sizeof(ourflags)); + strlcpy(ournoticeflags, printflags(NOGetNoticeLevel(au), no_noticeflags), sizeof(ournoticeflags)); + controlwall(NO_OPER, NL_MANAGEMENT, "%s/%s (%s) attempted to NOTICEFLAGS (%s): %s", np->nick, np->authname, ourflags, ournoticeflags, printflagdiff(fwas, fnow, no_noticeflags)); + return CMD_ERROR; + } + } else if(ret == REJECT_NONE) { + if(NOGetNoticeLevel(au) == fwas) { + controlreply(np, "No changes made to existing flags."); + } else { + char ourflags[FLAGBUFLEN], ournoticeflags[FLAGBUFLEN], diff[FLAGBUFLEN * 2 + 1], finalflags[FLAGBUFLEN]; + no_nicklist *nl = au->nick; + noperserv_update_autheduser(au); + controlreply(np, "Flag alterations complete."); + + strlcpy(ourflags, printflags(NOGetAuthLevel(au), no_userflags), sizeof(ourflags)); + strlcpy(ournoticeflags, printflags(fwas, no_noticeflags), sizeof(ournoticeflags)); + strlcpy(diff, printflagdiff(fwas, NOGetNoticeLevel(au), no_noticeflags), sizeof(diff)); + controlwall(NO_OPER, NL_MANAGEMENT, "%s/%s (%s) successfully used NOTICEFLAGS (%s): %s", np->nick, np->authname, ourflags, ournoticeflags, diff); + + strlcpy(finalflags, printflags(NOGetNoticeLevel(au), no_noticeflags), sizeof(finalflags)); + for(;nl;nl=nl->next) + if(nl->nick != np) { + controlreply(nl->nick, "!!! %s just used NOTICEFLAGS (%s): %s", np->nick, ournoticeflags, diff); + controlreply(nl->nick, "Your notice flags are %s", finalflags); + } + } + } + } else { + controlreply(np, "Unknown flag(s) supplied."); + return CMD_ERROR; + } + } else { + int typefound; + char *itemfound; + au = noperserv_autheduser_from_command(np, cargv[0], &typefound, &itemfound); + if(!au) + return CMD_ERROR; + + if(au != NOGetAuthedUser(np)) { + controlreply(np, "Notice flags for %s %s are: %s", typefound==NO_FOUND_NICKNAME?"user":"authname", itemfound, printflags(NOGetNoticeLevel(au), no_noticeflags)); + return CMD_OK; + } + } + } else { + au = NOGetAuthedUser(np); + } + + if(!au) /* shouldn't happen */ + return CMD_ERROR; + + controlreply(np, "Your notice flags are: %s", printflags(NOGetNoticeLevel(au), no_noticeflags)); + + return CMD_OK; +} + +/* @test */ +int noperserv_deluser(void *sender, int cargc, char **cargv) { + nick *np = (nick *)sender; + no_autheduser *target /* target user */, *au = NOGetAuthedUser(np); /* user executing command */ + char *userreturned = NULL; /* nickname or authname of the target, pulled from the db */ + int typefound; /* whether it was an authname or a username */ + no_nicklist *nl; + char targetflags[FLAGBUFLEN], ourflags[FLAGBUFLEN], deleteduser[NOMax(ACCOUNTLEN, NICKLEN) + 1]; + + if(cargc != 1) + return CMD_USAGE; + + target = noperserv_autheduser_from_command(np, cargv[0], &typefound, &userreturned); + if(!target) + return CMD_ERROR; + + strlcpy(targetflags, printflags(NOGetAuthLevel(target), no_userflags), sizeof(targetflags)); + strlcpy(ourflags, printflags(NOGetAuthLevel(au), no_userflags), sizeof(ourflags)); + + /* we have to copy it as it might point to an autheduser, which we're about to delete */ + strlcpy(deleteduser, userreturned, sizeof(deleteduser)); + + /* we have to check if target != au, because if successful policy_modification_permitted just returns the flags we're allowed + to modify, if we have no flags we won't be able to delete ourselves */ + if((target != au) && !noperserv_policy_permitted_modifications(au, target)) { + controlreply(np, "Deletion denied."); + controlwall(NO_OPER, NL_MANAGEMENT, "%s/%s (%s) attempted to DELUSER %s (%s)", np->nick, np->authname, ourflags, target->authname->content, targetflags); + + return CMD_ERROR; + } + + for(nl=target->nick;nl;nl=nl->next) + if(nl->nick != np) + controlreply(nl->nick, "!!! %s/%s (%s) just DELUSERed you.", np->nick, np->authname, ourflags); + + noperserv_delete_autheduser(target); + + controlwall(NO_OPER, NL_MANAGEMENT, "%s/%s (%s) successfully used DELUSER on %s (%s)", np->nick, np->authname, ourflags, target->authname->content, targetflags); + + if(target == au) { + controlreply(np, "You have been deleted."); + } else { + controlreply(np, "%s %s deleted.", typefound==NO_FOUND_AUTHNAME?"Auth":"User", deleteduser); + } + + return CMD_OK; +} + +/* @test */ +/* this command needs LOTS of checking */ +int noperserv_userflags(void *sender, int cargc, char **cargv) { + nick *np = (nick *)sender; + no_autheduser *au = NOGetAuthedUser(np), *target = NULL; + char *flags = NULL, *nicktarget = NULL; + int typefound; + + if(cargc == 0) { + target = au; + } else if(cargc == 1) { + if((cargv[0][0] == '+') || (cargv[0][0] == '-')) { /* modify our own */ + flags = cargv[0]; + target = au; + } else { /* viewing someone elses */ + nicktarget = cargv[0]; + } + } else if(cargc == 2) { + nicktarget = cargv[0]; + flags = cargv[1]; + } else { + return CMD_USAGE; + } + + if(nicktarget) { + target = noperserv_autheduser_from_command(np, nicktarget, &typefound, &nicktarget); + if(!target) + return CMD_ERROR; + } + + if(flags) { + int ret; + flag_t permitted = noperserv_policy_permitted_modifications(au, target), fwas = NOGetAuthLevel(target), fours = NOGetAuthLevel(au); + + ret = setflags(&target->authlevel, permitted, flags, no_userflags, REJECT_DISALLOWED | REJECT_UNKNOWN); + if(ret != REJECT_UNKNOWN) { + if(ret == REJECT_DISALLOWED) { + flag_t fnow = fwas; + setflags(&fnow, NO_ALL_FLAGS, flags, no_userflags, REJECT_NONE); + if(fnow == fwas) { + controlreply(np, "No changes made to existing flags."); + } else { + char targetflags[FLAGBUFLEN], ourflags[FLAGBUFLEN]; + controlreply(np, "Flag alterations denied."); + + strlcpy(targetflags, printflags(fwas, no_userflags), sizeof(targetflags)); + strlcpy(ourflags, printflags(fours, no_userflags), sizeof(ourflags)); + + controlwall(NO_OPER, NL_MANAGEMENT, "%s/%s (%s) attempted to use USERFLAGS on %s (%s): %s", np->nick, np->authname, ourflags, target->authname->content, targetflags, printflagdiff(fwas, fnow, no_userflags)); + return CMD_ERROR; + } + } else if(ret == REJECT_NONE) { + if(NOGetAuthLevel(target) == fwas) { + controlreply(np, "No changes made to existing flags."); + } else { + char targetflags[FLAGBUFLEN], ourflags[FLAGBUFLEN], finalflags[FLAGBUFLEN]; + no_nicklist *nl = target->nick; + + noperserv_policy_update_noticeflags(fwas, target); + noperserv_update_autheduser(target); + + controlreply(np, "Flag alterations complete."); + + strlcpy(targetflags, printflags(fwas, no_userflags), sizeof(targetflags)); + strlcpy(ourflags, printflags(fours, no_userflags), sizeof(ourflags)); + + controlwall(NO_OPER, NL_MANAGEMENT, "%s/%s (%s) successfully used USERFLAGS on %s (%s): %s", np->nick, np->authname, ourflags, target->authname->content, targetflags, printflagdiff(fwas, NOGetAuthLevel(target), no_userflags)); + + strlcpy(finalflags, printflags(NOGetAuthLevel(target), no_userflags), sizeof(finalflags)); + for(;nl;nl=nl->next) + if(nl->nick != np) { + controlreply(nl->nick, "!!! %s/%s (%s) just used USERFLAGS on you (%s): %s", np->nick, np->authname, ourflags, targetflags, printflagdiff(fwas, NOGetAuthLevel(target), no_userflags)); + controlreply(nl->nick, "Your user flags are now: %s", finalflags); + controlreply(nl->nick, "Your notice flags are now: %s", printflags(target->noticelevel, no_noticeflags)); + } + } + } + } else { + controlreply(np, "Unknown flag(s) supplied."); + return CMD_ERROR; + } + } + + if(target != au) { + controlreply(np, "User flags for %s %s: %s", typefound==NO_FOUND_AUTHNAME?"auth":"user", nicktarget, printflags(NOGetAuthLevel(target), no_userflags)); + controlreply(np, "Notice flags for %s %s: %s", typefound==NO_FOUND_AUTHNAME?"auth":"user", nicktarget, printflags(target->noticelevel, no_noticeflags)); + } else { + controlreply(np, "Your user flags are: %s", printflags(NOGetAuthLevel(target), no_userflags)); + controlreply(np, "Your notice flags are: %s", printflags(target->noticelevel, no_noticeflags)); + } + + return CMD_OK; +} + +void noperserv_oper_detection(int hooknum, void *arg) { + void **args = (void **)arg; + nick *np = args[0]; + char *modestr = args[1]; + flag_t after = np->umodes; + + setflags(&after, UMODE_ALL, modestr, umodeflags, REJECT_NONE); + if(np->umodes & UMODE_OPER) { + if(!(after & UMODE_OPER)) + controlwall(NO_OPER, NL_OPERING, "%s!%s@%s%s%s just DEOPERed", np->nick, np->ident, np->host->name->content, IsAccount(np)?"/":"", IsAccount(np)?np->authname:""); + } else { + if(after & UMODE_OPER) + controlwall(NO_OPER, NL_OPERING, "%s!%s@%s%s%s just OPERed", np->nick, np->ident, np->host->name->content, IsAccount(np)?"/":"", IsAccount(np)?np->authname:""); + } +} diff --git a/noperserv/noperserv.h b/noperserv/noperserv.h new file mode 100644 index 00000000..1297db91 --- /dev/null +++ b/noperserv/noperserv.h @@ -0,0 +1,22 @@ +#ifndef __NOPERSERV_H +#define __NOPERSERV_H + +#include "../control/control.h" +#include "../noperserv/noperserv_db.h" +#include "../lib/flags.h" + +int noperserv_ext; + +extern const flag no_userflags[]; +extern const flag no_noticeflags[]; +extern const flag no_commandflags[]; + +#define NO_NICKS_PER_WHOIS_LINE 3 + +#define NOGetAuthedUser(user) (no_autheduser *)(user->exts[noperserv_ext]) +#define NOGetAuthLevel(user) user->authlevel +#define NOGetNoticeLevel(user) user->noticelevel +#define NOMax(a, b) (a>b?a:b) +#define NOMin(a, b) (a +#include +#include +#include +#include + +#include "../control/control.h" +#include "../nick/nick.h" +#include "../localuser/localuserchannel.h" + +int controlkill(void *sender, int cargc, char **cargv); +int controlopchan(void *sender, int cargc, char **cargv); +int controlkick(void *sender, int cargc, char **cargv); + +void _init() { + registercontrolhelpcmd("kill",NO_OPER,2,&controlkill,"Usage: kill nick \nKill specificed user."); + registercontrolhelpcmd("opchan",NO_OPER,2,&controlopchan,"Usage: opchan channel nick\nGive user +o on channel."); + registercontrolhelpcmd("kick",NO_OPER,3,&controlkick,"Usage: kick channel user \nKick a user from the given channel"); +} + +void _fini() { + deregistercontrolcmd("kill",controlkill); + deregistercontrolcmd("opchan",controlopchan); + deregistercontrolcmd("kick",controlkick); +} + +int controlkick(void *sender, int cargc, char **cargv) { + nick *np=(nick *)sender; + nick *victim; + channel *cp; + modechanges changes; + nick *target; + + if (cargc<2) { + controlreply(sender,"Usage: kick channel user "); + return CMD_ERROR; + } + + if ((cp=findchannel(cargv[0]))!=NULL) { + if (cargv[1][0]=='#') { + if (!(target=getnickbynumericstr(cargv[1]+1))) { + controlreply(np,"Sorry, couldn't find numeric %s",cargv[0]+1); + return CMD_ERROR; + } + } else { + if ((target=getnickbynick(cargv[1]))==NULL) { + controlreply(np,"Sorry, couldn't find that user"); + return CMD_ERROR; + } + } + + if(cargc > 2) { + irc_send("%s K %s %s :%s",mynumeric->content,cp->index->name->content,longtonumeric(target->numeric,5),cargv[2]); + } else { + irc_send("%s K %s %s :Kicked",mynumeric->content,cp->index->name->content,longtonumeric(target->numeric,5)); + } + delnickfromchannel(cp, target->numeric, 1); + + controlreply(sender,"Put Kick for %s from %s.", target->nick, cp->index->name->content); + controlwall(NO_OPER, NL_KICKS, "%s/%s sent KICK for %s!%s@%s from %s", np->nick, np->authname, target->nick, target->ident, target->host->name->content,cp->index->name->content); + + } else { + controlreply(np,"Couldn't find channel %s.",cargv[0]); + return; + } + + return CMD_OK; +} + +int controlopchan(void *source, int cargc, char **cargv) { + nick *sender=(nick *)source; + nick *victim; + channel *cp; + modechanges changes; + nick *target; + unsigned long *lp; + + if (cargc<2) { + controlreply(sender,"Usage: opchan channel user"); + return CMD_ERROR; + } + + if ((cp=findchannel(cargv[0]))!=NULL) { + if (cargv[1][0]=='#') { + if (!(target=getnickbynumericstr(cargv[1]+1))) { + controlreply(sender,"Sorry, couldn't find numeric %s",cargv[0]+1); + return CMD_ERROR; + } + } else { + if ((target=getnickbynick(cargv[1]))==NULL) { + controlreply((nick *)sender,"Sorry, couldn't find that user"); + return CMD_ERROR; + } + } + + if ((lp=getnumerichandlefromchanhash(cp->users,target->numeric))==NULL) { + controlreply((nick *)sender,"Sorry, User not on channel"); + return CMD_ERROR; + } + + (*lp)|=CUMODE_OP; + irc_send("%s OM %s +o %s",mynumeric->content,cp->index->name->content,longtonumeric(target->numeric,5)); + controlreply(sender,"Put mode +o %s on %s.", target->nick, cp->index->name->content); + } else { + controlreply(sender,"Couldn't find channel %s.",cargv[0]); + return; + } + + return CMD_OK; +} + +int controlkill(void *sender, int cargc, char **cargv) { + nick *target; + char buf[BUFSIZE]; + int i; + nick *np = (nick *)sender; + + if (cargc<1) { + controlreply(np,"Usage: kill "); + return CMD_ERROR; + } + + if (cargv[0][0]=='#') { + if (!(target=getnickbynumericstr(cargv[0]+1))) { + controlreply(np,"Sorry, couldn't find numeric %s",cargv[0]+1); + return CMD_ERROR; + } + } else { + if ((target=getnickbynick(cargv[0]))==NULL) { + controlreply(np,"Sorry, couldn't find that user"); + return CMD_ERROR; + } + } + + killuser(NULL, target, (cargc>1)?cargv[1]:"Killed"); + controlreply(np,"KILL sent."); + controlwall(NO_OPER, NL_KILLS, "%s/%s sent KILL for %s!%s@%s (%s)", np->nick, np->authname, target->nick, target->ident, target->host->name->content,(cargc>1)?cargv[1]:"Killed"); + + return CMD_OK; +} diff --git a/noperserv/noperserv_db.c b/noperserv/noperserv_db.c new file mode 100644 index 00000000..c876c46b --- /dev/null +++ b/noperserv/noperserv_db.c @@ -0,0 +1,264 @@ +/* + * NOperserv v0.01 + * + * A replacement for Germania's ageing Operservice2 + * DB functions + * + * Copyright (C) 2005 Chris Porter. + */ + +#include "../nick/nick.h" +#include "../core/error.h" +#include "../lib/irc_string.h" +#include "../core/schedule.h" +#include "../pqsql/pqsql.h" + +#include "noperserv.h" +#include "noperserv_db.h" + +#include +#include + +int db_loaded = 0; +unsigned long loadedusers = 0; + +unsigned long lastuserid; + +no_autheduser *authedusers = NULL; + +void noperserv_create_tables(void); + +void noperserv_free_user(no_autheduser *au); +void noperserv_load_users(PGconn *dbconn, void *arg); + +void noperserv_check_nick(nick *np); +void noperserv_nick_account(int hooknum, void *arg); +void noperserv_quit_account(int hooknum, void *arg); + +void nopserserv_delete_from_autheduser(nick *np, no_autheduser *au); + +int noperserv_load_db(void) { + if(!pqconnected()) + return 0; + + if(db_loaded) + noperserv_cleanup_db(); + + db_loaded = 1; + + authedusers = NULL; + + noperserv_create_tables(); + + pqasyncquery(noperserv_load_users, NULL, + "SELECT ID, authname, flags, noticelevel FROM noperserv.users"); + + return 1; +} + +void noperserv_load_users(PGconn *dbconn, void *arg) { + PGresult *pgres = PQgetResult(dbconn); + int rows, i; + no_autheduser *nu; + nick *np; + + if(PQresultStatus(pgres) != PGRES_TUPLES_OK) { + Error("noperserv", ERR_ERROR, "Error loading user list."); + return; + } + + rows = PQntuples(pgres); + lastuserid = 0; + + for(i=0;iid = strtoul(PQgetvalue(pgres, i, 0), NULL, 10); + nu->authlevel = strtoul(PQgetvalue(pgres, i, 2), NULL, 10); + nu->noticelevel = strtoul(PQgetvalue(pgres, i, 3), NULL, 10); + nu->newuser = 0; + if(nu->id > lastuserid) + lastuserid = nu->id; + } + + Error("noperserv", ERR_INFO, "Loaded %d users", loadedusers); + + for(i=0;inext) + if(IsAccount(np)) + noperserv_check_nick(np); + + registerhook(HOOK_NICK_ACCOUNT, &noperserv_nick_account); + registerhook(HOOK_NICK_NEWNICK, &noperserv_nick_account); + registerhook(HOOK_NICK_LOSTNICK, &noperserv_quit_account); +} + +void noperserv_create_tables(void) { + pqcreatequery("CREATE SCHEMA noperserv"); + pqcreatequery( + "CREATE TABLE noperserv.users (" + "ID INT NOT NULL," + "authname VARCHAR(%d) NOT NULL," + "flags INT NOT NULL," + "noticelevel INT NOT NULL," + "PRIMARY KEY (ID))", ACCOUNTLEN); +} + +void noperserv_cleanup_db(void) { + no_autheduser *ap, *np; + + deregisterhook(HOOK_NICK_LOSTNICK, &noperserv_quit_account); + deregisterhook(HOOK_NICK_NEWNICK, &noperserv_nick_account); + deregisterhook(HOOK_NICK_ACCOUNT, &noperserv_nick_account); + + ap = authedusers; + while(ap) { + np = ap->next; + noperserv_free_user(ap); + ap = np; + } +} + +no_autheduser *noperserv_new_autheduser(char *authname) { + no_autheduser *au = (no_autheduser *)malloc(sizeof(no_autheduser)); + if(!au) + return NULL; + + au->authname = getsstring(authname, ACCOUNTLEN); + if(!au->authname) { + free(au); + return NULL; + } + + loadedusers++; + au->newuser = 1; + au->nick = NULL; + + au->next = authedusers; + authedusers = au; + + return au; +} + +void noperserv_delete_autheduser(no_autheduser *au) { + no_autheduser *ap = authedusers, *lp = NULL; + + if(!au->newuser) + pqquery("DELETE FROM noperserv.users WHERE id = %d", au->id); + + for(;ap;lp=ap,ap=ap->next) { + if(ap == au) { + if(lp) { + lp->next = ap->next; + } else { + authedusers = ap->next; + } + noperserv_free_user(ap); + return; + } + } +} + +void noperserv_update_autheduser(no_autheduser *au) { + if(au->newuser) { + char escapedauthname[ACCOUNTLEN * 2 + 1]; + PQescapeString(escapedauthname, au->authname->content, au->authname->length); + pqquery("INSERT INTO noperserv.users (id, authname, flags, noticelevel) VALUES (%lu,'%s',%lu,%lu)", au->id, au->authname->content, NOGetAuthLevel(au), NOGetNoticeLevel(au)); + au->newuser = 0; + } else { + pqquery("UPDATE noperserv.users SET flags = %lu, noticelevel = %lu WHERE id = %lu", NOGetAuthLevel(au), NOGetNoticeLevel(au), au->id); + } +} + +void noperserv_free_user(no_autheduser *au) { + no_nicklist *cp = au->nick, *np; + + while(cp) { + cp->nick->exts[noperserv_ext] = NULL; + np = cp->next; + free(cp); + cp = np; + } + + freesstring(au->authname); + free(au); + + loadedusers--; +} + +void noperserv_check_nick(nick *np) { + no_autheduser *au = noperserv_get_autheduser(np->authname); + if(au) + noperserv_add_to_autheduser(np, au); +} + +void noperserv_nick_account(int hooknum, void *arg) { + noperserv_check_nick((nick *)arg); +} + +void noperserv_quit_account(int hooknum, void *arg) { + nick *np = (void *)arg; + no_autheduser *au = NOGetAuthedUser(np); + no_nicklist *nl, *ln = NULL; + if(!au) + return; + + for(nl=au->nick;nl;ln=nl,nl=nl->next) + if(nl->nick == np) { + if(ln) { + ln->next = nl->next; + } else { + au->nick = nl->next; + } + free(nl); + break; + } +} + +no_autheduser *noperserv_get_autheduser(char *authname) { + no_autheduser *au = authedusers; + + for(;au;au=au->next) + if(!ircd_strcmp(authname, au->authname->content)) + return au; + + return NULL; +} + +unsigned long noperserv_get_autheduser_count(void) { + return loadedusers; +} + +unsigned long noperserv_next_autheduser_id(void) { + return ++lastuserid; +} + +void noperserv_add_to_autheduser(nick *np, no_autheduser *au) { + no_nicklist *nl = (no_nicklist *)malloc(sizeof(no_nicklist)); + if(!nl) + return; + + np->exts[noperserv_ext] = au; + + nl->nick = np; + + nl->next = au->nick; + au->nick = nl; +} + +void nopserserv_delete_from_autheduser(nick *np, no_autheduser *au) { + no_nicklist *cp = au->nick, *lp = NULL; + + for(;cp;lp=cp,cp=cp->next) + if(cp->nick == np) { + if(lp) { + lp->next = cp->next; + } else { + au->nick = cp->next; + } + free(cp); + break; + } +} diff --git a/noperserv/noperserv_db.h b/noperserv/noperserv_db.h new file mode 100644 index 00000000..16814b02 --- /dev/null +++ b/noperserv/noperserv_db.h @@ -0,0 +1,35 @@ +#ifndef __NOPERSERV_STRUCTS_H +#define __NOPERSERV_STRUCTS_H + +typedef unsigned long no_tableid; + +typedef struct no_nicklist { + nick *nick; + struct no_nicklist *next; +} no_nicklist; + +typedef struct no_autheduser { + unsigned newuser: 1; + sstring *authname; + flag_t authlevel; + flag_t noticelevel; + no_tableid id; + struct no_autheduser *next; + no_nicklist *nick; +} no_autheduser; + +int noperserv_load_db(void); +void noperserv_cleanup_db(void); + +extern no_autheduser *authedusers; + +void noperserv_delete_autheduser(no_autheduser *au); +no_autheduser *noperserv_new_autheduser(char *authname); +no_autheduser *noperserv_get_autheduser(char *authname); +void noperserv_update_autheduser(no_autheduser *au); +void noperserv_add_to_autheduser(nick *np, no_autheduser *au); + +unsigned long noperserv_get_autheduser_count(void); +unsigned long noperserv_next_autheduser_id(void); + +#endif diff --git a/noperserv/noperserv_hooks.c b/noperserv/noperserv_hooks.c new file mode 100644 index 00000000..c0d02383 --- /dev/null +++ b/noperserv/noperserv_hooks.c @@ -0,0 +1,460 @@ +#include "../control/control.h" +#include "../localuser/localuser.h" +#include "../core/schedule.h" +#include "../lib/splitline.h" +#include "../lib/flags.h" +#include "../lib/irc_string.h" +#include "../lib/strlfunc.h" + +#include "noperserv.h" +#include "noperserv_db.h" +#include "noperserv_hooks.h" +#include "noperserv_policy.h" + +#include +#include +#include + +struct storedhook { + CommandHandler old; + sstring *name; + char *oldhelp; + char *newhelp; + struct storedhook *next; +} storedhook; + +struct storedhook *storedhooks = NULL; + +int firsttime = 0; +nick *replynick = NULL; + +UserMessageHandler oldhandler; +ControlMsg oldreply; +ControlWall oldwall; + +void noperserv_trap_registration(int hooknum, void *arg); +int noperserv_showcommands(void *sender, int cargc, char **cargv); +int noperserv_rmmod(void *sender, int cargc, char **cargv); +int noperserv_reload(void *sender, int cargc, char **cargv); +int noperserv_whois(void *sender, int cargc, char **cargv); +int noperserv_help(void *sender, int cargc, char **cargv); +void noperserv_whois_handler(int hooknum, void *arg); +void noperserv_whois_account_handler(int hooknum, void *arg); +void noperserv_handle_messages(nick *target, int messagetype, void **args); +void noperserv_reply(nick *np, char *format, ...); +void noperserv_wall(flag_t permissionlevel, flag_t noticelevel, char *format, ...); + +struct specialsched special; + +#define HOOK_CONTROL_WHOISREQUEST_AUTHNAME -1 +#define HOOK_CONTROL_WHOISREQUEST_AUTHEDUSER -2 + +void noperserv_setup_hooks(void) { + oldreply = controlreply; + controlreply = &noperserv_reply; + + oldwall = controlwall; + controlwall = &noperserv_wall; + + memset(&special, 0, sizeof(specialsched)); + + if(!mynick) { + registerhook(HOOK_CONTROL_REGISTERED, &noperserv_trap_registration); + } else { + noperserv_trap_registration(0, (void *)mynick); + } + + registerhook(HOOK_CONTROL_WHOISREQUEST, &noperserv_whois_handler); +} + +int noperserv_hook_command(char *command, CommandHandler newcommand, char *newhelp) { + struct storedhook *newhook; + Command *fetchcommand = findcommandintree(controlcmds, command, 1); + + if(!fetchcommand) + return 1; + + newhook = (struct storedhook *)malloc(sizeof(struct storedhook)); + if(!newhook) + return 1; + + newhook->name = getsstring(command, strlen(command)); + if(!newhook->name) { + free(newhook); + return 1; + } + + newhook->old = fetchcommand->handler; + if(newhelp) { + int len = strlen(newhelp) + 1; + newhook->newhelp = (char *)malloc(len); + if(!newhook->newhelp) { + freesstring(newhook->name); + free(newhook); + } + strlcpy(newhook->newhelp, newhelp, len); + newhook->oldhelp = fetchcommand->help; + fetchcommand->help = newhook->newhelp; + } else { + newhook->newhelp = NULL; + } + + newhook->next = storedhooks; + storedhooks = newhook; + + fetchcommand->handler = newcommand; + + return 0; +} + +void noperserv_unhook_all_commands(void) { + struct storedhook *nh, *ch = storedhooks; + Command *fetchcommand; + + while(ch) { + if(ch->old && (fetchcommand = findcommandintree(controlcmds, ch->name->content, 1))) { + fetchcommand->handler = ch->old; + if(ch->newhelp) { + fetchcommand->help = ch->oldhelp; + free(ch->newhelp); + } + } + nh = ch->next; + freesstring(ch->name); + free(ch); + ch = nh; + } +} + +void noperserv_cleanup_hooks(void) { + deregisterhook(HOOK_CONTROL_WHOISREQUEST, &noperserv_whois_handler); + deregisterhook(HOOK_CONTROL_REGISTERED, &noperserv_trap_registration); + + if(firsttime) { + noperserv_unhook_all_commands(); + firsttime = 0; + } + + if(oldhandler) + hooklocaluserhandler(mynick, oldhandler); + + controlwall = oldwall; + controlreply = oldreply; +} + +void noperserv_trap_registration(int hooknum, void *arg) { + oldhandler = hooklocaluserhandler((nick *)arg, &noperserv_handle_messages); + if(!oldhandler) + return; + + if(!firsttime) { + firsttime = 1; + noperserv_hook_command("rmmod", &noperserv_rmmod, NULL); + noperserv_hook_command("reload", &noperserv_reload, NULL); + noperserv_hook_command("showcommands", &noperserv_showcommands, NULL); + noperserv_hook_command("whois", &noperserv_whois, "Usage: whois \nDisplays lots of information about the specified nickname, auth name or numeric."); + noperserv_hook_command("help", &noperserv_help, NULL); + } +} + +CommandHandler noperserv_find_hook(char *command) { + struct storedhook *hh = storedhooks; + for(;hh;hh=hh->next) + if(!ircd_strcmp(hh->name->content, command)) + return hh->old; + + return NULL; +} + +int noperserv_specialmod(nick *np, char *command, ScheduleCallback reloadhandler, int cargc, char **cargv) { + CommandHandler oldcommand = noperserv_find_hook(command); + if(cargc < 1) { + if(oldcommand) + return oldcommand(np, cargc, cargv); + return CMD_ERROR; + } + + if(!strcmp(cargv[0], "noperserv")) { + if(special.schedule) { + controlreply(np, "Previous attempt at un/reload still in progress."); + return CMD_OK; + } else { + special.modulename = getsstring(cargv[0], strlen(cargv[0])); + if(!special.modulename) { + controlreply(np, "Unable to copy module name. Seek cow herd to trample on server."); + return CMD_ERROR; + } else { + special.schedule = scheduleoneshot(time(NULL) + 1, reloadhandler, &special); + if(!special.schedule) { + freesstring(special.modulename); + special.modulename = NULL; + controlreply(np, "Unable to allocate schedule. Seek cow herd to trample on server."); + return CMD_ERROR; + } + controlreply(np, "Special case un/reload in <1 second, no response will be sent, standby. . ."); + return CMD_OK; + } + } + } else { + if(oldcommand) + return oldcommand(np, cargc, cargv); + return CMD_ERROR; + } +} + +int noperserv_rmmod(void *sender, int cargc, char **cargv) { + return noperserv_specialmod(sender, "rmmod", &controlspecialrmmod, cargc, cargv); +} + +int noperserv_reload(void *sender, int cargc, char **cargv) { + return noperserv_specialmod(sender, "reload", &controlspecialreloadmod, cargc, cargv); +} + +void noperserv_whois_hook(int hooknum, void *arg) { + controlreply(replynick, "%s", (char *)arg); +} + +int noperserv_whois(void *sender, int cargc, char **cargv) { + no_autheduser *au; + nick *np = (nick *)sender; + CommandHandler oldwhois = noperserv_find_hook("whois"); + + if(cargc < 1) { + if(oldwhois) + return oldwhois(sender, cargc, cargv); + return CMD_ERROR; + } + + if(cargv[0][0] != '#') { + if(cargv[0][0] == '*') + cargv[0][0] = '#'; + if(oldwhois) + return oldwhois(sender, cargc, cargv); + return CMD_ERROR; + } + + au = noperserv_get_autheduser(cargv[0] + 1); + if(!au) { + controlreply(np, "Account not registered."); + return CMD_OK; + } + + controlreply(np, "Account : %s", au->authname->content); + + replynick = np; + + registerhook(HOOK_CONTROL_WHOISREPLY, &noperserv_whois_hook); + noperserv_whois_account_handler(HOOK_CONTROL_WHOISREQUEST_AUTHEDUSER, (void *)au); + deregisterhook(HOOK_CONTROL_WHOISREPLY, &noperserv_whois_hook); + + controlreply(np, "Flags : %s", printflags(NOGetAuthLevel(au), no_userflags)); + + return CMD_OK; +} + +int noperserv_showcommands(void *sender, int cargc, char **cargv) { + nick *np = (nick *)sender; + Command *cmdlist[100]; + int i, n; + + n = getcommandlist(controlcmds, cmdlist, 100); + + controlreply(np, "The following commands are registered at present:"); + + for(i=0;icommand->content, printflags(cmdlist[i]->level, no_commandflags)); + + controlreply(np, "End of list."); + return CMD_OK; +} + +void noperserv_whois_handler(int hooknum, void *arg) { + char message[100]; + nick *np = (nick *)arg; + no_autheduser *au; + if(!np) + return; + + if(IsAccount(np)) { + au = NOGetAuthedUser(np); + if(au) { + snprintf(message, sizeof(message), "Flags : %s", printflags(NOGetAuthLevel(au), no_userflags)); + noperserv_whois_account_handler(HOOK_CONTROL_WHOISREQUEST_AUTHEDUSER, (void *)au); + } else { + snprintf(message, sizeof(message), "Flags : (user not known)"); + noperserv_whois_account_handler(HOOK_CONTROL_WHOISREQUEST_AUTHNAME, (void *)np->authname); + } + triggerhook(HOOK_CONTROL_WHOISREPLY, message); + } +} + +/* mmm, hacky */ +void noperserv_whois_account_handler(int hooknum, void *arg) { + int count = 0, found = 0; + char nickbuffer[(NICKLEN + 2) * NO_NICKS_PER_WHOIS_LINE - 1]; /* since we don't need space or comma for the first item we're fine NULL wise */ + char accountspace[NICKLEN + 3]; /* space, comma, null */ + char message[1024]; + + nickbuffer[0] = '\0'; + if(hooknum == HOOK_CONTROL_WHOISREQUEST_AUTHEDUSER) { + /* we can just read out the authed user linked list */ + no_autheduser *au = (void *)arg; + no_nicklist *nl = au->nick; + + if(nl) + found = 1; + + for(;nl;nl=nl->next) { + snprintf(accountspace, sizeof(accountspace), "%s%s", count++?", ":"", nl->nick->nick); + strlcat(nickbuffer, accountspace, sizeof(nickbuffer)); + + if(count >= NO_NICKS_PER_WHOIS_LINE) { + snprintf(message, sizeof(message), "Authed : %s", nickbuffer); + triggerhook(HOOK_CONTROL_WHOISREPLY, message); + nickbuffer[0] = '\0'; + count = 0; + } + } + } else { + /* inefficient way */ + char *authname = (char *)arg; + int i = 0; + nick *sp; + + for(;inext) + if(IsAccount(sp) && !ircd_strcmp(sp->authname, authname)) { + found = 1; + + snprintf(accountspace, sizeof(accountspace), "%s%s", count++?", ":"", sp->nick); + strlcat(nickbuffer, accountspace, sizeof(nickbuffer)); + + if(count >= NO_NICKS_PER_WHOIS_LINE) { + snprintf(message, sizeof(message), "Authed : %s", nickbuffer); + triggerhook(HOOK_CONTROL_WHOISREPLY, message); + nickbuffer[0] = '\0'; + count = 0; + } + } + } + + if(!found) { + snprintf(message, sizeof(message), "Authed : (no nicks authed)"); + triggerhook(HOOK_CONTROL_WHOISREPLY, message); + } else if(nickbuffer[0]) { + snprintf(message, sizeof(message), "Authed : %s", nickbuffer); + triggerhook(HOOK_CONTROL_WHOISREPLY, message); + } +} + +/* Obviously pinched from control.c */ +void noperserv_handle_messages(nick *target, int messagetype, void **args) { + Command *cmd; + char *cargv[50]; + int cargc; + nick *sender; + + switch(messagetype) { + case LU_PRIVMSG: /* override these two commands only */ + case LU_SECUREMSG: + /* If it's a message, first arg is nick and second is message */ + sender = (nick *)args[0]; + + controlwall(NO_DEVELOPER, NL_ALL_COMMANDS, "From: %s!%s@%s%s%s: %s", sender->nick, sender->ident, sender->host->name->content, IsAccount(sender)?"/":"", IsAccount(sender)?sender->authname:"", (char *)args[1]); + + /* Split the line into params */ + cargc = splitline((char *)args[1], cargv, 50, 0); + + if(!cargc) /* Blank line */ + return; + + cmd = findcommandintree(controlcmds,cargv[0],1); + if(!cmd) { + controlreply(sender, "Unknown command."); + return; + } + + /* If we were doing "authed user tracking" here we'd put a check in for authlevel */ + /* Here it is! */ + if (!noperserv_policy_command_permitted(cmd->level, sender)) { + controlreply(sender, "Access denied."); + return; + } + + /* Check the maxargs */ + if(cmd->maxparams < (cargc - 1)) { + /* We need to do some rejoining */ + rejoinline(cargv[cmd->maxparams], cargc - (cmd->maxparams)); + cargc = (cmd->maxparams) + 1; + } + + if((cmd->handler)((void *)sender,cargc-1,&(cargv[1])) == CMD_USAGE) + controlhelp(sender, cmd); + + break; + default: + if(oldhandler) + oldhandler(target, messagetype, args); + break; + } +} + +void noperserv_reply(nick *np, char *format, ...) { + char buf[512]; + va_list va; + no_autheduser *au = NOGetAuthedUser(np); + + va_start(va, format); + vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + + if(au && !(NOGetNoticeLevel(au) & NL_NOTICES)) { + controlmessage(np, "%s", buf); + } else { + controlnotice(np, "%s", buf); + } +} + +int noperserv_help(void *sender, int cargc, char **cargv) { + Command *cmd; + nick *np = (nick *)sender; + + if(cargc < 1) + return CMD_USAGE; + + cmd = findcommandintree(controlcmds, cargv[0], 1); + if(!cmd) { + controlreply(np, "Unknown command."); + return CMD_ERROR; + } + + if(!noperserv_policy_command_permitted(cmd->level, np)) { + controlreply(np, "Access denied."); + return CMD_ERROR; + } + + controlhelp(np, cmd); + return CMD_OK; +} + +void noperserv_wall(flag_t permissionlevel, flag_t noticelevel, char *format, ...) { + char buf[512]; + va_list va; + no_autheduser *au = authedusers; + no_nicklist *nl; + char *flags = printflags(noticelevel, no_noticeflags) + 1; + + va_start(va, format); + vsnprintf(buf, sizeof(buf), format, va); + va_end(va); + + Error("noperserv", ERR_INFO, "$%s$ %s", flags, buf); + + for(;au;au=au->next) { + if(NOGetNoticeLevel(au) & noticelevel) { + for(nl=au->nick;nl;nl=nl->next) + if(noperserv_policy_command_permitted(permissionlevel, nl->nick)) + controlreply(nl->nick, "$%s$ %s", flags, buf); + } + } +} diff --git a/noperserv/noperserv_hooks.h b/noperserv/noperserv_hooks.h new file mode 100644 index 00000000..9cc39dd7 --- /dev/null +++ b/noperserv/noperserv_hooks.h @@ -0,0 +1,2 @@ +void noperserv_setup_hooks(void); +void noperserv_cleanup_hooks(void); diff --git a/noperserv/noperserv_policy.c b/noperserv/noperserv_policy.c new file mode 100644 index 00000000..c7fdffeb --- /dev/null +++ b/noperserv/noperserv_policy.c @@ -0,0 +1,95 @@ +#include "../control/control.h" +#include "noperserv.h" +#include "noperserv_db.h" +#include "noperserv_policy.h" + +/* command access */ +int noperserv_policy_command_permitted(flag_t level, nick *user) { + no_autheduser *au; + if(level == __NO_ANYONE) + return 1; + if((level & __NO_OPERED) && !IsOper(user)) + return 0; + if(level & __NO_AUTHED) { + if(!IsAccount(user)) + return 0; + if(level & __NO_ACCOUNT) { + if(!(au = NOGetAuthedUser(user))) + return 0; + if((level & __NO_DEVELOPER) && !NOIsDeveloper(au)) { + return 0; + } else if((level & __NO_OPER) && !NOIsLeastOper(au)) { + return 0; + } else if((level & __NO_STAFF) && !NOIsLeastStaff(au)) { + return 0; + } + if ((level & __NO_SEC) && !NOIsLeastSec(au)) + return 0; + if ((level & __NO_TRUST) && !NOIsLeastTrust(au)) + return 0; + } + } + + return 1; +} + +/* return userflags permitted for each level */ +flag_t noperserv_policy_permitted_noticeflags(no_autheduser *target) { + flag_t result = NL_PEONIC_FLAGS; + if(NOIsDeveloper(target)) + result |= NL_DEV_FLAGS; + if(NOIsLeastOper(target)) + result |= NL_OPER_FLAGS; + if(NOIsLeastSec(target)) + result |= NL_SEC_FLAGS; + if(NOIsLeastTrust(target)) + result |= NL_TRUST_FLAGS; + + return result; +} + +/* @logic */ +/* updates target's noticeflags according to their new userflags, unsetting unpermitted and setting new permitted flags */ +void noperserv_policy_update_noticeflags(flag_t fwas, no_autheduser *target) { + flag_t newflags = NOGetNoticeLevel(target); + + newflags &= noperserv_policy_permitted_noticeflags(target); /* unset flags we're not supposed to have */ + + /* and add flags we're allowed */ + if(!(fwas & __NO_OPER) && NOIsLeastOper(target)) + newflags |= NL_OPER_FLAGS; + if(!(fwas & __NO_SEC) && NOIsLeastSec(target)) + newflags |= NL_SEC_FLAGS; + if(!(fwas & __NO_TRUST) && NOIsLeastTrust(target)) + newflags |= NL_TRUST_FLAGS; + if(!(fwas & __NO_DEVELOPER) && NOIsDeveloper(target)) + newflags |= NL_DEV_FLAGS; + + target->noticelevel = newflags; +} + +/* is au allowed to modified targets flags */ +flag_t noperserv_policy_permitted_modifications(no_autheduser *au, no_autheduser *target) { + flag_t permitted = 0; + + /* @policy */ + if(NOIsLeastOper(au)) { + permitted |= NO_OPER_FLAGS; + if(target == au) /* let opers remove their own oper flag */ + permitted |= __NO_OPER; + } + if(target == au) { + if(NOIsLeastSec(au)) + permitted |= __NO_SEC; + if(NOIsLeastTrust(au)) + permitted |= __NO_TRUST; + } + + if(NOIsDeveloper(au)) + permitted |= NO_DEV_FLAGS; + + if((target != au) && NOIsLeastOper(target) && !NOIsDeveloper(au)) + return 0; + + return permitted; +} diff --git a/noperserv/noperserv_policy.h b/noperserv/noperserv_policy.h new file mode 100644 index 00000000..169d7309 --- /dev/null +++ b/noperserv/noperserv_policy.h @@ -0,0 +1,32 @@ +#ifndef __NOPERSERV_POLICY_H +#define __NOPERSERV_POLICY_H + +int noperserv_policy_command_permitted(flag_t level, nick *user); +flag_t noperserv_policy_permitted_noticeflags(no_autheduser *target); +void noperserv_policy_update_noticeflags(flag_t fwas, no_autheduser *target); +flag_t noperserv_policy_permitted_modifications(no_autheduser *au, no_autheduser *target); + +#define NO_DEFAULT_LEVEL 0 +#define NO_FIRST_USER_LEVEL __NO_DEVELOPER | __NO_OPER | __NO_STAFF | NO_DEFAULT_LEVEL +#define NO_DEFAULT_NOTICELEVEL NL_NOTICES +#define NO_FIRST_USER_DEFAULT_NOTICELEVEL NL_ALL | NO_DEFAULT_NOTICELEVEL + +#define NOIsStaff(user) (NOGetAuthLevel(user) & __NO_STAFF) +#define NOIsTrust(user) (NOGetAuthLevel(user) & __NO_TRUST) +#define NOIsSec(user) (NOGetAuthLevel(user) & __NO_SEC) +#define NOIsOper(user) (NOGetAuthLevel(user) & __NO_OPER) +#define NOIsDeveloper(user) (NOGetAuthLevel(user) & __NO_DEVELOPER) + +#define NOIsLeastStaff(user) (NOGetAuthLevel(user) & (__NO_DEVELOPER | __NO_OPER | __NO_STAFF)) +#define NOIsLeastOper(user) (NOGetAuthLevel(user) & (__NO_OPER | __NO_DEVELOPER)) +#define NOIsLeastTrust(user) (NOGetAuthLevel(user) & (__NO_DEVELOPER | __NO_TRUST)) +#define NOIsLeastSec(user) (NOGetAuthLevel(user) & (__NO_DEVELOPER | __NO_SEC)) + +#define NL_PEONIC_FLAGS NL_NOTICES +#define NL_ALL NL_MANAGEMENT | NL_TRUSTS | NL_KICKS | NL_KILLS | NL_GLINES | NL_HITS | NL_CLONING | NL_CLEARCHAN | NL_FAKEUSERS | NL_BROADCASTS | NL_OPERATIONS | NL_OPERING | NL_ALL_COMMANDS +#define NL_OPER_FLAGS NL_MANAGEMENT | NL_TRUSTS | NL_KICKS | NL_KILLS | NL_GLINES | NL_HITS | NL_CLONING | NL_CLEARCHAN | NL_FAKEUSERS | NL_BROADCASTS | NL_OPERATIONS | NL_OPERING +#define NL_SEC_FLAGS NL_CLONING +#define NL_TRUST_FLAGS NL_TRUSTS | NL_CLONING +#define NL_DEV_FLAGS NL_ALL_COMMANDS + +#endif diff --git a/parser/parser.c b/parser/parser.c index 125f9c00..90111f40 100644 --- a/parser/parser.c +++ b/parser/parser.c @@ -74,15 +74,15 @@ int countcommandtree(CommandTree *ct) { } /* - * addcommandtotree: + * addcommandhelptotree: * - * This installs a specific command in a tree. + * This installs a specific command in a tree, with a help paramater. * * This function builds the Command structure in addition to * installing it in the tree */ -Command *addcommandtotree(CommandTree *ct, const char *cmdname, int level, int maxparams, CommandHandler handler) { +Command *addcommandhelptotree(CommandTree *ct, const char *cmdname, int level, int maxparams, CommandHandler handler, const char *help) { Command *nc, *c; int i; @@ -94,13 +94,25 @@ Command *addcommandtotree(CommandTree *ct, const char *cmdname, int level, int m nc->handler=handler; nc->ext=NULL; nc->next=NULL; - + if (help) { + int len=strlen(help); + nc->help=(char *)malloc(len+1); + if(nc->help) { + strncpy(nc->help, help, len); + nc->help[len] = '\0'; + } + } else { + nc->help=NULL; + } + /* Sanity check the string */ for (i=0;icommand->length;i++) { nc->command->content[i]=toupper(nc->command->content[i]); if (nc->command->content[i]<'A' || nc->command->content[i]>'Z') { /* Someone tried to register an invalid command name */ freesstring(nc->command); + if(nc->help) + free(nc->help); free(nc); return NULL; } @@ -114,6 +126,8 @@ Command *addcommandtotree(CommandTree *ct, const char *cmdname, int level, int m } else if (insertcommand(nc,ct,0)) { /* Erk, that didn't work.. */ freesstring(nc->command); + if(nc->help) + free(nc->help); free(nc); return NULL; } @@ -207,6 +221,8 @@ int deletecommand(sstring *cmdname, CommandTree *ct, int depth, CommandHandler h c=*ch; (*ch)=(Command *)((*ch)->next); freesstring(c->command); + if(c->help) + free(c->help); free(c); return 0; } @@ -224,6 +240,8 @@ int deletecommand(sstring *cmdname, CommandTree *ct, int depth, CommandHandler h c=*ch; (*ch)=(Command *)((*ch)->next); freesstring(c->command); + if(c->help) + free(c->help); free(c); return 0; } diff --git a/parser/parser.h b/parser/parser.h index bce50aa3..1aa03054 100644 --- a/parser/parser.h +++ b/parser/parser.h @@ -21,6 +21,7 @@ #define CMD_OK 0 #define CMD_ERROR 1 #define CMD_LAST 2 +#define CMD_USAGE 3 /* Generic CommandHandler type * void * = pointer to some relevant object to identify where the message came from @@ -32,6 +33,7 @@ typedef int (*CommandHandler)(void *, int, char**); typedef struct Command { sstring *command; /* Name of the command/token/thing */ + char *help; /* Help information, sorry splidge! */ int level; /* "level" required to use the command/token/thing */ int maxparams; /* Maximum number of parameters for the command/token/thing */ CommandHandler handler; /* Function to deal with the message */ @@ -47,9 +49,11 @@ typedef struct CommandTree { CommandTree *newcommandtree(); void destroycommandtree(CommandTree *ct); -Command *addcommandtotree(CommandTree *ct, const char *cmdname, int level, int maxparams, CommandHandler handler); +Command *addcommandhelptotree(CommandTree *ct, const char *cmdname, int level, int maxparams, CommandHandler handler, const char *help); int deletecommandfromtree(CommandTree *ct, const char *cmdname, CommandHandler handler); Command *findcommandintree(CommandTree *ct, const char *cmdname, int strictcheck); int getcommandlist(CommandTree *ct, Command **commandlist, int maxcommands); +#define addcommandtotree(a, b, c, d, e) addcommandhelptotree(a, b, c, d, e, NULL) + #endif diff --git a/pqsql/Makefile b/pqsql/Makefile new file mode 100644 index 00000000..19e913d2 --- /dev/null +++ b/pqsql/Makefile @@ -0,0 +1,7 @@ + +.PHONY: all +all: pqsql.so + +pqsql.so: pqsql.o + ld -shared -Bdynamic ${LIBPGSQL} -o $@ $^ + diff --git a/pqsql/pqsql.c b/pqsql/pqsql.c new file mode 100644 index 00000000..7325b177 --- /dev/null +++ b/pqsql/pqsql.c @@ -0,0 +1,222 @@ +/* + * PQSQL module + * + * 99% of the handling is stolen from Q9. + */ + +#include "../core/config.h" +#include "../core/error.h" +#include "../irc/irc_config.h" +#include "../core/events.h" +#include "../core/hooks.h" +#include "pqsql.h" + +#include +#include +#include + +typedef struct pqasyncquery_s { + sstring *query; + void *tag; + PQQueryHandler handler; + int flags; + struct pqasyncquery_s *next; +} pqasyncquery_s; + +pqasyncquery_s *queryhead = NULL, *querytail = NULL; + +int dbconnected = 0; +PGconn *dbconn; + +void dbhandler(int fd, short revents); +void dbstatus(int hooknum, void *arg); +void disconnectdb(void); +void connectdb(void); + +void _init(void) { + connectdb(); +} + +void _fini(void) { + disconnectdb(); +} + +void connectdb(void) { + sstring *dbhost, *dbusername, *dbpassword, *dbdatabase, *dbport; + char connectstr[1024]; + + if(pqconnected()) + return; + + /* stolen from chanserv as I'm lazy */ + dbhost = getcopyconfigitem("pqsql", "host", "localhost", HOSTLEN); + dbusername = getcopyconfigitem("pqsql", "username", "newserv", 20); + dbpassword = getcopyconfigitem("pqsql", "password", "moo", 20); + dbdatabase = getcopyconfigitem("pqsql", "database", "newserv", 20); + dbport = getcopyconfigitem("pqsql", "port", "431", 8); + + if(!dbhost || !dbusername || !dbpassword || !dbdatabase || !dbport) { + /* freesstring allows NULL */ + freesstring(dbhost); + freesstring(dbusername); + freesstring(dbpassword); + freesstring(dbdatabase); + freesstring(dbport); + return; + } + + snprintf(connectstr, sizeof(connectstr), "dbname=%s user=%s password=%s", dbdatabase->content, dbusername->content, dbpassword->content); + + freesstring(dbhost); + freesstring(dbusername); + freesstring(dbpassword); + freesstring(dbdatabase); + freesstring(dbport); + + Error("pqsql", ERR_INFO, "Attempting database connection: %s", connectstr); + + /* Blocking connect for now.. */ + dbconn = PQconnectdb(connectstr); + + if (!dbconn || (PQstatus(dbconn) != CONNECTION_OK)) + return; + + Error("pqsql", ERR_INFO, "Connected!"); + + dbconnected = 1; + + PQsetnonblocking(dbconn, 1); + + /* this kicks ass, thanks splidge! */ + registerhandler(PQsocket(dbconn), POLLIN, dbhandler); + registerhook(HOOK_CORE_STATSREQUEST, dbstatus); +} + +void dbhandler(int fd, short revents) { + PGresult *res; + pqasyncquery_s *qqp; + + if(revents & POLLIN) { + PQconsumeInput(dbconn); + + if(!PQisBusy(dbconn)) { /* query is complete */ + if(queryhead->handler) + (queryhead->handler)(dbconn, queryhead->tag); + + while((res = PQgetResult(dbconn))) { + switch(PQresultStatus(res)) { + case PGRES_TUPLES_OK: + Error("pqsql", ERR_WARNING, "Unhandled tuples output (query: %s)", queryhead->query->content); + break; + + case PGRES_NONFATAL_ERROR: + case PGRES_FATAL_ERROR: + /* if a create query returns an error assume it went ok, paul will winge about this */ + if(!(queryhead->flags & QH_CREATE)) + Error("pqsql", ERR_WARNING, "Unhandled error response (query: %s)", queryhead->query->content); + break; + + default: + break; + } + + PQclear(res); + } + + /* Free the query and advance */ + qqp = queryhead; + if(queryhead == querytail) + querytail = NULL; + + queryhead = queryhead->next; + + freesstring(qqp->query); + free(qqp); + + if(queryhead) { /* Submit the next query */ + PQsendQuery(dbconn, queryhead->query->content); + PQflush(dbconn); + } + } + } +} + +/* sorry Q9 */ +void pqasyncqueryf(PQQueryHandler handler, void *tag, int flags, char *format, ...) { + char querybuf[8192]; + va_list va; + int len; + pqasyncquery_s *qp; + + if(!pqconnected()) + return; + + va_start(va, format); + len = vsnprintf(querybuf, sizeof(querybuf), format, va); + va_end(va); + + /* PPA: no check here... */ + qp = (pqasyncquery_s *)malloc(sizeof(pqasyncquery_s)); + if(!qp) + return; + + qp->query = getsstring(querybuf, len); + qp->tag = tag; + qp->handler = handler; + qp->next = NULL; /* shove them at the end */ + qp->flags = flags; + + if(querytail) { + querytail->next = qp; + querytail = qp; + } else { + querytail = queryhead = qp; + PQsendQuery(dbconn, qp->query->content); + PQflush(dbconn); + } +} + +void disconnectdb(void) { + pqasyncquery_s *qqp = queryhead, *nqqp; + + if(!pqconnected()) + return; + + /* do this first else we may get conflicts */ + deregisterhandler(PQsocket(dbconn), 0); + + /* Throw all the queued queries away, beware of data malloc()ed inside the query item.. */ + while(qqp) { + nqqp = qqp->next; + freesstring(qqp->query); + free(qqp); + qqp = nqqp; + } + + deregisterhook(HOOK_CORE_STATSREQUEST, dbstatus); + PQfinish(dbconn); + dbconn = NULL; /* hmm? */ + + dbconnected = 0; +} + +/* more stolen code from Q9 */ +void dbstatus(int hooknum, void *arg) { + if ((int)arg > 10) { + int i = 0; + pqasyncquery_s *qqp; + char message[100]; + + if(queryhead) + for(qqp=queryhead;qqp;qqp=qqp->next) + i++; + + snprintf(message, sizeof(message), "PQSQL : %6d database queries queued.",i); + + triggerhook(HOOK_CORE_STATSREPLY, message); + } +} + +int pqconnected(void) { + return dbconnected; +} diff --git a/pqsql/pqsql.h b/pqsql/pqsql.h new file mode 100644 index 00000000..3f92e595 --- /dev/null +++ b/pqsql/pqsql.h @@ -0,0 +1,18 @@ +#ifndef __PQSQL_DB_H +#define __PQSQL_DB_H + +#include + +#define QH_CREATE 0x01 + +typedef void (*PQQueryHandler)(PGconn *, void *); + +void pqasyncqueryf(PQQueryHandler handler, void *tag, int flags, char *format, ...); + +#define pqasyncquery(handler, tag, format, ...) pqasyncqueryf(handler, tag, 0, format , ##__VA_ARGS__) +#define pqcreatequery(format, ...) pqasyncqueryf(NULL, NULL, QH_CREATE, format , ##__VA_ARGS__) +#define pqquery(format, ...) pqasyncquery(NULL, NULL, format , ##__VA_ARGS__) + +int pqconnected(void); + +#endif diff --git a/regexgline/regexgline.c b/regexgline/regexgline.c index 09c95011..add5e054 100644 --- a/regexgline/regexgline.c +++ b/regexgline/regexgline.c @@ -293,7 +293,7 @@ int rg_gline(void *source, int cargc, char **cargv) { rg_logevent(np, "regexgline", "%s %d %d %s", cargv[0], expiry, count, cargv[3]); controlreply(np, "Added regexgline: %s (expires in: %s, hit %d user%s): %s", cargv[0], expirybuf, count, (count!=1)?"s":"", cargv[3]); - controlnoticeopers("%s added regexgline: %s (expires in: %s, hit %d user%s): %s", np->nick, cargv[0], expirybuf, count, (count!=1)?"s":"", cargv[3]); + controlwall(NO_OPER, NL_GLINES, "%s added regexgline: %s (expires in: %s, hit %d user%s): %s", np->nick, cargv[0], expirybuf, count, (count!=1)?"s":"", cargv[3]); return CMD_OK; } @@ -372,7 +372,7 @@ int rg_delgline(void *source, int cargc, char **cargv) { } if (count > 0) { controlreply(np, "Deleted (matched: %d).", count); - controlnoticeopers("%s removed regexgline: %s (matches: %d)", np->nick, cargv[0], count); + controlwall(NO_OPER, NL_GLINES, "%s removed regexgline: %s (matches: %d)", np->nick, cargv[0], count); } else { controlreply(np, "No glines matched: %s", cargv[0]); }