]> jfr.im git - irc/quakenet/snircd.git/blame - ircd/m_silence.c
Update my e-mail address.
[irc/quakenet/snircd.git] / ircd / m_silence.c
CommitLineData
189935b1 1/*
2 * IRC - Internet Relay Chat, ircd/m_silence.c
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Computing Center
5 *
6 * See file AUTHORS in IRC package for additional names of
7 * the programmers.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 1, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 */
23/** @file
24 * @brief Handlers for SILENCE command.
11f9ccfa 25 * @version $Id: m_silence.c,v 1.11.2.2 2007/03/27 02:54:43 entrope Exp $
189935b1 26 */
27
28#include "config.h"
29
30#include "channel.h"
31#include "client.h"
32#include "hash.h"
33#include "ircd.h"
34#include "ircd_features.h"
35#include "ircd_log.h"
36#include "ircd_reply.h"
37#include "ircd_snprintf.h"
38#include "ircd_string.h"
39#include "list.h"
40#include "msg.h"
41#include "numeric.h"
42#include "numnicks.h"
43#include "s_user.h"
44#include "send.h"
45#include "struct.h"
46
47/* #include <assert.h> -- Now using assert in ircd_log.h */
48#include <stdlib.h>
49#include <string.h>
50
51/** Attempt to apply a SILENCE update to a user.
52 *
53 * Silences are propagated lazily between servers to save on bandwidth
54 * and remote memory. Any removal and any silence exception must be
55 * propagated until a server has not seen the mask being removed or
56 * has no positive silences for the user.
57 *
58 * @param[in] sptr Client to update.
59 * @param[in] mask Single silence mask to apply, optionally preceded by '+' or '-' and maybe '~'.
60 * @return The new ban entry on success, NULL on failure.
61 */
62static struct Ban *
63apply_silence(struct Client *sptr, char *mask)
64{
65 struct Ban *sile;
66 int flags;
11f9ccfa 67 int res;
9f8856e9 68 char orig_mask[NICKLEN+USERLEN+HOSTLEN+3];
189935b1 69
70 assert(mask && mask[0]);
71
72 /* Check for add or remove. */
73 if (mask[0] == '-') {
74 flags = BAN_DEL;
75 mask++;
76 } else if (mask[0] == '+') {
77 flags = BAN_ADD;
78 mask++;
79 } else
80 flags = BAN_ADD;
81
82 /* Check for being an exception. */
83 if (mask[0] == '~') {
84 flags |= BAN_EXCEPTION;
85 mask++;
86 }
87
9f8856e9 88 /* Make the silence and set additional flags. */
89 ircd_strncpy(orig_mask, mask, sizeof(orig_mask) - 1);
189935b1 90 sile = make_ban(pretty_mask(mask));
91 sile->flags |= flags;
9f8856e9 92
93 /* If they're a local user trying to ban too broad a mask, forbid it. */
94 if (MyUser(sptr)
95 && (sile->flags & BAN_IPMASK)
96 && sile->addrbits > 0
97 && sile->addrbits < (irc_in_addr_is_ipv4(&sile->address) ? 112 : 32)) {
98 send_reply(sptr, ERR_MASKTOOWIDE, orig_mask);
99 free_ban(sile);
100 return NULL;
101 }
102
103 /* Apply it to the silence list. */
11f9ccfa 104 res = apply_ban(&cli_user(sptr)->silence, sile, 1);
105 return res ? NULL : sile;
189935b1 106}
107
108/** Apply and send silence updates for a user.
109 * @param[in] sptr Client whose silence list has been updated.
110 * @param[in] silences Comma-separated list of silence updates.
111 * @param[in] dest Direction to send updates in (NULL for broadcast).
112 */
113static void
114forward_silences(struct Client *sptr, char *silences, struct Client *dest)
115{
116 struct Ban *accepted[MAXPARA], *sile, **plast;
117 char *cp, *p, buf[BUFSIZE];
118 size_t ac_count, buf_used, slen, ii;
119
120 /* Split the list of silences and try to apply each one in turn. */
121 for (cp = ircd_strtok(&p, silences, ","), ac_count = 0;
122 cp && (ac_count < MAXPARA);
123 cp = ircd_strtok(&p, 0, ",")) {
124 if ((sile = apply_silence(sptr, cp)))
125 accepted[ac_count++] = sile;
126 }
127
189935b1 128 if (MyUser(sptr)) {
129 size_t siles, maxsiles, totlength, maxlength, jj;
130
131 /* Check that silence count and total length are permitted. */
132 maxsiles = feature_int(FEAT_MAXSILES);
133 maxlength = maxsiles * feature_int(FEAT_AVBANLEN);
134 siles = totlength = 0;
135 /* Count number of current silences and their total length. */
136 plast = &cli_user(sptr)->silence;
137 for (sile = cli_user(sptr)->silence; sile; sile = sile->next) {
138 if (sile->flags & (BAN_OVERLAPPED | BAN_ADD | BAN_DEL))
139 continue;
140 siles++;
141 totlength += strlen(sile->banstr);
142 plast = &sile->next;
143 }
144 for (ii = jj = 0; ii < ac_count; ++ii) {
145 sile = accepted[ii];
146 /* If the update is being added, and we would exceed the maximum
147 * count or length, drop the update.
148 */
149 if (!(sile->flags & (BAN_OVERLAPPED | BAN_DEL))) {
150 slen = strlen(sile->banstr);
151 if ((siles >= maxsiles) || (totlength + slen >= maxlength)) {
152 *plast = NULL;
153 if (MyUser(sptr))
154 send_reply(sptr, ERR_SILELISTFULL, accepted[ii]->banstr);
155 free_ban(accepted[ii]);
156 continue;
157 }
158 /* Update counts. */
159 siles++;
160 totlength += slen;
161 plast = &sile->next;
162 }
163 /* Store the update. */
164 accepted[jj++] = sile;
165 }
166 /* Write back the number of accepted updates. */
167 ac_count = jj;
168
169 /* Send the silence update list, including overlapped silences (to
170 * make it easier on clients).
171 */
172 buf_used = 0;
173 for (sile = cli_user(sptr)->silence; sile; sile = sile->next) {
174 char ch;
175 if (sile->flags & (BAN_OVERLAPPED | BAN_DEL))
176 ch = '-';
177 else if (sile->flags & BAN_ADD)
178 ch = '+';
179 else
180 continue;
181 slen = strlen(sile->banstr);
182 if (buf_used + slen + 4 > 400) {
183 buf[buf_used] = '\0';
184 sendcmdto_one(sptr, CMD_SILENCE, sptr, "%s", buf);
185 buf_used = 0;
186 }
187 if (buf_used)
188 buf[buf_used++] = ',';
189 buf[buf_used++] = ch;
190 if (sile->flags & BAN_EXCEPTION)
191 buf[buf_used++] = '~';
192 memcpy(buf + buf_used, sile->banstr, slen);
193 buf_used += slen;
194 }
195 if (buf_used > 0) {
196 buf[buf_used] = '\0';
197 sendcmdto_one(sptr, CMD_SILENCE, sptr, "%s", buf);
198 buf_used = 0;
199 }
200 }
201
202 /* Forward any silence removals or exceptions updates to other
203 * servers if the user has positive silences.
204 */
205 if (!dest || !MyUser(dest)) {
206 for (ii = buf_used = 0; ii < ac_count; ++ii) {
207 char ch;
208 sile = accepted[ii];
209 if (sile->flags & BAN_OVERLAPPED)
210 continue;
211 else if (sile->flags & BAN_DEL)
212 ch = '-';
213 else if (sile->flags & BAN_ADD) {
214 if (!(sile->flags & BAN_EXCEPTION))
215 continue;
216 ch = '+';
217 } else
218 continue;
219 slen = strlen(sile->banstr);
220 if (buf_used + slen + 4 > 400) {
221 buf[buf_used] = '\0';
222 if (dest)
223 sendcmdto_one(sptr, CMD_SILENCE, dest, "%C %s", dest, buf);
224 else
225 sendcmdto_serv_butone(sptr, CMD_SILENCE, sptr, "* %s", buf);
226 buf_used = 0;
227 }
228 if (buf_used)
229 buf[buf_used++] = ',';
230 buf[buf_used++] = ch;
231 if (sile->flags & BAN_EXCEPTION)
232 buf[buf_used++] = '~';
233 memcpy(buf + buf_used, sile->banstr, slen);
234 buf_used += slen;
235 }
236 if (buf_used > 0) {
237 buf[buf_used] = '\0';
238 if (dest)
239 sendcmdto_one(sptr, CMD_SILENCE, dest, "%C %s", dest, buf);
240 else
241 sendcmdto_serv_butone(sptr, CMD_SILENCE, sptr, "* %s", buf);
242 buf_used = 0;
243 }
244 }
245
246 /* Remove overlapped and deleted silences from the user's silence
247 * list. Clear BAN_ADD since we're walking the list anyway.
248 */
249 for (plast = &cli_user(sptr)->silence; (sile = *plast) != NULL; ) {
250 if (sile->flags & (BAN_OVERLAPPED | BAN_DEL)) {
251 *plast = sile->next;
252 free_ban(sile);
253 } else {
254 sile->flags &= ~BAN_ADD;
255 *plast = sile;
256 plast = &sile->next;
257 }
258 }
259
260 /* Free any silence-deleting updates. */
261 for (ii = 0; ii < ac_count; ++ii) {
11f9ccfa 262 if ((accepted[ii]->flags & (BAN_ADD | BAN_DEL)) == BAN_DEL) {
189935b1 263 free_ban(accepted[ii]);
11f9ccfa 264 }
189935b1 265 }
266}
267
268/** Handle a SILENCE command from a local user.
269 * See @ref m_functions for general discussion of parameters.
270 *
271 * \a parv[1] may be any of the following:
272 * \li Omitted or empty, to view your own silence list.
273 * \li Nickname of a user, to view that user's silence list.
274 * \li A comma-separated list of silence updates
275 *
276 * @param[in] cptr Client that sent us the message.
277 * @param[in] sptr Original source of message.
278 * @param[in] parc Number of arguments.
279 * @param[in] parv Argument vector.
280 */
281int m_silence(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
282{
283 struct Client *acptr;
284 struct Ban *sile;
285
286 assert(0 != cptr);
287 assert(cptr == sptr);
288
289 /* See if the user is requesting a silence list. */
290 acptr = sptr;
291 if (parc < 2 || EmptyString(parv[1]) || (acptr = FindUser(parv[1]))) {
292 if (cli_user(acptr)) {
293 for (sile = cli_user(acptr)->silence; sile; sile = sile->next) {
294 send_reply(sptr, RPL_SILELIST, cli_name(acptr),
295 (sile->flags & BAN_EXCEPTION ? "~" : ""), sile->banstr);
296 }
297 }
298 send_reply(sptr, RPL_ENDOFSILELIST, cli_name(acptr));
299 return 0;
300 }
301
302 /* The user must be attempting to update their list. */
303 forward_silences(sptr, parv[1], NULL);
304 return 0;
305}
306
307/** Handle a SILENCE command from a server.
308 * See @ref m_functions for general discussion of parameters.
309 *
310 * \a parv[1] may be one of the following:
311 * \li "*" to indicate a broadcast update (removing a SILENCE)
312 * \li A client numnick that should be specifically SILENCEd.
313 *
314 * \a parv[2] is a comma-separated list of silence updates.
315 *
316 * @param[in] cptr Client that sent us the message.
317 * @param[in] sptr Original source of message.
318 * @param[in] parc Number of arguments.
319 * @param[in] parv Argument vector.
320 */
321int ms_silence(struct Client* cptr, struct Client* sptr, int parc, char* parv[])
322{
323 if (IsServer(sptr))
324 return protocol_violation(sptr, "Server trying to silence a user");
325 if (parc < 3 || EmptyString(parv[2]))
326 return need_more_params(sptr, "SILENCE");
327
328 /* Figure out which silences can be forwarded. */
329 forward_silences(sptr, parv[2], findNUser(parv[1]));
330 return 0;
331 (void)cptr;
332}