]> jfr.im git - irc/quakenet/newserv.git/blob - chansearch/chansearch.c
Sigh
[irc/quakenet/newserv.git] / chansearch / chansearch.c
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 #include "../lib/version.h"
9
10 #include <stdio.h>
11
12 MODULE_VERSION("$Id: chansearch.c 663 2006-05-16 17:27:36Z newserv $")
13
14 #define MAXTERMS 10
15 #define MAXMATCHES 500
16
17 CommandTree *searchfilters;
18 CommandTree *outputfilters;
19
20 typedef struct modeflags {
21 flag_t setflags;
22 flag_t clearflags;
23 } modeflags;
24
25 void cs_describe(nick *sender, chanindex *cip);
26 void cs_desctopic(nick *sender, chanindex *cip);
27 void cs_descservices(nick *sender, chanindex *cip);
28 int cs_exists(void *chan, int cargc, char **cargv);
29 int cs_services(void *chan, int cargc, char **cargv);
30 int cs_nick(void *chan, int cargc, char **cargv);
31 int cs_size(void *chan, int cargc, char **cargv);
32 int cs_namelen(void *chan, int cargc, char **cargv);
33 int cs_modes(void *chan, int cargc, char **cargv);
34 int cs_name(void *chan, int cargc, char **cargv);
35 int cs_topic(void *source, int cargc, char **cargv);
36 int cs_oppct(void *source, int cargc, char **cargv);
37 int cs_hostpct(void *source, int cargc, char **cargv);
38 int cs_authedpct(void *source, int cargc, char **cargv);
39
40 int dochansearch(void *source, int cargc, char **cargv);
41
42 void _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
62 registercontrolhelpcmd("chansearch",NO_OPER,19,&dochansearch,"Usage: chansearch <criteria>\nSearches for channels with specified criteria.\nSend chanstats with no arguments for more information.");
63 }
64
65 void _fini() {
66 deregistercontrolcmd("chansearch",&dochansearch);
67 destroycommandtree(searchfilters);
68 destroycommandtree(outputfilters);
69 }
70
71 void regchansearchfunc(const char *name, int args, CommandHandler handler) {
72 addcommandtotree(searchfilters, name, 0, args, handler);
73 }
74
75 void unregchansearchfunc(const char *name, CommandHandler handler) {
76 deletecommandfromtree(searchfilters, name, handler);
77 }
78
79 void regchansearchdisp(const char *name, DisplayFunc handler) {
80 addcommandtotree(outputfilters, name, 0, 0, (CommandHandler)handler);
81 }
82
83 void unregchansearchdisp(const char *name, DisplayFunc handler) {
84 deletecommandfromtree(outputfilters, name, (CommandHandler)handler);
85 }
86
87 int 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];
127 int i,n,bufpos,j;
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 */
245 abort:
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 */
257 int 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
265 int 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
274 int 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
282 int 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
293 int 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
315 int 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
324 int cs_existsexe(chanindex *cip, void *arg) {
325 return (cip->channel==NULL);
326 }
327
328 int 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
337 int 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
344 int 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
358 int 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
365 int 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
374 int cs_namelenexe(chanindex *cip, void *arg) {
375 int lim=(int)arg;
376
377 return (cip->name->length < lim);
378 }
379
380 int 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
389 int 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
413 int 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
422 int 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
456 int 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
465 int 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
488 int 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
497 int 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
505 int 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
528 void 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
571 void 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
579 void 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