]> jfr.im git - irc/quakenet/newserv.git/commitdiff
Merged revisions 310,312-314,322-323,328-329 via svnmerge from
authorChris Porter <redacted>
Fri, 30 Dec 2005 14:42:00 +0000 (14:42 +0000)
committerChris Porter <redacted>
Fri, 30 Dec 2005 14:42:00 +0000 (14:42 +0000)
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.
........

29 files changed:
carrot/carrot.c
chansearch/chansearch.c
chanstats/chansearch.c
control/control.c
control/control.h
core/hooks.h
countusers/countusers.c
fsck/fsck.c
horse/horse.c
lameisp/lameisp.c
lib/sha1.c
newsearch/newsearch.c
nick/nickhandlers.c
noperserv/Makefile [new file with mode: 0644]
noperserv/noperserv.c [new file with mode: 0644]
noperserv/noperserv.h [new file with mode: 0644]
noperserv/noperserv_commands.c [new file with mode: 0644]
noperserv/noperserv_db.c [new file with mode: 0644]
noperserv/noperserv_db.h [new file with mode: 0644]
noperserv/noperserv_hooks.c [new file with mode: 0644]
noperserv/noperserv_hooks.h [new file with mode: 0644]
noperserv/noperserv_policy.c [new file with mode: 0644]
noperserv/noperserv_policy.h [new file with mode: 0644]
parser/parser.c
parser/parser.h
pqsql/Makefile [new file with mode: 0644]
pqsql/pqsql.c [new file with mode: 0644]
pqsql/pqsql.h [new file with mode: 0644]
regexgline/regexgline.c

