1 /*
2 * Copyright (c) 2005-2007 William Pitcock, et al.
3 * The rights to this code are as documented under doc/LICENSE.
4 *
5 * Copyright (C) 2003-2007 Lee Hardy <leeh@leeh.co.uk>
6 * Copyright (C) 2003-2007 ircd-ratbox development team
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * 1.Redistributions of source code must retain the above copyright notice,
13 * this list of conditions and the following disclaimer.
14 * 2.Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3.The name of the author may not be used to endorse or promote products
18 * derived from this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
21 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
29 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 *
32 * ALIS, based on the ratbox-services implementation.
33 *
34 */
35
36 #include "atheme.h"
37 #include <limits.h>
38
39 DECLARE_MODULE_V1
40 (
41 "alis/main", false, _modinit, _moddeinit,
42 PACKAGE_STRING,
43 "William Pitcock <nenolod -at- nenolod.net>"
44 );
45
46 #define ALIS_MAX_PARC 10
47 #define ALIS_MAX_MATCH 60
48
49 #define DIR_NONE -1
50 #define DIR_UNSET 0
51 #define DIR_SET 1
52 #define DIR_EQUAL 2
53
54 service_t *alis;
55
56 static void alis_cmd_list(sourceinfo_t *si, int parc, char *parv[]);
57 static void alis_cmd_help(sourceinfo_t *si, int parc, char *parv[]);
58
59 command_t alis_list = { "LIST", "Lists channels matching given parameters.",
60 AC_NONE, ALIS_MAX_PARC, alis_cmd_list, { .path = "alis/list" } };
61 command_t alis_help = { "HELP", "Displays contextual help information.",
62 AC_NONE, 1, alis_cmd_help, { .path = "help" } };
63
64 struct alis_query
65 {
66 char *mask;
67 char *topic;
68 int min;
69 int max;
70 int show_mode;
71 int show_topicwho;
72 unsigned int mode;
73 int mode_dir;
74 int mode_key;
75 int mode_limit;
76 int mode_ext[256];
77 int skip;
78 int maxmatches;
79 int showsecret;
80 };
81
_modinit(module_t * m)82 void _modinit(module_t *m)
83 {
84 alis = service_add("alis", NULL);
85 service_bind_command(alis, &alis_list);
86 service_bind_command(alis, &alis_help);
87 }
88
_moddeinit(module_unload_intent_t intent)89 void _moddeinit(module_unload_intent_t intent)
90 {
91 service_unbind_command(alis, &alis_list);
92 service_unbind_command(alis, &alis_help);
93
94 service_delete(alis);
95 }
96
alis_parse_mode(const char * text,int * key,int * limit,int * ext)97 static int alis_parse_mode(const char *text, int *key, int *limit, int *ext)
98 {
99 int mode = 0, i;
100
101 if(!text)
102 return 0;
103
104 while(*text)
105 {
106 switch(*text)
107 {
108 case 'l':
109 *limit = 1;
110 break;
111 case 'k':
112 *key = 1;
113 break;
114 default:
115 mode |= mode_to_flag(*text);
116 for (i = 0; ignore_mode_list[i].mode != '\0'; i++)
117 if (*text == ignore_mode_list[i].mode)
118 ext[i] = 1;
119 break;
120 }
121
122 text++;
123 }
124
125 return mode;
126 }
127
parse_alis(sourceinfo_t * si,int parc,char * parv[],struct alis_query * query)128 static int parse_alis(sourceinfo_t *si, int parc, char *parv[], struct alis_query *query)
129 {
130 int i = 1;
131 char *opt = NULL, *arg = NULL;
132
133 if (parc < 1)
134 query->mask = sstrdup("*");
135 else if (!VALID_GLOBAL_CHANNEL_PFX(parv[0]) && strchr(parv[0], '*') == NULL && strchr(parv[0], '?') == NULL)
136 {
137 size_t max = 1 + strlen(parv[0]) + 2;
138 query->mask = smalloc(max);
139 snprintf(query->mask, max, "*%s*", parv[0]);
140 }
141 else
142 query->mask = sstrdup(parv[0]);
143
144 query->mode_dir = DIR_NONE;
145 while ((opt = parv[i++]))
146 {
147 if(!strcasecmp(opt, "-min"))
148 {
149 if((arg = parv[i++]) == NULL || (query->min = atoi(arg)) < 1)
150 {
151 command_fail(si, fault_badparams, "Invalid -min option");
152 return 0;
153 }
154 }
155 else if(!strcasecmp(opt, "-max"))
156 {
157 if((arg = parv[i++]) == NULL || (query->max = atoi(arg)) < 1)
158 {
159 command_fail(si, fault_badparams, "Invalid -max option");
160 return 0;
161 }
162 }
163 else if(!strcasecmp(opt, "-maxmatches"))
164 {
165 if((arg = parv[i++]) == NULL || (query->maxmatches = atoi(arg)) == 0)
166 {
167 command_fail(si, fault_badparams, "Invalid -maxmatches option");
168 return 0;
169 }
170 if(si->su != NULL && !has_priv(si, PRIV_CHAN_AUSPEX))
171 {
172 if(query->maxmatches > ALIS_MAX_MATCH)
173 {
174 command_fail(si, fault_badparams, "Invalid -maxmatches option");
175 return 0;
176 }
177 if(query->maxmatches <= 0)
178 query->maxmatches = ALIS_MAX_MATCH;
179 }
180 else
181 {
182 if(query->maxmatches <= 0)
183 query->maxmatches = INT_MAX;
184 }
185 }
186 else if(!strcasecmp(opt, "-skip"))
187 {
188 if((arg = parv[i++]) == NULL || (query->skip = atoi(arg)) < 1)
189 {
190 command_fail(si, fault_badparams, "Invalid -skip option");
191 return 0;
192 }
193 }
194 else if(!strcasecmp(opt, "-topic"))
195 {
196 arg = parv[i++];
197
198 if (arg == NULL)
199 {
200 command_fail(si, fault_badparams, "Invalid -topic option");
201 return 0;
202 }
203
204 if (strchr(arg, '*') == NULL)
205 {
206 size_t max = 1 + strlen(arg) + 2;
207 query->topic = smalloc(max);
208 snprintf(query->topic, max, "*%s*", arg);
209 }
210 else
211 query->topic = sstrdup(arg);
212 }
213 else if(!strcasecmp(opt, "-show"))
214 {
215 arg = parv[i++];
216
217 if (arg == NULL)
218 {
219 command_fail(si, fault_badparams, "Invalid -show option");
220 return 0;
221 }
222
223 if(arg[0] == 'm')
224 {
225 query->show_mode = 1;
226
227 if(arg[1] == 't')
228 query->show_topicwho = 1;
229 }
230 else if(arg[0] == 't')
231 {
232 query->show_topicwho = 1;
233
234 if(arg[1] == 'm')
235 query->show_mode = 1;
236 }
237 }
238 else if(!strcasecmp(opt, "-mode"))
239 {
240 arg = parv[i++];
241
242 if (arg == NULL)
243 {
244 command_fail(si, fault_badparams, "Invalid -mode option");
245 return 0;
246 }
247
248 switch(*arg)
249 {
250 case '+':
251 query->mode_dir = DIR_SET;
252 break;
253 case '-':
254 query->mode_dir = DIR_UNSET;
255 break;
256 case '=':
257 query->mode_dir = DIR_EQUAL;
258 break;
259 default:
260 command_fail(si, fault_badparams, "Invalid -mode option");
261 return 0;
262 }
263
264 query->mode = alis_parse_mode(arg+1,
265 &query->mode_key,
266 &query->mode_limit,
267 query->mode_ext);
268 }
269 else if (!strcasecmp(opt, "-showsecret"))
270 {
271 if (!has_priv(si, PRIV_CHAN_AUSPEX))
272 {
273 command_fail(si, fault_noprivs, _("You are not authorized to perform this operation."));
274 return 0;
275 }
276
277 query->showsecret = 1;
278 }
279 else
280 {
281 command_fail(si, fault_badparams, "Invalid option %s", opt);
282 return 0;
283 }
284 }
285
286 return 1;
287 }
288
free_alis(struct alis_query * query)289 static void free_alis(struct alis_query *query)
290 {
291 return_if_fail(query != NULL);
292
293 if (query->mask)
294 free(query->mask);
295
296 if (query->topic)
297 free(query->topic);
298 }
299
print_channel(sourceinfo_t * si,channel_t * chptr,struct alis_query * query)300 static void print_channel(sourceinfo_t *si, channel_t *chptr, struct alis_query *query)
301 {
302 int show_topicwho = query->show_topicwho;
303 int show_topic = 1;
304 char topic[BUFSIZE];
305
306 /* cant show a topicwho, when a channel has no topic. */
307 if(!chptr->topic)
308 {
309 show_topicwho = 0;
310 show_topic = 0;
311 }
312 if(show_topic)
313 {
314 mowgli_strlcpy(topic, chptr->topic, sizeof topic);
315 strip_ctrl(topic);
316 }
317
318 if(query->show_mode && show_topicwho && show_topic)
319 command_success_nodata(si, "%-50s %-8s %3zu :%s (%s)",
320 chptr->name, channel_modes(chptr, false),
321 MOWGLI_LIST_LENGTH(&chptr->members),
322 topic, chptr->topic_setter);
323 else if(query->show_mode && show_topic)
324 command_success_nodata(si, "%-50s %-8s %3zu :%s",
325 chptr->name, channel_modes(chptr, false),
326 MOWGLI_LIST_LENGTH(&chptr->members),
327 topic);
328 else if(query->show_mode)
329 command_success_nodata(si, "%-50s %-8s %3zu",
330 chptr->name, channel_modes(chptr, false),
331 MOWGLI_LIST_LENGTH(&chptr->members));
332 else if(show_topicwho && show_topic)
333 command_success_nodata(si, "%-50s %3zu :%s (%s)",
334 chptr->name, MOWGLI_LIST_LENGTH(&chptr->members),
335 topic, chptr->topic_setter);
336 else if(show_topic)
337 command_success_nodata(si, "%-50s %3zu :%s",
338 chptr->name, MOWGLI_LIST_LENGTH(&chptr->members),
339 topic);
340 else
341 command_success_nodata(si, "%-50s %3zu",
342 chptr->name, MOWGLI_LIST_LENGTH(&chptr->members));
343 }
344
show_channel(channel_t * chptr,struct alis_query * query)345 static int show_channel(channel_t *chptr, struct alis_query *query)
346 {
347 int i;
348
349 /* skip +s channels unless -showsecret is used */
350 if(chptr->modes & CMODE_SEC && !query->showsecret)
351 return 0;
352
353 if((int)MOWGLI_LIST_LENGTH(&chptr->members) < query->min ||
354 (query->max && (int)MOWGLI_LIST_LENGTH(&chptr->members) > query->max))
355 return 0;
356
357 if(query->mode_dir == DIR_SET)
358 {
359 if(((chptr->modes & query->mode) != query->mode) ||
360 (query->mode_key && chptr->key == NULL) ||
361 (query->mode_limit && !chptr->limit))
362 return 0;
363 for (i = 0; ignore_mode_list[i].mode != '\0'; i++)
364 if (query->mode_ext[i] && !chptr->extmodes[i])
365 return 0;
366 }
367 else if(query->mode_dir == DIR_UNSET)
368 {
369 if((chptr->modes & query->mode) ||
370 (query->mode_key && chptr->key != NULL) ||
371 (query->mode_limit && chptr->limit))
372 return 0;
373 for (i = 0; ignore_mode_list[i].mode != '\0'; i++)
374 if (query->mode_ext[i] && chptr->extmodes[i])
375 return 0;
376 }
377 else if(query->mode_dir == DIR_EQUAL)
378 {
379 if(((chptr->modes & ~(CMODE_LIMIT | CMODE_KEY)) != query->mode) ||
380 (query->mode_key && chptr->key == NULL) ||
381 (query->mode_limit && !chptr->limit))
382 return 0;
383 for (i = 0; ignore_mode_list[i].mode != '\0'; i++)
384 if (query->mode_ext[i] && !chptr->extmodes[i])
385 return 0;
386 }
387
388 if(match(query->mask, chptr->name))
389 return 0;
390
391 if(query->topic != NULL && match(query->topic, chptr->topic))
392 return 0;
393
394 if(query->skip)
395 {
396 query->skip--;
397 return 0;
398 }
399
400 return 1;
401 }
402
alis_cmd_list(sourceinfo_t * si,int parc,char * parv[])403 static void alis_cmd_list(sourceinfo_t *si, int parc, char *parv[])
404 {
405 channel_t *chptr;
406 struct alis_query query;
407 mowgli_patricia_iteration_state_t state;
408 int maxmatch;
409
410 memset(&query, 0, sizeof(struct alis_query));
411 query.maxmatches = ALIS_MAX_MATCH;
412
413 if (!parse_alis(si, parc, parv, &query))
414 {
415 free_alis(&query);
416 return;
417 }
418
419 logcommand(si, CMDLOG_GET, "LIST: \2%s\2", query.mask);
420
421 maxmatch = query.maxmatches;
422 command_success_nodata(si,
423 "Returning maximum of %d channel names matching '\2%s\2'",
424 query.maxmatches, query.mask);
425
426 /* hunting for one channel.. */
427 if(strchr(query.mask, '*') == NULL && strchr(query.mask, '?') == NULL)
428 {
429 if((chptr = channel_find(query.mask)) != NULL)
430 {
431 if(!(chptr->modes & CMODE_SEC) ||
432 (si->su != NULL &&
433 chanuser_find(chptr, si->su)))
434 print_channel(si, chptr, &query);
435 }
436
437 command_success_nodata(si, "End of output");
438 free_alis(&query);
439 return;
440 }
441
442 MOWGLI_PATRICIA_FOREACH(chptr, &state, chanlist)
443 {
444 /* matches, so show it */
445 if(show_channel(chptr, &query))
446 {
447 print_channel(si, chptr, &query);
448
449 if(--maxmatch == 0)
450 {
451 command_success_nodata(si, "Maximum channel output reached");
452 break;
453 }
454 }
455 }
456
457 command_success_nodata(si, "End of output");
458 free_alis(&query);
459 return;
460 }
461
alis_cmd_help(sourceinfo_t * si,int parc,char * parv[])462 static void alis_cmd_help(sourceinfo_t *si, int parc, char *parv[])
463 {
464 const char *command = parv[0];
465
466 if (command == NULL)
467 {
468 command_success_nodata(si, _("***** \2%s Help\2 *****"), alis->nick);
469 command_success_nodata(si, _("\2%s\2 allows searching for channels with more\n"
470 "flexibility than the /list command."),
471 alis->nick);
472 command_success_nodata(si, " ");
473 command_success_nodata(si, _("For more information on a command, type:"));
474 command_success_nodata(si, "\2/%s%s help <command>\2", (ircd->uses_rcommand == false) ? "msg " : "", alis->disp);
475 command_success_nodata(si, " ");
476 command_help(si, alis->commands);
477 command_success_nodata(si, _("***** \2End of Help\2 *****"));
478 return;
479 }
480
481 help_display(si, si->service, command, alis->commands);
482 }
483