]> jfr.im git - irc/quakenet/newserv.git/blobdiff - control/control.c
Rename trusts_cidr2str to CIDRtostr() and move it to lib/irc_ipv6.c.
[irc/quakenet/newserv.git] / control / control.c
index b00c7db725d6d77ab09767c14a51607dc94bd1fd..8c2676451d1b29005280db723371a1911ce80207 100644 (file)
 #include "../core/schedule.h"
 #include "../lib/base64.h"
 #include "../core/modules.h"
 #include "../core/schedule.h"
 #include "../lib/base64.h"
 #include "../core/modules.h"
+#include "../lib/version.h"
+#include "../lib/irc_string.h"
 #include "control.h"
 
 #include <stdio.h>
 #include <string.h>
 #include <stdarg.h>
 
 #include "control.h"
 
 #include <stdio.h>
 #include <string.h>
 #include <stdarg.h>
 
-nick *mynick;
+MODULE_VERSION("");
+
+nick *hooknick;
 
 
-nick *statsnick;
+nick *mynick;
 
 
-CommandTree *controlcmds; 
+CommandTree *controlcmds;
+ControlMsg controlreply;
+ControlWall controlwall;
+ControlPermitted controlpermitted;
+DestroyExt controldestroyext;
 
 
+void controldestroycmdext(void *ext); 
 void handlemessages(nick *target, int messagetype, void **args);
 int controlstatus(void *sender, int cargc, char **cargv);
 void controlconnect(void *arg);
 void handlemessages(nick *target, int messagetype, void **args);
 int controlstatus(void *sender, int cargc, char **cargv);
 void controlconnect(void *arg);
@@ -39,32 +48,111 @@ 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 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 controlrehash(void *sender, int cargc, char **cargv);
-int controlshowcommands(void *sender, int cargc, char **cargv);
 int controlreload(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, ...) __attribute__ ((format (printf, 3, 4)));
+void controlnoticeopers(flag_t permissionlevel, flag_t noticelevel, char *format, ...);
+int controlcheckpermitted(flag_t level, nick *user);
+void handlesignal(int hooknum, void *arg);
 
 void _init() {
   controlcmds=newcommandtree();
 
 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,"rehash",10,1,&controlrehash);
-  addcommandtotree(controlcmds,"showcommands",10,0,&controlshowcommands);
-  addcommandtotree(controlcmds,"reload",10,1,&controlreload);
-  
+  controlreply=&controlmessage;
+  controlwall=&controlnoticeopers;
+  controlpermitted=&controlcheckpermitted;
+  controldestroyext=&controldestroycmdext;
+
+  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_OPER,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.");
+  registerhook(HOOK_CORE_REHASH, &handlesignal);
+  registerhook(HOOK_CORE_SIGINT, &handlesignal);
   scheduleoneshot(time(NULL)+1,&controlconnect,NULL);
 }
 
   scheduleoneshot(time(NULL)+1,&controlconnect,NULL);
 }
 
-void registercontrolcmd(const char *name, int level, int maxparams, CommandHandler handler) {
-  addcommandtotree(controlcmds,name,level,maxparams,handler);
+void _fini() {
+  deleteallschedules(&controlconnect);
+  if (mynick) {
+    deregisterlocaluser(mynick,"Leaving");
+  }
+  
+  deregistercontrolcmd("status",&controlstatus);
+  deregistercontrolcmd("whois",&controlwhois);
+  deregistercontrolcmd("channel",&controlchannel);
+  deregistercontrolcmd("relink",&relink);
+  deregistercontrolcmd("die",&die);
+  deregistercontrolcmd("insmod",&controlinsmod);
+  deregistercontrolcmd("rmmod",&controlrmmod);
+  deregistercontrolcmd("lsmod",&controllsmod);
+  deregistercontrolcmd("rehash",&controlrehash);
+  deregistercontrolcmd("showcommands",&controlshowcommands);
+  deregistercontrolcmd("reload",&controlreload);
+  deregistercontrolcmd("help",&controlhelpcmd);
+  
+  destroycommandtree(controlcmds);
+
+  deregisterhook(HOOK_CORE_REHASH, &handlesignal);
+  deregisterhook(HOOK_CORE_SIGINT, &handlesignal);
+}
+
+void registercontrolhelpcmd(const char *name, int level, int maxparams, CommandHandler handler, char *helpstr) {
+  cmdhelp *help;
+  Command *newcmd;
+
+  newcmd = addcommandtotree(controlcmds,name,level,maxparams,handler);
+  if (helpstr) {
+    /* Allocate a help record */
+    help=(cmdhelp *)malloc(sizeof(cmdhelp));
+    if(!help) {
+      Error("control",ERR_ERROR,"Malloc failed: registercontrolhelpcmd");
+      return; 
+    }
+    memset((void *)help,0,sizeof(cmdhelp));
+
+    /* this isn't an sstring atm, as sstring has a max length (512) */ 
+    int len=strlen(helpstr);
+    help->helpstr=(char *)malloc(len+1);
+    if(help->helpstr) {
+      strncpy(help->helpstr, helpstr, len);
+      help->helpstr[len] = '\0';
+    }
+    newcmd->ext=(void *)help;
+    newcmd->destroyext=controldestroyext;
+  }
 }
 
 }
 
