]>
Commit | Line | Data |
---|---|---|
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. | |
25 | * @version $Id: m_silence.c,v 1.11 2005/04/22 23:36:31 entrope Exp $ | |
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 | */ | |
62 | static struct Ban * | |
63 | apply_silence(struct Client *sptr, char *mask) | |
64 | { | |
65 | struct Ban *sile; | |
66 | int flags; | |
67 | ||
68 | assert(mask && mask[0]); | |
69 | ||
70 | /* Check for add or remove. */ | |
71 | if (mask[0] == '-') { | |
72 | flags = BAN_DEL; | |
73 | mask++; | |
74 | } else if (mask[0] == '+') { | |
75 | flags = BAN_ADD; | |
76 | mask++; | |
77 | } else | |
78 | flags = BAN_ADD; | |
79 | ||
80 | /* Check for being an exception. */ | |
81 | if (mask[0] == '~') { | |
82 | flags |= BAN_EXCEPTION; | |
83 | mask++; | |
84 | } | |
85 | ||
86 | /* Make the silence, set flags, and apply it. */ | |
87 | sile = make_ban(pretty_mask(mask)); | |
88 | sile->flags |= flags; | |
89 | return apply_ban(&cli_user(sptr)->silence, sile, 1) ? NULL : sile; | |
90 | } | |
91 | ||
92 | /** Apply and send silence updates for a user. | |
93 | * @param[in] sptr Client whose silence list has been updated. | |
94 | * @param[in] silences Comma-separated list of silence updates. | |
95 | * @param[in] dest Direction to send updates in (NULL for broadcast). | |
96 | */ | |
97 | static void | |
98 | forward_silences(struct Client *sptr, char *silences, struct Client *dest) | |
99 | { | |
100 | struct Ban *accepted[MAXPARA], *sile, **plast; | |
101 | char *cp, *p, buf[BUFSIZE]; | |
102 | size_t ac_count, buf_used, slen, ii; | |
103 | ||
104 | /* Split the list of silences and try to apply each one in turn. */ | |
105 | for (cp = ircd_strtok(&p, silences, ","), ac_count = 0; | |
106 | cp && (ac_count < MAXPARA); | |
107 | cp = ircd_strtok(&p, 0, ",")) { | |
108 | if ((sile = apply_silence(sptr, cp))) | |
109 | accepted[ac_count++] = sile; | |
110 | } | |
111 | ||
112 | ||
113 | if (MyUser(sptr)) { | |
114 | size_t siles, maxsiles, totlength, maxlength, jj; | |
115 | ||
116 | /* Check that silence count and total length are permitted. */ | |
117 | maxsiles = feature_int(FEAT_MAXSILES); | |
118 | maxlength = maxsiles * feature_int(FEAT_AVBANLEN); | |
119 | siles = totlength = 0; | |
120 | /* Count number of current silences and their total length. */ | |
121 | plast = &cli_user(sptr)->silence; | |
122 | for (sile = cli_user(sptr)->silence; sile; sile = sile->next) { | |
123 | if (sile->flags & (BAN_OVERLAPPED | BAN_ADD | BAN_DEL)) | |
124 | continue; | |
125 | siles++; | |
126 | totlength += strlen(sile->banstr); | |
127 | plast = &sile->next; | |
128 | } | |
129 | for (ii = jj = 0; ii < ac_count; ++ii) { | |
130 | sile = accepted[ii]; | |
131 | /* If the update is being added, and we would exceed the maximum | |
132 | * count or length, drop the update. | |
133 | */ | |
134 | if (!(sile->flags & (BAN_OVERLAPPED | BAN_DEL))) { | |
135 | slen = strlen(sile->banstr); | |
136 | if ((siles >= maxsiles) || (totlength + slen >= maxlength)) { | |
137 | *plast = NULL; | |
138 | if (MyUser(sptr)) | |
139 | send_reply(sptr, ERR_SILELISTFULL, accepted[ii]->banstr); | |
140 | free_ban(accepted[ii]); | |
141 | continue; | |
142 | } | |
143 | /* Update counts. */ | |
144 | siles++; | |
145 | totlength += slen; | |
146 | plast = &sile->next; | |
147 | } | |
148 | /* Store the update. */ | |
149 | accepted[jj++] = sile; | |
150 | } | |
151 | /* Write back the number of accepted updates. */ | |
152 | ac_count = jj; | |
153 | ||
154 | /* Send the silence update list, including overlapped silences (to | |
155 | * make it easier on clients). | |
156 | */ | |
157 | buf_used = 0; | |
158 | for (sile = cli_user(sptr)->silence; sile; sile = sile->next) { | |
159 | char ch; | |
160 | if (sile->flags & (BAN_OVERLAPPED | BAN_DEL)) | |
161 | ch = '-'; | |
162 | else if (sile->flags & BAN_ADD) | |
163 | ch = '+'; | |
164 | else | |
165 | continue; | |
166 | slen = strlen(sile->banstr); | |
167 | if (buf_used + slen + 4 > 400) { | |
168 | buf[buf_used] = '\0'; | |
169 | sendcmdto_one(sptr, CMD_SILENCE, sptr, "%s", buf); | |
170 | buf_used = 0; | |
171 | } | |
172 | if (buf_used) | |
173 | buf[buf_used++] = ','; | |
174 | buf[buf_used++] = ch; | |
175 | if (sile->flags & BAN_EXCEPTION) | |
176 | buf[buf_used++] = '~'; | |
177 | memcpy(buf + buf_used, sile->banstr, slen); | |
178 | buf_used += slen; | |
179 | } | |
180 | if (buf_used > 0) { | |
181 | buf[buf_used] = '\0'; | |
182 | sendcmdto_one(sptr, CMD_SILENCE, sptr, "%s", buf); | |
183 | buf_used = 0; | |
184 | } | |
185 | } | |
186 | ||
187 | /* Forward any silence removals or exceptions updates to other | |
188 | * servers if the user has positive silences. | |
189 | */ | |
190 | if (!dest || !MyUser(dest)) { | |
191 | for (ii = buf_used = 0; ii < ac_count; ++ii) { | |
192 | char ch; | |
193 | sile = accepted[ii]; | |
194 | if (sile->flags & BAN_OVERLAPPED) | |
195 | continue; | |
196 | else if (sile->flags & BAN_DEL) | |
197 | ch = '-'; | |
198 | else if (sile->flags & BAN_ADD) { | |
199 | if (!(sile->flags & BAN_EXCEPTION)) | |
200 | continue; | |
201 | ch = '+'; | |
202 | } else | |
203 | continue; | |
204 | slen = strlen(sile->banstr); | |
205 | if (buf_used + slen + 4 > 400) { | |
206 | buf[buf_used] = '\0'; | |
207 | if (dest) | |
208 | sendcmdto_one(sptr, CMD_SILENCE, dest, "%C %s", dest, buf); | |
209 | else | |
210 | sendcmdto_serv_butone(sptr, CMD_SILENCE, sptr, "* %s", buf); | |
211 | buf_used = 0; | |
212 | } | |
213 | if (buf_used) | |
214 | buf[buf_used++] = ','; | |
215 | buf[buf_used++] = ch; | |
216 | if (sile->flags & BAN_EXCEPTION) | |
217 | buf[buf_used++] = '~'; | |
218 | memcpy(buf + buf_used, sile->banstr, slen); | |
219 | buf_used += slen; | |
220 | } | |
221 | if (buf_used > 0) { | |
222 | buf[buf_used] = '\0'; | |
223 | if (dest) | |
224 | sendcmdto_one(sptr, CMD_SILENCE, dest, "%C %s", dest, buf); | |
225 | else | |
226 | sendcmdto_serv_butone(sptr, CMD_SILENCE, sptr, "* %s", buf); | |
227 | buf_used = 0; | |
228 | } | |
229 | } | |
230 | ||
231 | /* Remove overlapped and deleted silences from the user's silence | |
232 | * list. Clear BAN_ADD since we're walking the list anyway. | |
233 | */ | |
234 | for (plast = &cli_user(sptr)->silence; (sile = *plast) != NULL; ) { | |
235 | if (sile->flags & (BAN_OVERLAPPED | BAN_DEL)) { | |
236 | *plast = sile->next; | |
237 | free_ban(sile); | |
238 | } else { | |
239 | sile->flags &= ~BAN_ADD; | |
240 | *plast = sile; | |
241 | plast = &sile->next; | |
242 | } | |
243 | } | |
244 | ||
245 | /* Free any silence-deleting updates. */ | |
246 | for (ii = 0; ii < ac_count; ++ii) { | |
247 | if (accepted[ii]->flags & BAN_DEL) | |
248 | free_ban(accepted[ii]); | |
249 | } | |
250 | } | |
251 | ||
252 | /** Handle a SILENCE command from a local user. | |
253 | * See @ref m_functions for general discussion of parameters. | |
254 | * | |
255 | * \a parv[1] may be any of the following: | |
256 | * \li Omitted or empty, to view your own silence list. | |
257 | * \li Nickname of a user, to view that user's silence list. | |
258 | * \li A comma-separated list of silence updates | |
259 | * | |
260 | * @param[in] cptr Client that sent us the message. | |
261 | * @param[in] sptr Original source of message. | |
262 | * @param[in] parc Number of arguments. | |
263 | * @param[in] parv Argument vector. | |
264 | */ | |
265 | int m_silence(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) | |
266 | { | |
267 | struct Client *acptr; | |
268 | struct Ban *sile; | |
269 | ||
270 | assert(0 != cptr); | |
271 | assert(cptr == sptr); | |
272 | ||
273 | /* See if the user is requesting a silence list. */ | |
274 | acptr = sptr; | |
275 | if (parc < 2 || EmptyString(parv[1]) || (acptr = FindUser(parv[1]))) { | |
276 | if (cli_user(acptr)) { | |
277 | for (sile = cli_user(acptr)->silence; sile; sile = sile->next) { | |
278 | send_reply(sptr, RPL_SILELIST, cli_name(acptr), | |
279 | (sile->flags & BAN_EXCEPTION ? "~" : ""), sile->banstr); | |
280 | } | |
281 | } | |
282 | send_reply(sptr, RPL_ENDOFSILELIST, cli_name(acptr)); | |
283 | return 0; | |
284 | } | |
285 | ||
286 | /* The user must be attempting to update their list. */ | |
287 | forward_silences(sptr, parv[1], NULL); | |
288 | return 0; | |
289 | } | |
290 | ||
291 | /** Handle a SILENCE command from a server. | |
292 | * See @ref m_functions for general discussion of parameters. | |
293 | * | |
294 | * \a parv[1] may be one of the following: | |
295 | * \li "*" to indicate a broadcast update (removing a SILENCE) | |
296 | * \li A client numnick that should be specifically SILENCEd. | |
297 | * | |
298 | * \a parv[2] is a comma-separated list of silence updates. | |
299 | * | |
300 | * @param[in] cptr Client that sent us the message. | |
301 | * @param[in] sptr Original source of message. | |
302 | * @param[in] parc Number of arguments. | |
303 | * @param[in] parv Argument vector. | |
304 | */ | |
305 | int ms_silence(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) | |
306 | { | |
307 | if (IsServer(sptr)) | |
308 | return protocol_violation(sptr, "Server trying to silence a user"); | |
309 | if (parc < 3 || EmptyString(parv[2])) | |
310 | return need_more_params(sptr, "SILENCE"); | |
311 | ||
312 | /* Figure out which silences can be forwarded. */ | |
313 | forward_silences(sptr, parv[2], findNUser(parv[1])); | |
314 | return 0; | |
315 | (void)cptr; | |
316 | } |