]>
Commit | Line | Data |
---|---|---|
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 | ||
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 | ||
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 | ||
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 |