5 #include "../irc/irc_config.h"
6 #include "../lib/irc_string.h"
7 #include "../parser/parser.h"
8 #include "../control/control.h"
9 #include "../lib/splitline.h"
10 #include "../lib/version.h"
11 #include "../lib/stringbuf.h"
15 CommandTree
*searchTree
;
16 CommandTree
*chanOutputTree
;
17 CommandTree
*nickOutputTree
;
18 CommandTree
*userOutputTree
;
20 int do_nicksearch(void *source
, int cargc
, char **cargv
);
21 int do_chansearch(void *source
, int cargc
, char **cargv
);
22 int do_usersearch(void *source
, int cargc
, char **cargv
);
24 void printnick_channels(searchCtx
*, nick
*, nick
*);
25 void printchannel(searchCtx
*, nick
*, chanindex
*);
26 void printchannel_topic(searchCtx
*, nick
*, chanindex
*);
27 void printchannel_services(searchCtx
*, nick
*, chanindex
*);
29 UserDisplayFunc defaultuserfn
= printuser
;
30 NickDisplayFunc defaultnickfn
= printnick
;
31 ChanDisplayFunc defaultchanfn
= printchannel
;
33 void registersearchterm(char *term
, parseFunc parsefunc
);
34 void deregistersearchterm(char *term
, parseFunc parsefunc
);
36 void regchandisp(const char *name
, ChanDisplayFunc handler
) {
37 addcommandtotree(chanOutputTree
, name
, 0, 0, (CommandHandler
)handler
);
40 void unregchandisp(const char *name
, ChanDisplayFunc handler
) {
41 deletecommandfromtree(chanOutputTree
, name
, (CommandHandler
)handler
);
44 void regnickdisp(const char *name
, NickDisplayFunc handler
) {
45 addcommandtotree(nickOutputTree
, name
, 0, 0, (CommandHandler
)handler
);
48 void unregnickdisp(const char *name
, NickDisplayFunc handler
) {
49 deletecommandfromtree(nickOutputTree
, name
, (CommandHandler
)handler
);
52 void reguserdisp(const char *name
, UserDisplayFunc handler
) {
53 addcommandtotree(userOutputTree
, name
, 0, 0, (CommandHandler
)handler
);
56 void unreguserdisp(const char *name
, UserDisplayFunc handler
) {
57 deletecommandfromtree(userOutputTree
, name
, (CommandHandler
)handler
);
60 const char *parseError
;
61 /* used for *_free functions that need to warn users of certain things
62 i.e. hitting too many users in a (kill) or (gline) */
66 searchTree
=newcommandtree();
67 chanOutputTree
=newcommandtree();
68 nickOutputTree
=newcommandtree();
69 userOutputTree
=newcommandtree();
71 /* Boolean operations */
72 registersearchterm("and",and_parse
);
73 registersearchterm("not",not_parse
);
74 registersearchterm("or",or_parse
);
76 registersearchterm("eq",eq_parse
);
78 registersearchterm("lt",lt_parse
);
79 registersearchterm("gt",gt_parse
);
81 /* String operations */
82 registersearchterm("match",match_parse
);
83 registersearchterm("regex",regex_parse
);
84 registersearchterm("length",length_parse
);
86 /* Nickname operations */
87 registersearchterm("hostmask",hostmask_parse
);
88 registersearchterm("realname",realname_parse
);
89 registersearchterm("authname",authname_parse
);
90 registersearchterm("authts",authts_parse
);
91 registersearchterm("ident",ident_parse
);
92 registersearchterm("host",host_parse
);
93 registersearchterm("channel",channel_parse
);
94 registersearchterm("timestamp",timestamp_parse
);
95 registersearchterm("country",country_parse
);
96 registersearchterm("ip",ip_parse
);
97 registersearchterm("channels",channels_parse
);
98 registersearchterm("server",server_parse
);
99 registersearchterm("authid",authid_parse
);
101 /* Channel operations */
102 registersearchterm("exists",exists_parse
);
103 registersearchterm("services",services_parse
);
104 registersearchterm("size",size_parse
);
105 registersearchterm("name",name_parse
);
106 registersearchterm("topic",topic_parse
);
107 registersearchterm("oppct",oppct_parse
);
108 registersearchterm("uniquehostpct",hostpct_parse
);
109 registersearchterm("authedpct",authedpct_parse
);
110 registersearchterm("kick",kick_parse
);
112 /* Nickname / channel operations */
113 registersearchterm("modes",modes_parse
);
114 registersearchterm("nick",nick_parse
);
116 /* Kill / gline parameters */
117 registersearchterm("kill",kill_parse
);
118 registersearchterm("gline",gline_parse
);
120 /* Notice functionality */
121 registersearchterm("notice",notice_parse
);
123 /* Nick output filters */
124 regnickdisp("default",printnick
);
125 regnickdisp("channels",printnick_channels
);
127 /* Channel output filters */
128 regchandisp("default",printchannel
);
129 regchandisp("topic",printchannel_topic
);
130 regchandisp("services",printchannel_services
);
132 /* Nick output filters */
133 reguserdisp("default",printuser
);
135 registercontrolhelpcmd("nicksearch",NO_OPER
,4,do_nicksearch
, "Usage: nicksearch <criteria>\nSearches for nicknames with the given criteria.");
136 registercontrolhelpcmd("chansearch",NO_OPER
,4,do_chansearch
, "Usage: chansearch <criteria>\nSearches for channels with the given criteria.");
137 registercontrolhelpcmd("usersearch",NO_OPER
,4,do_usersearch
, "Usage: usersearch <criteria>\nSearches for users with the given criteria.");
141 destroycommandtree(searchTree
);
142 destroycommandtree(chanOutputTree
);
143 destroycommandtree(nickOutputTree
);
144 destroycommandtree(userOutputTree
);
145 deregistercontrolcmd("nicksearch", do_nicksearch
);
146 deregistercontrolcmd("chansearch", do_chansearch
);
147 deregistercontrolcmd("usersearch", do_usersearch
);
150 void registersearchterm(char *term
, parseFunc parsefunc
) {
151 addcommandtotree(searchTree
, term
, 0, 0, (CommandHandler
) parsefunc
);
154 void deregistersearchterm(char *term
, parseFunc parsefunc
) {
155 deletecommandfromtree(searchTree
, term
, (CommandHandler
) parsefunc
);
158 static void controlwallwrapper(int level
, char *format
, ...) {
162 va_start(ap
, format
);
163 vsnprintf(buf
, sizeof(buf
), format
, ap
);
164 controlwall(NO_OPER
, level
, "%s", buf
);
168 static int parseopts(int cargc
, char **cargv
, int *arg
, int *limit
, void **display
, CommandTree
*tree
, replyFunc reply
, void *sender
) {
172 if (*cargv
[0] == '-') {
176 for (ch
=cargv
[0]+1;*ch
;ch
++) {
180 reply(sender
,"Error: -l switch requires an argument");
183 *limit
=strtoul(cargv
[(*arg
)++],NULL
,10);
188 reply(sender
,"Error: -d switch requires an argument");
191 cmd
=findcommandintree(tree
, cargv
[*arg
], 1);
193 reply(sender
,"Error: unknown output format %s",cargv
[*arg
]);
196 *display
=(void *)cmd
->handler
;
201 reply(sender
,"Unrecognised flag -%c.",*ch
);
209 int do_nicksearch_real(replyFunc reply
, wallFunc wall
, void *source
, int cargc
, char **cargv
) {
210 nick
*sender
= senderNSExtern
= source
;
211 struct searchNode
*search
;
214 NickDisplayFunc display
=defaultnickfn
;
221 ret
= parseopts(cargc
, cargv
, &arg
, &limit
, (void **)&display
, nickOutputTree
, reply
, sender
);
226 reply(sender
,"No search terms - aborting.");
231 rejoinline(cargv
[arg
],cargc
-arg
);
234 ctx
.parser
= search_parse
;
238 if (!(search
= ctx
.parser(&ctx
, SEARCHTYPE_NICK
, cargv
[arg
]))) {
239 reply(sender
,"Parse error: %s",parseError
);
243 nicksearch_exe(search
, &ctx
, sender
, display
, limit
);
245 (search
->free
)(&ctx
, search
);
250 int do_nicksearch(void *source
, int cargc
, char **cargv
) {
251 return do_nicksearch_real(controlreply
, controlwallwrapper
, source
, cargc
, cargv
);
254 void nicksearch_exe(struct searchNode
*search
, searchCtx
*ctx
, nick
*sender
, NickDisplayFunc display
, int limit
) {
257 unsigned int cmarker
;
258 unsigned int tchans
=0,uchans
=0;
262 /* Get a marker value to mark "seen" channels for unique count */
263 cmarker
=nextchanmarker();
265 /* The top-level node needs to return a BOOL */
266 search
=coerceNode(ctx
, search
, RETURNTYPE_BOOL
);
268 for (i
=0;i
<NICKHASHSIZE
;i
++) {
269 for (np
=nicktable
[i
];np
;np
=np
->next
) {
270 if ((search
->exe
)(ctx
, search
, np
)) {
271 /* Add total channels */
272 tchans
+= np
->channels
->cursi
;
274 /* Check channels for uniqueness */
275 cs
=(channel
**)np
->channels
->content
;
276 for (j
=0;j
<np
->channels
->cursi
;j
++) {
277 if (cs
[j
]->index
->marker
!= cmarker
) {
278 cs
[j
]->index
->marker
=cmarker
;
284 display(ctx
, sender
, np
);
287 ctx
->reply(sender
, "--- More than %d matches, skipping the rest",limit
);
293 ctx
->reply(sender
,"--- End of list: %d matches; users were on %u channels (%u unique, %.1f average clones)",
294 matches
, tchans
, uchans
, (float)tchans
/uchans
);
297 int do_chansearch_real(replyFunc reply
, wallFunc wall
, void *source
, int cargc
, char **cargv
) {
298 nick
*sender
= senderNSExtern
= source
;
299 struct searchNode
*search
;
302 ChanDisplayFunc display
=defaultchanfn
;
309 ret
= parseopts(cargc
, cargv
, &arg
, &limit
, (void **)&display
, chanOutputTree
, reply
, sender
);
314 reply(sender
,"No search terms - aborting.");
319 rejoinline(cargv
[arg
],cargc
-arg
);
322 ctx
.parser
= search_parse
;
326 if (!(search
= ctx
.parser(&ctx
, SEARCHTYPE_CHANNEL
, cargv
[arg
]))) {
327 reply(sender
,"Parse error: %s",parseError
);
331 chansearch_exe(search
, &ctx
, sender
, display
, limit
);
333 (search
->free
)(&ctx
, search
);
338 int do_chansearch(void *source
, int cargc
, char **cargv
) {
339 return do_chansearch_real(controlreply
, controlwallwrapper
, source
, cargc
, cargv
);
342 void chansearch_exe(struct searchNode
*search
, searchCtx
*ctx
, nick
*sender
, ChanDisplayFunc display
, int limit
) {
347 search
=coerceNode(ctx
, search
, RETURNTYPE_BOOL
);
349 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
350 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
351 if ((search
->exe
)(ctx
, search
, cip
)) {
353 display(ctx
, sender
, cip
);
355 ctx
->reply(sender
, "--- More than %d matches, skipping the rest",limit
);
361 ctx
->reply(sender
,"--- End of list: %d matches", matches
);
364 int do_usersearch_real(replyFunc reply
, wallFunc wall
, void *source
, int cargc
, char **cargv
) {
365 nick
*sender
= senderNSExtern
= source
;
366 struct searchNode
*search
;
369 UserDisplayFunc display
=defaultuserfn
;
376 ret
= parseopts(cargc
, cargv
, &arg
, &limit
, (void **)&display
, userOutputTree
, reply
, sender
);
381 reply(sender
,"No search terms - aborting.");
386 rejoinline(cargv
[arg
],cargc
-arg
);
389 ctx
.parser
= search_parse
;
393 if (!(search
= ctx
.parser(&ctx
, SEARCHTYPE_USER
, cargv
[arg
]))) {
394 reply(sender
,"Parse error: %s",parseError
);
398 usersearch_exe(search
, &ctx
, sender
, display
, limit
);
400 (search
->free
)(&ctx
, search
);
405 int do_usersearch(void *source
, int cargc
, char **cargv
) {
406 return do_usersearch_real(controlreply
, controlwallwrapper
, source
, cargc
, cargv
);
409 void usersearch_exe(struct searchNode
*search
, searchCtx
*ctx
, nick
*sender
, UserDisplayFunc display
, int limit
) {
414 search
=coerceNode(ctx
, search
, RETURNTYPE_BOOL
);
416 for (i
=0;i
<AUTHNAMEHASHSIZE
;i
++) {
417 for (aup
=authnametable
[i
];aup
;aup
=aup
->next
) {
418 if ((search
->exe
)(ctx
, search
, aup
)) {
420 display(ctx
, sender
, aup
);
422 ctx
->reply(sender
, "--- More than %d matches, skipping the rest",limit
);
428 ctx
->reply(sender
,"--- End of list: %d matches", matches
);
432 struct searchNode
*child
;
439 /* Free a coerce node */
440 void free_coerce(searchCtx
*ctx
, struct searchNode
*thenode
) {
441 struct coercedata
*cd
=thenode
->localdata
;
443 cd
->child
->free(ctx
, cd
->child
);
444 free(thenode
->localdata
);
448 /* Free a coerce node with a stringbuf allocated */
449 void free_coercestring(searchCtx
*ctx
, struct searchNode
*thenode
) {
450 free(((struct coercedata
*)thenode
->localdata
)->u
.stringbuf
);
451 free_coerce(ctx
, thenode
);
454 /* exe_tostr_null: return the constant string */
455 void *exe_tostr_null(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
456 struct coercedata
*cd
=thenode
->localdata
;
458 return cd
->u
.stringbuf
;
461 /* exe_val_null: return the constant value */
462 void *exe_val_null(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
463 struct coercedata
*cd
=thenode
->localdata
;
465 return (void *)cd
->u
.val
;
468 /* Lots of very dull type conversion functions */
469 void *exe_inttostr(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
470 struct coercedata
*cd
=thenode
->localdata
;
472 sprintf(cd
->u
.stringbuf
, "%lu", (unsigned long)(cd
->child
->exe
)(ctx
, cd
->child
, theinput
));
474 return cd
->u
.stringbuf
;
477 void *exe_booltostr(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
478 struct coercedata
*cd
=thenode
->localdata
;
480 if ((cd
->child
->exe
)(ctx
, cd
->child
, theinput
)) {
481 sprintf(cd
->u
.stringbuf
,"1");
483 cd
->u
.stringbuf
[0]='\0';
486 return cd
->u
.stringbuf
;
489 void *exe_strtoint(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
490 struct coercedata
*cd
=thenode
->localdata
;
492 return (void *)strtoul((cd
->child
->exe
)(ctx
,cd
->child
,theinput
),NULL
,10);
495 void *exe_booltoint(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
496 struct coercedata
*cd
=thenode
->localdata
;
498 /* Don't need to do anything */
499 return (cd
->child
->exe
)(ctx
, cd
->child
, theinput
);
502 void *exe_strtobool(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
503 struct coercedata
*cd
=thenode
->localdata
;
504 char *ch
=(cd
->child
->exe
)(ctx
, cd
->child
, theinput
);
506 if (!ch
|| *ch
=='\0' || (*ch
=='0' && ch
[1]=='\0')) {
513 void *exe_inttobool(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
514 struct coercedata
*cd
=thenode
->localdata
;
516 if ((cd
->child
->exe
)(ctx
, cd
->child
, theinput
)) {
523 struct searchNode
*coerceNode(searchCtx
*ctx
, struct searchNode
*thenode
, int type
) {
524 struct searchNode
*anode
;
525 struct coercedata
*cd
;
527 /* You can't coerce a NULL */
531 /* No effort required to coerce to the same type */
532 if (type
==(thenode
->returntype
& RETURNTYPE_TYPE
))
535 anode
=(struct searchNode
*)malloc(sizeof(struct searchNode
));
536 anode
->localdata
=cd
=(struct coercedata
*)malloc(sizeof(struct coercedata
));
538 anode
->returntype
=type
; /* We'll return what they want, always */
539 anode
->free
=free_coerce
;
542 case RETURNTYPE_STRING
:
543 /* For a string we'll need a buffer */
544 /* A 64-bit number prints out to 20 digits, this leaves some slack */
545 cd
->u
.stringbuf
=malloc(25);
546 anode
->free
=free_coercestring
;
548 switch(thenode
->returntype
& RETURNTYPE_TYPE
) {
551 if (thenode
->returntype
& RETURNTYPE_CONST
) {
552 /* Constant node: sort it out now */
553 sprintf(cd
->u
.stringbuf
, "%lu", (unsigned long)thenode
->exe(ctx
, thenode
, NULL
));
554 anode
->exe
=exe_tostr_null
;
555 anode
->returntype
|= RETURNTYPE_CONST
;
558 anode
->exe
=exe_inttostr
;
562 case RETURNTYPE_BOOL
:
563 if (thenode
->returntype
& RETURNTYPE_CONST
) {
564 /* Constant bool value */
565 if (thenode
->exe(ctx
, thenode
,NULL
)) {
567 sprintf(cd
->u
.stringbuf
, "1");
569 cd
->u
.stringbuf
[0] = '\0';
571 anode
->exe
=exe_tostr_null
;
572 anode
->returntype
|= RETURNTYPE_CONST
;
574 /* Variable bool value */
575 anode
->exe
=exe_booltostr
;
583 switch (thenode
->returntype
& RETURNTYPE_TYPE
) {
584 case RETURNTYPE_STRING
:
585 if (thenode
->returntype
& RETURNTYPE_CONST
) {
586 cd
->u
.val
=strtoul((thenode
->exe
)(ctx
, thenode
, NULL
), NULL
, 10);
587 anode
->exe
=exe_val_null
;
588 anode
->returntype
|= RETURNTYPE_CONST
;
590 anode
->exe
=exe_strtoint
;
595 case RETURNTYPE_BOOL
:
596 if (thenode
->returntype
& RETURNTYPE_CONST
) {
597 if ((thenode
->exe
)(ctx
, thenode
,NULL
))
602 anode
->exe
=exe_val_null
;
603 anode
->returntype
|= RETURNTYPE_CONST
;
605 anode
->exe
=exe_booltoint
;
612 case RETURNTYPE_BOOL
:
614 switch (thenode
->returntype
& RETURNTYPE_TYPE
) {
615 case RETURNTYPE_STRING
:
616 if (thenode
->returntype
& RETURNTYPE_CONST
) {
617 char *rv
=(char *)((thenode
->exe
)(ctx
, thenode
, NULL
));
618 if (!rv
|| *rv
=='\0' || (*rv
=='0' && rv
[1]=='\0'))
623 anode
->exe
=exe_val_null
;
624 anode
->returntype
|= RETURNTYPE_CONST
;
626 anode
->exe
=exe_strtobool
;
632 if (thenode
->returntype
& RETURNTYPE_CONST
) {
633 if ((thenode
->exe
)(ctx
, thenode
,NULL
))
638 anode
->exe
=exe_val_null
;
639 anode
->returntype
|= RETURNTYPE_CONST
;
641 anode
->exe
=exe_inttobool
;
651 /* Literals always return constant strings... */
652 void *literal_exe(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
653 if (thenode
->localdata
)
654 return ((sstring
*)thenode
->localdata
)->content
;
659 void literal_free(searchCtx
*ctx
, struct searchNode
*thenode
) {
660 freesstring(thenode
->localdata
);
665 * Given an input string, return a searchNode.
668 struct searchNode
*search_parse(searchCtx
*ctx
, int type
, char *input
) {
669 /* OK, we need to split the input into chunks on spaces and brackets.. */
670 char *argvector
[100];
675 struct searchNode
*thenode
;
677 /* If it starts with a bracket, it's a function call.. */
679 /* Skip past string */
680 for (ch
=input
;*ch
;ch
++);
681 if (*(ch
-1) != ')') {
682 parseError
= "Bracket mismatch!";
688 /* Split further args */
689 i
=-1; /* i = -1 BoW, 0 = inword, 1 = bracket nest depth */
690 j
=0; /* j = current arg */
694 for (ch
=input
;*ch
;ch
++) {
699 } else if (*ch
!= ' ') {
703 } else if (*ch
=='\"') {
715 } else if (*ch
=='\"') {
717 } else if (*ch
==' ') {
720 if(j
>= (sizeof(argvector
) / sizeof(*argvector
))) {
721 parseError
= "Too many arguments";
729 } else if (*ch
=='\"') {
731 } else if (*ch
=='(') {
733 } else if (*ch
==')') {
740 parseError
= "Bracket mismatch!";
744 if (*(ch
-1) == 0) /* if the last character was a space */
745 j
--; /* remove an argument */
747 if (!(cmd
=findcommandintree(searchTree
,argvector
[0],1))) {
748 parseError
= "Unknown command";
751 return ((parseFunc
)cmd
->handler
)(ctx
, type
, j
, argvector
+1);
756 for (ch
=input
;*ch
;ch
++);
758 if (*(ch
-1) != '\"') {
759 parseError
="Quote mismatch";
768 for (ch
=input
;*ch
;ch
++) {
772 } else if (*ch
=='\\') {
780 if (!(thenode
=(struct searchNode
*)malloc(sizeof(struct searchNode
)))) {
781 parseError
= "malloc: could not allocate memory for this search.";
785 thenode
->localdata
= getsstring(thestring
,512);
786 thenode
->returntype
= RETURNTYPE_CONST
| RETURNTYPE_STRING
;
787 thenode
->exe
= literal_exe
;
788 thenode
->free
= literal_free
;
794 void nssnprintf(char *buf
, size_t size
, const char *format
, nick
*np
) {
807 for(p
=format
;*p
;p
++) {
809 if(!sbaddchar(&b
, *p
))
817 if(!sbaddchar(&b
, *p
))
827 c
= np
->ident
; break;
829 c
= np
->host
->name
->content
; break;
831 snprintf(hostbuf
, sizeof(hostbuf
), "%s", IPtostr(np
->p_ipaddr
));
835 snprintf(hostbuf
, sizeof(hostbuf
), "%s!%s@%s", np
->nick
, np
->ident
, IPtostr(np
->p_ipaddr
));
839 c
= "(bad format specifier)";