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