]> jfr.im git - irc/quakenet/newserv.git/blob - control/control.c
Lua version now excludes "Lua engine", at least for MODULEVERSION.
[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 handlerehash(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, &handlerehash);
74 scheduleoneshot(time(NULL)+1,&controlconnect,NULL);
75 }
76
77 void _fini() {
78 deleteallschedules(&controlconnect);
79 if (mynick) {
80 deregisterlocaluser(mynick,"Leaving");
81 }
82
83 deregistercontrolcmd("status",&controlstatus);
84 deregistercontrolcmd("whois",&controlwhois);
85 deregistercontrolcmd("channel",&controlchannel);
86 deregistercontrolcmd("relink",&relink);
87 deregistercontrolcmd("die",&die);
88 deregistercontrolcmd("insmod",&controlinsmod);
89 deregistercontrolcmd("rmmod",&controlrmmod);
90 deregistercontrolcmd("lsmod",&controllsmod);
91 deregistercontrolcmd("rehash",&controlrehash);
92 deregistercontrolcmd("showcommands",&controlshowcommands);
93 deregistercontrolcmd("reload",&controlreload);
94 deregistercontrolcmd("help",&controlhelpcmd);
95
96 destroycommandtree(controlcmds);
97
98 deregisterhook(HOOK_CORE_REHASH, &handlerehash);
99 }
100
101 void registercontrolhelpcmd(const char *name, int level, int maxparams, CommandHandler handler, char *help) {
102 addcommandhelptotree(controlcmds,name,level,maxparams,handler,help);
103 }
104
105 int deregistercontrolcmd(const char *name, CommandHandler handler) {
106 return deletecommandfromtree(controlcmds, name, handler);
107 }
108
109 void controlconnect(void *arg) {
110 sstring *cnick, *myident, *myhost, *myrealname, *myauthname;
111 channel *cp;
112
113 cnick=getcopyconfigitem("control","nick","C",NICKLEN);
114 myident=getcopyconfigitem("control","ident","control",NICKLEN);
115 myhost=getcopyconfigitem("control","hostname",myserver->content,HOSTLEN);
116 myrealname=getcopyconfigitem("control","realname","NewServ Control Service",REALLEN);
117 myauthname=getcopyconfigitem("control","authname","C",ACCOUNTLEN);
118
119 mynick=registerlocaluser(cnick->content,myident->content,myhost->content,myrealname->content,myauthname->content,UMODE_SERVICE|UMODE_DEAF|UMODE_OPER|UMODE_ACCOUNT|UMODE_INV,&handlemessages);
120 triggerhook(HOOK_CONTROL_REGISTERED, mynick);
121 cp=findchannel("#twilightzone");
122 if (!cp) {
123 localcreatechannel(mynick,"#twilightzone");
124 } else {
125 localjoinchannel(mynick,cp);
126 localgetops(mynick,cp);
127 }
128
129 freesstring(cnick);
130 freesstring(myident);
131 freesstring(myhost);
132 freesstring(myrealname);
133 freesstring(myauthname);
134 }
135
136 void handlestats(int hooknum, void *arg) {
137 controlreply(hooknick,"%s",(char *)arg);
138 }
139
140 int controlstatus(void *sender, int cargc, char **cargv) {
141 unsigned long level=999;
142 hooknick=(nick *)sender;
143
144 if (cargc>0) {
145 level=strtoul(cargv[0],NULL,10);
146 }
147
148 registerhook(HOOK_CORE_STATSREPLY,&handlestats);
149
150 triggerhook(HOOK_CORE_STATSREQUEST,(void *)level);
151 deregisterhook(HOOK_CORE_STATSREPLY,&handlestats);
152 return CMD_OK;
153 }
154
155 int controlrehash(void *sender, int cargc, char **cargv) {
156 nick *np=(nick *)sender;
157
158 controlreply(np,"Rehashing the config file.");
159
160 rehashconfig();
161 triggerhook(HOOK_CORE_REHASH,(void *)0);
162 return CMD_OK;
163 }
164
165 void handlewhois(int hooknum, void *arg) {
166 controlreply(hooknick,"%s",(char *)arg);
167 }
168
169 int controlwhois(void *sender, int cargc, char **cargv) {
170 nick *target;
171 channel **channels;
172 char buf[BUFSIZE];
173 int i;
174
175 if (cargc<1)
176 return CMD_USAGE;
177
178 if (cargv[0][0]=='#') {
179 if (!(target=getnickbynumericstr(cargv[0]+1))) {
180 controlreply(sender,"Sorry, couldn't find numeric %s",cargv[0]+1);
181 return CMD_ERROR;
182 }
183 } else {
184 if ((target=getnickbynick(cargv[0]))==NULL) {
185 controlreply((nick *)sender,"Sorry, couldn't find that user");
186 return CMD_ERROR;
187 }
188 }
189
190 controlreply((nick *)sender,"Nick : %s",target->nick);
191 controlreply((nick *)sender,"Numeric : %s",longtonumeric(target->numeric,5));
192 controlreply((nick *)sender,"User@Host : %s@%s (%d user(s) on this host)",target->ident,target->host->name->content,target->host->clonecount);
193 if (IsSetHost(target)) {
194 if (target->shident) {
195 controlreply((nick *)sender,"Fakehost : %s@%s",target->shident->content, target->sethost->content);
196 } else {
197 controlreply((nick *)sender,"Fakehost : %s",target->sethost->content);
198 }
199 }
200 controlreply((nick *)sender,"Timestamp : %lu",target->timestamp);
201 controlreply((nick *)sender,"IP address: %s",IPtostr(target->p_ipaddr));
202 controlreply((nick *)sender,"Realname : %s (%d user(s) have this realname)",target->realname->name->content,target->realname->usercount);
203 if (target->umodes) {
204 controlreply((nick *)sender,"Umode(s) : %s",printflags(target->umodes,umodeflags));
205 }
206 if (IsOper(target) && target->opername)
207 controlreply((nick *)sender,"Opered as : %s",target->opername->content);
208 if (IsAccount(target)) {
209 controlreply((nick *)sender,"Account : %s",target->authname);
210 if (target->accountts)
211 controlreply((nick *)sender,"AccountTS : %ld",target->accountts);
212 if (target->auth) {
213 controlreply((nick *)sender,"UserID : %ld",target->auth->userid);
214 if (target->auth->flags)
215 controlreply((nick *)sender,"AccFlags : %s",printflags(target->auth->flags,accountflags));
216 }
217 }
218
219 hooknick=(nick *)sender;
220 registerhook(HOOK_CONTROL_WHOISREPLY,&handlewhois);
221 triggerhook(HOOK_CONTROL_WHOISREQUEST,target);
222 deregisterhook(HOOK_CONTROL_WHOISREPLY,&handlewhois);
223
224 if (target->channels->cursi==0) {
225 controlreply((nick *)sender,"Channels : none");
226 } else if (target->channels->cursi>50) {
227 controlreply((nick *)sender,"Channels : - (total: %d)",target->channels->cursi);
228 } else {
229 buf[0]='\0';
230 channels=(channel **)target->channels->content;
231 for (i=0;i<=target->channels->cursi;i++) {
232 if (!((i==target->channels->cursi) || ((70-strlen(buf))<channels[i]->index->name->length && strlen(buf)>0))) {
233 strcat(buf,channels[i]->index->name->content);
234 strcat(buf," ");
235 } else {
236 if (strlen(buf)==0) {
237 break;
238 } else {
239 controlreply((nick *)sender,"Channels : %s",buf);
240 buf[0]='\0';
241 i--;
242 }
243 }
244 }
245 }
246
247 return CMD_OK;
248 }
249
250 int controlinsmod(void *sender, int cargc, char **cargv) {
251 if (cargc<1)
252 return CMD_USAGE;
253
254 switch(insmod(cargv[0])) {
255 case -1:
256 controlreply((nick *)sender,"Unable to load module %s",cargv[0]);
257 return CMD_ERROR;
258
259 case 1:
260 controlreply((nick *)sender,"Module %s already loaded, or name not valid",cargv[0]);
261 return CMD_ERROR;
262
263 case 0:
264 controlreply((nick *)sender,"Module %s loaded.",cargv[0]);
265 return CMD_OK;
266
267 default:
268 controlreply((nick *)sender,"An unknown error occured.");
269 return CMD_ERROR;
270 }
271 }
272
273 int controlrmmod(void *sender, int cargc, char **cargv) {
274 if (cargc<1)
275 return CMD_USAGE;
276
277 switch(rmmod(cargv[0])) {
278 case 1:
279 controlreply((nick *)sender,"Module %s is not loaded.",cargv[0]);
280 return CMD_ERROR;
281
282 case 0:
283 controlreply((nick *)sender,"Module %s unloaded.",cargv[0]);
284 return CMD_OK;
285
286 default:
287 controlreply((nick *)sender,"An unknown error occured.");
288 return CMD_ERROR;
289 }
290 }
291
292 int controllsmod(void *sender, int cargc, char **cargv) {
293 int i=0;
294 char *ptr;
295
296 if (cargc < 1) { /* list all loaded modules */
297 const char *ver, *buildid;
298 time_t t, t2 = time(NULL);
299 ptr = lsmod(i, &ver, &buildid, &t);
300
301 /* 9999d 24h 59m 59s fbf2a4a69ee1-tip */
302 controlreply((nick *)sender,"Module Loaded for Version Build id");
303 while (ptr != NULL) {
304 controlreply((nick *)sender," %-40s %-17s %-30s %s", ptr, longtoduration(t2-t, 2), ver?ver:"", buildid?buildid:"");
305 ptr = lsmod(++i, &ver, &buildid, &t);
306 }
307 } else {
308 ptr = lsmod(getindex(cargv[0]), NULL, NULL, NULL);
309 controlreply((nick *)sender,"Module \"%s\" %s", cargv[0], (ptr ? "is loaded." : "is NOT loaded."));
310 }
311 return CMD_OK;
312 }
313
314 int controlreload(void *sender, int cargc, char **cargv) {
315 if (cargc<1)
316 return CMD_USAGE;
317
318 controlreply((nick *)sender,"Imma gonna try and reload %s",cargv[0]);
319
320 safereload(cargv[0]);
321
322 return CMD_OK;
323 }
324
325 int relink(void *sender, int cargc, char **cargv) {
326 if (cargc<1) {
327 controlreply((nick *)sender,"You must give a reason.");
328 return CMD_USAGE;
329 }
330
331 irc_send("%s SQ %s 0 :%s",mynumeric->content,myserver->content,cargv[0]);
332 irc_disconnected();
333
334 return CMD_OK;
335 }
336
337 int die(void *sender, int cargc, char **cargv) {
338 if (cargc<1 || (strlen(cargv[0]) < 10)) {
339 controlreply((nick *)sender,"You must give a reason.");
340 return CMD_USAGE;
341 }
342
343 controlwall(NO_OPER,NL_OPERATIONS,"DIE from %s: %s",((nick *)sender)->nick, cargv[0]);
344
345 newserv_shutdown_pending=1;
346
347 return CMD_OK;
348 }
349
350 int controlchannel(void *sender, int cargc, char **cargv) {
351 channel *cp;
352 nick *np;
353 chanban *cbp;
354 char buf[BUFSIZE];
355 char buf2[12];
356 int i,j, ops=0, voice=0;
357 char timebuf[30];
358
359 if (cargc<1)
360 return CMD_USAGE;
361
362 if ((cp=findchannel(cargv[0]))==NULL) {
363 controlreply((nick *)sender,"Couldn't find channel: %s",cargv[0]);
364 return CMD_ERROR;
365 }
366
367 if (IsLimit(cp)) {
368 sprintf(buf2,"%d",cp->limit);
369 }
370
371 controlreply((nick *)sender,"Channel : %s",cp->index->name->content);
372 strftime(timebuf, 30, "%d/%m/%y %H:%M", localtime(&(cp->timestamp)));
373 controlreply((nick *)sender,"C-time : %ld [%s]",cp->timestamp,timebuf);
374 if (cp->topic) {
375 controlreply((nick *)sender,"Topic : %s",cp->topic->content);
376 strftime(timebuf, 30, "%d/%m/%y %H:%M", localtime(&(cp->topictime)));
377 controlreply((nick *)sender,"T-time : %ld [%s]",cp->topictime,timebuf);
378 } else {
379 controlreply((nick *)sender,"Topic : (none)");
380 }
381 controlreply((nick *)sender,"Mode(s) : %s %s%s%s",printflags(cp->flags,cmodeflags),IsLimit(cp)?buf2:"",
382 IsLimit(cp)?" ":"",IsKey(cp)?cp->key->content:"");
383 controlreply((nick *)sender,"Users : %d (hash size %d, utilisation %.1f%%); %d unique hosts",
384 cp->users->totalusers,cp->users->hashsize,((float)(100*cp->users->totalusers)/cp->users->hashsize),
385 countuniquehosts(cp));
386 i=0;
387 memset(buf,' ',90);
388 buf[72]='\0';
389 for (j=0;j<=cp->users->hashsize;j++) {
390 if (i==4 || j==cp->users->hashsize) {
391 if(i>0) {
392 controlreply((nick *)sender,"Users : %s",buf);
393 }
394 i=0;
395 memset(buf,' ',72);
396 if (j==cp->users->hashsize)
397 break;
398 }
399 if (cp->users->content[j]!=nouser) {
400 if (cp->users->content[j]&CUMODE_OP)
401 ops++;
402 else if (cp->users->content[j]&CUMODE_VOICE)
403 voice++;
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 }
412 controlreply((nick *)sender, "Users : Opped: %d, Voiced: %d", ops,voice);
413 for (cbp=cp->bans;cbp;cbp=cbp->next) {
414 controlreply((nick *)sender,"Ban : %s",bantostringdebug(cbp));
415 }
416 return CMD_OK;
417 }
418
419 int 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++) {
429 controlreply(np,"%s",cmdlist[i]->command->content);
430 }
431
432 controlreply(np,"End of list.");
433 return CMD_OK;
434 }
435
436 void 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) {
460 controlreply(sender,"Unknown command.");
461 return;
462 }
463
464 if (cmd->level>0 && !IsOper(sender)) {
465 controlreply(sender,"You need to be opered to use this command.");
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
478 if((cmd->handler)((void *)sender,cargc-1,&(cargv[1])) == CMD_USAGE)
479 controlhelp(sender, cmd);
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
493 void controlmessage(nick *target, char *message, ... ) {
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
508 void 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
523 void 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
538 void controlspecialrmmod(void *arg) {
539 struct specialsched *a = (struct specialsched *)arg;
540 sstring *froo = a->modulename;
541
542 a->schedule = NULL;
543
544 rmmod(froo->content);
545 freesstring(froo);
546 }
547
548 void controlspecialreloadmod(void *arg) {
549 struct specialsched *a = (struct specialsched *)arg;
550 sstring *froo = a->modulename;
551
552 a->schedule = NULL;
553
554 safereload(froo->content);
555 freesstring(froo);
556 }
557
558 void controlhelp(nick *np, Command *cmd) {
559 sstring *scp = cmd->help;
560 if(!scp) {
561 controlreply(np, "Sorry, no help for this command.");
562 } else {
563 char *cp = scp->content, *sp = cp;
564 int finished = 0;
565 for(;;cp++) {
566 if(*cp == '\0' || *cp == '\n') {
567 if(*cp == '\0') {
568 finished = 1;
569 } else {
570 *cp = '\0';
571 }
572
573 if(sp != cp)
574 controlreply(np, "%s", sp);
575
576 if(finished)
577 break;
578
579 *cp = '\n';
580
581 sp = cp + 1;
582 }
583 }
584 }
585 }
586
587 int controlhelpcmd(void *sender, int cargc, char **cargv) {
588 Command *cmd;
589 nick *np = (nick *)sender;
590
591 if (cargc<1)
592 return CMD_USAGE;
593
594 cmd=findcommandintree(controlcmds,cargv[0],1);
595 if (cmd==NULL) {
596 controlreply(np,"Unknown command.");
597 return CMD_ERROR;
598 }
599
600 controlhelp(np, cmd);
601 return CMD_OK;
602 }
603
604 void controlnoticeopers(flag_t permissionlevel, flag_t noticelevel, char *format, ...) {
605 int i;
606 nick *np;
607 char broadcast[512];
608 va_list va;
609
610 va_start(va, format);
611 vsnprintf(broadcast, sizeof(broadcast), format, va);
612 va_end(va);
613
614 for(i=0;i<NICKHASHSIZE;i++)
615 for(np=nicktable[i];np;np=np->next)
616 if (IsOper(np))
617 controlnotice(np, "%s", broadcast);
618 }
619
620 void controlnswall(int noticelevel, char *format, ...) {
621 char broadcast[512];
622 va_list va;
623
624 va_start(va, format);
625 vsnprintf(broadcast, sizeof(broadcast), format, va);
626 va_end(va);
627
628 controlwall(NO_OPER, noticelevel, "%s", broadcast);
629 }
630
631 void handlerehash(int hooknum, void *arg) {
632 long hupped = (long)arg;
633 if(hupped)
634 controlwall(NO_OPER, NL_OPERATIONS, "SIGHUP received, rehashing...");
635 }
636