]> jfr.im git - irc/quakenet/newserv.git/blob - versionscan/versionscan.c
Merge pull request #132 from retropc/lua_country
[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 = irc_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 break;
477 }
478 prevv=pv;
479 }
480 }
481 else {
482 v->flags=flags;
483 }
484 sendnoticetouser(versionscan_nick, np, "Done.");
485 return CMD_OK;
486 }
487 else {
488 i=0;
489 if ((cargv[1][0] == '+') || (cargv[1][0] =='-')) {
490 plus=(cargv[1][0] == '+')?1:0;
491 i++;
492 }
493 for (; cargv[1][i]; i++) {
494 switch (cargv[1][i]) {
495 case 'a':
496 flags=(plus)?flags | VS_ADMIN:flags & (~VS_ADMIN);
497 break;
498 case 'g':
499 flags=(plus)?flags | VS_GLINE:flags & (~VS_GLINE);
500 break;
501 case 's':
502 flags=(plus)?flags | VS_STAFF:flags & (~VS_STAFF);
503 break;
504 default:
505 sendnoticetouser(versionscan_nick, np, "Invalid level '%c'.", cargv[1][i]);
506 return CMD_ERROR;
507 break;
508 }
509 }
510 if (flags) {
511 for (v=vsauths; v; v=v->next) {
512 if (!v->next) {
513 v->next=(vsauthdata*)malloc(sizeof(vsauthdata));
514 strncpy(v->next->account, target->authname, ACCOUNTLEN);
515 v->next->flags=flags;
516 v->next->next=0;
517 sendnoticetouser(versionscan_nick, np, "Done.");
518 return CMD_OK;
519 }
520 }
521 sendnoticetouser(versionscan_nick, np, "Error adding user to database.");
522 }
523 else {
524 sendnoticetouser(versionscan_nick, np, "No level specified.");
525 }
526 }
527
528 return CMD_ERROR;
529 }
530
531 int versionscan_scan(void* sender, int cargc, char** cargv) {
532 nick* np=(nick*)sender;
533 nick* n;
534 channel* cp;
535
536 if (cargc < 1) {
537 sendnoticetouser(versionscan_nick, np, "Syntax: scan <target>");
538 return CMD_ERROR;
539 }
540
541 if (versionscan_mode != VS_SCAN) {
542 sendnoticetouser(versionscan_nick, np, "Scanning of users is currently disabled.");
543 return CMD_ERROR;
544 }
545
546 if (cargv[0][0] == '#') {
547 if ((cp=findchannel(cargv[0]))) {
548 sendmessagetochannel(versionscan_nick, cp, "\001VERSION\001");
549 sendnoticetouser(versionscan_nick, np, "Done.");
550 }
551 else {
552 sendnoticetouser(versionscan_nick, np, "No such channel.");
553 return CMD_ERROR;
554 }
555 }
556 else {
557 if ((n=getnickbynick(cargv[0]))) {
558 if (IsOper(n)) {
559 sendnoticetouser(versionscan_nick, np, "Cannot scan IRC Operators.");
560 return CMD_ERROR;
561 }
562 sendmessagetouser(versionscan_nick, n, "\001VERSION\001");
563 sendnoticetouser(versionscan_nick, np, "Done.");
564 }
565 else {
566 sendnoticetouser(versionscan_nick, np, "No such nick.");
567 return CMD_ERROR;
568 }
569 }
570 return CMD_OK;
571 }
572
573 int versionscan_modecmd(void* sender, int cargc, char** cargv) {
574 nick* np=(nick*)sender;
575 int oldmode=versionscan_mode;
576
577 if (cargc < 1) {
578 sendnoticetouser(versionscan_nick, np, "Currently running in %s mode.", (versionscan_mode == VS_SCAN)?"SCAN":(versionscan_mode == VS_STAT)?"STATISTICS":"IDLE");
579 return CMD_OK;
580 }
581
582 if (!ircd_strcmp(cargv[0], "idle")) {
583 versionscan_mode=VS_IDLE;
584 sendnoticetouser(versionscan_nick, np, "Now operating in IDLE mode.");
585 }
586 else if (!ircd_strcmp(cargv[0], "scan")) {
587 versionscan_mode=VS_SCAN;
588 sendnoticetouser(versionscan_nick, np, "Now operating in SCAN mode.");
589 }
590 else if (!ircd_strcmp(cargv[0], "stat")) {
591 versionscan_mode=VS_STAT;
592 sendnoticetouser(versionscan_nick, np, "Now operating in STATISTICS mode.");
593 }
594 else {
595 sendnoticetouser(versionscan_nick, np, "Invalid mode of operation.");
596 return CMD_ERROR;
597 }
598
599 if (oldmode == VS_STAT) {
600 vsstatistic* v, *nv;
601
602 for (v=vsstats; v;) {
603 nv=v->next;
604 free(v->reply);
605 free(v);
606 v=nv;
607 }
608 vsstats=0;
609 }
610
611 return CMD_OK;
612 }
613
614 int versionscan_statistics(void* sender, int cargc, char** cargv) {
615 nick* np=(nick*)sender;
616 vsstatistic* v;
617 long rlimit=0, limit=100;
618
619 if (versionscan_mode != VS_STAT) {
620 sendnoticetouser(versionscan_nick, np, "No statistics are available unless STATISTICS mode of operation is enabled.");
621 return CMD_ERROR;
622 }
623 if (cargc) {
624 limit=atoi(cargv[0]);
625 }
626 if ((limit < 1) || (limit > 500)) {
627 sendnoticetouser(versionscan_nick, np, "Invalid results limit. Valid values are 1-500.");
628 return CMD_ERROR;
629 }
630 sendnoticetouser(versionscan_nick, np, "Reply: [Count]:");
631 for (v=vsstats; (v && (rlimit < limit)); v=v->next) {
632 sendnoticetouser(versionscan_nick, np, "%s [%lu]", v->reply, v->count);
633 rlimit++;
634 }
635 sendnoticetouser(versionscan_nick, np, "End of list - %lu results returned.", rlimit);
636 return CMD_OK;
637 }
638
639 int versionscan_statsdump(void* sender, int cargc, char** cargv) {
640 nick* np=(nick*)sender;
641 vsstatistic* v;
642 long rlimit=0;
643 FILE *fout;
644
645 if (versionscan_mode != VS_STAT) {
646 sendnoticetouser(versionscan_nick, np, "No statistics are available unless STATISTICS mode of operation is enabled.");
647 return CMD_ERROR;
648 }
649 if (!(fout=fopen("data/versionscanstats","w"))) {
650 sendnoticetouser(versionscan_nick, np, "Unable to open save file.");
651 return CMD_ERROR;
652 }
653 for (v=vsstats; v; v=v->next) {
654 fprintf(fout, "%lu:%s\n", v->count, v->reply);
655 rlimit++;
656 }
657 fclose(fout);
658 sendnoticetouser(versionscan_nick, np, "%lu results saved.", rlimit);
659 return CMD_OK;
660 }
661
662 int versionscan_broadcast(void* sender, int cargc, char** cargv) {
663 nick* np=(nick*)sender;
664 int force=0;
665
666 if (cargc) {
667 if (strcmp(cargv[0], "-f")) {
668 sendnoticetouser(versionscan_nick, np, "Invalid flag.");
669 return CMD_ERROR;
670 }
671 force=1;
672 }
673
674 if (versionscan_mode != VS_STAT) {
675 if (!force) {
676 sendnoticetouser(versionscan_nick, np, "Statistics collection mode is not currently enabled. Use the 'mode' command to change current mode of operation.");
677 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.");
678 return CMD_ERROR;
679 }
680 sendnoticetouser(versionscan_nick, np, "Forcing network-wide CTCP version.");
681 }
682
683 irc_send("%s P $* :\001VERSION\001\r\n", longtonumeric(versionscan_nick->numeric, 5));
684 sendnoticetouser(versionscan_nick, np, "Done.");
685
686 return CMD_OK;
687 }
688
689 void versionscan_newnick(int hooknum, void* arg) {
690 nick* np=(nick*)arg;
691
692 /* ignore opers or auth'd users, helps cut down on spam during a burst */
693 if (!(IsOper(np) || IsAccount(np)) && (versionscan_mode == VS_SCAN)) {
694 sendmessagetouser(versionscan_nick, np, "\001VERSION\001");
695 }
696 }
697
698 void versionscan_handler(nick* me, int type, void** args) {
699 nick* sender;
700 Command* cmd;
701 char* cargv[50];
702 int cargc;
703 vspattern* v;
704 char* p;
705
706 switch (type) {
707 case LU_PRIVMSG:
708 case LU_SECUREMSG:
709 /* nick */
710 sender=args[0];
711
712 if (!strncmp("\001VERSION", args[1], 8)) {
713 sendnoticetouser(versionscan_nick, sender, "\001VERSION QuakeNet %s v%s.\001", VS_RNDESC, VS_VERSION);
714 return;
715 }
716
717 cargc=splitline((char*)args[1], cargv, 50, 0);
718
719 cmd=findcommandintree(versionscan_commands, cargv[0], 1);
720 if (!cmd) {
721 sendnoticetouser(versionscan_nick, sender, "Unknown command.");
722 return;
723 }
724
725 if ((cmd->level & VS_AUTHED) && !IsAccount(sender)) {
726 sendnoticetouser(versionscan_nick, sender, "Sorry, you need to be authed to use this command.");
727 return;
728 }
729
730 if ((cmd->level & VS_OPER) && !IsOper(sender)) {
731 sendnoticetouser(versionscan_nick, sender, "Sorry, you need to be opered to use this command.");
732 return;
733 }
734
735 if (((cmd->level & VS_STAFF) && !IsVersionscanStaff(sender)) ||
736 ((cmd->level & VS_GLINE) && !IsVersionscanGlineAccess(sender)) ||
737 ((cmd->level & VS_ADMIN) && !IsVersionscanAdmin(sender))) {
738 sendnoticetouser(versionscan_nick, sender, "Sorry, you do not have access to this command.");
739 return;
740 }
741
742 if (cmd->maxparams < (cargc-1)) {
743 /* We need to do some rejoining */
744 rejoinline(cargv[cmd->maxparams], cargc-(cmd->maxparams));
745 cargc=(cmd->maxparams)+1;
746 }
747
748 (cmd->handler)((void*)sender, cargc-1, &(cargv[1]));
749 break;
750 case LU_PRIVNOTICE:
751 sender=args[0];
752
753 if (strncmp("\001VERSION ", args[1], 9)) {
754 break;
755 }
756 if ((p=strchr((char *)args[1] + 9, '\001'))) {
757 *p++='\0';
758 }
759 if (versionscan_mode == VS_SCAN) {
760 if (IsOper(sender)) {
761 break;
762 }
763 for (v=vspatterns; v; v=v->next) {
764 if (match2strings(v->pattern, (char *)args[1] + 9)) {
765 v->hitcount++;
766 hcount++;
767 switch (v->action) {
768 case VS_WARN:
769 sendnoticetouser(versionscan_nick, sender, "%s", v->data);
770 wcount++;
771 break;
772 case VS_KILL:
773 killuser(versionscan_nick, sender, "%s", v->data);
774 kcount++;
775 break;
776 case VS_GLUSER:
777 glinebynick(sender, 3600, v->data, GLINE_ALWAYS_USER, "versionscan");
778 gcount++;
779 break;
780 case VS_GLHOST:
781 glinebynick(sender, 3600, v->data, 0, "versionscan");
782 gcount++;
783 break;
784 default:
785 /* oh dear, something's fucked */
786 break;
787 }
788 break;
789 }
790 }
791 }
792 else if (versionscan_mode == VS_STAT) {
793 versionscan_addstat((char *)args[1] + 9);
794 }
795 break;
796 case LU_KILLED:
797 versionscan_nick=NULL;
798 scheduleoneshot(time(NULL)+1, &versionscan_createfakeuser, NULL);
799 break;
800 }
801 }
802
803 void versionscan_createfakeuser(void* arg) {
804 channel* cp;
805 char buf[200];
806
807 vsconnect=NULL;
808 sprintf(buf, "%s v%s", VS_RNDESC, VS_VERSION);
809 versionscan_nick=registerlocaluser(VS_NICK, VS_IDENT, VS_HOST, buf, VS_AUTHNAME, UMODE_ACCOUNT | UMODE_DEAF | UMODE_OPER | UMODE_SERVICE, versionscan_handler);
810 if ((cp=findchannel(OPER_CHAN))) {
811 localjoinchannel(versionscan_nick, cp);
812 localgetops(versionscan_nick, cp);
813 } else {
814 localcreatechannel(versionscan_nick, OPER_CHAN);
815 }
816 }
817
818 void _init() {
819 vspatterns=NULL;
820 vsauths=NULL;
821 vsstats=NULL;
822 versionscan_mode=VS_IDLE;
823
824 versionscan_commands=newcommandtree();
825
826 addcommandtotree(versionscan_commands, "showcommands", VS_AUTHED | VS_STAFF, 0, versionscan_showcommands);
827 addcommandtotree(versionscan_commands, "help", VS_AUTHED | VS_STAFF, 1, versionscan_help);
828 addcommandtotree(versionscan_commands, "hello", VS_AUTHED | VS_OPER, 0, versionscan_hello);
829 addcommandtotree(versionscan_commands, "scan", VS_AUTHED | VS_STAFF, 1, versionscan_scan);
830 addcommandtotree(versionscan_commands, "changelev", VS_AUTHED | VS_OPER | VS_ADMIN, 2, versionscan_changelev);
831 addcommandtotree(versionscan_commands, "listpatterns", VS_AUTHED | VS_STAFF | VS_OPER, 0, versionscan_listpatterns);
832 addcommandtotree(versionscan_commands, "addpattern", VS_AUTHED | VS_STAFF | VS_OPER, 3, versionscan_addpatterncmd);
833 addcommandtotree(versionscan_commands, "delpattern", VS_AUTHED | VS_OPER | VS_ADMIN, 1, versionscan_delpatterncmd);
834 addcommandtotree(versionscan_commands, "status", VS_AUTHED | VS_OPER | VS_ADMIN, 0, versionscan_status);
835 addcommandtotree(versionscan_commands, "mode", VS_AUTHED | VS_OPER | VS_ADMIN, 1, versionscan_modecmd);
836 addcommandtotree(versionscan_commands, "statistics", VS_AUTHED | VS_OPER | VS_STAFF, 1, versionscan_statistics);
837 addcommandtotree(versionscan_commands, "statsdump", VS_AUTHED | VS_OPER | VS_STAFF, 1, versionscan_statsdump);
838 addcommandtotree(versionscan_commands, "broadcast", VS_AUTHED | VS_OPER | VS_ADMIN, 1, versionscan_broadcast);
839 addcommandtotree(versionscan_commands, "whois", VS_AUTHED | VS_STAFF, 1, versionscan_whois);
840
841 registerhook(HOOK_NICK_NEWNICK, &versionscan_newnick);
842
843 vsconnect=scheduleoneshot(time(NULL)+1, &versionscan_createfakeuser, NULL);
844 }
845
846 void _fini() {
847 void* p, *np;
848
849 deregisterhook(HOOK_NICK_NEWNICK, &versionscan_newnick);
850
851 if (vsconnect) {
852 deleteschedule(vsconnect, &versionscan_createfakeuser, NULL);
853 vsconnect=NULL;
854 }
855
856 if (versionscan_nick) {
857 deregisterlocaluser(versionscan_nick, "Module unloaded.");
858 versionscan_nick=NULL;
859 }
860
861 destroycommandtree(versionscan_commands);
862
863 for (p=vspatterns; p;) {
864 np=((vspattern*)p)->next;
865 free(p);
866 p=np;
867 }
868 for (p=vsauths; p;) {
869 np=((vsauthdata*)p)->next;
870 free(p);
871 p=np;
872 }
873 for (p=vsstats; p;) {
874 np=((vsstatistic*)p)->next;
875 free(((vsstatistic*)p)->reply);
876 free(p);
877 p=np;
878 }
879 }