index ce89788acbf533d40c692bc4bcea1a962f37228e..e39bef28f1105c0bcfb697caa2db72230969894f 100644 (file)
@@ -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() {
index 734c884bf5b00475219b2fc2a86894bb91b96b1b..4adfcc7b6eafe61dea97452f99a8510b4d10d451 100644 (file)
@@ -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 <criteria>\nSearches for channels with specified criteria.\nSend chanstats with no arguments for more information.");
 }
 
 void _fini() {
index bb0d3c1e656ed59b14a4953a8e2b0763c75acdb2..3f4564a2f1e3ec2009c43eb96b613a1356a6c9fc 100644 (file)
@@ -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 <search terms>\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<cargc;) {
     if (cargv[i][0]=='!') {
index de8b031b9cac5b2637c85d76544a93a9730158e6..ed07a99441b16f89c3b9f64facaf71d8dcb99de1 100644 (file)
 #include <string.h>
 #include <stdarg.h>
 
-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 <nickname|#numeric>\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 <reason>\nTerminates the service.");
+  registercontrolhelpcmd("insmod",NO_DEVELOPER,1,&controlinsmod,"Usage: insmod <module>\nAdds a module to the running instance.");
+  registercontrolhelpcmd("rmmod",NO_DEVELOPER,1,&controlrmmod,"Usage: rmmod <module>\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 <module>\nReloads specified module.");
+  registercontrolhelpcmd("help",NO_ANYONE,1,&controlhelpcmd,"Usage: help <command>\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 <user>");
-    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 <modulename>");
-    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 <modulename>");
-    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 <modulename>");
-    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;i<n;i++) {
-    controlreply(np,"%s (level %d)",cmdlist[i]->command->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);
index bbc63c4d37ddf795a7f9671bce5f43976ca4e491..248e8970a7d7dc596be960577145a7d74c06aeca 100644 (file)
@@ -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  
index 483574f72ad729b9e98b1449325aeef113f99b00..1c256d394b1a3193d898aa590bf832c4079d4900 100644 (file)
@@ -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) */
 
 #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();
index 2c3e3ee7ec8774145f1e424dab5a1be85201465f..31826ee7f6a4c4f3daa1c4430c8e4dd3ab6dc01f 100644 (file)
@@ -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 <user@host or host>");
-    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 <hostmask>\nReturns users on specified hostmask.");
 }
 
 void _fini() {
index 5cec1c915409898ce603177927337254bb67858f..1f5bda035f42e8434b4ec75aefa48aba88e6bc4a 100644 (file)
@@ -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() {
index d3c347d620c0a557cad38969224e20ede0ad4c5d..67d7206775223b75bf65fafa7d9bd4338430ccb8 100644 (file)
@@ -7,10 +7,8 @@ int ho_horse(void *source, int cargc, char **cargv) {
   nick *victim;
   channel *cp;
   
-  if (cargc<1) {
-    controlreply(sender,"Usage: horse <target>");
-    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 <target>\nSpams a horse at target.");
 }
 
 void _fini() {
index 993f2d9e762b2e3559cf5ed671f026bd61a3a913..92957d74980f8f6681b20c1bd9eb6758da026a1e 100644 (file)
@@ -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
+}
index 52cde648f490037306d7b8dcb259765562790139..2172392fa8e09c2bb144001011d9959fc8c7ee08 100644 (file)
@@ -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];
index 466559ced750ffcf19d2796b9b7244dccedea219..85dd2bd823395520b91d83720d19528a489eae32 100644 (file)
@@ -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 <criteria>\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 (cargc<arg) {
          controlreply(sender,"Error: -l switch requires an argument");
-         return CMD_ERROR;
+         return CMD_USAGE;
        }
        limit=strtoul(cargv[arg++],NULL,10);
        break;
index e5abb13eb1257c4b93e66fe3803fac88c321bbea..b91137475502d62e86cd7cd58d2132b76662cd7e 100644 (file)
@@ -239,6 +239,12 @@ int handleusermodemsg(void *source, int cargc, char **cargv) {
       return CMD_OK;
     }
     oldflags=np->umodes;
+    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 (file)
index 0000000..7d4b879
--- /dev/null
@@ -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 (file)
index 0000000..43b1600
--- /dev/null
@@ -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 <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+
+#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 <nickname|#authname> ?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 <nickname|#authname>\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;i<NICKHASHSIZE;i++)
+        for(np2=nicktable[i];np2;np2=np2->next)
+          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;i<NICKHASHSIZE;i++)
+    for(np2=nicktable[i];np2;np2=np2->next)
+      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 (file)
index 0000000..1297db9
--- /dev/null
@@ -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<b?b:a)
+
+#endif  
diff --git a/noperserv/noperserv_commands.c b/noperserv/noperserv_commands.c
new file mode 100644 (file)
index 0000000..be30344
--- /dev/null
@@ -0,0 +1,140 @@
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <stdarg.h>\r
+#include <string.h>\r
+#include <time.h>\r
+\r
+#include "../control/control.h"\r
+#include "../nick/nick.h"\r
+#include "../localuser/localuserchannel.h"\r
+\r
+int controlkill(void *sender, int cargc, char **cargv);\r
+int controlopchan(void *sender, int cargc, char **cargv);\r
+int controlkick(void *sender, int cargc, char **cargv);\r
+\r
+void _init() {\r
+  registercontrolhelpcmd("kill",NO_OPER,2,&controlkill,"Usage: kill nick <reason>\nKill specificed user.");\r
+  registercontrolhelpcmd("opchan",NO_OPER,2,&controlopchan,"Usage: opchan channel nick\nGive user +o on channel.");\r
+  registercontrolhelpcmd("kick",NO_OPER,3,&controlkick,"Usage: kick channel user <reason>\nKick a user from the given channel");\r
+}\r
+\r
+void _fini() {\r
+  deregistercontrolcmd("kill",controlkill); \r
+  deregistercontrolcmd("opchan",controlopchan);\r
+  deregistercontrolcmd("kick",controlkick);\r
+}\r
+\r
+int controlkick(void *sender, int cargc, char **cargv) {\r
+  nick *np=(nick *)sender;\r
+  nick *victim;\r
+  channel *cp;\r
+  modechanges changes;\r
+  nick *target;\r
+\r
+  if (cargc<2) {\r
+    controlreply(sender,"Usage: kick channel user <reason>");\r
+    return CMD_ERROR;\r
+  }\r
+\r
+  if ((cp=findchannel(cargv[0]))!=NULL) {\r
+    if (cargv[1][0]=='#') {\r
+      if (!(target=getnickbynumericstr(cargv[1]+1))) {\r
+        controlreply(np,"Sorry, couldn't find numeric %s",cargv[0]+1);\r
+        return CMD_ERROR;\r
+      }\r
+    } else {\r
+      if ((target=getnickbynick(cargv[1]))==NULL) {\r
+        controlreply(np,"Sorry, couldn't find that user");\r
+        return CMD_ERROR;\r
+      }\r
+    }\r
+\r
+    if(cargc > 2) {\r
+      irc_send("%s K %s %s :%s",mynumeric->content,cp->index->name->content,longtonumeric(target->numeric,5),cargv[2]);\r
+    } else {\r
+      irc_send("%s K %s %s :Kicked",mynumeric->content,cp->index->name->content,longtonumeric(target->numeric,5));\r
+    }\r
+    delnickfromchannel(cp, target->numeric, 1);\r
+\r
+    controlreply(sender,"Put Kick for %s from %s.", target->nick, cp->index->name->content);\r
+    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);\r
+\r
+  } else {\r
+    controlreply(np,"Couldn't find channel %s.",cargv[0]);\r
+    return;\r
+  }\r
+\r
+  return CMD_OK;\r
+}\r
+\r
+int controlopchan(void *source, int cargc, char **cargv) {\r
+  nick *sender=(nick *)source;\r
+  nick *victim;\r
+  channel *cp;\r
+  modechanges changes;\r
+  nick *target;\r
+  unsigned long *lp;\r
+\r
+  if (cargc<2) {\r
+    controlreply(sender,"Usage: opchan channel user");\r
+    return CMD_ERROR;\r
+  }\r
+  \r
+  if ((cp=findchannel(cargv[0]))!=NULL) {\r
+    if (cargv[1][0]=='#') {\r
+      if (!(target=getnickbynumericstr(cargv[1]+1))) {\r
+        controlreply(sender,"Sorry, couldn't find numeric %s",cargv[0]+1);\r
+        return CMD_ERROR;\r
+      }\r
+    } else {\r
+      if ((target=getnickbynick(cargv[1]))==NULL) {\r
+        controlreply((nick *)sender,"Sorry, couldn't find that user");\r
+        return CMD_ERROR;\r
+      }\r
+    }\r
+\r
+    if ((lp=getnumerichandlefromchanhash(cp->users,target->numeric))==NULL) {\r
+      controlreply((nick *)sender,"Sorry, User not on channel");\r
+      return CMD_ERROR;\r
+    }\r
+\r
+    (*lp)|=CUMODE_OP;\r
+    irc_send("%s OM %s +o %s",mynumeric->content,cp->index->name->content,longtonumeric(target->numeric,5));\r
+    controlreply(sender,"Put mode +o %s on %s.", target->nick, cp->index->name->content);  \r
+  } else {\r
+    controlreply(sender,"Couldn't find channel %s.",cargv[0]);\r
+    return;\r
+  }\r
+  \r
+  return CMD_OK;\r
+}\r
+\r
+int controlkill(void *sender, int cargc, char **cargv) {\r
+  nick *target;\r
+  char buf[BUFSIZE];\r
+  int i;\r
+  nick *np = (nick *)sender;\r
\r
+  if (cargc<1) {\r
+    controlreply(np,"Usage: kill <user> <reason>");\r
+    return CMD_ERROR;\r
+  }\r
+  \r
+  if (cargv[0][0]=='#') {\r
+    if (!(target=getnickbynumericstr(cargv[0]+1))) {\r
+      controlreply(np,"Sorry, couldn't find numeric %s",cargv[0]+1);\r
+      return CMD_ERROR;\r
+    }\r
+  } else {\r
+    if ((target=getnickbynick(cargv[0]))==NULL) {\r
+      controlreply(np,"Sorry, couldn't find that user");\r
+      return CMD_ERROR;\r
+    }\r
+  }\r
+\r
+  killuser(NULL, target, (cargc>1)?cargv[1]:"Killed");\r
+  controlreply(np,"KILL sent.");\r
+  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");\r
+\r
+  return CMD_OK;\r
+}\r
diff --git a/noperserv/noperserv_db.c b/noperserv/noperserv_db.c
new file mode 100644 (file)
index 0000000..c876c46
--- /dev/null
@@ -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 <libpq-fe.h>
+#include <stdlib.h>
+
+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;i<rows;i++) {
+    nu = noperserv_new_autheduser(PQgetvalue(pgres, i, 1));
+    if(!nu)
+      continue;
+
+    nu->id = 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;i<NICKHASHSIZE;i++)
+    for(np=nicktable[i];np;np=np->next)
+      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 (file)
index 0000000..16814b0
--- /dev/null
@@ -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 (file)
index 0000000..c0d0238
--- /dev/null
@@ -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 <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+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 <nickname|#authname|*numeric>\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;i<n;i++)
+    controlreply(np, "%s (%s)", cmdlist[i]->command->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(;i<NICKHASHSIZE;i++)
+      for(sp=nicktable[i];sp;sp=sp->next)
+        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 (file)
index 0000000..9cc39dd
--- /dev/null
@@ -0,0 +1,2 @@
+void noperserv_setup_hooks(void);\r
+void noperserv_cleanup_hooks(void);\r
diff --git a/noperserv/noperserv_policy.c b/noperserv/noperserv_policy.c
new file mode 100644 (file)
index 0000000..c7fdffe
--- /dev/null
@@ -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 (file)
index 0000000..169d730
--- /dev/null
@@ -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
index 125f9c0072b1e19a94b4091df112d2e7d9add843..90111f40e949db543f24846daa464ebb44ac988e 100644 (file)
@@ -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;i<nc->command->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;
       }
index bce50aa30cf01aa1045b8535c861169dc2e18fb9..1aa030548ea465acd18e788818e215f11a23af3d 100644 (file)
@@ -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 (file)
index 0000000..19e913d
--- /dev/null
@@ -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 (file)
index 0000000..7325b17
--- /dev/null
@@ -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 <stdlib.h>
+#include <sys/poll.h>
+#include <stdarg.h>
+
+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 (file)
index 0000000..3f92e59
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef __PQSQL_DB_H
+#define __PQSQL_DB_H
+
+#include <libpq-fe.h>
+
+#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
index 09c9501185d80f1545b9618ab6e839d0eca04437..add5e0540050939aca431d8edecdef1d7786bc39 100644 (file)
@@ -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]);
   }