]> jfr.im git - solanum.git/blob - modules/m_list.c
LIST: more cleanups
[solanum.git] / modules / m_list.c
1 /*
2 * charybdis: An advanced ircd.
3 * m_list_safelist.c: Version of /list that uses the safelist code.
4 *
5 * Copyright (c) 2006 William Pitcock <nenolod@nenolod.net>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions are
9 * met:
10 *
11 * 1. Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 *
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 *
18 * 3. The name of the author may not be used to endorse or promote products
19 * derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 *
33 * $Id: m_list.c 3372 2007-04-03 10:18:07Z nenolod $
34 */
35
36 #include "stdinc.h"
37 #include "channel.h"
38 #include "client.h"
39 #include "hash.h"
40 #include "match.h"
41 #include "ircd.h"
42 #include "numeric.h"
43 #include "s_conf.h"
44 #include "s_newconf.h"
45 #include "s_serv.h"
46 #include "supported.h"
47 #include "send.h"
48 #include "msg.h"
49 #include "parse.h"
50 #include "modules.h"
51 #include "inline/stringops.h"
52 #include "s_assert.h"
53 #include "logger.h"
54
55 static int _modinit(void);
56 static void _moddeinit(void);
57
58 static int m_list(struct Client *, struct Client *, int, const char **);
59 static int mo_list(struct Client *, struct Client *, int, const char **);
60
61 struct ListOptions
62 {
63 unsigned int users_min, users_max;
64 time_t created_min, created_max, topic_min, topic_max;
65 int operspy;
66 };
67
68 static void list_one_channel(struct Client *source_p, struct Channel *chptr, int visible);
69
70 static void safelist_one_channel(struct Client *source_p, struct Channel *chptr, struct ListOptions *params);
71 static void safelist_channel_named(struct Client *source_p, const char *name, int operspy);
72
73 struct Message list_msgtab = {
74 "LIST", 0, 0, 0, MFLG_SLOW,
75 {mg_unreg, {m_list, 0}, mg_ignore, mg_ignore, mg_ignore, {mo_list, 0}}
76 };
77
78 mapi_clist_av1 list_clist[] = { &list_msgtab, NULL };
79
80 DECLARE_MODULE_AV1(list, _modinit, _moddeinit, list_clist, NULL, NULL, "$Revision: 3372 $");
81
82 static int _modinit(void)
83 {
84 /* ELIST=[tokens]:
85 *
86 * M = mask search
87 * N = !mask search
88 * U = user count search (< >)
89 * C = creation time search (C> C<)
90 * T = topic search (T> T<)
91 */
92 add_isupport("SAFELIST", isupport_string, "");
93 add_isupport("ELIST", isupport_string, "CTU");
94
95 return 0;
96 }
97
98 static void _moddeinit(void)
99 {
100 delete_isupport("SAFELIST");
101 delete_isupport("ELIST");
102 }
103
104 /* m_list()
105 * parv[1] = channel
106 *
107 * XXX - With SAFELIST, do we really need to continue pacing?
108 * In theory, the server cannot be lagged by this. --nenolod
109 */
110 static int m_list(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
111 {
112 static time_t last_used = 0L;
113
114 if (parc < 2 || !IsChannelName(parv[1]))
115 {
116 /* pace this due to the sheer traffic involved */
117 if (((last_used + ConfigFileEntry.pace_wait) > rb_current_time()))
118 {
119 sendto_one(source_p, form_str(RPL_LOAD2HI), me.name, source_p->name, "LIST");
120 sendto_one(source_p, form_str(RPL_LISTEND), me.name, source_p->name);
121 return 0;
122 }
123 else
124 last_used = rb_current_time();
125 }
126
127 return mo_list(client_p, source_p, parc, parv);
128 }
129
130 /* mo_list()
131 * parv[1] = channel
132 */
133 static int mo_list(struct Client *client_p, struct Client *source_p, int parc, const char *parv[])
134 {
135 struct ListOptions *params;
136 char *p;
137 char *args = NULL;
138 int i;
139 int operspy = 0;
140 int sendq_limit = get_sendq_hard(client_p);
141 rb_dlink_node *ptr;
142
143 sendq_limit /= 10;
144 sendq_limit *= 9;
145
146 if (parc > 1)
147 {
148 args = LOCAL_COPY(parv[1]);
149 }
150
151 if (args && *args == '!' && IsOperSpy(source_p))
152 {
153 args++;
154 report_operspy(source_p, "LIST", args);
155 operspy = 1;
156 }
157
158 /* Single channel. */
159 if (args && IsChannelName(args))
160 {
161 safelist_channel_named(source_p, args, operspy);
162 return 0;
163 }
164
165 /* Multiple channels, possibly with parameters. */
166 params = rb_malloc(sizeof(struct ListOptions));
167
168 params->users_min = ConfigChannel.displayed_usercount;
169 params->users_max = INT_MAX;
170 params->operspy = operspy;
171 params->created_min = params->topic_min =
172 params->created_max = params->topic_max = 0;
173
174 if (args && !EmptyString(args))
175 {
176 /* Cancel out default minimum. */
177 params->users_min = 0;
178
179 for (i = 0; i < 7; i++)
180 {
181 if ((p = strchr(args, ',')) != NULL)
182 *p++ = '\0';
183
184 if (*args == '<')
185 {
186 args++;
187 if (IsDigit(*args))
188 {
189 params->users_max = atoi(args);
190 if (params->users_max == 0)
191 params->users_max = INT_MAX;
192 else
193 params->users_max--;
194 }
195 }
196 else if (*args == '>')
197 {
198 args++;
199 if (IsDigit(*args))
200 params->users_min = atoi(args) + 1;
201 else
202 params->users_min = 0;
203 }
204 else if (*args == 'C' || *args == 'c')
205 {
206 args++;
207 if (*args == '>')
208 {
209 /* Creation time earlier than last x minutes. */
210 args++;
211 if (IsDigit(*args))
212 {
213 params->created_max = rb_current_time() - (60 * atoi(args));
214 }
215 }
216 else if (*args == '<')
217 {
218 /* Creation time within last x minutes. */
219 args++;
220 if (IsDigit(*args))
221 {
222 params->created_min = rb_current_time() - (60 * atoi(args));
223 }
224 }
225 }
226 else if (*args == 'T' || *args == 't')
227 {
228 args++;
229 if (*args == '>')
230 {
231 /* Topic change time earlier than last x minutes. */
232 args++;
233 if (IsDigit(*args))
234 {
235 params->topic_max = rb_current_time() - (60 * atoi(args));
236 }
237 }
238 else if (*args == '<')
239 {
240 /* Topic change time within last x minutes. */
241 args++;
242 if (IsDigit(*args))
243 {
244 params->topic_min = rb_current_time() - (60 * atoi(args));
245 }
246 }
247 }
248
249 if (EmptyString(p))
250 break;
251 else
252 args = p;
253 }
254 }
255
256 sendto_one(client_p, form_str(RPL_LISTSTART), me.name, client_p->name);
257
258 RB_DLINK_FOREACH(ptr, global_channel_list.head)
259 {
260 safelist_one_channel(client_p, ptr->data, params);
261
262 if (rb_linebuf_len(&client_p->localClient->buf_sendq) > sendq_limit)
263 {
264 sendto_one(source_p, form_str(ERR_TOOMANYMATCHES), me.name, source_p->name, "LIST");
265 break;
266 }
267 }
268
269 sendto_one(source_p, form_str(RPL_LISTEND), me.name, source_p->name);
270
271 return 0;
272 }
273
274 /*
275 * list_one_channel()
276 *
277 * inputs - client pointer, channel pointer, whether normally visible
278 * outputs - none
279 * side effects - a channel is listed
280 */
281 static void list_one_channel(struct Client *source_p, struct Channel *chptr,
282 int visible)
283 {
284 char topic[TOPICLEN + 1];
285
286 if (chptr->topic != NULL)
287 rb_strlcpy(topic, chptr->topic, sizeof topic);
288 else
289 topic[0] = '\0';
290 strip_colour(topic);
291 sendto_one(source_p, form_str(RPL_LIST), me.name, source_p->name,
292 visible ? "" : "!",
293 chptr->chname, rb_dlink_list_length(&chptr->members),
294 topic);
295 }
296
297 /*
298 * safelist_sendq_exceeded()
299 *
300 * inputs - pointer to client that needs checking
301 * outputs - 1 if a client has exceeded the reserved
302 * sendq limit, 0 if not
303 * side effects - none
304 *
305 * When safelisting, we only use half of the SendQ at any
306 * given time.
307 */
308 static int safelist_sendq_exceeded(struct Client *client_p)
309 {
310 if (rb_linebuf_len(&client_p->localClient->buf_sendq) > (get_sendq(client_p) / 2))
311 return YES;
312 else
313 return NO;
314 }
315
316 /*
317 * safelist_channel_named()
318 *
319 * inputs - client pointer, channel name, operspy
320 * outputs - none
321 * side effects - a named channel is listed
322 */
323 static void safelist_channel_named(struct Client *source_p, const char *name, int operspy)
324 {
325 struct Channel *chptr;
326 char *p;
327 int visible;
328
329 sendto_one(source_p, form_str(RPL_LISTSTART), me.name, source_p->name);
330
331 if ((p = strchr(name, ',')))
332 *p = '\0';
333
334 if (*name == '\0')
335 {
336 sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), name);
337 sendto_one(source_p, form_str(RPL_LISTEND), me.name, source_p->name);
338 return;
339 }
340
341 chptr = find_channel(name);
342
343 if (chptr == NULL)
344 {
345 sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), name);
346 sendto_one(source_p, form_str(RPL_LISTEND), me.name, source_p->name);
347 return;
348 }
349
350 visible = !SecretChannel(chptr) || IsMember(source_p, chptr);
351 if (visible || operspy)
352 list_one_channel(source_p, chptr, visible);
353
354 sendto_one(source_p, form_str(RPL_LISTEND), me.name, source_p->name);
355 return;
356 }
357
358 /*
359 * safelist_one_channel()
360 *
361 * inputs - client pointer and channel pointer
362 * outputs - none
363 * side effects - a channel is listed if it meets the
364 * requirements
365 */
366 static void safelist_one_channel(struct Client *source_p, struct Channel *chptr, struct ListOptions *params)
367 {
368 int visible;
369
370 visible = !SecretChannel(chptr) || IsMember(source_p, chptr);
371 if (!visible && !params->operspy)
372 return;
373
374 if ((unsigned int)chptr->members.length < params->users_min
375 || (unsigned int)chptr->members.length > params->users_max)
376 return;
377
378 if (params->topic_min && chptr->topic_time < params->topic_min)
379 return;
380
381 /* If a topic TS is provided, don't show channels without a topic set. */
382 if (params->topic_max && (chptr->topic_time > params->topic_max
383 || chptr->topic_time == 0))
384 return;
385
386 if (params->created_min && chptr->channelts < params->created_min)
387 return;
388
389 if (params->created_max && chptr->channelts > params->created_max)
390 return;
391
392 list_one_channel(source_p, chptr, visible);
393 }