]> jfr.im git - irc/quakenet/newserv.git/blob - chansearch/chansearch.c
Began manual merge.
[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
9 #include <stdio.h>
10
11 #define MAXTERMS 10
12 #define MAXMATCHES 500
13
14 CommandTree *searchfilters;
15 CommandTree *outputfilters;
16
17 typedef struct modeflags {
18 flag_t setflags;
19 flag_t clearflags;
20 } modeflags;
21
22 void cs_describe(nick *sender, chanindex *cip);
23 void cs_desctopic(nick *sender, chanindex *cip);
24 void cs_descservices(nick *sender, chanindex *cip);
25 int cs_exists(void *chan, int cargc, char **cargv);
26 int cs_services(void *chan, int cargc, char **cargv);
27 int cs_nick(void *chan, int cargc, char **cargv);
28 int cs_size(void *chan, int cargc, char **cargv);
29 int cs_namelen(void *chan, int cargc, char **cargv);
30 int cs_modes(void *chan, int cargc, char **cargv);
31 int cs_name(void *chan, int cargc, char **cargv);
32 int cs_topic(void *source, int cargc, char **cargv);
33 int cs_oppct(void *source, int cargc, char **cargv);
34 int cs_hostpct(void *source, int cargc, char **cargv);
35 int cs_authedpct(void *source, int cargc, char **cargv);
36
37 int dochansearch(void *source, int cargc, char **cargv);
38
39 void _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
59 registercontrolhelpcmd("chansearch",NO_OPER,19,&dochansearch,"Usage: chansearch <criteria>\nSearches for channels with specified criteria.\nSend chanstats with no arguments for more information.");
60 }
61
62 void _fini() {
63 deregistercontrolcmd("chansearch",&dochansearch);
64 destroycommandtree(searchfilters);
65 destroycommandtree(outputfilters);
66 }
67
68 void regchansearchfunc(const char *name, int args, CommandHandler handler) {
69 addcommandtotree(searchfilters, name, 0, args, handler);
70 }
71
72 void unregchansearchfunc(const char *name, CommandHandler handler) {
73 deletecommandfromtree(searchfilters, name, handler);
74 }
75
76 void regchansearchdisp(const char *name, DisplayFunc handler) {
77 addcommandtotree(outputfilters, name, 0, 0, (CommandHandler)handler);
78 }
79
80 void unregchansearchdisp(const char *name, DisplayFunc handler) {
81 deletecommandfromtree(outputfilters, name, (CommandHandler)handler);
82 }
83
84 int 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 */
242 abort:
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 */
254 int 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
262 int 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
271 int 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
279 int 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
290 int 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
312 int 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
321 int cs_existsexe(chanindex *cip, void *arg) {
322 return (cip->channel==NULL);
323 }
324
325 int 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
334 int 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
341 int 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
355 int 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
362 int 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
371 int cs_namelenexe(chanindex *cip, void *arg) {
372 int lim=(int)arg;
373
374 return (cip->name->length < lim);
375 }
376
377 int 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
386 int 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
410 int 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
419 int 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
453 int 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
462 int 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
485 int 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
494 int 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
502 int 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
525 void 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
568 void 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
576 void 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