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