]> jfr.im git - irc/quakenet/newserv.git/blob - newsearch/newsearch.c
Added channel counter to nicksearch.
[irc/quakenet/newserv.git] / newsearch / newsearch.c
1
2 #include <stdio.h>
3 #include "newsearch.h"
4
5 #include "../irc/irc_config.h"
6 #include "../lib/irc_string.h"
7 #include "../parser/parser.h"
8 #include "../control/control.h"
9 #include "../lib/splitline.h"
10 #include "../lib/version.h"
11
12 MODULE_VERSION("");
13
14 CommandTree *searchTree;
15 CommandTree *chanOutputTree;
16 CommandTree *nickOutputTree;
17
18 int do_nicksearch(void *source, int cargc, char **cargv);
19 int do_chansearch(void *source, int cargc, char **cargv);
20 struct searchNode *search_parse(int type, char *input);
21
22 void printnick(nick *, nick *);
23 void printnick_channels(nick *, nick *);
24 void printchannel(nick *, chanindex *);
25 void printchannel_topic(nick *, chanindex *);
26 void printchannel_services(nick *, chanindex *);
27
28 void registersearchterm(char *term, parseFunc parsefunc);
29 void deregistersearchterm(char *term, parseFunc parsefunc);
30
31 void regchandisp(const char *name, ChanDisplayFunc handler) {
32 addcommandtotree(chanOutputTree, name, 0, 0, (CommandHandler)handler);
33 }
34
35 void unregchandisp(const char *name, ChanDisplayFunc handler) {
36 deletecommandfromtree(chanOutputTree, name, (CommandHandler)handler);
37 }
38
39 void regnickdisp(const char *name, NickDisplayFunc handler) {
40 addcommandtotree(nickOutputTree, name, 0, 0, (CommandHandler)handler);
41 }
42
43 void unregnickdisp(const char *name, NickDisplayFunc handler) {
44 deletecommandfromtree(nickOutputTree, name, (CommandHandler)handler);
45 }
46
47 const char *parseError;
48 /* used for *_free functions that need to warn users of certain things
49 i.e. hitting too many users in a (kill) or (gline) */
50 nick *senderNSExtern;
51
52 void _init() {
53 searchTree=newcommandtree();
54 chanOutputTree=newcommandtree();
55 nickOutputTree=newcommandtree();
56
57 /* Boolean operations */
58 registersearchterm("and",and_parse);
59 registersearchterm("not",not_parse);
60 registersearchterm("or",or_parse);
61
62 registersearchterm("eq",eq_parse);
63
64 registersearchterm("lt",lt_parse);
65 registersearchterm("gt",gt_parse);
66
67 /* String operations */
68 registersearchterm("match",match_parse);
69 registersearchterm("regex",regex_parse);
70 registersearchterm("length",length_parse);
71
72 /* Nickname operations */
73 registersearchterm("hostmask",hostmask_parse);
74 registersearchterm("realname",realname_parse);
75 registersearchterm("authname",authname_parse);
76 registersearchterm("authts",authts_parse);
77 registersearchterm("ident",ident_parse);
78 registersearchterm("host",host_parse);
79 registersearchterm("channel",channel_parse);
80 registersearchterm("timestamp",timestamp_parse);
81 registersearchterm("country",country_parse);
82 registersearchterm("ip",ip_parse);
83
84 /* Channel operations */
85 registersearchterm("exists",exists_parse);
86 registersearchterm("services",services_parse);
87 registersearchterm("size",size_parse);
88 registersearchterm("name",name_parse);
89 registersearchterm("topic",topic_parse);
90 registersearchterm("oppct",oppct_parse);
91 registersearchterm("uniquehostpct",hostpct_parse);
92 registersearchterm("authedpct",authedpct_parse);
93 registersearchterm("kick",kick_parse);
94
95 /* Nickname / channel operations */
96 registersearchterm("modes",modes_parse);
97 registersearchterm("nick",nick_parse);
98
99 /* Kill / gline parameters */
100 registersearchterm("kill",kill_parse);
101 registersearchterm("gline",gline_parse);
102
103 /* Nick output filters */
104 regnickdisp("default",printnick);
105 regnickdisp("channels",printnick_channels);
106
107 /* Channel output filters */
108 regchandisp("default",printchannel);
109 regchandisp("topic",printchannel_topic);
110 regchandisp("services",printchannel_services);
111
112 registercontrolhelpcmd("nicksearch",NO_OPER,4,do_nicksearch, "Usage: nicksearch <criteria>\nSearches for nicknames with the given criteria.");
113 registercontrolhelpcmd("chansearch",NO_OPER,4,do_chansearch, "Usage: chansearch <criteria>\nSearches for channels with the given criteria.");
114 }
115
116 void _fini() {
117 destroycommandtree(searchTree);
118 destroycommandtree(chanOutputTree);
119 destroycommandtree(nickOutputTree);
120 deregistercontrolcmd("nicksearch", do_nicksearch);
121 deregistercontrolcmd("chansearch", do_chansearch);
122 }
123
124 void registersearchterm(char *term, parseFunc parsefunc) {
125 addcommandtotree(searchTree, term, 0, 0, (CommandHandler) parsefunc);
126 }
127
128 void deregistersearchterm(char *term, parseFunc parsefunc) {
129 deletecommandfromtree(searchTree, term, (CommandHandler) parsefunc);
130 }
131
132 int do_nicksearch(void *source, int cargc, char **cargv) {
133 nick *sender = senderNSExtern = source, *np;
134 int i,j;
135 struct searchNode *search;
136 int limit=500,matches=0;
137 char *ch;
138 int arg=0;
139 struct Command *cmd;
140 NickDisplayFunc display=printnick;
141 unsigned int cmarker;
142 unsigned int tchans=0,uchans=0;
143 struct channel **cs;
144
145 if (cargc<1)
146 return CMD_USAGE;
147
148 if (*cargv[0] == '-') {
149 /* options */
150 arg++;
151
152 for (ch=cargv[0]+1;*ch;ch++) {
153 switch(*ch) {
154 case 'l':
155 if (cargc<arg) {
156 controlreply(sender,"Error: -l switch requires an argument");
157 return CMD_USAGE;
158 }
159 limit=strtoul(cargv[arg++],NULL,10);
160 break;
161
162 case 'd':
163 if (cargc<arg) {
164 controlreply(sender,"Error: -d switch requires an argument");
165 return CMD_USAGE;
166 }
167 cmd=findcommandintree(nickOutputTree, cargv[arg], 1);
168 if (!cmd) {
169 controlreply(sender,"Error: unknown output format %s",cargv[arg]);
170 return CMD_USAGE;
171 }
172 display=(NickDisplayFunc)cmd->handler;
173 arg++;
174 break;
175
176 default:
177 controlreply(sender,"Unrecognised flag -%c.",*ch);
178 }
179 }
180 }
181
182 if (arg>=cargc) {
183 controlreply(sender,"No search terms - aborting.");
184 return CMD_ERROR;
185 }
186
187 if (arg<(cargc-1)) {
188 rejoinline(cargv[arg],cargc-arg);
189 }
190
191 if (!(search = search_parse(SEARCHTYPE_NICK, cargv[arg]))) {
192 controlreply(sender,"Parse error: %s",parseError);
193 return CMD_ERROR;
194 }
195
196 /* Get a marker value to mark "seen" channels for unique count */
197 cmarker=nextchanmarker();
198
199 /* The top-level node needs to return a BOOL */
200 search=coerceNode(search, RETURNTYPE_BOOL);
201
202 for (i=0;i<NICKHASHSIZE;i++) {
203 for (np=nicktable[i];np;np=np->next) {
204 if ((search->exe)(search, np)) {
205 /* Add total channels */
206 tchans += np->channels->cursi;
207
208 /* Check channels for uniqueness */
209 cs=(channel **)np->channels->content;
210 for (j=0;j<np->channels->cursi;j++) {
211 if (cs[j]->index->marker != cmarker) {
212 cs[j]->index->marker=cmarker;
213 uchans++;
214 }
215 }
216
217 if (matches<limit)
218 display(sender, np);
219
220 if (matches==limit)
221 controlreply(sender, "--- More than %d matches, skipping the rest",limit);
222 matches++;
223 }
224 }
225 }
226
227 (search->free)(search);
228
229 controlreply(sender,"--- End of list: %d matches; users were on %u channels (%u unique, %.1f average clones)",
230 matches, tchans, uchans, (float)tchans/uchans);
231
232 return CMD_OK;
233 }
234
235 int do_chansearch(void *source, int cargc, char **cargv) {
236 nick *sender = senderNSExtern = source;
237 chanindex *cip;
238 int i;
239 struct searchNode *search;
240 int limit=500,matches=0;
241 char *ch;
242 int arg=0;
243 struct Command *cmd;
244 ChanDisplayFunc display=printchannel;
245
246 if (cargc<1)
247 return CMD_USAGE;
248
249 if (*cargv[0] == '-') {
250 /* options */
251 arg++;
252
253 for (ch=cargv[0]+1;*ch;ch++) {
254 switch(*ch) {
255 case 'l':
256 if (cargc<arg) {
257 controlreply(sender,"Error: -l switch requires an argument");
258 return CMD_USAGE;
259 }
260 limit=strtoul(cargv[arg++],NULL,10);
261 break;
262
263 case 'd':
264 if (cargc<arg) {
265 controlreply(sender,"Error: -d switch requires an argument");
266 return CMD_USAGE;
267 }
268 cmd=findcommandintree(chanOutputTree, cargv[arg], 1);
269 if (!cmd) {
270 controlreply(sender,"Error: unknown output format %s",cargv[arg]);
271 return CMD_USAGE;
272 }
273 display=(ChanDisplayFunc)cmd->handler;
274 arg++;
275 break;
276
277 default:
278 controlreply(sender,"Unrecognised flag -%c.",*ch);
279 }
280 }
281 }
282
283 if (arg>=cargc) {
284 controlreply(sender,"No search terms - aborting.");
285 return CMD_ERROR;
286 }
287
288 if (arg<(cargc-1)) {
289 rejoinline(cargv[arg],cargc-arg);
290 }
291
292 if (!(search = search_parse(SEARCHTYPE_CHANNEL, cargv[arg]))) {
293 controlreply(sender,"Parse error: %s",parseError);
294 return CMD_ERROR;
295 }
296
297 search=coerceNode(search, RETURNTYPE_BOOL);
298
299 for (i=0;i<CHANNELHASHSIZE;i++) {
300 for (cip=chantable[i];cip;cip=cip->next) {
301 if ((search->exe)(search, cip)) {
302 if (matches<limit)
303 display(sender, cip);
304 if (matches==limit)
305 controlreply(sender, "--- More than %d matches, skipping the rest",limit);
306 matches++;
307 }
308 }
309 }
310
311 (search->free)(search);
312
313 controlreply(sender,"--- End of list: %d matches", matches);
314
315 return CMD_OK;
316 }
317
318 struct coercedata {
319 struct searchNode *child;
320 union {
321 char *stringbuf;
322 unsigned long val;
323 } u;
324 };
325
326 /* Free a coerce node */
327 void free_coerce(struct searchNode *thenode) {
328 struct coercedata *cd=thenode->localdata;
329
330 cd->child->free(cd->child);
331 free(thenode->localdata);
332 free(thenode);
333 }
334
335 /* Free a coerce node with a stringbuf allocated */
336 void free_coercestring(struct searchNode *thenode) {
337 free(((struct coercedata *)thenode->localdata)->u.stringbuf);
338 free_coerce(thenode);
339 }
340
341 /* exe_tostr_null: return the constant string */
342 void *exe_tostr_null(struct searchNode *thenode, void *theinput) {
343 struct coercedata *cd=thenode->localdata;
344
345 return cd->u.stringbuf;
346 }
347
348 /* exe_val_null: return the constant value */
349 void *exe_val_null(struct searchNode *thenode, void *theinput) {
350 struct coercedata *cd=thenode->localdata;
351
352 return (void *)cd->u.val;
353 }
354
355 /* Lots of very dull type conversion functions */
356 void *exe_inttostr(struct searchNode *thenode, void *theinput) {
357 struct coercedata *cd=thenode->localdata;
358
359 sprintf(cd->u.stringbuf, "%lu", (unsigned long)(cd->child->exe)(cd->child, theinput));
360
361 return cd->u.stringbuf;
362 }
363
364 void *exe_booltostr(struct searchNode *thenode, void *theinput) {
365 struct coercedata *cd=thenode->localdata;
366
367 if ((cd->child->exe)(cd->child, theinput)) {
368 sprintf(cd->u.stringbuf,"1");
369 } else {
370 cd->u.stringbuf[0]='\0';
371 }
372
373 return cd->u.stringbuf;
374 }
375
376 void *exe_strtoint(struct searchNode *thenode, void *theinput) {
377 struct coercedata *cd=thenode->localdata;
378
379 return (void *)strtoul((cd->child->exe)(cd->child,theinput),NULL,10);
380 }
381
382 void *exe_booltoint(struct searchNode *thenode, void *theinput) {
383 struct coercedata *cd=thenode->localdata;
384
385 /* Don't need to do anything */
386 return (cd->child->exe)(cd->child, theinput);
387 }
388
389 void *exe_strtobool(struct searchNode *thenode, void *theinput) {
390 struct coercedata *cd=thenode->localdata;
391 char *ch=(cd->child->exe)(cd->child, theinput);
392
393 if (!ch || *ch=='\0' || (*ch=='0' && ch[1]=='\0')) {
394 return (void *)0;
395 } else {
396 return (void *)1;
397 }
398 }
399
400 void *exe_inttobool(struct searchNode *thenode, void *theinput) {
401 struct coercedata *cd=thenode->localdata;
402
403 if ((cd->child->exe)(cd->child, theinput)) {
404 return (void *)1;
405 } else {
406 return (void *)0;
407 }
408 }
409
410 struct searchNode *coerceNode(struct searchNode *thenode, int type) {
411 struct searchNode *anode;
412 struct coercedata *cd;
413
414 /* You can't coerce a NULL */
415 if (!thenode)
416 return NULL;
417
418 /* No effort required to coerce to the same type */
419 if (type==(thenode->returntype & RETURNTYPE_TYPE))
420 return thenode;
421
422 anode=(struct searchNode *)malloc(sizeof(struct searchNode));
423 anode->localdata=cd=(struct coercedata *)malloc(sizeof(struct coercedata));
424 cd->child=thenode;
425 anode->returntype=type; /* We'll return what they want, always */
426 anode->free=free_coerce;
427
428 switch(type) {
429 case RETURNTYPE_STRING:
430 /* For a string we'll need a buffer */
431 /* A 64-bit number prints out to 20 digits, this leaves some slack */
432 cd->u.stringbuf=malloc(25);
433 anode->free=free_coercestring;
434
435 switch(thenode->returntype & RETURNTYPE_TYPE) {
436 default:
437 case RETURNTYPE_INT:
438 if (thenode->returntype & RETURNTYPE_CONST) {
439 /* Constant node: sort it out now */
440 sprintf(cd->u.stringbuf, "%lu", (unsigned long)thenode->exe(thenode, NULL));
441 anode->exe=exe_tostr_null;
442 anode->returntype |= RETURNTYPE_CONST;
443 } else {
444 /* Variable data */
445 anode->exe=exe_inttostr;
446 }
447 break;
448
449 case RETURNTYPE_BOOL:
450 if (thenode->returntype & RETURNTYPE_CONST) {
451 /* Constant bool value */
452 if (thenode->exe(thenode,NULL)) {
453 /* True! */
454 sprintf(cd->u.stringbuf, "1");
455 } else {
456 cd->u.stringbuf[0] = '\0';
457 }
458 anode->exe=exe_tostr_null;
459 anode->returntype |= RETURNTYPE_CONST;
460 } else {
461 /* Variable bool value */
462 anode->exe=exe_booltostr;
463 }
464 break;
465 }
466 break;
467
468 case RETURNTYPE_INT:
469 /* we want an int */
470 switch (thenode->returntype & RETURNTYPE_TYPE) {
471 case RETURNTYPE_STRING:
472 if (thenode->returntype & RETURNTYPE_CONST) {
473 cd->u.val=strtoul((thenode->exe)(thenode, NULL), NULL, 10);
474 anode->exe=exe_val_null;
475 anode->returntype |= RETURNTYPE_CONST;
476 } else {
477 anode->exe=exe_strtoint;
478 }
479 break;
480
481 default:
482 case RETURNTYPE_BOOL:
483 if (thenode->returntype & RETURNTYPE_CONST) {
484 if ((thenode->exe)(thenode,NULL))
485 cd->u.val=1;
486 else
487 cd->u.val=0;
488
489 anode->exe=exe_val_null;
490 anode->returntype |= RETURNTYPE_CONST;
491 } else {
492 anode->exe=exe_booltoint;
493 }
494 break;
495 }
496 break;
497
498 default:
499 case RETURNTYPE_BOOL:
500 /* we want a bool */
501 switch (thenode->returntype & RETURNTYPE_TYPE) {
502 case RETURNTYPE_STRING:
503 if (thenode->returntype & RETURNTYPE_CONST) {
504 char *rv=(char *)((thenode->exe)(thenode, NULL));
505 if (!rv || *rv=='\0' || (*rv=='0' && rv[1]=='\0'))
506 cd->u.val=0;
507 else
508 cd->u.val=1;
509
510 anode->exe=exe_val_null;
511 anode->returntype |= RETURNTYPE_CONST;
512 } else {
513 anode->exe=exe_strtobool;
514 }
515 break;
516
517 default:
518 case RETURNTYPE_INT:
519 if (thenode->returntype & RETURNTYPE_CONST) {
520 if ((thenode->exe)(thenode,NULL))
521 cd->u.val=1;
522 else
523 cd->u.val=0;
524
525 anode->exe=exe_val_null;
526 anode->returntype |= RETURNTYPE_CONST;
527 } else {
528 anode->exe=exe_inttobool;
529 }
530 break;
531 }
532 break;
533 }
534
535 return anode;
536 }
537
538 /* Literals always return constant strings... */
539 void *literal_exe(struct searchNode *thenode, void *theinput) {
540 return ((sstring *)thenode->localdata)->content;
541 }
542
543 void literal_free(struct searchNode *thenode) {
544 freesstring(thenode->localdata);
545 free(thenode);
546 }
547
548 /* search_parse:
549 * Given an input string, return a searchNode.
550 */
551
552 struct searchNode *search_parse(int type, char *input) {
553 /* OK, we need to split the input into chunks on spaces and brackets.. */
554 char *argvector[100];
555 char thestring[500];
556 int i,j,q=0,e=0;
557 char *ch,*ch2;
558 struct Command *cmd;
559 struct searchNode *thenode;
560
561 /* If it starts with a bracket, it's a function call.. */
562 if (*input=='(') {
563 /* Skip past string */
564 for (ch=input;*ch;ch++);
565 if (*(ch-1) != ')') {
566 parseError = "Bracket mismatch!";
567 return NULL;
568 }
569 input++;
570 *(ch-1)='\0';
571
572 /* Split further args */
573 i=-1; /* i = -1 BoW, 0 = inword, 1 = bracket nest depth */
574 j=0; /* j = current arg */
575 e=0;
576 q=0;
577 argvector[0]="";
578 for (ch=input;*ch;ch++) {
579 if (i==-1) {
580 argvector[j]=ch;
581 if (*ch=='(') {
582 i=1;
583 } else if (*ch != ' ') {
584 i=0;
585 if (*ch=='\\') {
586 e=1;
587 } else if (*ch=='\"') {
588 q=1;
589 }
590 }
591 } else if (e==1) {
592 e=0;
593 } else if (q==1) {
594 if (*ch=='\"')
595 q=0;
596 } else if (i==0) {
597 if (*ch=='\\') {
598 e=1;
599 } else if (*ch=='\"') {
600 q=1;
601 } else if (*ch==' ') {
602 *ch='\0';
603 j++;
604 if(j >= (sizeof(argvector) / sizeof(*argvector))) {
605 parseError = "Too many arguments";
606 return NULL;
607 }
608 i=-1;
609 }
610 } else {
611 if (*ch=='\\') {
612 e=1;
613 } else if (*ch=='\"') {
614 q=1;
615 } else if (*ch=='(') {
616 i++;
617 } else if (*ch==')') {
618 i--;
619 }
620 }
621 }
622
623 if (i>0) {
624 parseError = "Bracket mismatch!";
625 return NULL;
626 }
627
628 if (*(ch-1) == 0) /* if the last character was a space */
629 j--; /* remove an argument */
630
631 if (!(cmd=findcommandintree(searchTree,argvector[0],1))) {
632 parseError = "Unknown command";
633 return NULL;
634 } else {
635 return ((parseFunc)cmd->handler)(type, j, argvector+1);
636 }
637 } else {
638 /* Literal */
639 if (*input=='\"') {
640 for (ch=input;*ch;ch++);
641
642 if (*(ch-1) != '\"') {
643 parseError="Quote mismatch";
644 return NULL;
645 }
646
647 *(ch-1)='\0';
648 input++;
649 }
650
651 ch2=thestring;
652 for (ch=input;*ch;ch++) {
653 if (e) {
654 e=0;
655 *ch2++=*ch;
656 } else if (*ch=='\\') {
657 e=1;
658 } else {
659 *ch2++=*ch;
660 }
661 }
662 *ch2='\0';
663
664 if (!(thenode=(struct searchNode *)malloc(sizeof(struct searchNode)))) {
665 parseError = "malloc: could not allocate memory for this search.";
666 return NULL;
667 }
668
669 thenode->localdata = getsstring(thestring,512);
670 thenode->returntype = RETURNTYPE_CONST | RETURNTYPE_STRING;
671 thenode->exe = literal_exe;
672 thenode->free = literal_free;
673
674 return thenode;
675 }
676 }