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