+void registercontrolhelpfunccmd(const char *name, int level, int maxparams, CommandHandler handler, CommandHelp helpcmd) {
+  cmdhelp *help;
+  Command *newcmd;
+
+  /* Allocate a help record */
+  help=(cmdhelp *)malloc(sizeof(cmdhelp));
+  if (!help) {
+    Error("control",ERR_ERROR,"Malloc failed: registercontrolhelpfunccmd");
+    return;
+  }
+  memset((void *)help,0,sizeof(cmdhelp));
+
+  help->helpcmd=helpcmd;
+
+  newcmd = addcommandexttotree(controlcmds,name,level,maxparams,handler, (void *)help);
+  newcmd->destroyext=controldestroyext;
+}
+
+
 int deregistercontrolcmd(const char *name, CommandHandler handler) {
   return deletecommandfromtree(controlcmds, name, handler);
 }
 int deregistercontrolcmd(const char *name, CommandHandler handler) {
   return deletecommandfromtree(controlcmds, name, handler);
 }
@@ -79,7 +167,8 @@ void controlconnect(void *arg) {
   myrealname=getcopyconfigitem("control","realname","NewServ Control Service",REALLEN);
   myauthname=getcopyconfigitem("control","authname","C",ACCOUNTLEN);
 
   myrealname=getcopyconfigitem("control","realname","NewServ Control Service",REALLEN);
   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);
+  mynick=registerlocaluser(cnick->content,myident->content,myhost->content,myrealname->content,myauthname->content,UMODE_SERVICE|UMODE_DEAF|UMODE_OPER|UMODE_ACCOUNT|UMODE_INV,&handlemessages);
+  triggerhook(HOOK_CONTROL_REGISTERED, mynick);
   cp=findchannel("#twilightzone");
   if (!cp) {
     localcreatechannel(mynick,"#twilightzone");
   cp=findchannel("#twilightzone");
   if (!cp) {
     localcreatechannel(mynick,"#twilightzone");
@@ -96,19 +185,19 @@ void controlconnect(void *arg) {
 }
 
 void handlestats(int hooknum, 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) {
 }
 
 int controlstatus(void *sender, int cargc, char **cargv) {
-  unsigned int level=5;
-
-  statsnick=(nick *)sender;
+  unsigned long level=999;
+  hooknick=(nick *)sender;
   
   if (cargc>0) {
     level=strtoul(cargv[0],NULL,10);
   }
   
   if (cargc>0) {
     level=strtoul(cargv[0],NULL,10);
   }
-  
+
   registerhook(HOOK_CORE_STATSREPLY,&handlestats);
   registerhook(HOOK_CORE_STATSREPLY,&handlestats);
+
   triggerhook(HOOK_CORE_STATSREQUEST,(void *)level);
   deregisterhook(HOOK_CORE_STATSREPLY,&handlestats);
   return CMD_OK;
   triggerhook(HOOK_CORE_STATSREQUEST,(void *)level);
   deregisterhook(HOOK_CORE_STATSREPLY,&handlestats);
   return CMD_OK;
@@ -123,55 +212,88 @@ int controlrehash(void *sender, int cargc, char **cargv) {
   triggerhook(HOOK_CORE_REHASH,(void *)0);
   return CMD_OK;
 }
   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;
   
 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))) {
   
   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) {
       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;
     }
   }
   
       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) {
   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 {
     } 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);
