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