6 #include "../irc/irc_config.h"
7 #include "../lib/irc_string.h"
8 #include "../parser/parser.h"
9 #include "../control/control.h"
10 #include "../lib/splitline.h"
11 #include "../lib/version.h"
12 #include "../lib/stringbuf.h"
13 #include "../lib/strlfunc.h"
14 #include "../lib/array.h"
15 #include "newsearch.h"
20 CommandTree
*searchCmdTree
;
21 searchList
*globalterms
= NULL
;
23 int do_nicksearch(void *source
, int cargc
, char **cargv
);
24 int do_chansearch(void *source
, int cargc
, char **cargv
);
25 int do_usersearch(void *source
, int cargc
, char **cargv
);
26 int do_whowassearch(void *source
, int cargc
, char **cargv
);
28 void printnick_channels(searchCtx
*, nick
*, nick
*);
29 void printchannel(searchCtx
*, nick
*, chanindex
*);
30 void printchannel_topic(searchCtx
*, nick
*, chanindex
*);
31 void printchannel_services(searchCtx
*, nick
*, chanindex
*);
33 UserDisplayFunc defaultuserfn
= printuser
;
34 NickDisplayFunc defaultnickfn
= printnick
;
35 ChanDisplayFunc defaultchanfn
= printchannel
;
36 WhowasDisplayFunc defaultwhowasfn
= printwhowas
;
38 searchCmd
*reg_nicksearch
, *reg_chansearch
, *reg_usersearch
, *reg_whowassearch
;
39 void displaycommandhelp(nick
*, Command
*);
40 void displaystrerror(replyFunc
, nick
*, const char *);
42 searchCmd
*registersearchcommand(char *name
, int level
, CommandHandler cmd
, void *defaultdisplayfunc
) {
46 registercontrolhelpfunccmd(name
, NO_OPER
,4, cmd
, &displaycommandhelp
);
47 acmd
=(struct searchCmd
*)malloc(sizeof(struct searchCmd
));
49 Error("newsearch", ERR_ERROR
, "malloc failed: registersearchcommand");
54 acmd
->name
= getsstring( name
, NSMAX_COMMAND_LEN
);
55 acmd
->outputtree
= newcommandtree();
56 acmd
->searchtree
= newcommandtree();
58 addcommandtotree(searchCmdTree
, name
, 0, 0, (CommandHandler
)acmd
);
60 for (sl
=globalterms
; sl
; sl
=sl
->next
) {
61 addcommandexttotree(acmd
->searchtree
, sl
->name
->content
, 0, 1, (CommandHandler
)sl
->cmd
, sl
->help
);
67 void deregistersearchcommand(searchCmd
*scmd
) {
68 deregistercontrolcmd(scmd
->name
->content
, (CommandHandler
)scmd
->handler
);
69 destroycommandtree(scmd
->outputtree
);
70 destroycommandtree(scmd
->searchtree
);
71 deletecommandfromtree(searchCmdTree
, scmd
->name
->content
, (CommandHandler
) scmd
);
72 freesstring(scmd
->name
);
76 void displaycommandhelp(nick
*np
, Command
*cmd
) {
78 Command
*cmdlist
[100], *acmdlist
[100];
81 n
=getcommandlist(searchCmdTree
,cmdlist
,100);
83 /* note: we may want to only deregister a command if we've already registered it, for now, try de-registering new commands anyway */
84 if ( ((searchCmd
*)cmdlist
[i
]->handler
)->handler
!= cmd
->handler
)
86 acmd
= ((searchCmd
*)(cmdlist
[i
]->handler
));
88 controlreply(np
, "Usage: [flags] <criteria>\n");
89 controlreply(np
, "Flags:\n");
90 controlreply(np
, " -l int : Limit number of rows of results\n");
91 controlreply(np
, " -d string : a valid output format for the results\n");
92 controlreply(np
, " -s subset : ipmask subset of network to search (only for node search)\n");
93 controlreply(np
, " \n");
94 controlreply(np
, "Available Output Formats:\n");
96 m
=getcommandlist(acmd
->outputtree
,acmdlist
,100);
98 if ( controlpermitted( acmdlist
[j
]->level
, np
) ) {
99 char *help
=(char *)acmdlist
[j
]->ext
;
100 if ( help
&& help
[0] != '\0')
101 controlreply(np
, "%-10s: %s\n", acmdlist
[j
]->command
->content
, help
);
103 controlreply(np
, "%s\n", acmdlist
[j
]->command
->content
);
107 controlreply(np
, " \n");
108 controlreply(np
, "Available Global Commands and Operators:\n" );
109 m
=getcommandlist(acmd
->searchtree
,acmdlist
,100);
111 if ( acmdlist
[j
]->maxparams
) {
112 char *help
=(char *)acmdlist
[j
]->ext
;
113 if ( help
&& help
[0] != '\0')
114 controlreply(np
, "%-10s: %s\n", acmdlist
[j
]->command
->content
, help
);
116 controlreply(np
, "%s\n", acmdlist
[j
]->command
->content
);
120 controlreply(np
, " \n");
121 controlreply(np
, "Available Commands and Operators for %s:\n", acmd
->name
->content
);
123 m
=getcommandlist(acmd
->searchtree
,acmdlist
,100);
125 if ( !acmdlist
[j
]->maxparams
&& controlpermitted( acmdlist
[j
]->level
, np
) ) {
126 char *help
=(char *)acmdlist
[j
]->ext
;
127 if ( help
&& help
[0] != '\0')
128 controlreply(np
, "%-10s: %s\n", acmdlist
[j
]->command
->content
, help
);
130 controlreply(np
, "%s\n", acmdlist
[j
]->command
->content
);
136 void regdisp( searchCmd
*cmd
, const char *name
, void *handler
, int level
, char *help
) {
137 addcommandexttotree(cmd
->outputtree
, name
, level
, 0, (CommandHandler
) handler
, (char *)help
);
140 void unregdisp( searchCmd
*cmd
, const char *name
, void *handler
) {
141 deletecommandfromtree(cmd
->outputtree
, name
, (CommandHandler
) handler
);
144 void *findcommandinlist( searchList
*sl
, char *name
){
146 if(strcmp(sl
->name
->content
,name
) == 0 ) {
154 const char *parseError
;
155 /* used for *_free functions that need to warn users of certain things
156 i.e. hitting too many users in a (kill) or (gline) */
157 nick
*senderNSExtern
;
160 searchCmdTree
=newcommandtree();
162 reg_nicksearch
= (searchCmd
*)registersearchcommand("nicksearch",NO_OPER
,&do_nicksearch
, printnick
);
163 reg_chansearch
= (searchCmd
*)registersearchcommand("chansearch",NO_OPER
,&do_chansearch
, printchannel
);
164 reg_usersearch
= (searchCmd
*)registersearchcommand("usersearch",NO_OPER
,&do_usersearch
, printuser
);
165 reg_whowassearch
= (searchCmd
*)registersearchcommand("whowassearch",NO_OPER
,&do_whowassearch
, printwhowas
);
167 /* Boolean operations */
168 registerglobalsearchterm("and",and_parse
, "usage: (and (X) (X))" );
169 registerglobalsearchterm("not",not_parse
, "usage: (not (X))");
170 registerglobalsearchterm("or",or_parse
, "usage: (or (X) (X))" );
172 registerglobalsearchterm("eq",eq_parse
, "usage: (eq (X) Y)");
174 registerglobalsearchterm("lt",lt_parse
, "usage: (lt (X) int)");
175 registerglobalsearchterm("gt",gt_parse
, "usage: (gt (X) int)");
177 /* String operations */
178 registerglobalsearchterm("match",match_parse
, "usage: (match (X) string)");
179 registerglobalsearchterm("regex",regex_parse
, "usage: (regex (X) string)");
180 registerglobalsearchterm("length",length_parse
, "usage: (length string)");
182 /* Nickname operations */
183 registersearchterm(reg_nicksearch
, "hostmask",hostmask_parse
, 0, "The user's nick!user@host; \"hostmask real\" returns nick!user@host\rreal");
184 registersearchterm(reg_whowassearch
, "hostmask",hostmask_parse
, 0, "The user's nick!user@host; \"hostmask real\" returns nick!user@host\rreal");
186 registersearchterm(reg_nicksearch
, "realname",realname_parse
, 0, "User's current realname"); /* nick only */
187 registersearchterm(reg_whowassearch
, "realname",realname_parse
, 0, "User's current realname"); /* nick only */
189 registersearchterm(reg_nicksearch
, "away",away_parse
, 0, "User's current away message");
190 registersearchterm(reg_whowassearch
, "away",away_parse
, 0, "User's current away message");
191 registersearchterm(reg_nicksearch
, "authname",authname_parse
, 0, "User's current authname or false");
192 registersearchterm(reg_whowassearch
, "authname",authname_parse
, 0, "User's current authname or false");
193 registersearchterm(reg_nicksearch
, "authts",authts_parse
, 0, "User's Auth timestamp");
194 registersearchterm(reg_whowassearch
, "authts",authts_parse
, 0, "User's Auth timestamp");
195 registersearchterm(reg_nicksearch
, "ident",ident_parse
, 0, "User's current ident");
196 registersearchterm(reg_whowassearch
, "ident",ident_parse
, 0, "User's current ident");
197 registersearchterm(reg_nicksearch
, "host",host_parse
, 0, "User's host, allow \"host real\" to match real host");
198 registersearchterm(reg_whowassearch
, "host",host_parse
, 0, "User's host, allow \"host real\" to match real host");
199 registersearchterm(reg_nicksearch
, "channel",channel_parse
, 0, "Valid Channel Name to match users against");
200 registersearchterm(reg_whowassearch
, "channel",channel_parse
, 0, "Valid Channel Name to match users against");
201 registersearchterm(reg_nicksearch
, "timestamp",timestamp_parse
, 0, "User's Timestamp");
202 registersearchterm(reg_whowassearch
, "timestamp",timestamp_parse
, 0, "User's Timestamp");
203 registersearchterm(reg_nicksearch
, "country",country_parse
, 0, "2 letter country code (data source is geoip)");
204 registersearchterm(reg_nicksearch
, "ip",ip_parse
, 0, "User's IP - ipv4 or ipv6 format as appropriate. Note: not 6to4");
205 registersearchterm(reg_whowassearch
, "ip",ip_parse
, 0, "User's IP - ipv4 or ipv6 format as appropriate. Note: not 6to4");
206 registersearchterm(reg_nicksearch
, "channels",channels_parse
, 0, "Channel Count"); /* nick only */
207 registersearchterm(reg_nicksearch
, "server",server_parse
, 0, "Server Name. Either (server string) or (match (server) string)");
208 registersearchterm(reg_whowassearch
, "server",server_parse
, 0, "Server Name. Either (server string) or (match (server) string)");
209 registersearchterm(reg_whowassearch
, "server",server_parse
, 0, "Server Name. Either (server string) or (match (server) string)");
210 registersearchterm(reg_nicksearch
, "authid",authid_parse
, 0, "User's Auth ID");
211 registersearchterm(reg_whowassearch
, "authid",authid_parse
, 0, "User's Auth ID");
212 registersearchterm(reg_nicksearch
, "cidr",cidr_parse
, 0, "CIDR matching");
213 registersearchterm(reg_whowassearch
, "cidr",cidr_parse
, 0, "CIDR matching");
214 registersearchterm(reg_nicksearch
, "ipvsix",ipv6_parse
, 0, "IPv6 user");
215 registersearchterm(reg_whowassearch
, "ipvsix",ipv6_parse
, 0, "IPv6 user");
216 registersearchterm(reg_nicksearch
, "message",message_parse
, 0, "Last message");
217 registersearchterm(reg_nicksearch
, "age",age_parse
, 0, "Nick record age in seconds");
218 registersearchterm(reg_whowassearch
, "age",age_parse
, 0, "Whowas record age in seconds");
221 /* Whowas operations */
222 registersearchterm(reg_whowassearch
, "quit",quit_parse
, 0, "User quit");
223 registersearchterm(reg_whowassearch
, "killed",killed_parse
, 0, "User was killed");
224 registersearchterm(reg_whowassearch
, "renamed",renamed_parse
, 0, "User changed nick");
225 registersearchterm(reg_whowassearch
, "newnick",newnick_parse
, 0, "New nick (for rename whowas records)");
226 registersearchterm(reg_whowassearch
, "reason",reason_parse
, 0, "Quit/kill reason");
228 /* Channel operations */
229 registersearchterm(reg_chansearch
, "exists",exists_parse
, 0, "Returns if channel exists on network. Note: newserv may store data on empty channels"); /* channel only */
230 registersearchterm(reg_chansearch
, "services",services_parse
, 0, ""); /* channel only */
231 registersearchterm(reg_chansearch
, "size",size_parse
, 0, "Channel user count"); /* channel only */
232 registersearchterm(reg_chansearch
, "name",name_parse
, 0, "Channel Name"); /* channel only */
233 registersearchterm(reg_chansearch
, "topic",topic_parse
, 0, "Channel topic"); /* channel only */
234 registersearchterm(reg_chansearch
, "oppct",oppct_parse
, 0, "Percentage Opped"); /* channel only */
235 registersearchterm(reg_chansearch
, "cumodecount",cumodecount_parse
, 0, "Count of users with given channel modes"); /* channel only */
236 registersearchterm(reg_chansearch
, "cumodepct",cumodepct_parse
, 0, "Percentage of users with given channel modes"); /* channel only */
237 registersearchterm(reg_chansearch
, "uniquehostpct",hostpct_parse
, 0, "uniquehost percent"); /* channel only */
238 registersearchterm(reg_chansearch
, "authedpct",authedpct_parse
, 0, "Percentage of authed users"); /* channel only */
239 registersearchterm(reg_chansearch
, "kick",kick_parse
, 0, "KICK users channels in newsearch result. Note: evaluation order"); /* channel only */
241 /* Nickname / channel operations */
242 registersearchterm(reg_chansearch
, "modes",modes_parse
, 0, "Channel Modes");
243 registersearchterm(reg_nicksearch
, "modes",modes_parse
, 0, "User Modes");
244 registersearchterm(reg_whowassearch
, "modes",modes_parse
, 0, "User Modes");
245 registersearchterm(reg_chansearch
, "nick",nick_parse
, 0, "Nickname");
246 registersearchterm(reg_nicksearch
, "nick",nick_parse
, 0, "Nickname");
247 registersearchterm(reg_whowassearch
, "nick",nick_parse
, 0, "Nickname");
249 /* Kill / gline parameters */
250 registersearchterm(reg_chansearch
,"kill",kill_parse
, 0, "KILL users in newsearch result. Note: evaluation order");
251 registersearchterm(reg_chansearch
,"gline",gline_parse
, 0, "GLINE users in newsearch result. Note: evaluation order");
252 registersearchterm(reg_chansearch
,"delaygline",delaygline_parse
, 0, "GLINE users in newsearch result with a delay. Note: evaluation order");
253 registersearchterm(reg_nicksearch
,"kill",kill_parse
, 0, "KILL users in newsearch result. Note: evaluation order");
254 registersearchterm(reg_nicksearch
,"gline",gline_parse
, 0, "GLINE users in newsearch result. Note: evaluation order");
255 registersearchterm(reg_nicksearch
,"delaygline",delaygline_parse
, 0, "GLINE users in newsearch result with a delay. Note: evaluation order");
256 registersearchterm(reg_whowassearch
,"gline",gline_parse
, 0, "GLINE users in newsearch result. Note: evaluation order");
258 /* Iteration functionality */
259 registerglobalsearchterm("any",any_parse
, "usage: any (generatorfn x) (fn ... (var x) ...)");
260 registerglobalsearchterm("all",all_parse
, "usage: all (generatorfn x) (fn ... (var x) ...)");
261 registerglobalsearchterm("var",var_parse
, "usage: var variable");
263 /* Iterable functions */
264 registersearchterm(reg_nicksearch
, "channeliter",channeliter_parse
, 0, "Channel Iterable function - usage: (any (channeliter x) (match (var x) #twilight*))"); /* nick only */
265 registersearchterm(reg_chansearch
, "nickiter",nickiter_parse
, 0, "Nick Iterable function - usage: (any (nickiter x) (match (var x) nickname*))"); /* channel only */
267 /* Functions that work on strings?! */
268 registersearchterm(reg_nicksearch
, "cumodes", cumodes_parse
, 0, "usage: (cumodes (var x) <modes>)");
269 registersearchterm(reg_chansearch
, "cumodes", cumodes_parse
, 0, "usage: (cumodes (var x) <modes>)");
271 /* Notice functionality */
272 registersearchterm(reg_chansearch
,"notice",notice_parse
, 0, "NOTICE users in newsearch result. Note: evaluation order");
273 registersearchterm(reg_nicksearch
,"notice",notice_parse
, 0, "NOTICE users in newsearch result. Note: evaluation order");
275 /* Nick output filters */
276 regdisp(reg_nicksearch
,"default",printnick
, 0, "");
277 regdisp(reg_nicksearch
,"channels",printnick_channels
, 0, "include channels in output");
279 /* Channel output filters */
280 regdisp(reg_chansearch
,"default",printchannel
, 0, "");
281 regdisp(reg_chansearch
,"topic",printchannel_topic
, 0, "display channel topics");
282 regdisp(reg_chansearch
,"services",printchannel_services
, 0, "display services on channels");
284 /* Nick output filters */
285 regdisp(reg_usersearch
,"default",printuser
, 0, "");
290 searchList
*sl
, *psl
;
292 Command
*cmdlist
[100];
299 n
=getcommandlist(searchCmdTree
,cmdlist
,100);
301 deregistersearchterm( (searchCmd
*)cmdlist
[i
]->handler
, psl
->name
->content
, psl
->cmd
);
304 freesstring(psl
->name
);
310 deregistersearchcommand( reg_nicksearch
);
311 deregistersearchcommand( reg_chansearch
);
312 deregistersearchcommand( reg_usersearch
);
313 deregistersearchcommand( reg_whowassearch
);
314 destroycommandtree( searchCmdTree
);
317 void registerglobalsearchterm(char *term
, parseFunc parsefunc
, char *help
) {
319 Command
*cmdlist
[100];
320 searchList
*sl
= malloc(sizeof(searchList
));
322 Error("newsearch", ERR_ERROR
, "malloc failed: registerglobalsearchterm");
327 sl
->name
= getsstring(term
, NSMAX_COMMAND_LEN
);
330 int len
=strlen(help
);
331 sl
->help
=(char *)malloc(len
+1);
333 freesstring(sl
->name
);
335 Error("newsearch", ERR_ERROR
, "malloc failed: registerglobalsearchterm");
338 strncpy(sl
->help
, help
, len
);
339 sl
->help
[len
] = '\0';
345 if ( globalterms
!= NULL
) {
346 sl
->next
= globalterms
;
350 n
=getcommandlist(searchCmdTree
,cmdlist
,100);
352 /* maxparams is set to 1 to indicate a global term */
353 /* access level is set to 0 for all global terms */
354 addcommandexttotree( ((searchCmd
*)cmdlist
[i
]->handler
)->searchtree
,term
, 0, 1, (CommandHandler
) parsefunc
, help
);
358 void deregisterglobalsearchterm(char *term
, parseFunc parsefunc
) {
360 Command
*cmdlist
[100];
361 searchList
*sl
, *psl
=NULL
;
363 for (sl
=globalterms
; sl
; sl
=sl
->next
) {
364 if ( strcmp( sl
->name
->content
, term
) == 0 ) {
372 psl
->next
= sl
->next
;
375 n
=getcommandlist(searchCmdTree
,cmdlist
,100);
377 deletecommandfromtree( ((searchCmd
*)cmdlist
[i
]->handler
)->searchtree
, term
, (CommandHandler
) parsefunc
);
379 freesstring(sl
->name
);
384 void registersearchterm(searchCmd
*cmd
, char *term
, parseFunc parsefunc
, int level
, char *help
) {
385 /* NOTE: global terms are added to the tree elsewhere as we set maxparams to 1 to indicate global */
386 addcommandexttotree(cmd
->searchtree
, term
, level
, 0, (CommandHandler
) parsefunc
, help
);
389 void deregistersearchterm(searchCmd
*cmd
, char *term
, parseFunc parsefunc
) {
390 /* NOTE: global terms are removed from the tree within deregisterglobalsearchterm */
391 deletecommandfromtree(cmd
->searchtree
, term
, (CommandHandler
) parsefunc
);
394 static void controlwallwrapper(int level
, char *format
, ...) __attribute__ ((format (printf
, 2, 3)));
395 static void controlwallwrapper(int level
, char *format
, ...) {
399 va_start(ap
, format
);
400 vsnprintf(buf
, sizeof(buf
), format
, ap
);
401 controlwall(NO_OPER
, level
, "%s", buf
);
405 int parseopts(int cargc
, char **cargv
, int *arg
, int *limit
, void **subset
, void *display
, CommandTree
*sl
, replyFunc reply
, void *sender
) {
408 struct irc_in_addr sin
; unsigned char bits
;
410 if (*cargv
[0] == '-') {
414 for (ch
=cargv
[0]+1;*ch
;ch
++) {
418 reply(sender
,"Error: -l switch requires an argument (for help, see help <searchcmd>)");
421 *limit
=strtoul(cargv
[(*arg
)++],NULL
,10);
426 reply(sender
,"Error: -d switch requires an argument (for help, see help <searchcmd>)");
429 cmd
=findcommandintree(sl
, cargv
[*arg
],1);
431 reply(sender
,"Error: unknown output format %s (for help, see help <searchcmd>)",cargv
[*arg
]);
434 if ( !controlpermitted( cmd
->level
, sender
) ) {
435 reply(sender
,"Error: Access Denied for output format %s (for help, see help <searchcmd>)", cargv
[*arg
]);
438 *((void **)display
)=(void *)cmd
->handler
;
443 if (subset
== NULL
) {
444 reply(sender
,"Error: -s switch not supported for this search.");
448 reply(sender
,"Error: -s switch requires an argument (for help, see help <searchcmd>)");
451 if (ipmask_parse(cargv
[*arg
], &sin
, &bits
) == 0) {
452 reply(sender
, "Error: Invalid CIDR mask supplied (for help, see help <searchcmd>)");
455 *subset
= (void *)refnode(iptree
, &sin
, bits
);
460 reply(sender
,"Unrecognised flag -%c. (for help, see help <searchcmd>)",*ch
);
469 void newsearch_ctxinit(searchCtx
*ctx
, searchParseFunc searchfn
, replyFunc replyfn
, wallFunc wallfn
, void *arg
, searchCmd
*cmd
, nick
*np
, void *displayfn
, int limit
, array
*targets
) {
470 memset(ctx
, 0, sizeof(searchCtx
));
472 ctx
->reply
= replyfn
;
474 ctx
->parser
= searchfn
;
476 ctx
->searchcmd
= cmd
;
479 ctx
->targets
= targets
;
480 ctx
->displayfn
= displayfn
;
483 int do_nicksearch_real(replyFunc reply
, wallFunc wall
, void *source
, int cargc
, char **cargv
) {
484 nick
*sender
= source
;
487 NickDisplayFunc display
=defaultnickfn
;
492 reply( sender
, "Usage: [flags] <criteria>");
493 reply( sender
, "For help, see help nicksearch");
497 ret
= parseopts(cargc
, cargv
, &arg
, &limit
, NULL
, (void *)&display
, reg_nicksearch
->outputtree
, reply
, sender
);
502 reply(sender
,"No search terms - aborting.");
507 rejoinline(cargv
[arg
],cargc
-arg
);
510 tree
= parse_string(reg_nicksearch
, cargv
[arg
]);
512 displaystrerror(reply
, sender
, cargv
[arg
]);
516 ast_nicksearch(tree
->root
, reply
, sender
, wall
, display
, NULL
, NULL
, limit
, NULL
);
523 int do_nicksearch(void *source
, int cargc
, char **cargv
) {
524 return do_nicksearch_real(controlreply
, controlwallwrapper
, source
, cargc
, cargv
);
527 void nicksearch_exe(struct searchNode
*search
, searchCtx
*ctx
) {
530 unsigned int cmarker
;
531 unsigned int tchans
=0,uchans
=0;
533 nick
*np
, *sender
= ctx
->sender
;
534 senderNSExtern
= sender
;
535 NickDisplayFunc display
= ctx
->displayfn
;
536 int limit
= ctx
->limit
;
538 /* Get a marker value to mark "seen" channels for unique count */
539 cmarker
=nextchanmarker();
541 /* The top-level node needs to return a BOOL */
542 search
=coerceNode(ctx
, search
, RETURNTYPE_BOOL
);
544 for (i
=0;i
<NICKHASHSIZE
;i
++) {
545 for (np
=nicktable
[i
], k
= 0;ctx
->targets
? (k
< ctx
->targets
->cursi
) : (np
!= NULL
);np
=np
->next
, k
++) {
547 np
= ((nick
**)ctx
->targets
->content
)[k
];
552 if ((search
->exe
)(ctx
, search
, np
)) {
553 /* Add total channels */
554 tchans
+= np
->channels
->cursi
;
556 /* Check channels for uniqueness */
557 cs
=(channel
**)np
->channels
->content
;
558 for (j
=0;j
<np
->channels
->cursi
;j
++) {
559 if (cs
[j
]->index
->marker
!= cmarker
) {
560 cs
[j
]->index
->marker
=cmarker
;
566 display(ctx
, sender
, np
);
569 ctx
->reply(sender
, "--- More than %d matches, skipping the rest",limit
);
578 ctx
->reply(sender
,"--- End of list: %d matches; users were on %u channels (%u unique, %.1f average clones)",
579 matches
, tchans
, uchans
, (float)tchans
/uchans
);
582 int do_whowassearch_real(replyFunc reply
, wallFunc wall
, void *source
, int cargc
, char **cargv
) {
583 nick
*sender
= source
;
586 WhowasDisplayFunc display
=defaultwhowasfn
;
591 reply( sender
, "Usage: [flags] <criteria>");
592 reply( sender
, "For help, see help whowassearch");
596 ret
= parseopts(cargc
, cargv
, &arg
, &limit
, NULL
, (void *)&display
, reg_whowassearch
->outputtree
, reply
, sender
);
601 reply(sender
,"No search terms - aborting.");
606 rejoinline(cargv
[arg
],cargc
-arg
);
609 tree
= parse_string(reg_whowassearch
, cargv
[arg
]);
611 displaystrerror(reply
, sender
, cargv
[arg
]);
615 ast_whowassearch(tree
->root
, reply
, sender
, wall
, display
, NULL
, NULL
, limit
, NULL
);
622 int do_whowassearch(void *source
, int cargc
, char **cargv
) {
623 return do_whowassearch_real(controlreply
, controlwallwrapper
, source
, cargc
, cargv
);
626 void whowassearch_exe(struct searchNode
*search
, searchCtx
*ctx
) {
629 nick
*sender
= ctx
->sender
;
630 senderNSExtern
= sender
;
631 WhowasDisplayFunc display
= ctx
->displayfn
;
632 int limit
= ctx
->limit
;
634 assert(!ctx
->targets
);
636 /* The top-level node needs to return a BOOL */
637 search
=coerceNode(ctx
, search
, RETURNTYPE_BOOL
);
639 for (i
= whowasoffset
; i
< whowasoffset
+ whowasmax
; i
++) {
640 ww
= &whowasrecs
[i
% whowasmax
];
642 if (ww
->type
== WHOWAS_UNUSED
)
645 /* Note: We're passing the nick to the filter function. The original
646 * whowas record is in the nick's ->next field. */
647 if ((search
->exe
)(ctx
, search
, &ww
->nick
)) {
649 display(ctx
, sender
, ww
);
652 ctx
->reply(sender
, "--- More than %d matches, skipping the rest",limit
);
657 ctx
->reply(sender
,"--- End of list: %d matches", matches
);
660 int do_chansearch_real(replyFunc reply
, wallFunc wall
, void *source
, int cargc
, char **cargv
) {
661 nick
*sender
= source
;
664 ChanDisplayFunc display
=defaultchanfn
;
669 reply( sender
, "Usage: [flags] <criteria>");
670 reply( sender
, "For help, see help chansearch");
674 ret
= parseopts(cargc
, cargv
, &arg
, &limit
, NULL
, (void *)&display
, reg_chansearch
->outputtree
, reply
, sender
);
679 reply(sender
,"No search terms - aborting.");
684 rejoinline(cargv
[arg
],cargc
-arg
);
687 tree
= parse_string(reg_chansearch
, cargv
[arg
]);
689 displaystrerror(reply
, sender
, cargv
[arg
]);
693 ast_chansearch(tree
->root
, reply
, sender
, wall
, display
, NULL
, NULL
, limit
, NULL
);
700 int do_chansearch(void *source
, int cargc
, char **cargv
) {
701 return do_chansearch_real(controlreply
, controlwallwrapper
, source
, cargc
, cargv
);
704 void chansearch_exe(struct searchNode
*search
, searchCtx
*ctx
) {
708 nick
*sender
= ctx
->sender
;
709 senderNSExtern
= sender
;
710 ChanDisplayFunc display
= ctx
->displayfn
;
711 int limit
= ctx
->limit
;
713 assert(!ctx
->targets
);
715 search
=coerceNode(ctx
, search
, RETURNTYPE_BOOL
);
717 for (i
=0;i
<CHANNELHASHSIZE
;i
++) {
718 for (cip
=chantable
[i
];cip
;cip
=cip
->next
) {
719 if ((search
->exe
)(ctx
, search
, cip
)) {
721 display(ctx
, sender
, cip
);
723 ctx
->reply(sender
, "--- More than %d matches, skipping the rest",limit
);
729 ctx
->reply(sender
,"--- End of list: %d matches", matches
);
732 int do_usersearch_real(replyFunc reply
, wallFunc wall
, void *source
, int cargc
, char **cargv
) {
733 nick
*sender
= source
;
736 UserDisplayFunc display
=defaultuserfn
;
741 reply( sender
, "Usage: [flags] <criteria>");
742 reply( sender
, "For help, see help usersearch");
746 ret
= parseopts(cargc
, cargv
, &arg
, &limit
, NULL
, (void *)&display
, reg_usersearch
->outputtree
, reply
, sender
);
751 reply(sender
,"No search terms - aborting.");
756 rejoinline(cargv
[arg
],cargc
-arg
);
759 tree
= parse_string(reg_usersearch
, cargv
[arg
]);
761 displaystrerror(reply
, sender
, cargv
[arg
]);
765 ast_usersearch(tree
->root
, reply
, sender
, wall
, display
, NULL
, NULL
, limit
, NULL
);
772 int do_usersearch(void *source
, int cargc
, char **cargv
) {
773 return do_usersearch_real(controlreply
, controlwallwrapper
, source
, cargc
, cargv
);
776 void usersearch_exe(struct searchNode
*search
, searchCtx
*ctx
) {
780 nick
*sender
= ctx
->sender
;
781 int limit
= ctx
->limit
;
782 UserDisplayFunc display
= ctx
->displayfn
;
783 senderNSExtern
= sender
;
785 assert(!ctx
->targets
);
787 search
=coerceNode(ctx
, search
, RETURNTYPE_BOOL
);
789 for (i
=0;i
<AUTHNAMEHASHSIZE
;i
++) {
790 for (aup
=authnametable
[i
];aup
;aup
=aup
->next
) {
791 if ((search
->exe
)(ctx
, search
, aup
)) {
793 display(ctx
, sender
, aup
);
795 ctx
->reply(sender
, "--- More than %d matches, skipping the rest",limit
);
801 ctx
->reply(sender
,"--- End of list: %d matches", matches
);
804 /* Free a coerce node */
805 void free_coerce(searchCtx
*ctx
, struct searchNode
*thenode
) {
806 struct coercedata
*cd
=thenode
->localdata
;
808 cd
->child
->free(ctx
, cd
->child
);
809 free(thenode
->localdata
);
813 /* Free a coerce node with a stringbuf allocated */
814 void free_coercestring(searchCtx
*ctx
, struct searchNode
*thenode
) {
815 free(((struct coercedata
*)thenode
->localdata
)->u
.stringbuf
);
816 free_coerce(ctx
, thenode
);
819 /* exe_tostr_null: return the constant string */
820 void *exe_tostr_null(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
821 struct coercedata
*cd
=thenode
->localdata
;
823 return cd
->u
.stringbuf
;
826 /* exe_val_null: return the constant value */
827 void *exe_val_null(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
828 struct coercedata
*cd
=thenode
->localdata
;
830 return (void *)cd
->u
.val
;
833 /* Lots of very dull type conversion functions */
834 void *exe_inttostr(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
835 struct coercedata
*cd
=thenode
->localdata
;
837 sprintf(cd
->u
.stringbuf
, "%lu", (unsigned long)(cd
->child
->exe
)(ctx
, cd
->child
, theinput
));
839 return cd
->u
.stringbuf
;
842 void *exe_booltostr(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
843 struct coercedata
*cd
=thenode
->localdata
;
845 if ((cd
->child
->exe
)(ctx
, cd
->child
, theinput
)) {
846 sprintf(cd
->u
.stringbuf
,"1");
848 cd
->u
.stringbuf
[0]='\0';
851 return cd
->u
.stringbuf
;
854 void *exe_strtoint(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
855 struct coercedata
*cd
=thenode
->localdata
;
857 return (void *)strtoul((cd
->child
->exe
)(ctx
,cd
->child
,theinput
),NULL
,10);
860 void *exe_booltoint(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
861 struct coercedata
*cd
=thenode
->localdata
;
863 /* Don't need to do anything */
864 return (cd
->child
->exe
)(ctx
, cd
->child
, theinput
);
867 void *exe_strtobool(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
868 struct coercedata
*cd
=thenode
->localdata
;
869 char *ch
=(cd
->child
->exe
)(ctx
, cd
->child
, theinput
);
871 if (!ch
|| *ch
=='\0' || (*ch
=='0' && ch
[1]=='\0')) {
878 void *exe_inttobool(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
879 struct coercedata
*cd
=thenode
->localdata
;
881 if ((cd
->child
->exe
)(ctx
, cd
->child
, theinput
)) {
888 struct searchNode
*coerceNode(searchCtx
*ctx
, struct searchNode
*thenode
, int type
) {
889 struct searchNode
*anode
;
890 struct coercedata
*cd
;
892 /* You can't coerce a NULL */
896 /* No effort required to coerce to the same type */
897 if (type
==(thenode
->returntype
& RETURNTYPE_TYPE
))
900 anode
=(struct searchNode
*)malloc(sizeof(struct searchNode
));
901 anode
->localdata
=cd
=(struct coercedata
*)malloc(sizeof(struct coercedata
));
903 anode
->returntype
=type
; /* We'll return what they want, always */
904 anode
->free
=free_coerce
;
907 case RETURNTYPE_STRING
:
908 /* For a string we'll need a buffer */
909 /* A 64-bit number prints out to 20 digits, this leaves some slack */
910 cd
->u
.stringbuf
=malloc(25);
911 anode
->free
=free_coercestring
;
913 switch(thenode
->returntype
& RETURNTYPE_TYPE
) {
916 if (thenode
->returntype
& RETURNTYPE_CONST
) {
917 /* Constant node: sort it out now */
918 sprintf(cd
->u
.stringbuf
, "%lu", (unsigned long)thenode
->exe(ctx
, thenode
, NULL
));
919 anode
->exe
=exe_tostr_null
;
920 anode
->returntype
|= RETURNTYPE_CONST
;
923 anode
->exe
=exe_inttostr
;
927 case RETURNTYPE_BOOL
:
928 if (thenode
->returntype
& RETURNTYPE_CONST
) {
929 /* Constant bool value */
930 if (thenode
->exe(ctx
, thenode
,NULL
)) {
932 sprintf(cd
->u
.stringbuf
, "1");
934 cd
->u
.stringbuf
[0] = '\0';
936 anode
->exe
=exe_tostr_null
;
937 anode
->returntype
|= RETURNTYPE_CONST
;
939 /* Variable bool value */
940 anode
->exe
=exe_booltostr
;
948 switch (thenode
->returntype
& RETURNTYPE_TYPE
) {
949 case RETURNTYPE_STRING
:
950 if (thenode
->returntype
& RETURNTYPE_CONST
) {
951 cd
->u
.val
=strtoul((thenode
->exe
)(ctx
, thenode
, NULL
), NULL
, 10);
952 anode
->exe
=exe_val_null
;
953 anode
->returntype
|= RETURNTYPE_CONST
;
955 anode
->exe
=exe_strtoint
;
960 case RETURNTYPE_BOOL
:
961 if (thenode
->returntype
& RETURNTYPE_CONST
) {
962 if ((thenode
->exe
)(ctx
, thenode
,NULL
))
967 anode
->exe
=exe_val_null
;
968 anode
->returntype
|= RETURNTYPE_CONST
;
970 anode
->exe
=exe_booltoint
;
977 case RETURNTYPE_BOOL
:
979 switch (thenode
->returntype
& RETURNTYPE_TYPE
) {
980 case RETURNTYPE_STRING
:
981 if (thenode
->returntype
& RETURNTYPE_CONST
) {
982 char *rv
=(char *)((thenode
->exe
)(ctx
, thenode
, NULL
));
983 if (!rv
|| *rv
=='\0' || (*rv
=='0' && rv
[1]=='\0'))
988 anode
->exe
=exe_val_null
;
989 anode
->returntype
|= RETURNTYPE_CONST
;
991 anode
->exe
=exe_strtobool
;
997 if (thenode
->returntype
& RETURNTYPE_CONST
) {
998 if ((thenode
->exe
)(ctx
, thenode
,NULL
))
1003 anode
->exe
=exe_val_null
;
1004 anode
->returntype
|= RETURNTYPE_CONST
;
1006 anode
->exe
=exe_inttobool
;
1016 /* Literals always return constant strings... */
1017 void *literal_exe(searchCtx
*ctx
, struct searchNode
*thenode
, void *theinput
) {
1018 if (thenode
->localdata
)
1019 return ((sstring
*)thenode
->localdata
)->content
;
1024 void literal_free(searchCtx
*ctx
, struct searchNode
*thenode
) {
1025 freesstring(thenode
->localdata
);
1029 void nssnprintf(char *buf
, size_t size
, const char *format
, nick
*np
) {
1042 for(p
=format
;*p
;p
++) {
1044 if(!sbaddchar(&b
, *p
))
1052 if(!sbaddchar(&b
, *p
))
1060 c
= np
->nick
; break;
1062 c
= np
->ident
; break;
1064 c
= np
->host
->name
->content
; break;
1066 snprintf(hostbuf
, sizeof(hostbuf
), "%s", IPtostr(np
->ipaddress
));
1070 snprintf(hostbuf
, sizeof(hostbuf
), "%s!%s@%s", np
->nick
, np
->ident
, IPtostr(np
->ipaddress
));
1074 c
= "(bad format specifier)";
1077 if(!sbaddstr(&b
, c
))
1089 static char *var_tochar(searchCtx
*ctx
, char *arg
, searchNode
**variable
) {
1090 *variable
= ctx
->parser(ctx
, arg
);
1091 if (!(*variable
= coerceNode(ctx
, *variable
, RETURNTYPE_STRING
)))
1094 if(!((*variable
)->returntype
& RETURNTYPE_CONST
)) {
1095 parseError
= "only constant variables allowed";
1096 ((*variable
)->free
)(ctx
, *variable
);
1100 return (char *)((*variable
)->exe
)(ctx
, *variable
, NULL
);
1103 void free_val_null(searchCtx
*ctx
, struct searchNode
*thenode
) {
1106 struct searchVariable
*var_register(searchCtx
*ctx
, char *arg
, int type
) {
1107 searchNode
*variable
;
1108 struct searchVariable
*us
;
1112 if(ctx
->lastvar
>= MAX_VARIABLES
) {
1113 parseError
= "Maximum number of variables reached";
1117 us
= &ctx
->vars
[ctx
->lastvar
];
1119 var
= var_tochar(ctx
, arg
, &variable
);
1123 strlcpy(us
->name
, var
, sizeof(us
->name
));
1124 (variable
->free
)(ctx
, variable
);
1126 for(i
=0;i
<ctx
->lastvar
;i
++) {
1127 if(!strcmp(us
->name
, ctx
->vars
[i
].name
)) {
1128 parseError
= "variable name already in use";
1134 us
->data
.returntype
= type
;
1135 us
->data
.localdata
= &us
->cdata
;
1136 us
->data
.exe
= exe_val_null
;
1137 us
->data
.free
= free_val_null
;
1139 us
->cdata
.child
= NULL
;
1143 searchNode
*var_get(searchCtx
*ctx
, char *arg
) {
1144 searchNode
*variable
, *found
= NULL
;
1146 char *var
= var_tochar(ctx
, arg
, &variable
);
1150 for(i
=0;i
<ctx
->lastvar
;i
++) {
1151 if(!strcmp(var
, ctx
->vars
[i
].name
)) {
1152 found
= &ctx
->vars
[i
].data
;
1156 (variable
->free
)(ctx
, variable
);
1159 parseError
= "variable not found";
1163 void var_setstr(struct searchVariable
*v
, char *data
) {
1164 v
->cdata
.u
.stringbuf
= data
;
1167 void displaystrerror(replyFunc reply
, nick
*np
, const char *input
) {
1170 if((parseStrErrorPos
>= 0) && (parseStrErrorPos
< sizeof(buf
) - 3)) {
1173 for(i
=0;i
<parseStrErrorPos
;i
++)
1179 reply(np
, "%s", input
);
1180 reply(np
, "%s", buf
);
1183 reply(np
, "Parse error: %s", parseStrError
);
1186 struct searchNode
*argtoconststr(char *command
, searchCtx
*ctx
, char *arg
, char **p
) {
1187 struct searchNode
*c
;
1188 static char errorbuf
[512];
1190 c
= ctx
->parser(ctx
, arg
);
1191 if (!(c
= coerceNode(ctx
, c
, RETURNTYPE_STRING
))) {
1192 snprintf(errorbuf
, sizeof(errorbuf
), "%s: unable to coerce argument to string", command
);
1193 parseError
= errorbuf
;
1197 if (!(c
->returntype
& RETURNTYPE_CONST
)) {
1198 snprintf(errorbuf
, sizeof(errorbuf
), "%s: constant argument required", command
);
1199 parseError
= errorbuf
;
1205 *p
= (char *)(c
->exe
)(ctx
, c
, NULL
);