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