+
+  /* HACK */
+  {
+    int ext = findnickext("signontracker");
+
+    if(ext >= 0) {
+      time_t signedon = (time_t)(target->exts[ext]);
+      if(signedon) {
+        controlreply((nick *)sender,"Signed on : %lu",signedon);
+      } else {
+        controlreply((nick *)sender,"Signed on : ???");
+      }
+    }
+  }
+  controlreply((nick *)sender,"IP address: %s",IPtostr(target->p_ipaddr));
+  controlreply((nick *)sender,"Realname  : %s (%d user(s) have this realname)",target->realname->name->content,target->realname->usercount);
   if (target->umodes) {
   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 (IsOper(target) && target->opername)
+    controlreply((nick *)sender,"Opered as : %s",target->opername->content);
   if (IsAccount(target)) {
   if (IsAccount(target)) {
-    sendmessagetouser(mynick,(nick *)sender,"Account   : %s",target->authname);
+    controlreply((nick *)sender,"Account   : %s",target->authname);
     if (target->accountts) 
     if (target->accountts) 
-      sendmessagetouser(mynick,(nick *)sender,"AccountTS : %ld",target->accountts);
+      controlreply((nick *)sender,"AccountTS : %ld",target->accountts);
+    if (target->auth)  {
+      controlreply((nick *)sender,"UserID    : %ld",target->auth->userid);
+      if (target->auth->flags) 
+        controlreply((nick *)sender,"AccFlags  : %s",printflags(target->auth->flags,accountflags));
+    }
+  }
+
+  if (target->away) {
+    controlreply((nick *)sender, "Away      : %s",target->away->content);
   }
   }
+
+  hooknick=(nick *)sender;
+  registerhook(HOOK_CONTROL_WHOISREPLY,&handlewhois);
+  triggerhook(HOOK_CONTROL_WHOISREQUEST,target);
+  deregisterhook(HOOK_CONTROL_WHOISREPLY,&handlewhois);
+
   if (target->channels->cursi==0) {
   if (target->channels->cursi==0) {
-    sendmessagetouser(mynick,(nick *)sender,"Channels  : none");
+    controlreply((nick *)sender,"Channels  : none");
   } else if (target->channels->cursi>50) {
   } 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;
   } else {
     buf[0]='\0';
     channels=(channel **)target->channels->content;
@@ -183,7 +305,7 @@ int controlwhois(void *sender, int cargc, char **cargv) {
         if (strlen(buf)==0) {
           break;
         } else {
         if (strlen(buf)==0) {
           break;
         } else {
-          sendmessagetouser(mynick,(nick *)sender,"Channels  : %s",buf);
+          controlreply((nick *)sender,"Channels  : %s",buf);
           buf[0]='\0';
           i--;
         }
           buf[0]='\0';
           i--;
         }
@@ -195,65 +317,84 @@ int controlwhois(void *sender, int cargc, char **cargv) {
 }
 
 int controlinsmod(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:
   
   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:
       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:
       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:
       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) {
       return CMD_ERROR;
   }
 }
 
 int controlrmmod(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(rmmod(cargv[0])) {
+  switch(rmmod(cargv[0], 1)) {
     case 1:
     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:
       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:
       return CMD_OK;
     
     default:
-      sendmessagetouser(mynick,(nick *)sender,"An unknown error occured.");
+      controlreply((nick *)sender,"An unknown error occured.");
       return CMD_ERROR;
   }
 }
 
       return CMD_ERROR;
   }
 }
 
-int controlreload(void *sender, int cargc, char **cargv) {
-  if (cargc<1) {
-    sendmessagetouser(mynick,(nick *)sender,"Usage: reload <modulename>");
-    return CMD_ERROR;
+int controllsmod(void *sender, int cargc, char **cargv) {
+  int i=0;
+  char *ptr;
+
+  if (cargc < 1) { /* list all loaded modules */
+    const char *ver, *buildid;
+    time_t t, t2 = time(NULL);
+    ptr = lsmod(i, &ver, &buildid, &t);
+
+    /*                                                                    9999d 24h 59m 59s fbf2a4a69ee1-tip */
+    controlreply((nick *)sender,"Module                                   Loaded for        Version                        Build id");
+    while (ptr != NULL) {
+      controlreply((nick *)sender," %-40s %-17s %-30s %s", ptr, longtoduration(t2-t, 2), ver?ver:"", buildid?buildid:"");
+      ptr = lsmod(++i, &ver, &buildid, &t);
+    }
+  } else {
+    ptr = lsmod(getindex(cargv[0]), NULL, NULL, NULL);
+    controlreply((nick *)sender,"Module \"%s\" %s", cargv[0], (ptr ? "is loaded." : "is NOT loaded."));
   }
   }
-    
-  controlrmmod(sender, cargc, cargv);
-  return controlinsmod(sender, cargc, cargv);
+  return CMD_OK;
 }
 
 }
 
+int controlreload(void *sender, int cargc, char **cargv) {
+  if (cargc<1)
+    return CMD_USAGE;
+
+  controlreply((nick *)sender,"Imma gonna try and reload %s",cargv[0]);
+  
+  safereload(cargv[0]);
+  
+  return CMD_OK;
+}  
+
 int relink(void *sender, int cargc, char **cargv) {
   if (cargc<1) {
 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]);
   }
   
   irc_send("%s SQ %s 0 :%s",mynumeric->content,myserver->content,cargv[0]);
