]> jfr.im git - irc/quakenet/newserv.git/blame - versionscan/versionscan.c
merge
[irc/quakenet/newserv.git] / versionscan / versionscan.c
CommitLineData
4c08bc95
CP
1#include "versionscan.h"
2
3CommandTree* versionscan_commands;
4nick* versionscan_nick;
5int versionscannext;
6int versionscan_mode;
7vspattern* vspatterns;
8vsauthdata* vsauths;
9vsstatistic* vsstats;
10unsigned long hcount=0;
11unsigned long wcount=0;
12unsigned long kcount=0;
13unsigned long gcount=0;
14schedule *vsconnect;
15
16void versionscan_addstat(char* reply) {
b7fa7fa8
CP
17 unsigned int replylen;
18 unsigned long replycrc;
4c08bc95 19 vsstatistic* v, *pv;
b7fa7fa8
CP
20
21 replylen = strlen(reply);
22 replycrc = crc32i(reply);
23
24 pv=NULL;
4c08bc95 25 for (v=vsstats; v; v=v->next) {
b7fa7fa8 26 if (v->replylen==replylen && v->replycrc==replycrc) {
4c08bc95
CP
27 v->count++;
28 return;
29 }
30 pv=v;
31 }
32 if (!pv) {
33 vsstats=(vsstatistic*)malloc(sizeof(vsstatistic));
b7fa7fa8 34 vsstats->reply=(char*)malloc(replylen + 1);
4c08bc95 35 strcpy(vsstats->reply, reply);
b7fa7fa8
CP
36 vsstats->replylen = replylen;
37 vsstats->replycrc = replycrc;
4c08bc95 38 vsstats->count=1;
b7fa7fa8 39 vsstats->next=NULL;
4c08bc95
CP
40 }
41 else {
42 pv->next=(vsstatistic*)malloc(sizeof(vsstatistic));
b7fa7fa8 43 pv->next->reply=(char*)malloc(replylen + 1);
4c08bc95 44 strcpy(pv->next->reply, reply);
b7fa7fa8
CP
45 pv->next->replylen = replylen;
46 pv->next->replycrc = replycrc;
4c08bc95 47 pv->next->count=1;
b7fa7fa8 48 pv->next->next=NULL;
4c08bc95
CP
49 }
50}
51
52unsigned char versionscan_getlevelbyauth(char* auth) {
53 vsauthdata* v;
54
55 for (v=vsauths; v; v=v->next) {
56 if (!ircd_strcmp(v->account, auth)) {
57 return v->flags;
58 }
59 }
60 return 0;
61}
62
63vsauthdata* versionscan_getauthbyauth(char* auth) {
64 vsauthdata* v;
65
66 for (v=vsauths; v; v=v->next) {
67 if (!ircd_strcmp(v->account, auth)) {
68 return v;
69 }
70 }
71 return 0;
72}
73
74int IsVersionscanStaff(nick* np) {
75 unsigned char level;
76
77 if (!IsAccount(np)) {
78 return 0;
79 }
80 level=versionscan_getlevelbyauth(np->authname);
81 if (level & (VS_STAFF | VS_GLINE | VS_ADMIN)) {
82 return 1;
83 }
84 return 0;
85}
86
87int IsVersionscanGlineAccess(nick* np) {
88 unsigned char level;
89
90 if (!IsAccount(np)) {
91 return 0;
92 }
93 level=versionscan_getlevelbyauth(np->authname);
94 if (level & (VS_GLINE | VS_ADMIN)) {
95 return 1;
96 }
97 return 0;
98}
99
100int IsVersionscanAdmin(nick* np) {
101 unsigned char level;
102
103 if (!IsAccount(np)) {
104 return 0;
105 }
106 level=versionscan_getlevelbyauth(np->authname);
107 if (level & VS_ADMIN) {
108 return 1;
109 }
110 return 0;
111}
112
113const char* versionscan_flagstochar(unsigned char flags) {
114 static char outstring[50];
115 int pos=0;
116
117 outstring[pos++]='+';
118 if (flags & VS_ADMIN) { outstring[pos++]='a'; }
119 if (flags & VS_GLINE) { outstring[pos++]='g'; }
120 if (flags & VS_STAFF) { outstring[pos++]='s'; }
121 outstring[pos]='\0';
122
123 return outstring;
124}
125
126void versionscan_addpattern(char* pattern, char* data, unsigned char action) {
127 vspattern* v;
128
129 if (!vspatterns) {
130 vspatterns=(vspattern*)malloc(sizeof(vspattern));
131 strncpy(vspatterns->pattern, pattern, VSPATTERNLEN);
132 strncpy(vspatterns->data, data, VSDATALEN);
133 vspatterns->action=action;
134 vspatterns->next=0;
135 vspatterns->hitcount=0;
136 return;
137 }
138
139 for (v=vspatterns; v; v=v->next) {
140 if (!v->next) {
141 v->next=(vspattern*)malloc(sizeof(vspattern));
142 strncpy(v->next->pattern, pattern, VSPATTERNLEN);
143 strncpy(v->next->data, data, VSDATALEN);
144 v->next->action=action;
145 v->next->next=0;
146 v->next->hitcount=0;
147 return;
148 }
149 }
150}
151
152void versionscan_delpattern(char* pattern) {
153 vspattern* v, *pv;
154
155 pv=0;
156 for (v=vspatterns; v; v=v->next) {
157 if (!ircd_strcmp(v->pattern, pattern)) {
158 if (pv) {
159 pv->next=v->next;
160 free(v);
161 }
162 else {
163 vspatterns=v->next;
164 free(v);
165 }
166 return;
167 }
168 pv=v;
169 }
170}
171
172vspattern* versionscan_getpattern(char* pattern) {
173 vspattern* v;
174
175 for (v=vspatterns; v; v=v->next) {
176 if (!ircd_strcmp(v->pattern, pattern)) {
177 return v;
178 }
179 }
180 return 0;
181}
182
183int versionscan_whois(void* sender, int cargc, char** cargv) {
184 nick* np=(nick*)sender;
185 nick* target;
186 vsauthdata* v;
187
188 if (cargc < 1) {
189 sendnoticetouser(versionscan_nick, np, "Syntax: whois <nickname>");
190 return CMD_ERROR;
191 }
192 if (!(target=getnickbynick(cargv[0]))) {
193 sendnoticetouser(versionscan_nick, np, "No such nick.");
194 return CMD_ERROR;
195 }
196 if (!IsAccount(target)) {
197 sendnoticetouser(versionscan_nick, np, "%s is not authed with the network.", target->nick);
198 return CMD_ERROR;
199 }
200 if (!(v=versionscan_getauthbyauth(target->authname))) {
201 sendnoticetouser(versionscan_nick, np, "User %s is not in my database.", target->nick);
202 return CMD_ERROR;
203 }
204 sendnoticetouser(versionscan_nick, np, "%s is authed as %s, with flags: %s", target->nick, target->authname, versionscan_flagstochar(v->flags));
205 return CMD_OK;
206}
207
208int versionscan_showcommands(void* sender, int cargc, char** cargv) {
209 nick* np=(nick*)sender;
210 Command* cmdlist[150];
211 int i, j;
212
213 sendnoticetouser(versionscan_nick, np, "The following commands are registered at present:");
214 j=getcommandlist(versionscan_commands, cmdlist, 150);
215 for (i=0; i<j; i++) {
216 if (cmdlist[i]->level & (VS_STAFF | VS_GLINE | VS_ADMIN)) {
217 sendnoticetouser(versionscan_nick, np, "%s (%s)", cmdlist[i]->command->content, versionscan_flagstochar(cmdlist[i]->level));
218 }
219 else {
220 sendnoticetouser(versionscan_nick, np, "%s", cmdlist[i]->command->content);
221 }
222 }
223 sendnoticetouser(versionscan_nick, np, "End of list.");
224
225 return CMD_OK;
226}
227
228int versionscan_help(void* sender, int cargc, char** cargv) {
229 nick* np=(nick*)sender;
230
231 if (cargc < 1) {
232 sendnoticetouser(versionscan_nick, np, "Syntax: help <command>");
233 return CMD_ERROR;
234 }
235
236 if (!strcasecmp(cargv[0], "help")) {
237 sendnoticetouser(versionscan_nick, np, "Syntax: help <command>");
238 sendnoticetouser(versionscan_nick, np, "Gives help on commands.");
239 return CMD_OK;
240 }
241 if (!strcasecmp(cargv[0], "hello")) {
242 sendnoticetouser(versionscan_nick, np, "Syntax: hello");
243 sendnoticetouser(versionscan_nick, np, "Creates the first account on the bot.");
244 return CMD_OK;
245 }
246 if (!strcasecmp(cargv[0], "scan")) {
247 sendnoticetouser(versionscan_nick, np, "Syntax: scan <target>");
248 sendnoticetouser(versionscan_nick, np, "Sends a version request to the specified target, which may be a nick or a channel.");
249 return CMD_OK;
250 }
251 if (!strcasecmp(cargv[0], "broadcast")) {
252 sendnoticetouser(versionscan_nick, np, "Syntax: broadcast [-f]");
253 sendnoticetouser(versionscan_nick, np, "Send a network-wide CTCP version.");
254 return CMD_OK;
255 }
256 if (!strcasecmp(cargv[0], "changelev")) {
257 sendnoticetouser(versionscan_nick, np, "Syntax: changelev <nick> <level>");
258 sendnoticetouser(versionscan_nick, np, "Changes a user's privileges.");
259 sendnoticetouser(versionscan_nick, np, "+a -> admin access");
260 sendnoticetouser(versionscan_nick, np, "+g -> g-line access");
261 sendnoticetouser(versionscan_nick, np, "+s -> staff access");
262 return CMD_OK;
263 }
264 if (!strcasecmp(cargv[0], "mode")) {
265 sendnoticetouser(versionscan_nick, np, "Syntax: mode [<mode of operation>]");
266 sendnoticetouser(versionscan_nick, np, "Where <mode of operation> is one of:");
267 sendnoticetouser(versionscan_nick, np, "idle: do nothing");
268 sendnoticetouser(versionscan_nick, np, "scan: scan newly connecting users and those targeted by the 'scan' command");
269 sendnoticetouser(versionscan_nick, np, "stat: collect statistics after a network-wide CTCP version request");
270 return CMD_OK;
271 }
272 if (!strcasecmp(cargv[0], "showcommands")) {
273 sendnoticetouser(versionscan_nick, np, "Syntax: showcommands");
274 sendnoticetouser(versionscan_nick, np, "Displays registered commands.");
275 return CMD_OK;
276 }
277 if (!strcasecmp(cargv[0], "whois")) {
278 sendnoticetouser(versionscan_nick, np, "Syntax: whois <nickname>");
279 sendnoticetouser(versionscan_nick, np, "Display information about the specified user.");
280 return CMD_OK;
281 }
282 if (!strcasecmp(cargv[0], "statistics")) {
283 sendnoticetouser(versionscan_nick, np, "Syntax: statistics [<limit>]");
284 sendnoticetouser(versionscan_nick, np, "Display statistics of collected CTCP version replies.");
285 return CMD_OK;
286 }
287 if (!strcasecmp(cargv[0], "listpatterns")) {
288 sendnoticetouser(versionscan_nick, np, "Syntax: listpatterns");
289 sendnoticetouser(versionscan_nick, np, "Lists CTCP version reply patterns.");
290 return CMD_OK;
291 }
292 if (!strcasecmp(cargv[0], "addpattern")) {
293 sendnoticetouser(versionscan_nick, np, "Syntax: addpattern <pattern> <action> <data>");
294 sendnoticetouser(versionscan_nick, np, "Adds a CTCP version reply pattern, where action is one of the following:");
295 sendnoticetouser(versionscan_nick, np, "%d - warn", VS_WARN);
296 sendnoticetouser(versionscan_nick, np, "%d - kill", VS_KILL);
297 sendnoticetouser(versionscan_nick, np, "%d - g-line user@host", VS_GLUSER);
298 sendnoticetouser(versionscan_nick, np, "%d - g-line *@host", VS_GLHOST);
299 return CMD_OK;
300 }
301 if (!strcasecmp(cargv[0], "delpattern")) {
302 sendnoticetouser(versionscan_nick, np, "Syntax: delpattern <pattern>");
303 sendnoticetouser(versionscan_nick, np, "Deletes a CTCP version reply pattern.");
304 return CMD_OK;
305 }
306 if (!strcasecmp(cargv[0], "status")) {
307 sendnoticetouser(versionscan_nick, np, "Syntax: status");
308 sendnoticetouser(versionscan_nick, np, "Gives various bits of information about the bot.");
309 return CMD_OK;
310 }
311
312 return CMD_OK;
313}
314
315int versionscan_listpatterns(void* sender, int cargc, char** cargv) {
316 nick* np=(nick*)sender;
317 vspattern* v;
318
319 for (v=vspatterns; v; v=v->next) {
320 sendnoticetouser(versionscan_nick, np, "Pattern [%s]:", v->pattern);
321 sendnoticetouser(versionscan_nick, np, "Data: %s", v->data);
322 sendnoticetouser(versionscan_nick, np, "Action: %s", (v->action == VS_WARN)?"warn":(v->action == VS_KILL)?"kill":(v->action == VS_GLUSER)?"g-line user@host":"g-line *@host");
323 sendnoticetouser(versionscan_nick, np, "Hit count: %lu", v->hitcount);
324 }
325 sendnoticetouser(versionscan_nick, np, "End of list.");
326 return CMD_OK;
327}
328
329int versionscan_addpatterncmd(void* sender, int cargc, char** cargv) {
330 nick* np=(nick*)sender;
331 int action;
332
333 if (cargc < 3) {
334 sendnoticetouser(versionscan_nick, np, "Syntax: addpattern <pattern> <action> <data>");
335 return CMD_ERROR;
336 }
337
338 action=atoi(cargv[1]);
339 if ((action < VS_WARN) || (action > VS_GLHOST)) {
340 sendnoticetouser(versionscan_nick, np, "Action must be a number between 1 and 4.");
341 return CMD_ERROR;
342 }
343
344 if (versionscan_getpattern(cargv[0])) {
345 sendnoticetouser(versionscan_nick, np, "That pattern already exists.");
346 return CMD_ERROR;
347 }
348
349 if ((action > VS_KILL) && !IsVersionscanGlineAccess(np)) {
350 sendnoticetouser(versionscan_nick, np, "You are not allowed to add G-Lines.");
351 return CMD_ERROR;
352 }
353
354 versionscan_addpattern(cargv[0], cargv[2], (unsigned char)action);
355 sendnoticetouser(versionscan_nick, np, "Done.");
356 return CMD_OK;
357}
358
359int versionscan_delpatterncmd(void* sender, int cargc, char** cargv) {
360 nick* np=(nick*)sender;
361
362 if (cargc < 1) {
363 sendnoticetouser(versionscan_nick, np, "Syntax: delpattern <pattern>");
364 return CMD_ERROR;
365 }
366
367 if (!versionscan_getpattern(cargv[0])) {
368 sendnoticetouser(versionscan_nick, np, "That pattern does not exist.");
369 return CMD_ERROR;
370 }
371
372 versionscan_delpattern(cargv[0]);
373 sendnoticetouser(versionscan_nick, np, "Done.");
374 return CMD_OK;
375}
376
377int versionscan_status(void* sender, int cargc, char** cargv) {
378 nick* np=(nick*)sender;
379 vspattern* v;
380 int pcount=0; unsigned long chcount=0;
381
382 for (v=vspatterns; v; v=v->next) {
383 pcount++;
384 chcount+=v->hitcount;
385 }
386
387 sendnoticetouser(versionscan_nick, np, "Patterns: %d", pcount);
388 sendnoticetouser(versionscan_nick, np, "Users hit: %lu (%lu from current patterns)", hcount, chcount);
389 sendnoticetouser(versionscan_nick, np, "Warnings given: %lu", wcount);
390 sendnoticetouser(versionscan_nick, np, "Kills sent: %lu", kcount);
391 sendnoticetouser(versionscan_nick, np, "G-Lines set: %lu", gcount);
392 return CMD_OK;
393}
394
395int versionscan_hello(void* sender, int cargc, char** cargv) {
396 nick* np=(nick*)sender;
397
398 if (vsauths) {
399 sendnoticetouser(versionscan_nick, np, "The hello command cannot be used after the first user account has been created.");
400 return CMD_ERROR;
401 }
402
403 vsauths=(vsauthdata*)malloc(sizeof(vsauthdata));
404 strncpy(vsauths->account, np->authname, ACCOUNTLEN);
405 vsauths->flags=VS_STAFF | VS_GLINE | VS_ADMIN;
406 vsauths->next=NULL;
407
408 sendnoticetouser(versionscan_nick, np, "An account has been created for you with the following flags: %s.", versionscan_flagstochar(vsauths->flags));
409 return CMD_OK;
410}
411
412int versionscan_changelev(void* sender, int cargc, char** cargv) {
413 nick* np=(nick*)sender;
414 vsauthdata* v;
415 nick* target;
416 unsigned char flags=0;
417 int i; int plus=1;
418
419 if (cargc < 2) {
420 sendnoticetouser(versionscan_nick, np, "Syntax: changelev <nick> [+|-]<level>");
421 return CMD_ERROR;
422 }
423
424 if (!(target=getnickbynick(cargv[0]))) {
425 sendnoticetouser(versionscan_nick, np, "No such nick.");
426 return CMD_ERROR;
427 }
428
429 if (!IsAccount(target)) {
430 sendnoticetouser(versionscan_nick, np, "%s is not authed.", target->nick);
431 return CMD_ERROR;
432 }
433
434 if ((v=versionscan_getauthbyauth(target->authname))) {
435 i=0;
436 if ((cargv[1][0] == '+') || (cargv[1][0] =='-')) {
437 plus=(cargv[1][0] == '+')?1:0;
438 i++;
439 flags=v->flags;
440 }
441 for (; cargv[1][i]; i++) {
442 switch (cargv[1][i]) {
443 case 'a':
444 flags=(plus)?flags | VS_ADMIN:flags & (~VS_ADMIN);
445 break;
446 case 'g':
447 flags=(plus)?flags | VS_GLINE:flags & (~VS_GLINE);
448 break;
449 case 's':
450 flags=(plus)?flags | VS_STAFF:flags & (~VS_STAFF);
451 break;
452 default:
453 sendnoticetouser(versionscan_nick, np, "Invalid level '%c'.", cargv[1][i]);
454 return CMD_ERROR;
455 break;
456 }
457 }
458 if (!flags) {
459 vsauthdata* pv, *prevv;
460
461 prevv=0;
462 for (pv=vsauths; pv; pv++) {
463 if (pv == v) {
464 if (prevv) {
465 prevv->next=pv->next;
466 free(pv);
467 }
468 else {
469 vsauths=pv->next;
470 free(pv);
471 }
472 }
473 prevv=pv;
474 }
475 }
476 else {
477 v->flags=flags;
478 }
479 sendnoticetouser(versionscan_nick, np, "Done.");
480 return CMD_OK;
481 }
482 else {
483 i=0;
484 if ((cargv[1][0] == '+') || (cargv[1][0] =='-')) {
485 plus=(cargv[1][0] == '+')?1:0;
486 i++;
487 }
488 for (; cargv[1][i]; i++) {
489 switch (cargv[1][i]) {
490 case 'a':
491 flags=(plus)?flags | VS_ADMIN:flags & (~VS_ADMIN);
492 break;
493 case 'g':
494 flags=(plus)?flags | VS_GLINE:flags & (~VS_GLINE);
495 break;
496 case 's':
497 flags=(plus)?flags | VS_STAFF:flags & (~VS_STAFF);
498 break;
499 default:
500 sendnoticetouser(versionscan_nick, np, "Invalid level '%c'.", cargv[1][i]);
501 return CMD_ERROR;
502 break;
503 }
504 }
505 if (flags) {
506 for (v=vsauths; v; v=v->next) {
507 if (!v->next) {
508 v->next=(vsauthdata*)malloc(sizeof(vsauthdata));
509 strncpy(v->next->account, target->authname, ACCOUNTLEN);
510 v->next->flags=flags;
511 v->next->next=0;
512 sendnoticetouser(versionscan_nick, np, "Done.");
513 return CMD_OK;
514 }
515 }
516 sendnoticetouser(versionscan_nick, np, "Error adding user to database.");
517 }
518 else {
519 sendnoticetouser(versionscan_nick, np, "No level specified.");
520 }
521 }
522
523 return CMD_ERROR;
524}
525
526int versionscan_scan(void* sender, int cargc, char** cargv) {
527 nick* np=(nick*)sender;
528 nick* n;
529 channel* cp;
530
531 if (cargc < 1) {
532 sendnoticetouser(versionscan_nick, np, "Syntax: scan <target>");
533 return CMD_ERROR;
534 }
535
536 if (versionscan_mode != VS_SCAN) {
537 sendnoticetouser(versionscan_nick, np, "Scanning of users is currently disabled.");
538 return CMD_ERROR;
539 }
540
541 if (cargv[0][0] == '#') {
542 if ((cp=findchannel(cargv[0]))) {
543 sendmessagetochannel(versionscan_nick, cp, "\001VERSION\001");
544 sendnoticetouser(versionscan_nick, np, "Done.");
545 }
546 else {
547 sendnoticetouser(versionscan_nick, np, "No such channel.");
548 return CMD_ERROR;
549 }
550 }
551 else {
552 if ((n=getnickbynick(cargv[0]))) {
553 if (IsOper(n)) {
554 sendnoticetouser(versionscan_nick, np, "Cannot scan IRC Operators.");
555 return CMD_ERROR;
556 }
557 sendmessagetouser(versionscan_nick, n, "\001VERSION\001");
558 sendnoticetouser(versionscan_nick, np, "Done.");
559 }
560 else {
561 sendnoticetouser(versionscan_nick, np, "No such nick.");
562 return CMD_ERROR;
563 }
564 }
565 return CMD_OK;
566}
567
568int versionscan_modecmd(void* sender, int cargc, char** cargv) {
569 nick* np=(nick*)sender;
570 int oldmode=versionscan_mode;
571
572 if (cargc < 1) {
573 sendnoticetouser(versionscan_nick, np, "Currently running in %s mode.", (versionscan_mode == VS_SCAN)?"SCAN":(versionscan_mode == VS_STAT)?"STATISTICS":"IDLE");
574 return CMD_OK;
575 }
576
577 if (!ircd_strcmp(cargv[0], "idle")) {
578 versionscan_mode=VS_IDLE;
579 sendnoticetouser(versionscan_nick, np, "Now operating in IDLE mode.");
580 }
581 else if (!ircd_strcmp(cargv[0], "scan")) {
582 versionscan_mode=VS_SCAN;
583 sendnoticetouser(versionscan_nick, np, "Now operating in SCAN mode.");
584 }
585 else if (!ircd_strcmp(cargv[0], "stat")) {
586 versionscan_mode=VS_STAT;
587 sendnoticetouser(versionscan_nick, np, "Now operating in STATISTICS mode.");
588 }
589 else {
590 sendnoticetouser(versionscan_nick, np, "Invalid mode of operation.");
591 return CMD_ERROR;
592 }
593
594 if (oldmode == VS_STAT) {
595 vsstatistic* v, *nv;
596
597 for (v=vsstats; v;) {
598 nv=v->next;
599 free(v->reply);
600 free(v);
601 v=nv;
602 }
603 vsstats=0;
604 }
605
606 return CMD_OK;
607}
608
609int versionscan_statistics(void* sender, int cargc, char** cargv) {
610 nick* np=(nick*)sender;
611 vsstatistic* v;
612 long rlimit=0, limit=100;
613
614 if (versionscan_mode != VS_STAT) {
615 sendnoticetouser(versionscan_nick, np, "No statistics are available unless STATISTICS mode of operation is enabled.");
616 return CMD_ERROR;
617 }
618 if (cargc) {
619 limit=atoi(cargv[0]);
620 }
621 if ((limit < 1) || (limit > 500)) {
622 sendnoticetouser(versionscan_nick, np, "Invalid results limit. Valid values are 1-500.");
623 return CMD_ERROR;
624 }
625 sendnoticetouser(versionscan_nick, np, "Reply: [Count]:");
626 for (v=vsstats; (v && (rlimit < limit)); v=v->next) {
627 sendnoticetouser(versionscan_nick, np, "%s [%lu]", v->reply, v->count);
628 rlimit++;
629 }
630 sendnoticetouser(versionscan_nick, np, "End of list - %lu results returned.", rlimit);
631 return CMD_OK;
632}
633
634int versionscan_statsdump(void* sender, int cargc, char** cargv) {
635 nick* np=(nick*)sender;
636 vsstatistic* v;
637 long rlimit=0;
638 FILE *fout;
639
640 if (versionscan_mode != VS_STAT) {
641 sendnoticetouser(versionscan_nick, np, "No statistics are available unless STATISTICS mode of operation is enabled.");
642 return CMD_ERROR;
643 }
644 if (!(fout=fopen("versionscanstats","w"))) {
645 sendnoticetouser(versionscan_nick, np, "Unable to open save file.");
646 return CMD_ERROR;
647 }
648 for (v=vsstats; v; v=v->next) {
b7fa7fa8 649 fprintf(fout, "%lu:%s\n", v->count, v->reply);
4c08bc95
CP
650 rlimit++;
651 }
652 fclose(fout);
653 sendnoticetouser(versionscan_nick, np, "%lu results saved.", rlimit);
654 return CMD_OK;
655}
656
657int versionscan_broadcast(void* sender, int cargc, char** cargv) {
658 nick* np=(nick*)sender;
659 int force=0;
660
661 if (cargc) {
662 if (strcmp(cargv[0], "-f")) {
663 sendnoticetouser(versionscan_nick, np, "Invalid flag.");
664 return CMD_ERROR;
665 }
666 force=1;
667 }
668
669 if (versionscan_mode != VS_STAT) {
670 if (!force) {
671 sendnoticetouser(versionscan_nick, np, "Statistics collection mode is not currently enabled. Use the 'mode' command to change current mode of operation.");
672 sendnoticetouser(versionscan_nick, np, "If you really wish to send a network-wide CTCP version whilst running in SCAN or IDLE mode, use the -f flag.");
673 return CMD_ERROR;
674 }
675 sendnoticetouser(versionscan_nick, np, "Forcing network-wide CTCP version.");
676 }
677
678 irc_send("%s P $* :\001VERSION\001\r\n", longtonumeric(versionscan_nick->numeric, 5));
679 sendnoticetouser(versionscan_nick, np, "Done.");
680
681 return CMD_OK;
682}
683
684void versionscan_newnick(int hooknum, void* arg) {
685 nick* np=(nick*)arg;
686
687 /* ignore opers or auth'd users, helps cut down on spam during a burst */
688 if (!(IsOper(np) || IsAccount(np)) && (versionscan_mode == VS_SCAN)) {
689 sendmessagetouser(versionscan_nick, np, "\001VERSION\001");
690 }
691}
692
693void versionscan_handler(nick* me, int type, void** args) {
694 nick* sender;
695 Command* cmd;
696 char* cargv[50];
697 int cargc;
698 vspattern* v;
699 char* p;
700
701 switch (type) {
702 case LU_PRIVMSG:
703 case LU_SECUREMSG:
704 /* nick */
705 sender=args[0];
706
707 if (!strncmp("\001VERSION", args[1], 8)) {
708 sendnoticetouser(versionscan_nick, sender, "\001VERSION QuakeNet %s v%s.\001", VS_RNDESC, VS_VERSION);
709 return;
710 }
711
712 cargc=splitline((char*)args[1], cargv, 50, 0);
713
714 cmd=findcommandintree(versionscan_commands, cargv[0], 1);
715 if (!cmd) {
716 sendnoticetouser(versionscan_nick, sender, "Unknown command.");
717 return;
718 }
719
720 if ((cmd->level & VS_AUTHED) && !IsAccount(sender)) {
721 sendnoticetouser(versionscan_nick, sender, "Sorry, you need to be authed to use this command.");
722 return;
723 }
724
725 if ((cmd->level & VS_OPER) && !IsOper(sender)) {
726 sendnoticetouser(versionscan_nick, sender, "Sorry, you need to be opered to use this command.");
727 return;
728 }
729
730 if (((cmd->level & VS_STAFF) && !IsVersionscanStaff(sender)) ||
731 ((cmd->level & VS_GLINE) && !IsVersionscanGlineAccess(sender)) ||
732 ((cmd->level & VS_ADMIN) && !IsVersionscanAdmin(sender))) {
733 sendnoticetouser(versionscan_nick, sender, "Sorry, you do not have access to this command.");
734 return;
735 }
736
737 if (cmd->maxparams < (cargc-1)) {
738 /* We need to do some rejoining */
739 rejoinline(cargv[cmd->maxparams], cargc-(cmd->maxparams));
740 cargc=(cmd->maxparams)+1;
741 }
742
743 (cmd->handler)((void*)sender, cargc-1, &(cargv[1]));
744 break;
745 case LU_PRIVNOTICE:
746 sender=args[0];
747
748 if (strncmp("\001VERSION ", args[1], 9)) {
749 break;
750 }
751 if ((p=strchr((char *)args[1] + 9, '\001'))) {
752 *p++='\0';
753 }
754 if (versionscan_mode == VS_SCAN) {
755 if (IsOper(sender)) {
756 break;
757 }
758 for (v=vspatterns; v; v=v->next) {
759 if (match2strings(v->pattern, (char *)args[1] + 9)) {
760 v->hitcount++;
761 hcount++;
762 switch (v->action) {
763 case VS_WARN:
764 sendnoticetouser(versionscan_nick, sender, v->data);
765 wcount++;
766 break;
767 case VS_KILL:
768 killuser(versionscan_nick, sender, v->data);
769 kcount++;
770 break;
771 case VS_GLUSER:
772 irc_send("%s GL * +*!%s@%s 3600 :%s\r\n", mynumeric->content, sender->ident, sender->host->name->content, v->data);
773 gcount++;
774 break;
775 case VS_GLHOST:
776 irc_send("%s GL * +*!*@%s 3600 :%s\r\n", mynumeric->content, sender->host->name->content, v->data);
777 gcount++;
778 break;
779 default:
780 /* oh dear, something's fucked */
781 break;
782 }
783 break;
784 }
785 }
786 }
787 else if (versionscan_mode == VS_STAT) {
788 versionscan_addstat((char *)args[1] + 9);
789 }
790 break;
791 case LU_KILLED:
792 versionscan_nick=NULL;
793 scheduleoneshot(time(NULL)+1, &versionscan_createfakeuser, NULL);
794 break;
795 }
796}
797
798void versionscan_createfakeuser(void* arg) {
799 channel* cp;
800 char buf[200];
801
802 vsconnect=NULL;
803 sprintf(buf, "%s v%s", VS_RNDESC, VS_VERSION);
804 versionscan_nick=registerlocaluser(VS_NICK, VS_IDENT, VS_HOST, buf, VS_AUTHNAME, UMODE_ACCOUNT | UMODE_DEAF | UMODE_OPER | UMODE_SERVICE, versionscan_handler);
805 if ((cp=findchannel(OPER_CHAN))) {
806 localjoinchannel(versionscan_nick, cp);
807 localgetops(versionscan_nick, cp);
808 } else {
809 localcreatechannel(versionscan_nick, OPER_CHAN);
810 }
811}
812
813void _init() {
814 vspatterns=NULL;
815 vsauths=NULL;
816 vsstats=NULL;
817 versionscan_mode=VS_IDLE;
818
819 versionscan_commands=newcommandtree();
820
821 addcommandtotree(versionscan_commands, "showcommands", VS_AUTHED | VS_STAFF, 0, versionscan_showcommands);
822 addcommandtotree(versionscan_commands, "help", VS_AUTHED | VS_STAFF, 1, versionscan_help);
823 addcommandtotree(versionscan_commands, "hello", VS_AUTHED | VS_OPER, 0, versionscan_hello);
824 addcommandtotree(versionscan_commands, "scan", VS_AUTHED | VS_STAFF, 1, versionscan_scan);
825 addcommandtotree(versionscan_commands, "changelev", VS_AUTHED | VS_OPER | VS_ADMIN, 2, versionscan_changelev);
826 addcommandtotree(versionscan_commands, "listpatterns", VS_AUTHED | VS_STAFF | VS_OPER, 0, versionscan_listpatterns);
827 addcommandtotree(versionscan_commands, "addpattern", VS_AUTHED | VS_STAFF | VS_OPER, 3, versionscan_addpatterncmd);
828 addcommandtotree(versionscan_commands, "delpattern", VS_AUTHED | VS_OPER | VS_ADMIN, 1, versionscan_delpatterncmd);
829 addcommandtotree(versionscan_commands, "status", VS_AUTHED | VS_OPER | VS_ADMIN, 0, versionscan_status);
830 addcommandtotree(versionscan_commands, "mode", VS_AUTHED | VS_OPER | VS_ADMIN, 1, versionscan_modecmd);
831 addcommandtotree(versionscan_commands, "statistics", VS_AUTHED | VS_OPER | VS_STAFF, 1, versionscan_statistics);
832 addcommandtotree(versionscan_commands, "statsdump", VS_AUTHED | VS_OPER | VS_STAFF, 1, versionscan_statsdump);
833 addcommandtotree(versionscan_commands, "broadcast", VS_AUTHED | VS_OPER | VS_ADMIN, 1, versionscan_broadcast);
834 addcommandtotree(versionscan_commands, "whois", VS_AUTHED | VS_STAFF, 1, versionscan_whois);
835
836 registerhook(HOOK_NICK_NEWNICK, &versionscan_newnick);
837
838 vsconnect=scheduleoneshot(time(NULL)+1, &versionscan_createfakeuser, NULL);
839}
840
841void _fini() {
842 void* p, *np;
843
844 deregisterhook(HOOK_NICK_NEWNICK, &versionscan_newnick);
845
846 if (vsconnect) {
847 deleteschedule(vsconnect, &versionscan_createfakeuser, NULL);
848 vsconnect=NULL;
849 }
850
851 if (versionscan_nick) {
852 deregisterlocaluser(versionscan_nick, "Module unloaded.");
853 versionscan_nick=NULL;
854 }
855
856 destroycommandtree(versionscan_commands);
857
858 for (p=vspatterns; p;) {
859 np=((vspattern*)p)->next;
860 free(p);
861 p=np;
862 }
863 for (p=vsauths; p;) {
864 np=((vsauthdata*)p)->next;
865 free(p);
866 p=np;
867 }
868 for (p=vsstats; p;) {
869 np=((vsstatistic*)p)->next;
870 free(((vsstatistic*)p)->reply);
871 free(p);
872 p=np;
873 }
874}