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"
12 #include "../lib/strlfunc.h"
13 #include "../lib/array.h"
14 #include "newsearch.h"
18 CommandTree
*searchCmdTree
;
19 searchList
*globalterms
= NULL
;
21 int do_nicksearch(void *source
, int cargc
, char **cargv
);
22 int do_chansearch(void *source
, int cargc
, char **cargv
);
23 int do_usersearch(void *source
, int cargc
, char **cargv
);
25 void printnick_channels(searchCtx
*, nick
*, nick
*);
26 void printchannel(searchCtx
*, nick
*, chanindex
*);
27 void printchannel_topic(searchCtx
*, nick
*, chanindex
*);
28 void printchannel_services(searchCtx
*, nick
*, chanindex
*);
30 UserDisplayFunc defaultuserfn
= printuser
;
31 NickDisplayFunc defaultnickfn
= printnick
;
32 ChanDisplayFunc defaultchanfn
= printchannel
;
34 searchCmd
*reg_nicksearch
, *reg_chansearch
, *reg_usersearch
;
35 void displaycommandhelp(nick
*, Command
*);
37 searchCmd
*registersearchcommand(char *name
, int level
, CommandHandler cmd
, void *defaultdisplayfunc
) {
41 registercontrolhelpfunccmd(name
, NO_OPER
,4, cmd
, &displaycommandhelp
);
42 acmd
=(struct searchCmd
*)malloc(sizeof(struct searchCmd
));
44 Error("newsearch", ERR_ERROR
, "malloc failed: registersearchcommand");
49 acmd
->name
= getsstring( name
, NSMAX_COMMAND_LEN
);
50 acmd
->outputtree
= newcommandtree();
51 acmd
->searchtree
= newcommandtree();
53 addcommandtotree(searchCmdTree
, name
, 0, 0, (CommandHandler
)acmd
);
55 for (sl
=globalterms
; sl
; sl
=sl
->next
) {
56 addcommandexttotree(acmd
->searchtree
, sl
->name
->content
, 0, 1, (CommandHandler
)sl
->cmd
, sl
->help
);
62 void deregistersearchcommand(searchCmd
*scmd
) {
63 deregistercontrolcmd(scmd
->name
->content
, (CommandHandler
)scmd
->handler
);
64 destroycommandtree(scmd
->outputtree
);
65 destroycommandtree(scmd
->searchtree
);
66 deletecommandfromtree(searchCmdTree
, scmd
->name
->content
, (CommandHandler
) scmd
);
67 freesstring(scmd
->name
);
71 void displaycommandhelp(nick
*np
, Command
*cmd
) {
73 Command
*cmdlist
[100], *acmdlist
[100];
76 n
=getcommandlist(searchCmdTree
,cmdlist
,100);
78 /* note: we may want to only deregister a command if we've already registered it, for now, try de-registering new commands anyway */
79 if ( ((searchCmd
*)cmdlist
[i
]->handler
)->handler
!= cmd
->handler
)
81 acmd
= ((searchCmd
*)(cmdlist
[i
]->handler
));
83 controlreply(np
, "Usage: [flags] <criteria>\n");
84 controlreply(np
, "Flags:\n");
85 controlreply(np
, " -l int : Limit number of rows of results\n");
86 controlreply(np
, " -d string : a valid output format for the results\n");
87 controlreply(np
, " -s subset : ipmask subset of network to search (only for node search)\n");
88 controlreply(np
, " \n");
89 controlreply(np
, "Available Output Formats:\n");
91 m
=getcommandlist(acmd
->outputtree
,acmdlist
,100);
93 if ( controlpermitted( acmdlist
[j
]->level
, np
) ) {
94 char *help
=(char *)acmdlist
[j
]->ext
;
95 if ( help
&& help
[0] != '\0')
96 controlreply(np
, "%-10s: %s\n", acmdlist
[j
]->command
->content
, help
);
98 controlreply(np
, "%s\n", acmdlist
[j
]->command
->content
);
102 controlreply(np
, " \n");
103 controlreply(np
, "Available Global Commands and Operators:\n" );
104 m
=getcommandlist(acmd
->searchtree
,acmdlist
,100);
106 if ( acmdlist
[j
]->maxparams
) {
107 char *help
=(char *)acmdlist
[j
]->ext
;
108 if ( help
&& help
[0] != '\0')
109 controlreply(np
, "%-10s: %s\n", acmdlist
[j
]->command
->content
, help
);
111 controlreply(np
, "%s\n", acmdlist
[j
]->command
->content
);
115 controlreply(np
, " \n");
116 controlreply(np
, "Available Commands and Operators for %s:\n", acmd
->name
->content
);
118 m
=getcommandlist(acmd
->searchtree
,acmdlist
,100);
120 if ( !acmdlist
[j
]->maxparams
&& controlpermitted( acmdlist
[j
]->level
, np
) ) {
121 char *help
=(char *)acmdlist
[j
]->ext
;
122 if ( help
&& help
[0] != '\0')
123 controlreply(np
, "%-10s: %s\n", acmdlist
[j
]->command
->content
, help
);
125 controlreply(np
, "%s\n", acmdlist
[j
]->command
->content
);
131 void regdisp( searchCmd
*cmd
, const char *name
, void *handler
, int level
, char *help
) {
132 addcommandexttotree(cmd
->outputtree
, name
, level
, 0, (CommandHandler
) handler
, (char *)help
);
135 void unregdisp( searchCmd
*cmd
, const char *name
, void *handler
) {
136 deletecommandfromtree(cmd
->outputtree
, name
, (CommandHandler
) handler
);
139 void *findcommandinlist( searchList
*sl
, char *name
){
141 if(strcmp(sl
->name
->content
,name
) == 0 ) {
149 const char *parseError
;
150 /* used for *_free functions that need to warn users of certain things
151 i.e. hitting too many users in a (kill) or (gline) */
152 nick
*senderNSExtern
;
155 searchCmdTree
=newcommandtree();
157 reg_nicksearch
= (searchCmd
*)registersearchcommand("nicksearch",NO_OPER
,&do_nicksearch
, printnick
);
158 reg_chansearch
= (searchCmd
*)registersearchcommand("chansearch",NO_OPER
,&do_chansearch
, printchannel
);
159 reg_usersearch
= (searchCmd
*)registersearchcommand("usersearch",NO_OPER
,&do_usersearch
, printuser
);
161 /* Boolean operations */
162 registerglobalsearchterm("and",and_parse
, "usage: (and (X) (X))" );
163 registerglobalsearchterm("not",not_parse
, "usage: (not (X))");
164 registerglobalsearchterm("or",or_parse
, "usage: (or (X) (X))" );
166 registerglobalsearchterm("eq",eq_parse
, "usage: (eq (X) Y)");
168 registerglobalsearchterm("lt",lt_parse
, "usage: (lt (X) int)");
169 registerglobalsearchterm("gt",gt_parse
, "usage: (gt (X) int)");
171 /* String operations */
172 registerglobalsearchterm("match",match_parse
, "usage: (match (X) string)");
173 registerglobalsearchterm("regex",regex_parse
, "usage: (regex (X) string)");
174 registerglobalsearchterm("length",length_parse
, "usage: (length string)");
176 /* Nickname operations */
177 registersearchterm(reg_nicksearch
, "hostmask",hostmask_parse
, 0, "The user's nick!user@host; \"hostmask real\" returns nick!user@host\rreal"); /* nick only */
178 registersearchterm(reg_nicksearch
, "realname",realname_parse
, 0, "User's current realname"); /* nick only */
179 registersearchterm(reg_nicksearch
, "authname",authname_parse
, 0, "User's current authname or false"); /* nick only */
180 registersearchterm(reg_nicksearch
, "authts",authts_parse
, 0, "User's Auth timestamp"); /* nick only */
181 registersearchterm(reg_nicksearch
, "ident",ident_parse
, 0, "User's current ident"); /* nick only */
182 registersearchterm(reg_nicksearch
, "host",host_parse
, 0, "User's host, allow \"host real\" to match real host"); /* nick only */
183 registersearchterm(reg_nicksearch
, "channel",channel_parse
, 0, "Valid Channel Name to match users against"); /* nick only */
184 registersearchterm(reg_nicksearch
, "timestamp",timestamp_parse
, 0, "User's Timestamp"); /* nick only */
185 registersearchterm(reg_nicksearch
, "country",country_parse
, 0, "2 letter country code (data source is geoip)"); /* nick only */
186 registersearchterm(reg_nicksearch
, "ip",ip_parse
, 0, "User's IP - ipv4 or ipv6 format as appropriate. Note: not 6to4"); /* nick only */
187 registersearchterm(reg_nicksearch
, "channels",channels_parse
, 0, "Channel Count"); /* nick only */
188 registersearchterm(reg_nicksearch
, "server",server_parse
, 0, "Server Name. Either (server string) or (match (server) string)"); /* nick only */
189 registersearchterm(reg_nicksearch
, "authid",authid_parse
, 0, "User's Auth ID"); /* nick only */
190 registersearchterm(reg_nicksearch
, "cidr",cidr_parse
, 0, "CIDR matching"); /* nick only */
192 /* Channel operations */
193 registersearchterm(reg_chansearch
, "exists",exists_parse
, 0, "Returns if channel exists on network. Note: newserv may store data on empty channels"); /* channel only */
194 registersearchterm(reg_chansearch
, "services",services_parse
, 0, ""); /* channel only */
195 registersearchterm(reg_chansearch
, "size",size_parse
, 0, "Channel user count"); /* channel only */
196 registersearchterm(reg_chansearch
, "name",name_parse
, 0, "Channel Name"); /* channel only */
197 registersearchterm(reg_chansearch
, "topic",topic_parse
, 0, "Channel topic"); /* channel only */
198 registersearchterm(reg_chansearch
, "oppct",oppct_parse
, 0, "Percentage Opped"); /* channel only */
199 registersearchterm(reg_chansearch
, "uniquehostpct",hostpct_parse
, 0, "uniquehost percent"); /* channel only */
200 registersearchterm(reg_chansearch
, "authedpct",authedpct_parse
, 0, "Percentage of authed users"); /* channel only */
201 registersearchterm(reg_chansearch
, "kick",kick_parse
, 0, "KICK users channels in newsearch result. Note: evaluation order"); /* channel only */
203 /* Nickname / channel operations */
204 registersearchterm(reg_chansearch
, "modes",modes_parse
, 0, "User Modes");
205 registersearchterm(reg_nicksearch
, "modes",modes_parse
, 0, "Channel Modes");
206 registersearchterm(reg_chansearch
, "nick",nick_parse
, 0, "Nickname");
207 registersearchterm(reg_nicksearch
, "nick",nick_parse
, 0, "Nickname");
209 /* Kill / gline parameters */
210 registersearchterm(reg_chansearch
,"kill",kill_parse
, 0, "KILL users in newsearch result. Note: evaluation order");
211 registersearchterm(reg_chansearch
,"gline",gline_parse
, 0, "GLINE users in newsearch result. Note: evaluation order");
212 registersearchterm(reg_nicksearch
,"kill",kill_parse
, 0, "KILL users in newsearch result. Note: evaluation order");
213 registersearchterm(reg_nicksearch
,"gline",gline_parse
, 0, "GLINE users in newsearch result. Note: evaluation order");
215 /* Iteration functionality */
216 registerglobalsearchterm("any",any_parse
, "usage: any (generatorfn x) (fn ... (var x) ...)");
217 registerglobalsearchterm("all",all_parse
, "usage: all (generatorfn x) (fn ... (var x) ...)");
218 registerglobalsearchterm("var",var_parse
, "usage: var variable");
220 /* Iterable functions */
221 registersearchterm(reg_nicksearch
, "channeliter",channeliter_parse
, 0, "Channel Iterable function - usage: (any (channeliter x) (match (var x) #twilight*))"); /* nick only */
222 registersearchterm(reg_chansearch
, "nickiter",nickiter_parse
, 0, "Nick Iterable function - usage: (any (nickiter x) (match (var x) nickname*))"); /* channel only */
224 /* Functions that work on strings?! */
225 registersearchterm(reg_nicksearch
, "cumodes", cumodes_parse
, 0, "usage: (cumodes (var x) <modes>)");
227 /* Notice functionality */
228 registersearchterm(reg_chansearch
,"notice",notice_parse
, 0, "NOTICE users in newsearch result. Note: evaluation order");
229 registersearchterm(reg_nicksearch
,"notice",notice_parse
, 0, "NOTICE users in newsearch result. Note: evaluation order");
231 /* Nick output filters */
232 regdisp(reg_nicksearch
,"default",printnick
, 0, "");
233 regdisp(reg_nicksearch
,"channels",printnick_channels
, 0, "include channels in output");
235 /* Channel output filters */
236 regdisp(reg_chansearch
,"default",printchannel
, 0, "");
237 regdisp(reg_chansearch
,"topic",printchannel_topic
, 0, "display channel topics");
238 regdisp(reg_chansearch
,"services",printchannel_services
, 0, "display services on channels");
240 /* Nick output filters */
241 regdisp(reg_usersearch
,"default",printuser
, 0, "");
246 searchList
*sl
, *psl
;
248 Command
*cmdlist
[100];
255 n
=getcommandlist(searchCmdTree
,cmdlist
,100);
257 deregistersearchterm( (searchCmd
*)cmdlist
[i
]->handler
, psl
->name
->content
, psl
->cmd
);
260 freesstring(psl
->name
);
266 deregistersearchcommand( reg_nicksearch
);
267 deregistersearchcommand( reg_chansearch
);
268 deregistersearchcommand( reg_usersearch
);
269 destroycommandtree( searchCmdTree
);
272 void registerglobalsearchterm(char *term
, parseFunc parsefunc
, char *help
) {
274 Command
*cmdlist
[100];
275 searchList
*sl
= malloc(sizeof(searchList
));
277 Error("newsearch", ERR_ERROR
, "malloc failed: registerglobalsearchterm");
282 sl
->name
= getsstring(term
, NSMAX_COMMAND_LEN
);
285 int len
=strlen(help
);
286 sl
->help
=(char *)malloc(len
+1);
288 Error("newsearch", ERR_ERROR
, "malloc failed: registerglobalsearchterm");
291 strncpy(sl
->help
, help
, len
);
292 sl
->help
[len
] = '\0';
298 if ( globalterms
!= NULL
) {
299 sl
->next
= globalterms
;
303 n
=getcommandlist(searchCmdTree
,cmdlist
,100);
305 /* maxparams is set to 1 to indicate a global term */
306 /* access level is set to 0 for all global terms */
307 addcommandexttotree( ((searchCmd
*)cmdlist
[i
]->handler
)->searchtree
,term
, 0, 1, (CommandHandler
) parsefunc
, help
);
311 void deregisterglobalsearchterm(char *term
, parseFunc parsefunc
) {
313 Command
*cmdlist
[100];
314 searchList
*sl
, *psl
=NULL
;
316 for (sl
=globalterms
; sl
; sl
=sl
->next
) {
317 if ( strcmp( sl
->name
->content
, term
) == 0 ) {
325 psl
->next
= sl
->next
;
328 n
=getcommandlist(searchCmdTree
,cmdlist
,100);
330 deletecommandfromtree( ((searchCmd
*)cmdlist
[i
]->handler
)->searchtree
, term
, (CommandHandler
) parsefunc
);
332 freesstring(sl
->name
);
337 void registersearchterm(searchCmd
*cmd
, char *term
, parseFunc parsefunc
, int level
, char *help
) {
338 /* NOTE: global terms are added to the tree elsewhere as we set maxparams to 1 to indicate global */
339 addcommandexttotree(cmd
->searchtree
, term
, level
, 0, (CommandHandler
) parsefunc
, help
);
342 void deregistersearchterm(searchCmd
*cmd
, char *term
, parseFunc parsefunc
) {
343 /* NOTE: global terms are removed from the tree within deregisterglobalsearchterm */
344 deletecommandfromtree(cmd
->searchtree
, term
, (CommandHandler
) parsefunc
);
347 static void controlwallwrapper(int level
, char *format
, ...) __attribute__ ((format (printf
, 2, 3)));
348 static void controlwallwrapper(int level
, char *format
, ...) {
352 va_start(ap
, format
);
353 vsnprintf(buf
, sizeof(buf
), format
, ap
);
354 controlwall(NO_OPER
, level
, "%s", buf
);
358 int parseopts(int cargc
, char **cargv
, int *arg
, int *limit
, void **subset
, void *display
, CommandTree
*sl
, replyFunc reply
, void *sender
) {
361 struct irc_in_addr sin
; unsigned char bits
;
363 if (*cargv
[0] == '-') {
367 for (ch
=cargv
[0]+1;*ch
;ch
++) {
371 reply(sender
,"Error: -l switch requires an argument (for help, see help <searchcmd>)");
374 *limit
=strtoul(cargv
[(*arg
)++],NULL
,10);
379 reply(sender
,"Error: -d switch requires an argument (for help, see help <searchcmd>)");
382 cmd
=findcommandintree(sl
, cargv
[*arg
],1);
384 reply(sender
,"Error: unknown output format %s (for help, see help <searchcmd>)",cargv
[*arg
]);
387 if ( !controlpermitted( cmd
->level
, sender
) ) {
388 reply(sender
,"Error: Access Denied for output format %s (for help, see help <searchcmd>)", cargv
[*arg
]);
391 *((void **)display
)=(void *)cmd
->handler
;
397 reply(sender
,"Error: -s switch requires an argument (for help, see help <searchcmd>)");
400 if (ipmask_parse(cargv
[*arg
], &sin
, &bits
) == 0) {
401 reply(sender
, "Error: Invalid CIDR mask supplied (for help, see help <searchcmd>)");
404 *subset
= (void *)refnode(iptree
, &sin
, bits
);
409 reply(sender
,"Unrecognised flag -%c. (for help, see help <searchcmd>)",*ch
);
418 void newsearch_ctxinit(searchCtx
*ctx
, searchParseFunc searchfn
, replyFunc replyfn
, wallFunc wallfn
, void *arg
, searchCmd
*cmd
, nick
*np
, void *displayfn
, int limit
) {
419 memset(ctx
, 0, sizeof(searchCtx
));
421 ctx
->reply
= replyfn
;
423 ctx
->parser
= searchfn
;
425 ctx
->searchcmd
= cmd
;
428 ctx
->displayfn
= displayfn
;
431 int do_nicksearch_real(replyFunc reply
, wallFunc wall
, void *source
, int cargc
, char **cargv
) {
432 nick
*sender
= source
;
433 struct searchNode
*search
;
436 NickDisplayFunc display
=defaultnickfn
;
441 reply( sender
, "Usage: [flags] <criteria>");
442 reply( sender
, "For help, see help nicksearch");
446 ret
= parseopts(cargc
, cargv
, &arg
, &limit
, NULL
, (void *)&display
, reg_nicksearch
->outputtree
, reply
, sender
);
451 reply(sender
,"No search terms - aborting.");
456 rejoinline(cargv
[arg
],cargc
-arg
);
459 newsearch_ctxinit(&ctx
, search_parse
, reply
, wall
, NULL
, reg_nicksearch
, sender
, display
, limit
);
461 if (!(search
= ctx
.parser(&ctx
, cargv
[arg
]))) {
462 reply(sender
,"Parse error: %s",parseError
);
466 nicksearch_exe(search
, &ctx
);
468 (search
->free
)(&ctx
, search
);
473 int do_nicksearch(void *source
, int cargc
, char **cargv
) {
474 return do_nicksearch_real(controlreply
, controlwallwrapper
, source
, cargc
, cargv
);
477 void nicksearch_exe(struct searchNode
*search
, searchCtx
*ctx
) {
480 unsigned int cmarker
;
481 unsigned int tchans
=0,uchans
=0;
483 nick
*np
, *sender
= ctx
->sender
;
484 senderNSExtern
= sender
;
485 NickDisplayFunc display
= ctx
->displayfn
;
486 int limit
= ctx
->limit
;
488 /* Get a marker value to mark "seen" channels for unique count */
489 cmarker
=nextchanmarker();
491 /* The top-level node needs to return a BOOL */
492 search
=coerceNode(ctx
, search
, RETURNTYPE_BOOL
);
494 for (i
=0;i
<NICKHASHSIZE
;i
++) {
495 for (np
=nicktable
[i
];np
;np
=np
->next
) {
496 if ((search
->exe
)(ctx
, search
, np
)) {
497 /* Add total channels */
498 tchans
+= np
->channels
->cursi
;
500 /* Check channels for uniqueness */
501 cs
=(channel
**)np
->channels
->content
;
502 for (j
=0;j
<np
->channels
->cursi
;j
++) {
503 if (cs
[j
]->index
->marker
!= cmarker
) {
504 cs
[j
]->index
->marker
=cmarker
;
510 display(ctx
, sender
, np
);
513 ctx
->reply(sender
, "--- More than %d matches, skipping the rest",limit
);
519 ctx
->reply(sender
,"--- End of list: %d matches; users were on %u channels (%u unique, %.1f average clones)",
520 matches
, tchans
, uchans
, (float)tchans
/uchans
);
523 int do_chansearch_real(replyFunc reply
, wallFunc wall
, void *source
, int cargc
, char **cargv
) {
524 nick
*sender
= source
;
525 struct searchNode
*search
;
528 ChanDisplayFunc display
=defaultchanfn
;
533 reply( sender
, "Usage: [flags] <criteria>");
534 reply( sender
, "For help, see help chansearch");
538 ret
= parseopts(cargc
, cargv
, &arg
, &limit
, NULL
, (void *)&display
, reg_chansearch
->outputtree
, reply
, sender
);
543 reply(sender
,"No search terms - aborting.");
548 rejoinline(cargv
[arg
],cargc
-arg
);
551 newsearch_ctxinit(&ctx
, search_parse
, reply
, wall
, NULL
, reg_chansearch
, sender
, display
, limit
);
552 if (!(search
= ctx
.parser(&ctx
, cargv
[arg
]))) {
553 reply(sender
,"Parse error: %s",parseError
);
557 chansearch_exe(search
, &ctx
);
559 (search
->free
)(&ctx
, search
);
564 int do_chansearch(void *source
, int cargc
, char **cargv
) {
565 return do_chansearch_real(controlreply
, controlwallwrapper
, source
, cargc
, cargv
);
568 void chansearch_exe(struct searchNode
*search
, searchCtx
*ctx
) {
572 nick
*sender
= ctx
->sender
;
573 senderNSExtern
= sender
;
574 ChanDisplayFunc display
= ctx
->displayfn
;
575 int limit
= ctx
->limit
;
577 search
=coerceNode(ctx
, search
, RETURNTYPE_BOOL
);
579 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
580 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
581 if ((search
->exe
)(ctx
, search
, cip
)) {
583 display(ctx
, sender
, cip
);
585 ctx
->reply(sender
, "--- More than %d matches, skipping the rest",limit
);
591 ctx
->reply(sender
,"--- End of list: %d matches", matches
);
594 int do_usersearch_real(replyFunc reply
, wallFunc wall
, void *source
, int cargc
, char **cargv
) {
595 nick
*sender
= source
;
596 struct searchNode
*search
;
599 UserDisplayFunc display
=defaultuserfn
;
604 reply( sender
, "Usage: [flags] <criteria>");
605 reply( sender
, "For help, see help usersearch");
609 ret
= parseopts(cargc
, cargv
, &arg
, &limit
, NULL
, (void *)&display
, reg_usersearch
->outputtree
, reply
, sender
);
614 reply(sender
,"No search terms - aborting.");
619 rejoinline(cargv
[arg
],cargc
-arg
);
622 newsearch_ctxinit(&ctx
, search_parse
, reply
, wall
, NULL
, reg_usersearch
, sender
, display
, limit
);
624 if (!(search
= ctx
.parser(&ctx
, cargv
[arg
]))) {
625 reply(sender
,"Parse error: %s",parseError
);
629 usersearch_exe(search
, &ctx
);
631 (search
->free
)(&ctx
, search
);
636 int do_usersearch(void *source
, int cargc
, char **cargv
) {
637 return do_usersearch_real(controlreply
, controlwallwrapper
, source
, cargc
, cargv
);
640 void usersearch_exe(struct searchNode
*search
, searchCtx
*ctx
) {
644 nick
*sender
= ctx
->sender
;
645 int limit
= ctx
->limit
;
646 UserDisplayFunc display
= ctx
->displayfn
;
647 senderNSExtern
= sender
;
649 search
=coerceNode(ctx
, search
, RETURNTYPE_BOOL
);
651 for (i
=0;i
<AUTHNAMEHASHSIZE
;i
++) {
652 for (aup
=authnametable
[i
];aup
;aup
=aup
->next
) {
653 if ((search
->exe
)(ctx
, search
, aup
)) {
655 display(ctx
, sender
, aup
);
657 ctx
->reply(sender
, "--- More than %d matches, skipping the rest",limit
);
663 ctx
->reply(sender
,"--- End of list: %d matches", matches
);
666 /* Free a coerce node */
667 void free_coerce(searchCtx
*ctx
, struct searchNode
*thenode
) {
668 struct coercedata
*cd
=thenode
->localdata
;
670 cd
->child
->free(ctx
, cd
->child
);
671 free(thenode
->localdata
);
675 /* Free a coerce node with a stringbuf allocated */
676 void free_coercestring(searchCtx
*ctx
, struct searchNode
*thenode
) {
677 free(((struct coercedata
*)thenode
->localdata
)->u
.stringbuf
);
678 free_coerce(ctx
, thenode
);
681 /* exe_tostr_null: return the constant string */
682 void *exe_tostr_null(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
683 struct coercedata
*cd
=thenode
->localdata
;
685 return cd
->u
.stringbuf
;
688 /* exe_val_null: return the constant value */
689 void *exe_val_null(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
690 struct coercedata
*cd
=thenode
->localdata
;
692 return (void *)cd
->u
.val
;
695 /* Lots of very dull type conversion functions */
696 void *exe_inttostr(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
697 struct coercedata
*cd
=thenode
->localdata
;
699 sprintf(cd
->u
.stringbuf
, "%lu", (unsigned long)(cd
->child
->exe
)(ctx
, cd
->child
, theinput
));
701 return cd
->u
.stringbuf
;
704 void *exe_booltostr(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
705 struct coercedata
*cd
=thenode
->localdata
;
707 if ((cd
->child
->exe
)(ctx
, cd
->child
, theinput
)) {
708 sprintf(cd
->u
.stringbuf
,"1");
710 cd
->u
.stringbuf
[0]='\0';
713 return cd
->u
.stringbuf
;
716 void *exe_strtoint(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
717 struct coercedata
*cd
=thenode
->localdata
;
719 return (void *)strtoul((cd
->child
->exe
)(ctx
,cd
->child
,theinput
),NULL
,10);
722 void *exe_booltoint(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
723 struct coercedata
*cd
=thenode
->localdata
;
725 /* Don't need to do anything */
726 return (cd
->child
->exe
)(ctx
, cd
->child
, theinput
);
729 void *exe_strtobool(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
730 struct coercedata
*cd
=thenode
->localdata
;
731 char *ch
=(cd
->child
->exe
)(ctx
, cd
->child
, theinput
);
733 if (!ch
|| *ch
=='\0' || (*ch
=='0' && ch
[1]=='\0')) {
740 void *exe_inttobool(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
741 struct coercedata
*cd
=thenode
->localdata
;
743 if ((cd
->child
->exe
)(ctx
, cd
->child
, theinput
)) {
750 struct searchNode
*coerceNode(searchCtx
*ctx
, struct searchNode
*thenode
, int type
) {
751 struct searchNode
*anode
;
752 struct coercedata
*cd
;
754 /* You can't coerce a NULL */
758 /* No effort required to coerce to the same type */
759 if (type
==(thenode
->returntype
& RETURNTYPE_TYPE
))
762 anode
=(struct searchNode
*)malloc(sizeof(struct searchNode
));
763 anode
->localdata
=cd
=(struct coercedata
*)malloc(sizeof(struct coercedata
));
765 anode
->returntype
=type
; /* We'll return what they want, always */
766 anode
->free
=free_coerce
;
769 case RETURNTYPE_STRING
:
770 /* For a string we'll need a buffer */
771 /* A 64-bit number prints out to 20 digits, this leaves some slack */
772 cd
->u
.stringbuf
=malloc(25);
773 anode
->free
=free_coercestring
;
775 switch(thenode
->returntype
& RETURNTYPE_TYPE
) {
778 if (thenode
->returntype
& RETURNTYPE_CONST
) {
779 /* Constant node: sort it out now */
780 sprintf(cd
->u
.stringbuf
, "%lu", (unsigned long)thenode
->exe(ctx
, thenode
, NULL
));
781 anode
->exe
=exe_tostr_null
;
782 anode
->returntype
|= RETURNTYPE_CONST
;
785 anode
->exe
=exe_inttostr
;
789 case RETURNTYPE_BOOL
:
790 if (thenode
->returntype
& RETURNTYPE_CONST
) {
791 /* Constant bool value */
792 if (thenode
->exe(ctx
, thenode
,NULL
)) {
794 sprintf(cd
->u
.stringbuf
, "1");
796 cd
->u
.stringbuf
[0] = '\0';
798 anode
->exe
=exe_tostr_null
;
799 anode
->returntype
|= RETURNTYPE_CONST
;
801 /* Variable bool value */
802 anode
->exe
=exe_booltostr
;
810 switch (thenode
->returntype
& RETURNTYPE_TYPE
) {
811 case RETURNTYPE_STRING
:
812 if (thenode
->returntype
& RETURNTYPE_CONST
) {
813 cd
->u
.val
=strtoul((thenode
->exe
)(ctx
, thenode
, NULL
), NULL
, 10);
814 anode
->exe
=exe_val_null
;
815 anode
->returntype
|= RETURNTYPE_CONST
;
817 anode
->exe
=exe_strtoint
;
822 case RETURNTYPE_BOOL
:
823 if (thenode
->returntype
& RETURNTYPE_CONST
) {
824 if ((thenode
->exe
)(ctx
, thenode
,NULL
))
829 anode
->exe
=exe_val_null
;
830 anode
->returntype
|= RETURNTYPE_CONST
;
832 anode
->exe
=exe_booltoint
;
839 case RETURNTYPE_BOOL
:
841 switch (thenode
->returntype
& RETURNTYPE_TYPE
) {
842 case RETURNTYPE_STRING
:
843 if (thenode
->returntype
& RETURNTYPE_CONST
) {
844 char *rv
=(char *)((thenode
->exe
)(ctx
, thenode
, NULL
));
845 if (!rv
|| *rv
=='\0' || (*rv
=='0' && rv
[1]=='\0'))
850 anode
->exe
=exe_val_null
;
851 anode
->returntype
|= RETURNTYPE_CONST
;
853 anode
->exe
=exe_strtobool
;
859 if (thenode
->returntype
& RETURNTYPE_CONST
) {
860 if ((thenode
->exe
)(ctx
, thenode
,NULL
))
865 anode
->exe
=exe_val_null
;
866 anode
->returntype
|= RETURNTYPE_CONST
;
868 anode
->exe
=exe_inttobool
;
878 /* Literals always return constant strings... */
879 void *literal_exe(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
880 if (thenode
->localdata
)
881 return ((sstring
*)thenode
->localdata
)->content
;
886 void literal_free(searchCtx
*ctx
, struct searchNode
*thenode
) {
887 freesstring(thenode
->localdata
);
891 static int unescape(char *input
, char *output
, size_t buflen
) {
896 for (ch
=input
;*ch
;ch
++) {
897 if(ch
- input
>= buflen
) {
898 parseError
="Buffer overflow";
903 if (*(ch
-1) != '\"') {
904 parseError
="Quote mismatch";
913 for (ch
=input
;*ch
;ch
++) {
914 if(ch
- input
>= buflen
) {
915 parseError
="Buffer overflow";
922 } else if (*ch
=='\\') {
934 * Given an input string, return a searchNode.
937 struct searchNode
*search_parse(searchCtx
*ctx
, char *cinput
) {
938 /* OK, we need to split the input into chunks on spaces and brackets.. */
939 char *argvector
[100];
946 struct searchNode
*thenode
;
948 strlcpy(inputb
, cinput
, sizeof(inputb
));
951 /* If it starts with a bracket, it's a function call.. */
953 /* Skip past string */
954 for (ch
=input
;*ch
;ch
++);
955 if (*(ch
-1) != ')') {
956 parseError
= "Bracket mismatch!";
962 /* Split further args */
963 i
=-1; /* i = -1 BoW, 0 = inword, 1 = bracket nest depth */
964 j
=0; /* j = current arg */
968 for (ch
=input
;*ch
;ch
++) {
969 /*printf("i: %d j: %d e: %d q: %d ch: '%c'\n", i, j, e, q, *ch);*/
974 } else if (*ch
!= ' ') {
978 } else if (*ch
=='\"') {
987 } else if (*ch
=='\"') {
993 } else if (*ch
=='\"') {
995 } else if (*ch
==' ') {
998 if(j
>= (sizeof(argvector
) / sizeof(*argvector
))) {
999 parseError
= "Too many arguments";
1007 } else if (*ch
=='\"') {
1009 } else if (*ch
=='(') {
1011 } else if (*ch
==')') {
1018 parseError
= "Bracket mismatch!";
1022 if (*(ch
-1) == 0) /* if the last character was a space */
1023 j
--; /* remove an argument */
1025 /* for(k=1;k<=j;k++)
1026 if(!unescape(argvector[k], argvector[k], sizeof(inputb)))
1030 if (!(cmd
=findcommandintree(ctx
->searchcmd
->searchtree
,argvector
[0],1))) {
1031 parseError
= "Unknown command (for valid command list, see help <searchcmd>)";
1034 if (!controlpermitted(cmd
->level
, ctx
->sender
)) {
1035 parseError
= "Access denied (for valid command list, see help <searchcmd>)";
1038 return ((parseFunc
)cmd
->handler
)(ctx
, j
, argvector
+1);
1043 /* slug: disabled now we unescape during the main parse stage */
1044 if(!unescape(input
, thestring
, sizeof(thestring
)))
1047 if (!(thenode
=(struct searchNode
*)malloc(sizeof(struct searchNode
)))) {
1048 parseError
= "malloc: could not allocate memory for this search.";
1052 thenode
->localdata
= getsstring(input
,512);
1053 thenode
->returntype
= RETURNTYPE_CONST
| RETURNTYPE_STRING
;
1054 thenode
->exe
= literal_exe
;
1055 thenode
->free
= literal_free
;
1061 void nssnprintf(char *buf
, size_t size
, const char *format
, nick
*np
) {
1074 for(p
=format
;*p
;p
++) {
1076 if(!sbaddchar(&b
, *p
))
1084 if(!sbaddchar(&b
, *p
))
1092 c
= np
->nick
; break;
1094 c
= np
->ident
; break;
1096 c
= np
->host
->name
->content
; break;
1098 snprintf(hostbuf
, sizeof(hostbuf
), "%s", IPtostr(np
->p_ipaddr
));
1102 snprintf(hostbuf
, sizeof(hostbuf
), "%s!%s@%s", np
->nick
, np
->ident
, IPtostr(np
->p_ipaddr
));
1106 c
= "(bad format specifier)";
1109 if(!sbaddstr(&b
, c
))
1121 static char *var_tochar(searchCtx
*ctx
, char *arg
, searchNode
**variable
) {
1122 *variable
= ctx
->parser(ctx
, arg
);
1123 if (!(*variable
= coerceNode(ctx
, *variable
, RETURNTYPE_STRING
)))
1126 if(!((*variable
)->returntype
& RETURNTYPE_CONST
)) {
1127 parseError
= "only constant variables allowed";
1128 ((*variable
)->free
)(ctx
, *variable
);
1132 return (char *)((*variable
)->exe
)(ctx
, *variable
, NULL
);
1135 void free_val_null(searchCtx
*ctx
, struct searchNode
*thenode
) {
1138 struct searchVariable
*var_register(searchCtx
*ctx
, char *arg
, int type
) {
1139 searchNode
*variable
;
1140 struct searchVariable
*us
;
1144 if(ctx
->lastvar
>= MAX_VARIABLES
) {
1145 parseError
= "Maximum number of variables reached";
1149 us
= &ctx
->vars
[ctx
->lastvar
];
1151 var
= var_tochar(ctx
, arg
, &variable
);
1155 strlcpy(us
->name
, var
, sizeof(us
->name
));
1156 (variable
->free
)(ctx
, variable
);
1158 for(i
=0;i
<ctx
->lastvar
;i
++) {
1159 if(!strcmp(us
->name
, ctx
->vars
[i
].name
)) {
1160 parseError
= "variable name already in use";
1166 us
->data
.returntype
= type
;
1167 us
->data
.localdata
= &us
->cdata
;
1168 us
->data
.exe
= exe_val_null
;
1169 us
->data
.free
= free_val_null
;
1171 us
->cdata
.child
= NULL
;
1175 searchNode
*var_get(searchCtx
*ctx
, char *arg
) {
1176 searchNode
*variable
, *found
= NULL
;
1178 char *var
= var_tochar(ctx
, arg
, &variable
);
1182 for(i
=0;i
<ctx
->lastvar
;i
++) {
1183 if(!strcmp(var
, ctx
->vars
[i
].name
)) {
1184 found
= &ctx
->vars
[i
].data
;
1188 (variable
->free
)(ctx
, variable
);
1191 parseError
= "variable not found";
1195 void var_setstr(struct searchVariable
*v
, char *data
) {
1196 v
->cdata
.u
.stringbuf
= data
;