@@ -263,15 +404,16 @@ int relink(void *sender, int cargc, char **cargv) {
 }
 
 int die(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;
+  if (cargc<1 || (strlen(cargv[0]) < 10)) {
+    controlreply((nick *)sender,"You must give a reason.");
+    return CMD_USAGE;
   }
   }
+
+  controlwall(NO_OPER,NL_OPERATIONS,"DIE from %s: %s",((nick *)sender)->nick, cargv[0]);
   
   
-  irc_send("%s SQ %s 0 :%s",mynumeric->content,myserver->content,cargv[0]);
-  irc_disconnected();
+  newserv_shutdown_pending=1;
   
   
-  exit(0);
+  return CMD_OK;
 }
 
 int controlchannel(void *sender, int cargc, char **cargv) {
 }
 
 int controlchannel(void *sender, int cargc, char **cargv) {
@@ -280,15 +422,14 @@ int controlchannel(void *sender, int cargc, char **cargv) {
   chanban *cbp;
   char buf[BUFSIZE];
   char buf2[12];
   chanban *cbp;
   char buf[BUFSIZE];
   char buf2[12];
-  int i,j;
-  
-  if (cargc<1) {
-    sendmessagetouser(mynick,(nick *)sender,"Usage: channel #chan");
-    return CMD_ERROR;
-  }
+  int i,j, ops=0, voice=0;
+  char timebuf[30];
+
+  if (cargc<1)
+    return CMD_USAGE;
   
   if ((cp=findchannel(cargv[0]))==NULL) {
   
   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;
   }
   
     return CMD_ERROR;
   }
   
@@ -296,14 +437,19 @@ int controlchannel(void *sender, int cargc, char **cargv) {
     sprintf(buf2,"%d",cp->limit);
   }
   
     sprintf(buf2,"%d",cp->limit);
   }
   
-  sendmessagetouser(mynick,(nick *)sender,"Channel : %s",cp->index->name->content);
+  controlreply((nick *)sender,"Channel : %s",cp->index->name->content);
+  strftime(timebuf, 30, "%d/%m/%y %H:%M", localtime(&(cp->timestamp)));
+  controlreply((nick *)sender,"C-time  : %ld [%s]",cp->timestamp,timebuf);
   if (cp->topic) {
   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);
+    strftime(timebuf, 30, "%d/%m/%y %H:%M", localtime(&(cp->topictime)));
+    controlreply((nick *)sender,"T-time  : %ld [%s]",cp->topictime,timebuf);
+  } else { 
+    controlreply((nick *)sender,"Topic   : (none)");
   }
   }
-  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:"");
     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;
     cp->users->totalusers,cp->users->hashsize,((float)(100*cp->users->totalusers)/cp->users->hashsize),
     countuniquehosts(cp));
   i=0;
@@ -312,7 +458,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) {
   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);
       }
       i=0;
       memset(buf,' ',72);
@@ -320,6 +466,10 @@ int controlchannel(void *sender, int cargc, char **cargv) {
         break;
     }
     if (cp->users->content[j]!=nouser) {      
         break;
     }
     if (cp->users->content[j]!=nouser) {      
+      if (cp->users->content[j]&CUMODE_OP)
+        ops++;
+      else if (cp->users->content[j]&CUMODE_VOICE) 
+        voice++;
       np=getnickbynumeric(cp->users->content[j]);
       sprintf(&buf[i*18],"%c%c%-15s ",cp->users->content[j]&CUMODE_VOICE?'+':' ',
         cp->users->content[j]&CUMODE_OP?'@':' ', np?np->nick:"!BUG-NONICK!");
       np=getnickbynumeric(cp->users->content[j]);
       sprintf(&buf[i*18],"%c%c%-15s ",cp->users->content[j]&CUMODE_VOICE?'+':' ',
         cp->users->content[j]&CUMODE_OP?'@':' ', np?np->nick:"!BUG-NONICK!");
@@ -328,9 +478,9 @@ int controlchannel(void *sender, int cargc, char **cargv) {
         buf[i*18]=' ';
     }
   }
         buf[i*18]=' ';
     }
   }
-    
+  controlreply((nick *)sender, "Users   : Opped: %d, Voiced: %d", ops,voice);
   for (cbp=cp->bans;cbp;cbp=cbp->next) {
   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;
 }
   }
   return CMD_OK;
 }
