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