]> jfr.im git - irc/quakenet/newserv.git/blame - chansearch/chansearch.c
merge
[irc/quakenet/newserv.git] / chansearch / chansearch.c
CommitLineData
c86edd1d
Q
1#include "chansearch.h"
2#include "../parser/parser.h"
3#include "../nick/nick.h"
4#include "../channel/channel.h"
5#include "../control/control.h"
6#include "../lib/flags.h"
7#include "../lib/irc_string.h"
87698d77 8#include "../lib/version.h"
c86edd1d
Q
9
10#include <stdio.h>
11
70b0a4e5 12MODULE_VERSION("");
87698d77 13
c86edd1d
Q
14#define MAXTERMS 10
15#define MAXMATCHES 500
16
17CommandTree *searchfilters;
18CommandTree *outputfilters;
19
20typedef struct modeflags {
21 flag_t setflags;
22 flag_t clearflags;
23} modeflags;
24
25void cs_describe(nick *sender, chanindex *cip);
26void cs_desctopic(nick *sender, chanindex *cip);
27void cs_descservices(nick *sender, chanindex *cip);
28int cs_exists(void *chan, int cargc, char **cargv);
29int cs_services(void *chan, int cargc, char **cargv);
30int cs_nick(void *chan, int cargc, char **cargv);
31int cs_size(void *chan, int cargc, char **cargv);
32int cs_namelen(void *chan, int cargc, char **cargv);
33int cs_modes(void *chan, int cargc, char **cargv);
34int cs_name(void *chan, int cargc, char **cargv);
35int cs_topic(void *source, int cargc, char **cargv);
36int cs_oppct(void *source, int cargc, char **cargv);
37int cs_hostpct(void *source, int cargc, char **cargv);
38int cs_authedpct(void *source, int cargc, char **cargv);
39
40int dochansearch(void *source, int cargc, char **cargv);
41
42void _init() {
43 searchfilters=newcommandtree();
44 outputfilters=newcommandtree();
45
46 regchansearchfunc("name", 1, cs_name);
47 regchansearchfunc("exists", 0, cs_exists);
48 regchansearchfunc("services", 1, cs_services);
49 regchansearchfunc("nick", 1, cs_nick);
50 regchansearchfunc("size", 1, cs_size);
51 regchansearchfunc("namelen", 1, cs_namelen);
52 regchansearchfunc("modes", 1, cs_modes);
53 regchansearchfunc("topic", 1, cs_topic);
54 regchansearchfunc("oppercent", 1, cs_oppct);
55 regchansearchfunc("uniquehostpct", 1, cs_hostpct);
56 regchansearchfunc("authedpct", 1, cs_authedpct);
57
58 regchansearchdisp("default", cs_describe);
59 regchansearchdisp("topic", cs_desctopic);
60 regchansearchdisp("services", cs_descservices);
61
3e3692bf 62 registercontrolhelpcmd("chansearch",NO_OPER,19,&dochansearch,"Usage: chansearch <criteria>\nSearches for channels with specified criteria.\nSend chanstats with no arguments for more information.");
c86edd1d
Q
63}
64
65void _fini() {
9fdda8b1
P
66 unregchansearchfunc("name", cs_name);
67 unregchansearchfunc("exists", cs_exists);
68 unregchansearchfunc("services", cs_services);
69 unregchansearchfunc("nick", cs_nick);
70 unregchansearchfunc("size", cs_size);
71 unregchansearchfunc("namelen", cs_namelen);
72 unregchansearchfunc("modes", cs_modes);
73 unregchansearchfunc("topic", cs_topic);
74 unregchansearchfunc("oppercent", cs_oppct);
75 unregchansearchfunc("uniquehostpct", cs_hostpct);
76 unregchansearchfunc("authedpct", cs_authedpct);
77
78 unregchansearchdisp("default", cs_describe);
79 unregchansearchdisp("topic", cs_desctopic);
80 unregchansearchdisp("services", cs_descservices);
81
c86edd1d
Q
82 deregistercontrolcmd("chansearch",&dochansearch);
83 destroycommandtree(searchfilters);
84 destroycommandtree(outputfilters);
85}
86
87void regchansearchfunc(const char *name, int args, CommandHandler handler) {
88 addcommandtotree(searchfilters, name, 0, args, handler);
89}
90
91void unregchansearchfunc(const char *name, CommandHandler handler) {
92 deletecommandfromtree(searchfilters, name, handler);
93}
94
95void regchansearchdisp(const char *name, DisplayFunc handler) {
96 addcommandtotree(outputfilters, name, 0, 0, (CommandHandler)handler);
97}
98
99void unregchansearchdisp(const char *name, DisplayFunc handler) {
100 deletecommandfromtree(outputfilters, name, (CommandHandler)handler);
101}
102
103int dochansearch(void *source, int cargc, char **cargv) {
104 nick *sender=(nick *)source;
105 filter terms[MAXTERMS];
106 int i,j;
107 int numterms=0;
108 Command *mc;
109 chanindex *cip;
110 int matched=0;
111 int offset;
112 int res;
113 char *ch;
114 int limit=MAXMATCHES;
115 DisplayFunc df=cs_describe;
116
117 i=0;
118 if (cargc>0 && cargv[0][0]=='-') {
119 /* We have options, parse them */
120 i++;
121 for (ch=cargv[0];*ch;ch++) {
122 switch(*ch) {
123 case 'l': limit=strtol(cargv[i++],NULL,10);
124 break;
125
126 case 'd': if ((mc=findcommandintree(outputfilters,cargv[i],1))==NULL) {
127 controlreply(sender, "Invalid display format %s",cargv[i]);
128 return CMD_ERROR;
129 } else {
130 df=(DisplayFunc)(mc->handler);
131 }
132 i++;
133 break;
134 }
135 if (i>=cargc) {
136 break;
137 }
138 }
139 }
140
141 if (i>=cargc) {
142 Command *cmdlist[100];
4ad1cf7a 143 int i,n,bufpos,j;
c86edd1d
Q
144 char buf[200];
145
146 controlreply(sender,"Usage: chansearch [options] (search terms)");
147 controlreply(sender," Note that all options must be bundled together (-ld 5 default not -l 5 -d default)");
148 controlreply(sender," Current valid options:");
149 controlreply(sender," -l <number> Set maximum number of results to return");
150 controlreply(sender," -d <format> Set output display format");
151
152 /* Get list of display formats */
153
154 bufpos=0;
155
156 n=getcommandlist(outputfilters,cmdlist,100);
157 for (i=0;i<n;i++) {
158 for (j=0;j<cmdlist[i]->command->length;j++) {
159 buf[bufpos++]=ToLower(cmdlist[i]->command->content[j]);
160 }
161 if (i<(n-1)) {
162 bufpos+=sprintf(&buf[bufpos],", ");
163 }
164 if (bufpos>180) {
165 bufpos+=sprintf(&buf[bufpos]," ...");
166 break;
167 }
168 }
169
170 buf[bufpos]='\0';
171
172 controlreply(sender," Valid display formats: %s",buf);
173
174 /* Get list of search terms */
175
176 bufpos=0;
177
178 n=getcommandlist(searchfilters,cmdlist,100);
179 for (i=0;i<n;i++) {
180 for (j=0;j<cmdlist[i]->command->length;j++) {
181 buf[bufpos++]=ToLower(cmdlist[i]->command->content[j]);
182 }
183 if (i<(n-1)) {
184 bufpos+=sprintf(&buf[bufpos],", ");
185 }
186 if (bufpos>180) {
187 bufpos+=sprintf(&buf[bufpos]," ...");
188 break;
189 }
190 }
191 buf[bufpos]='\0';
192
193 controlreply(sender," Valid search terms: %s",buf);
194 controlreply(sender," Terms can be inverted with !");
195 return CMD_OK;
196 }
197
198 for (;i<cargc;) {
199 if (cargv[i][0]=='!') {
200 offset=1;
201 terms[numterms].invert=1;
202 } else {
203 offset=0;
204 terms[numterms].invert=0;
205 }
206 terms[numterms].mallocarg=0;
207
208 /* Do some sanity checking. Note that the "goto"s allows memory
209 * to be freed if successful filters have already allocated some */
210 if ((mc=findcommandintree(searchfilters, &cargv[i][offset], 1))==NULL) {
211 controlreply(sender,"Unrecognised search term: %s",cargv[i]);
212 goto abort;
213 }
214 if ((cargc-i-1) < mc->maxparams) {
215 controlreply(sender,"Not enough arguments supplied for %s",cargv[i]);
216 goto abort;
217 }
218
219 /* Call the setup function. This should fill in the function and argument in the structure */
220 if ((mc->handler)((void *)&terms[numterms],mc->maxparams,cargv+i+1)) {
221 controlreply(sender,"Error setting up filter: %s",cargv[i]);
222 goto abort;
223 }
224 i+=(mc->maxparams+1);
225 numterms++;
226
227 if (numterms==MAXTERMS)
228 break;
229 }
230
231 controlreply(sender,"The following channels match your criteria:");
232
233 for(i=0;i<CHANNELHASHSIZE;i++) {
234 for(cip=chantable[i];cip;cip=cip->next) {
235 for(j=0;j<numterms;j++) {
236 res=(terms[j].sf)(cip,terms[j].arg);
237 if (res==0 && terms[j].invert)
238 break;
239 if (res==1 && !terms[j].invert)
240 break;
241 }
242 if (j==numterms) {
243 if (matched == limit) {
244 controlreply(sender,"--- More than %d matches found, truncating list.",limit);
245 }
246 if (matched < limit) {
247 (df)(sender, cip);
248 }
249 matched++;
250 }
251 }
252 }
253
254 if (matched==0) {
255 controlreply(sender,"--- No matches found.");
256 } else {
257 controlreply(sender,"--- End of list: %d match(es).",matched);
258 }
259
260 /* GOTO target: here we just free the args if any were malloc()d */
261abort:
262 for (i=0;i<numterms;i++) {
263 if (terms[i].mallocarg) {
264 free(terms[i].arg);
265 }
266 }
267
268 return CMD_OK;
269}
270
271
272/* Name search execute function: call match2strings */
273int cs_nameexe(chanindex *cip, void *arg) {
274 char *pattern=(char *)arg;
275
276 return !match2strings(pattern, cip->name->content);
277}
278
279/* Name search setup function: fill in the struct */
280
281int cs_name(void *source, int cargc, char **cargv) {
282 filter *thefilter=(filter *)source;
283
284 thefilter->sf=cs_nameexe;
285 thefilter->arg=(char *)cargv[0];
286
287 return 0;
288}
289
290int cs_topicexe(chanindex *cip, void *arg) {
291 char *pattern=(char *)arg;
292
293 return ((cip->channel==NULL) ||
294 (cip->channel->topic==NULL) ||
295 (!match2strings(pattern, cip->channel->topic->content)));
296}
297
298int cs_topic(void *source, int cargc, char **cargv) {
299 filter *thefilter=(filter *)source;
300
301 thefilter->sf=cs_topicexe;
302 thefilter->arg=(void *)cargv[0];
303
304 return 0;
305}
306
307/* services - matches if the specified number of services are in the channel */
308
309int cs_servicesexe(chanindex *cip, void *arg) {
310 nick *np;
311 int i;
c3db6f7e 312 long count=(long)arg;
c86edd1d
Q
313
314 if (cip->channel==NULL)
315 return 1;
316
317 for(i=0;i<cip->channel->users->hashsize;i++) {
318 if (cip->channel->users->content[i]==nouser)
319 continue;
320
321 if (!(np=getnickbynumeric(cip->channel->users->content[i])))
322 continue;
323
324 if (IsService(np) && !--count)
325 return 0;
326 }
327
328 return 1;
329}
330
331int cs_services(void *source, int cargc, char **cargv) {
332 filter *thefilter=(filter *)source;
333
334 thefilter->sf=cs_servicesexe;
335 thefilter->arg=(void *)strtoul(cargv[0],NULL,10);
336
337 return 0;
338}
339
340int cs_existsexe(chanindex *cip, void *arg) {
341 return (cip->channel==NULL);
342}
343
344int cs_exists(void *source, int cargc, char **cargv) {
345 filter *thefilter=(filter *)source;
346
347 thefilter->sf=cs_existsexe;
348 thefilter->arg=NULL;
349
350 return 0;
351}
352
353int cs_nickexe(chanindex *cip, void *arg) {
354 nick *np=(nick *)arg;
355
356 return ((cip->channel==NULL) ||
357 (getnumerichandlefromchanhash(cip->channel->users, np->numeric)==NULL));
358}
359
360int cs_nick(void *source, int cargc, char **cargv) {
361 filter *thefilter=(filter *)source;
362 nick *np;
363
364 if ((np=getnickbynick(cargv[0]))==NULL) {
365 return 1;
366 }
367
368 thefilter->sf=cs_nickexe;
369 thefilter->arg=(void *)np;
370
371 return 0;
372}
373
374int cs_sizeexe(chanindex *cip, void *arg) {
c3db6f7e 375 long lim=(long)arg;
c86edd1d
Q
376
377 return ((cip->channel==NULL) ||
378 (cip->channel->users->totalusers < lim));
379}
380
381int cs_size(void *source, int cargc, char **cargv) {
382 filter *thefilter=(filter *)source;
383
384 thefilter->sf=cs_sizeexe;
385 thefilter->arg=(void *)strtol(cargv[0],NULL,10);
386
387 return 0;
388}
389
390int cs_namelenexe(chanindex *cip, void *arg) {
c3db6f7e 391 long lim=(long)arg;
c86edd1d
Q
392
393 return (cip->name->length < lim);
394}
395
396int cs_namelen(void *source, int cargc, char **cargv) {
397 filter *thefilter=(filter *)source;
398
399 thefilter->sf=cs_namelenexe;
400 thefilter->arg=(void *)strtol(cargv[0],NULL,10);
401
402 return 0;
403}
404
405int cs_oppctexe(chanindex *cip, void *arg) {
c3db6f7e 406 long percent=(long)arg;
c86edd1d
Q
407 int nonop;
408 int i;
409
410 if (cip->channel==NULL) {
411 return 1;
412 }
413
414 nonop=(cip->channel->users->totalusers)-((cip->channel->users->totalusers*percent)/100);
415
416 for (i=0;i<cip->channel->users->hashsize;i++) {
417 if (cip->channel->users->content[i]!=nouser) {
418 if (!(cip->channel->users->content[i] & CUMODE_OP)) {
419 if (!nonop--) {
420 return 1;
421 }
422 }
423 }
424 }
425
426 return 0;
427}
428
429int cs_oppct(void *source, int cargc, char **cargv) {
430 filter *thefilter=(filter *)source;
431
432 thefilter->sf=cs_oppctexe;
433 thefilter->arg=(void *)strtol(cargv[0],NULL,10);
434
435 return 0;
436}
437
438int cs_hostpctexe(chanindex *cip, void *arg) {
c3db6f7e 439 long percent=(long)arg;
c86edd1d
Q
440 int hostreq;
441 int i;
442 unsigned int marker;
443 nick *np;
444
445 if (cip->channel==NULL) {
446 return 1;
447 }
448
449 marker=nexthostmarker();
450
451 hostreq=(cip->channel->users->totalusers * percent) / 100;
452
453 for (i=0;i<cip->channel->users->hashsize;i++) {
454 if (cip->channel->users->content[i]==nouser)
455 continue;
456
457 if (!(np=getnickbynumeric(cip->channel->users->content[i])))
458 continue;
459
460 if (np->host->marker!=marker) {
461 /* new unique host */
462 if (--hostreq <= 0) {
463 return 0;
464 }
465 np->host->marker=marker;
466 }
467 }
468
469 return 1;
470}
471
472int cs_hostpct(void *source, int cargc, char **cargv) {
473 filter *thefilter=source;
474
475 thefilter->sf=cs_hostpctexe;
476 thefilter->arg=(void *)strtol(cargv[0],NULL,10);
477
478 return 0;
479}
480
481int cs_authedpctexe(chanindex *cip, void *arg) {
482 int i;
483 int j=0;
484 nick *np;
c3db6f7e 485 long pct=(long)arg;
c86edd1d
Q
486
487 if (!cip->channel)
488 return 1;
489
490 for (i=0;i<cip->channel->users->hashsize;i++) {
491 if (cip->channel->users->content[i]==nouser)
492 continue;
493
494 if ((np=getnickbynumeric(cip->channel->users->content[i])) && IsAccount(np))
495 j++;
496 }
497
498 if (((j * 100) / cip->channel->users->totalusers) >= pct)
499 return 0;
500 else
501 return 1;
502}
503
504int cs_authedpct(void *source, int cargc, char **cargv) {
505 filter *thefilter=source;
506
507 thefilter->sf=cs_authedpctexe;
508 thefilter->arg=(void *)strtol(cargv[0],NULL,10);
509
510 return 0;
511}
512
513int cs_modesexe(chanindex *cip, void *arg) {
514 struct modeflags *mf=(struct modeflags *)arg;
515
516 return ((cip->channel == NULL) ||
517 ((cip->channel->flags & mf->setflags) != mf->setflags) ||
518 (cip->channel->flags & mf->clearflags));
519}
520
521int cs_modes(void *source, int cargc, char **cargv) {
522 filter *thefilter=(filter *)source;
523 struct modeflags *mf;
524 flag_t flags=0;
525
526 mf=(void *)malloc(sizeof(struct modeflags));
527
528 setflags(&flags, CHANMODE_ALL, cargv[0], cmodeflags, REJECT_NONE);
529
530 mf->setflags=flags;
531
532 flags=CHANMODE_ALL;
533 setflags(&flags, CHANMODE_ALL, cargv[0], cmodeflags, REJECT_NONE);
534
535 mf->clearflags=~flags;
536
537 thefilter->sf=cs_modesexe;
538 thefilter->arg=(void *)mf;
539 thefilter->mallocarg=1;
540
541 return 0;
542}
543
544void cs_describe(nick *sender, chanindex *cip) {
545 int i;
546 int op,voice,peon;
547 int oper,service,hosts;
548 nick *np;
549 chanuserhash *cuhp;
550 unsigned int marker;
551
552 op=voice=peon=oper=service=hosts=0;
553 marker=nexthostmarker();
554
555 if (cip->channel==NULL) {
556 controlreply(sender,"[ Channel currently empty ] %s",cip->name->content);
557 } else {
558 cuhp=cip->channel->users;
559 for (i=0;i<cuhp->hashsize;i++) {
560 if (cuhp->content[i]!=nouser) {
561 if (cuhp->content[i]&CUMODE_OP) {
562 op++;
563 } else if (cuhp->content[i]&CUMODE_VOICE) {
564 voice++;
565 } else {
566 peon++;
567 }
568 if ((np=getnickbynumeric(cuhp->content[i]&CU_NUMERICMASK))!=NULL) {
569 if (IsOper(np)) {
570 oper++;
571 }
572 if (IsService(np)) {
573 service++;
574 }
575 if (np->host->marker!=marker) {
576 np->host->marker=marker;
577 hosts++;
578 }
579 }
580 }
581 }
582 controlreply(sender,"[ %4dU %4d@ %4d+ %4d %4d* %4dk %4dH ] %s (%s)",cuhp->totalusers,op,voice,peon,oper,service,hosts,
583 cip->name->content, printflags(cip->channel->flags, cmodeflags));
584 }
585}
586
587void cs_desctopic(nick *sender, chanindex *cip) {
588 if (cip->channel==NULL) {
589 controlreply(sender,"[ empty ] %-30s",cip->name->content);
590 } else {
591 controlreply(sender,"[%4u users] %s (%s)",cip->channel->users->totalusers,cip->name->content,cip->channel->topic?cip->channel->topic->content:"no topic");
592 }
593}
594
595void cs_descservices(nick *sender, chanindex *cip) {
596 int i;
597 chanuserhash *cuhp;
598 char servlist[300];
599 int slpos=0,slfull=0;
600 nick *np;
601 int servs=0;
602
603 if (cip->channel==NULL) {
604 controlreply(sender,"%-30s empty",cip->name->content);
605 } else {
606 cuhp=cip->channel->users;
607 for (i=0;i<cuhp->hashsize;i++) {
608 if (cuhp->content[i]!=nouser) {
609 if ((np=getnickbynumeric(cuhp->content[i]&CU_NUMERICMASK))) {
610 if (IsService(np)) {
611 servs++;
612 if (!slfull) {
613 if (slpos) {
614 slpos+=sprintf(&servlist[slpos],", ");
615 }
616 slpos+=sprintf(&servlist[slpos],"%s",np->nick);
617 if (slpos>280) {
618 sprintf(&servlist[slpos],", ...");
619 slfull=1;
620 }
621 }
622 }
623 }
624 }
625 }
626
627 controlreply(sender,"%-30s %5d user%c %2d service%c %s%s%s",cip->name->content,cuhp->totalusers,
628 cuhp->totalusers>1?'s':' ',servs,(servs==1)?' ':'s',servs?"(":"",slpos?servlist:"",servs?")":"");
629 }
630}
631