@@ -345,7 +495,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,"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.");
   }
 
   controlreply(np,"End of list.");
@@ -376,12 +526,12 @@ void handlemessages(nick *target, int messagetype, void **args) {
                
       cmd=findcommandintree(controlcmds,cargv[0],1);
       if (cmd==NULL) {
                
       cmd=findcommandintree(controlcmds,cargv[0],1);
       if (cmd==NULL) {
-        sendmessagetouser(mynick,sender,"Unknown command.");
+        controlreply(sender,"Unknown command.");
         return;
       }
       
         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;
       }
       
         return;
       }
       
@@ -394,13 +544,15 @@ void handlemessages(nick *target, int messagetype, void **args) {
         cargc=(cmd->maxparams)+1;
       }
       
         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:
       /* someone killed me?  Bastards */
       scheduleoneshot(time(NULL)+1,&controlconnect,NULL);
       mynick=NULL;
       break;
       
     case LU_KILLED:
       /* someone killed me?  Bastards */
       scheduleoneshot(time(NULL)+1,&controlconnect,NULL);
       mynick=NULL;
+      triggerhook(HOOK_CONTROL_REGISTERED, NULL);
       break;
       
     default:
       break;
       
     default:
@@ -408,7 +560,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;
     
   char buf[512];
   va_list va;
     
@@ -453,13 +605,91 @@ void controlnotice(nick *target, char *message, ... ) {
   sendnoticetouser(mynick,target,"%s",buf);
 }
 
   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;
+  sstring *froo = a->modulename;
+  
+  a->schedule = NULL;
+
+  rmmod(froo->content, 1);
+  freesstring(froo);
+}
+
+void controlspecialreloadmod(void *arg) {
+  struct specialsched *a = (struct specialsched *)arg;
+  sstring *froo = a->modulename;
+
+  a->schedule = NULL;
+
+  safereload(froo->content);
+  freesstring(froo);
+}
+
+void controlhelp(nick *np, Command *cmd) {
+  char *cp, *sp;
+  char *scp;
+  cmdhelp *help=(cmdhelp *)cmd->ext;
+
+  if (!help) { 
+    controlreply(np, "Sorry, no help for this command.");
+    return;
+  }
+  if ( help->helpcmd ) {
+    (help->helpcmd)(np, cmd); 
+  } else {
+    scp = help->helpstr;
+    if (!scp) {
+      controlreply(np, "Sorry, no help for this command.");
+    } else {
+      cp = scp;
+      sp = cp;
+      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;
   int i;
   nick *np;
-  char broadcast[BUFSIZE];
+  char broadcast[512];
   va_list va;
   va_list va;
-  
+
   va_start(va, format);
   vsnprintf(broadcast, sizeof(broadcast), format, va);
   va_end(va);
   va_start(va, format);
   vsnprintf(broadcast, sizeof(broadcast), format, va);
   va_end(va);
@@ -469,3 +699,51 @@ void controlnoticeopers(char *format, ...) {
       if (IsOper(np))
         controlnotice(np, "%s", broadcast);
 }
       if (IsOper(np))
         controlnotice(np, "%s", broadcast);
 }
+
+void controlnswall(int noticelevel, char *format, ...) {
+  char broadcast[512];
+  va_list va;
+
+  va_start(va, format);
+  vsnprintf(broadcast, sizeof(broadcast), format, va);
+  va_end(va);
+
+  controlwall(NO_OPER, noticelevel, "%s", broadcast);
+}
+
+int controlcheckpermitted(flag_t level, nick *user) {
+  return 1;
+}
+
+void handlesignal(int hooknum, void *arg) {
+  char *signal, *action;
+
+  if(hooknum == HOOK_CORE_SIGINT) {
+    signal = "INT";
+    action = "terminating";
+  } else {
+    long hupped = (long)arg;
+    if(!hupped)
+      return;
+
+    signal = "HUP";
+    action = "rehashing";
+  }
+
+  controlwall(NO_OPER, NL_OPERATIONS, "SIG%s received, %s...", signal, action);
+}
+
+void controldestroycmdext(void *ext) {
+  if ( ((cmdhelp *)ext)->helpstr)
+    free( ((cmdhelp *)ext)->helpstr);
+  free(ext);
+}
+
+char *controlid(nick *np) {
+  static char buf[512];
+
+  snprintf(buf, sizeof(buf), "%s!%s@%s/%s", np->nick, np->ident, np->host->name->content, np->authname);
+
+  return buf;
+}
+