]> jfr.im git - irc/quakenet/newserv.git/blame - control/control.c
Die command warning wasn't being sent to anyone.
[irc/quakenet/newserv.git] / control / control.c
CommitLineData
c86edd1d
Q
1/*
2 * This is the first client module for newserv :)
3 *
4 * A very simple bot which should give people some ideas for how to
5 * implement stuff on this thing
6 */
7
8#include "../irc/irc_config.h"
9#include "../parser/parser.h"
10#include "../localuser/localuser.h"
11#include "../localuser/localuserchannel.h"
12#include "../nick/nick.h"
13#include "../lib/sstring.h"
14#include "../core/config.h"
15#include "../irc/irc.h"
16#include "../lib/splitline.h"
17#include "../channel/channel.h"
18#include "../lib/flags.h"
19#include "../core/schedule.h"
20#include "../lib/base64.h"
21#include "../core/modules.h"
87698d77 22#include "../lib/version.h"
c56c22bc 23#include "../core/nsmalloc.h"
c86edd1d
Q
24#include "control.h"
25
26#include <stdio.h>
27#include <string.h>
28#include <stdarg.h>
29
70b0a4e5 30MODULE_VERSION("");
87698d77 31
38cee035 32nick *hooknick;
c86edd1d 33
38cee035 34nick *mynick;
c86edd1d 35
38cee035
CP
36CommandTree *controlcmds;
37ControlMsg controlreply;
38ControlWall controlwall;
c86edd1d
Q
39
40void handlemessages(nick *target, int messagetype, void **args);
41int controlstatus(void *sender, int cargc, char **cargv);
42void controlconnect(void *arg);
43int controlwhois(void *sender, int cargc, char **cargv);
44int controlchannel(void *sender, int cargc, char **cargv);
45int relink(void *sender, int cargc, char **cargv);
46int die(void *sender, int cargc, char **cargv);
47int controlinsmod(void *sender, int cargc, char **cargv);
1fab6211 48int controllsmod(void *sender, int cargc, char **cargv);
c86edd1d 49int controlrehash(void *sender, int cargc, char **cargv);
c86edd1d 50int controlreload(void *sender, int cargc, char **cargv);
38cee035
CP
51int controlhelpcmd(void *sender, int cargc, char **cargv);
52void controlnoticeopers(flag_t permissionlevel, flag_t noticelevel, char *format, ...);
c86edd1d
Q
53
54void _init() {
55 controlcmds=newcommandtree();
38cee035
CP
56 controlreply=&controlmessage;
57 controlwall=&controlnoticeopers;
58
59 registercontrolhelpcmd("status",NO_DEVELOPER,1,&controlstatus,"Usage: status ?level?\nDisplays status information, increasing level gives more verbose information.");
60 registercontrolhelpcmd("whois",NO_OPERED,1,&controlwhois,"Usage: whois <nickname|#numeric>\nDisplays lots of information about the specified nickname or numeric.");
61 registercontrolhelpcmd("channel",NO_OPER,1,&controlchannel,"Usage: channel <#channel>\nDisplays channel information.");
62 registercontrolhelpcmd("relink",NO_DEVELOPER,1,&relink,"Usage: relink\nRelinks service to the network.");
63 registercontrolhelpcmd("die",NO_DEVELOPER,1,&die,"Usage: die <reason>\nTerminates the service.");
64 registercontrolhelpcmd("insmod",NO_DEVELOPER,1,&controlinsmod,"Usage: insmod <module>\nAdds a module to the running instance.");
65 registercontrolhelpcmd("rmmod",NO_DEVELOPER,1,&controlrmmod,"Usage: rmmod <module>\nRemoves a module from the running instance.");
66 registercontrolhelpcmd("lsmod",NO_DEVELOPER,0,&controllsmod,"Usage: lsmod\nLists currently running modules.");
67 registercontrolhelpcmd("rehash",NO_DEVELOPER,1,&controlrehash,"Usage: rehash\nReloads configuration file.");
68 registercontrolhelpcmd("showcommands",NO_ACCOUNT,0,&controlshowcommands,"Usage: showcommands\nShows all registered commands.");
69 registercontrolhelpcmd("reload",NO_DEVELOPER,1,&controlreload,"Usage: reload <module>\nReloads specified module.");
70 registercontrolhelpcmd("help",NO_ANYONE,1,&controlhelpcmd,"Usage: help <command>\nShows help for specified command.");
c86edd1d
Q
71
72 scheduleoneshot(time(NULL)+1,&controlconnect,NULL);
73}
74
0f003446 75void _fini() {
a7f301e5 76 deleteallschedules(&controlconnect);
0f003446 77 if (mynick) {
78 deregisterlocaluser(mynick,"Leaving");
79 }
a7f301e5 80
81 deregistercontrolcmd("status",&controlstatus);
82 deregistercontrolcmd("whois",&controlwhois);
83 deregistercontrolcmd("channel",&controlchannel);
84 deregistercontrolcmd("relink",&relink);
85 deregistercontrolcmd("die",&die);
86 deregistercontrolcmd("insmod",&controlinsmod);
87 deregistercontrolcmd("rmmod",&controlrmmod);
88 deregistercontrolcmd("lsmod",&controllsmod);
89 deregistercontrolcmd("rehash",&controlrehash);
90 deregistercontrolcmd("showcommands",&controlshowcommands);
91 deregistercontrolcmd("reload",&controlreload);
92 deregistercontrolcmd("help",&controlhelpcmd);
93
94 destroycommandtree(controlcmds);
0f003446 95}
96
38cee035
CP
97void registercontrolhelpcmd(const char *name, int level, int maxparams, CommandHandler handler, char *help) {
98 addcommandhelptotree(controlcmds,name,level,maxparams,handler,help);
c86edd1d
Q
99}
100
101int deregistercontrolcmd(const char *name, CommandHandler handler) {
102 return deletecommandfromtree(controlcmds, name, handler);
103}
104
105void controlconnect(void *arg) {
106 sstring *cnick, *myident, *myhost, *myrealname, *myauthname;
107 channel *cp;
108
109 cnick=getcopyconfigitem("control","nick","C",NICKLEN);
110 myident=getcopyconfigitem("control","ident","control",NICKLEN);
111 myhost=getcopyconfigitem("control","hostname",myserver->content,HOSTLEN);
112 myrealname=getcopyconfigitem("control","realname","NewServ Control Service",REALLEN);
113 myauthname=getcopyconfigitem("control","authname","C",ACCOUNTLEN);
114
84e923c7 115 mynick=registerlocaluser(cnick->content,myident->content,myhost->content,myrealname->content,myauthname->content,UMODE_SERVICE|UMODE_DEAF|UMODE_OPER|UMODE_ACCOUNT|UMODE_INV,&handlemessages);
38cee035 116 triggerhook(HOOK_CONTROL_REGISTERED, mynick);
c86edd1d
Q
117 cp=findchannel("#twilightzone");
118 if (!cp) {
119 localcreatechannel(mynick,"#twilightzone");
120 } else {
121 localjoinchannel(mynick,cp);
122 localgetops(mynick,cp);
123 }
124
125 freesstring(cnick);
126 freesstring(myident);
127 freesstring(myhost);
128 freesstring(myrealname);
129 freesstring(myauthname);
130}
131
132void handlestats(int hooknum, void *arg) {
38cee035 133 controlreply(hooknick,"%s",(char *)arg);
c86edd1d
Q
134}
135
136int controlstatus(void *sender, int cargc, char **cargv) {
c3db6f7e 137 unsigned long level=5;
c56c22bc 138 char buf[1024];
38cee035 139 hooknick=(nick *)sender;
c86edd1d
Q
140
141 if (cargc>0) {
142 level=strtoul(cargv[0],NULL,10);
143 }
c56c22bc 144
c86edd1d 145 registerhook(HOOK_CORE_STATSREPLY,&handlestats);
c56c22bc
CP
146
147 if (level >= 50) {
148 int i;
149 unsigned long count;
150 size_t size;
151
152 for(i=0;;i++) {
153 if(!nspoolstats(i, &size, &count))
154 break;
155
156 if (count == 0)
157 continue;
158
e892c093 159 snprintf(buf, sizeof(buf), "NSMalloc: pool %2d: %luKb, %lu items", i, (unsigned long)size / 1024, count);
c56c22bc
CP
160 triggerhook(HOOK_CORE_STATSREPLY, buf);
161 }
162 }
163
c86edd1d
Q
164 triggerhook(HOOK_CORE_STATSREQUEST,(void *)level);
165 deregisterhook(HOOK_CORE_STATSREPLY,&handlestats);
166 return CMD_OK;
167}
168
169int controlrehash(void *sender, int cargc, char **cargv) {
170 nick *np=(nick *)sender;
171
172 controlreply(np,"Rehashing the config file.");
173
174 rehashconfig();
175 triggerhook(HOOK_CORE_REHASH,(void *)0);
176 return CMD_OK;
177}
38cee035
CP
178
179void handlewhois(int hooknum, void *arg) {
180 controlreply(hooknick,"%s",(char *)arg);
181}
182
c86edd1d
Q
183int controlwhois(void *sender, int cargc, char **cargv) {
184 nick *target;
185 channel **channels;
186 char buf[BUFSIZE];
187 int i;
188
38cee035
CP
189 if (cargc<1)
190 return CMD_USAGE;
c86edd1d
Q
191
192 if (cargv[0][0]=='#') {
193 if (!(target=getnickbynumericstr(cargv[0]+1))) {
38cee035 194 controlreply(sender,"Sorry, couldn't find numeric %s",cargv[0]+1);
c86edd1d
Q
195 return CMD_ERROR;
196 }
197 } else {
198 if ((target=getnickbynick(cargv[0]))==NULL) {
38cee035 199 controlreply((nick *)sender,"Sorry, couldn't find that user");
c86edd1d
Q
200 return CMD_ERROR;
201 }
202 }
203
38cee035
CP
204 controlreply((nick *)sender,"Nick : %s",target->nick);
205 controlreply((nick *)sender,"Numeric : %s",longtonumeric(target->numeric,5));
206 controlreply((nick *)sender,"User@Host : %s@%s (%d user(s) on this host)",target->ident,target->host->name->content,target->host->clonecount);
c86edd1d
Q
207 if (IsSetHost(target)) {
208 if (target->shident) {
38cee035 209 controlreply((nick *)sender,"Fakehost : %s@%s",target->shident->content, target->sethost->content);
c86edd1d 210 } else {
38cee035 211 controlreply((nick *)sender,"Fakehost : %s",target->sethost->content);
c86edd1d
Q
212 }
213 }
38cee035 214 controlreply((nick *)sender,"Timestamp : %lu",target->timestamp);
526e7c1d 215 controlreply((nick *)sender,"IP address: %s",IPtostr(target->p_ipaddr));
38cee035 216 controlreply((nick *)sender,"Realname : %s (%d user(s) have this realname)",target->realname->name->content,target->realname->usercount);
c86edd1d 217 if (target->umodes) {
38cee035 218 controlreply((nick *)sender,"Umode(s) : %s",printflags(target->umodes,umodeflags));
c86edd1d
Q
219 }
220 if (IsAccount(target)) {
38cee035 221 controlreply((nick *)sender,"Account : %s",target->authname);
c86edd1d 222 if (target->accountts)
38cee035 223 controlreply((nick *)sender,"AccountTS : %ld",target->accountts);
c56c22bc
CP
224 if (target->auth)
225 controlreply((nick *)sender,"UserID : %ld",target->auth->userid);
c86edd1d 226 }
38cee035
CP
227
228 hooknick=(nick *)sender;
229 registerhook(HOOK_CONTROL_WHOISREPLY,&handlewhois);
230 triggerhook(HOOK_CONTROL_WHOISREQUEST,target);
231 deregisterhook(HOOK_CONTROL_WHOISREPLY,&handlewhois);
232
c86edd1d 233 if (target->channels->cursi==0) {
38cee035 234 controlreply((nick *)sender,"Channels : none");
c86edd1d 235 } else if (target->channels->cursi>50) {
38cee035 236 controlreply((nick *)sender,"Channels : - (total: %d)",target->channels->cursi);
c86edd1d
Q
237 } else {
238 buf[0]='\0';
239 channels=(channel **)target->channels->content;
240 for (i=0;i<=target->channels->cursi;i++) {
241 if (!((i==target->channels->cursi) || ((70-strlen(buf))<channels[i]->index->name->length && strlen(buf)>0))) {
242 strcat(buf,channels[i]->index->name->content);
243 strcat(buf," ");
244 } else {
245 if (strlen(buf)==0) {
246 break;
247 } else {
38cee035 248 controlreply((nick *)sender,"Channels : %s",buf);
c86edd1d
Q
249 buf[0]='\0';
250 i--;
251 }
252 }
253 }
254 }
255
256 return CMD_OK;
257}
258
259int controlinsmod(void *sender, int cargc, char **cargv) {
38cee035
CP
260 if (cargc<1)
261 return CMD_USAGE;
c86edd1d
Q
262
263 switch(insmod(cargv[0])) {
264 case -1:
38cee035 265 controlreply((nick *)sender,"Unable to load module %s",cargv[0]);
c86edd1d
Q
266 return CMD_ERROR;
267
268 case 1:
38cee035 269 controlreply((nick *)sender,"Module %s already loaded, or name not valid",cargv[0]);
c86edd1d
Q
270 return CMD_ERROR;
271
272 case 0:
38cee035 273 controlreply((nick *)sender,"Module %s loaded.",cargv[0]);
c86edd1d
Q
274 return CMD_OK;
275
276 default:
38cee035 277 controlreply((nick *)sender,"An unknown error occured.");
c86edd1d
Q
278 return CMD_ERROR;
279 }
280}
281
282int controlrmmod(void *sender, int cargc, char **cargv) {
38cee035
CP
283 if (cargc<1)
284 return CMD_USAGE;
c86edd1d
Q
285
286 switch(rmmod(cargv[0])) {
287 case 1:
38cee035 288 controlreply((nick *)sender,"Module %s is not loaded.",cargv[0]);
c86edd1d
Q
289 return CMD_ERROR;
290
291 case 0:
38cee035 292 controlreply((nick *)sender,"Module %s unloaded.",cargv[0]);
c86edd1d
Q
293 return CMD_OK;
294
295 default:
38cee035 296 controlreply((nick *)sender,"An unknown error occured.");
c86edd1d
Q
297 return CMD_ERROR;
298 }
299}
300
1fab6211 301int controllsmod(void *sender, int cargc, char **cargv) {
302 int i=0;
303 char *ptr;
304
305 if (cargc < 1) { /* list all loaded modules */
306 ptr = lsmod(i);
38cee035 307 controlreply((nick *)sender,"Module");
1fab6211 308 while (ptr != NULL) {
87698d77
CP
309 const char *ver = lsmodver(i);
310 controlreply((nick *)sender,"%s%s%s%s", ptr, ver?" (":"", ver?ver:"", ver?")":"");
1fab6211 311 ptr = lsmod(++i);
312 }
313 } else {
314 ptr = lsmod(getindex(cargv[0]));
38cee035 315 controlreply((nick *)sender,"Module \"%s\" %s", cargv[0], (ptr ? "is loaded." : "is NOT loaded."));
1fab6211 316 }
317 return CMD_OK;
318}
319
c86edd1d 320int controlreload(void *sender, int cargc, char **cargv) {
38cee035
CP
321 if (cargc<1)
322 return CMD_USAGE;
65f2c6a3 323
324 controlreply((nick *)sender,"Imma gonna try and reload %s",cargv[0]);
3fa581ac 325
65f2c6a3 326 safereload(cargv[0]);
3fa581ac 327
328 return CMD_OK;
65f2c6a3 329}
c86edd1d
Q
330
331int relink(void *sender, int cargc, char **cargv) {
332 if (cargc<1) {
38cee035
CP
333 controlreply((nick *)sender,"You must give a reason.");
334 return CMD_USAGE;
c86edd1d
Q
335 }
336
337 irc_send("%s SQ %s 0 :%s",mynumeric->content,myserver->content,cargv[0]);
338 irc_disconnected();
339
340 return CMD_OK;
341}
342
343int die(void *sender, int cargc, char **cargv) {
344 if (cargc<1) {
38cee035
CP
345 controlreply((nick *)sender,"You must give a reason.");
346 return CMD_USAGE;
c86edd1d 347 }
83951d54 348
bec7f5c6 349 controlwall(NO_OPER,NL_OPERATIONS,"DIE from %s: %s",((nick *)sender)->nick, cargv[0]);
c86edd1d 350
83951d54 351 newserv_shutdown_pending=1;
c86edd1d 352
83951d54 353 return CMD_OK;
c86edd1d
Q
354}
355
356int controlchannel(void *sender, int cargc, char **cargv) {
357 channel *cp;
358 nick *np;
359 chanban *cbp;
360 char buf[BUFSIZE];
361 char buf2[12];
362 int i,j;
08454d38
P
363 char timebuf[30];
364
38cee035
CP
365 if (cargc<1)
366 return CMD_USAGE;
c86edd1d
Q
367
368 if ((cp=findchannel(cargv[0]))==NULL) {
38cee035 369 controlreply((nick *)sender,"Couldn't find channel: %s",cargv[0]);
c86edd1d
Q
370 return CMD_ERROR;
371 }
372
373 if (IsLimit(cp)) {
374 sprintf(buf2,"%d",cp->limit);
375 }
376
38cee035 377 controlreply((nick *)sender,"Channel : %s",cp->index->name->content);
08454d38
P
378 strftime(timebuf, 30, "%d/%m/%y %H:%M", localtime(&(cp->timestamp)));
379 controlreply((nick *)sender,"C-time : %ld [%s]",cp->timestamp,timebuf);
c86edd1d 380 if (cp->topic) {
38cee035 381 controlreply((nick *)sender,"Topic : %s",cp->topic->content);
08454d38
P
382 strftime(timebuf, 30, "%d/%m/%y %H:%M", localtime(&(cp->topictime)));
383 controlreply((nick *)sender,"T-time : %ld [%s]",cp->topictime,timebuf);
c86edd1d 384 }
38cee035 385 controlreply((nick *)sender,"Mode(s) : %s %s%s%s",printflags(cp->flags,cmodeflags),IsLimit(cp)?buf2:"",
c86edd1d 386 IsLimit(cp)?" ":"",IsKey(cp)?cp->key->content:"");
38cee035 387 controlreply((nick *)sender,"Users : %d (hash size %d, utilisation %.1f%%); %d unique hosts",
c86edd1d
Q
388 cp->users->totalusers,cp->users->hashsize,((float)(100*cp->users->totalusers)/cp->users->hashsize),
389 countuniquehosts(cp));
390 i=0;
391 memset(buf,' ',90);
392 buf[72]='\0';
393 for (j=0;j<=cp->users->hashsize;j++) {
394 if (i==4 || j==cp->users->hashsize) {
395 if(i>0) {
38cee035 396 controlreply((nick *)sender,"Users : %s",buf);
c86edd1d
Q
397 }
398 i=0;
399 memset(buf,' ',72);
400 if (j==cp->users->hashsize)
401 break;
402 }
403 if (cp->users->content[j]!=nouser) {
404 np=getnickbynumeric(cp->users->content[j]);
405 sprintf(&buf[i*18],"%c%c%-15s ",cp->users->content[j]&CUMODE_VOICE?'+':' ',
406 cp->users->content[j]&CUMODE_OP?'@':' ', np?np->nick:"!BUG-NONICK!");
407 i++;
408 if (i<4)
409 buf[i*18]=' ';
410 }
411 }
08454d38 412
c86edd1d 413 for (cbp=cp->bans;cbp;cbp=cbp->next) {
38cee035 414 controlreply((nick *)sender,"Ban : %s",bantostringdebug(cbp));
c86edd1d
Q
415 }
416 return CMD_OK;
417}
418
419int controlshowcommands(void *sender, int cargc, char **cargv) {
420 nick *np=(nick *)sender;
421 Command *cmdlist[100];
422 int i,n;
423
424 n=getcommandlist(controlcmds,cmdlist,100);
425
426 controlreply(np,"The following commands are registered at present:");
427
428 for(i=0;i<n;i++) {
38cee035 429 controlreply(np,"%s",cmdlist[i]->command->content);
c86edd1d
Q
430 }
431
432 controlreply(np,"End of list.");
433 return CMD_OK;
434}
435
436void handlemessages(nick *target, int messagetype, void **args) {
437 Command *cmd;
438 char *cargv[50];
439 int cargc;
440 nick *sender;
441
442 switch(messagetype) {
443 case LU_PRIVMSG:
444 case LU_SECUREMSG:
445 /* If it's a message, first arg is nick and second is message */
446 sender=(nick *)args[0];
447
448 Error("control",ERR_INFO,"From: %s!%s@%s: %s",sender->nick,sender->ident,sender->host->name->content, (char *)args[1]);
449
450 /* Split the line into params */
451 cargc=splitline((char *)args[1],cargv,50,0);
452
453 if (!cargc) {
454 /* Blank line */
455 return;
456 }
457
458 cmd=findcommandintree(controlcmds,cargv[0],1);
459 if (cmd==NULL) {
38cee035 460 controlreply(sender,"Unknown command.");
c86edd1d
Q
461 return;
462 }
463
38cee035
CP
464 if (cmd->level>0 && !IsOper(sender)) {
465 controlreply(sender,"You need to be opered to use this command.");
c86edd1d
Q
466 return;
467 }
468
469 /* If we were doing "authed user tracking" here we'd put a check in for authlevel */
470
471 /* Check the maxargs */
472 if (cmd->maxparams<(cargc-1)) {
473 /* We need to do some rejoining */
474 rejoinline(cargv[cmd->maxparams],cargc-(cmd->maxparams));
475 cargc=(cmd->maxparams)+1;
476 }
477
38cee035
CP
478 if((cmd->handler)((void *)sender,cargc-1,&(cargv[1])) == CMD_USAGE)
479 controlhelp(sender, cmd);
c86edd1d
Q
480 break;
481
482 case LU_KILLED:
483 /* someone killed me? Bastards */
484 scheduleoneshot(time(NULL)+1,&controlconnect,NULL);
485 mynick=NULL;
486 break;
487
488 default:
489 break;
490 }
491}
492
38cee035 493void controlmessage(nick *target, char *message, ... ) {
c86edd1d
Q
494 char buf[512];
495 va_list va;
496
497 if (mynick==NULL) {
498 return;
499 }
500
501 va_start(va,message);
502 vsnprintf(buf,512,message,va);
503 va_end(va);
504
505 sendmessagetouser(mynick,target,"%s",buf);
506}
507
508void controlchanmsg(channel *cp, char *message, ...) {
509 char buf[512];
510 va_list va;
511
512 if (mynick==NULL) {
513 return;
514 }
515
516 va_start(va,message);
517 vsnprintf(buf,512,message,va);
518 va_end(va);
519
520 sendmessagetochannel(mynick,cp,"%s",buf);
521}
522
523void controlnotice(nick *target, char *message, ... ) {
524 char buf[512];
525 va_list va;
526
527 if (mynick==NULL) {
528 return;
529 }
530
531 va_start(va,message);
532 vsnprintf(buf,512,message,va);
533 va_end(va);
534
535 sendnoticetouser(mynick,target,"%s",buf);
536}
537
38cee035
CP
538void controlspecialrmmod(void *arg) {
539 struct specialsched *a = (struct specialsched *)arg;
b015c3e9 540
38cee035
CP
541 a->schedule = NULL;
542 sstring *froo = a->modulename;
543
544 rmmod(froo->content);
545 freesstring(froo);
546}
547
548void controlspecialreloadmod(void *arg) {
549 struct specialsched *a = (struct specialsched *)arg;
550
551 a->schedule = NULL;
552 sstring *froo = a->modulename;
553
65f2c6a3 554 safereload(froo->content);
38cee035
CP
555 freesstring(froo);
556}
557
558void controlhelp(nick *np, Command *cmd) {
559 char *cp = cmd->help, *sp = cp;
560 if(!cp || !*cp) {
561 controlreply(np, "Sorry, no help for this command.");
562 } else {
563 int finished = 0;
564 for(;;cp++) {
565 if(*cp == '\0' || *cp == '\n') {
566 if(*cp == '\0') {
567 finished = 1;
568 } else {
569 *cp = '\0';
570 }
571
572 if(sp != cp)
573 controlreply(np, "%s", sp);
574
575 if(finished)
576 break;
577
578 *cp = '\n';
579
580 sp = cp + 1;
581 }
582 }
583 }
584}
b015c3e9 585
38cee035
CP
586int controlhelpcmd(void *sender, int cargc, char **cargv) {
587 Command *cmd;
588 nick *np = (nick *)sender;
589
590 if (cargc<1)
591 return CMD_USAGE;
592
593 cmd=findcommandintree(controlcmds,cargv[0],1);
594 if (cmd==NULL) {
595 controlreply(np,"Unknown command.");
596 return CMD_ERROR;
597 }
598
599 controlhelp(np, cmd);
600 return CMD_OK;
b015c3e9 601}
a80cbef6
CP
602
603void controlnoticeopers(flag_t permissionlevel, flag_t noticelevel, char *format, ...) {
604 int i;
605 nick *np;
606 char broadcast[512];
607 va_list va;
608
609 va_start(va, format);
610 vsnprintf(broadcast, sizeof(broadcast), format, va);
611 va_end(va);
612
613 for(i=0;i<NICKHASHSIZE;i++)
614 for(np=nicktable[i];np;np=np->next)
615 if (IsOper(np))
616 controlnotice(np, "%s", broadcast);
617}