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