]>
Commit | Line | Data |
---|---|---|
212380e3 AC |
1 | /* |
2 | * ircd-ratbox: A slightly useful ircd. | |
3 | * m_mode.c: Sets a user or channel mode. | |
4 | * | |
5 | * Copyright (C) 1990 Jarkko Oikarinen and University of Oulu, Co Center | |
6 | * Copyright (C) 1996-2002 Hybrid Development Team | |
7 | * Copyright (C) 2002-2005 ircd-ratbox development team | |
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 2 of the License, or | |
12 | * (at your option) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 | |
22 | * USA | |
212380e3 AC |
23 | */ |
24 | ||
25 | #include "stdinc.h" | |
212380e3 AC |
26 | #include "channel.h" |
27 | #include "client.h" | |
28 | #include "hash.h" | |
4562c604 | 29 | #include "match.h" |
212380e3 AC |
30 | #include "ircd.h" |
31 | #include "numeric.h" | |
32 | #include "s_user.h" | |
33 | #include "s_conf.h" | |
34 | #include "s_serv.h" | |
4016731b | 35 | #include "logger.h" |
212380e3 AC |
36 | #include "send.h" |
37 | #include "msg.h" | |
38 | #include "parse.h" | |
39 | #include "modules.h" | |
40 | #include "packet.h" | |
212380e3 AC |
41 | #include "s_newconf.h" |
42 | ||
eeabf33a EM |
43 | static const char mode_desc[] = |
44 | "Provides the MODE and MLOCK client and server commands, and TS6 server-to-server TMODE and BMASK commands"; | |
45 | ||
3c7d6fcc EM |
46 | static void m_mode(struct MsgBuf *, struct Client *, struct Client *, int, const char **); |
47 | static void ms_mode(struct MsgBuf *, struct Client *, struct Client *, int, const char **); | |
48 | static void ms_tmode(struct MsgBuf *, struct Client *, struct Client *, int, const char **); | |
49 | static void ms_mlock(struct MsgBuf *, struct Client *, struct Client *, int, const char **); | |
50 | static void ms_bmask(struct MsgBuf *, struct Client *, struct Client *, int, const char **); | |
fdd8cad9 | 51 | static void ms_ebmask(struct MsgBuf *, struct Client *, struct Client *, int, const char **); |
212380e3 AC |
52 | |
53 | struct Message mode_msgtab = { | |
7baa37a9 | 54 | "MODE", 0, 0, 0, 0, |
212380e3 AC |
55 | {mg_unreg, {m_mode, 2}, {m_mode, 3}, {ms_mode, 3}, mg_ignore, {m_mode, 2}} |
56 | }; | |
57 | struct Message tmode_msgtab = { | |
7baa37a9 | 58 | "TMODE", 0, 0, 0, 0, |
212380e3 AC |
59 | {mg_ignore, mg_ignore, {ms_tmode, 4}, {ms_tmode, 4}, mg_ignore, mg_ignore} |
60 | }; | |
8727cbe8 | 61 | struct Message mlock_msgtab = { |
7baa37a9 | 62 | "MLOCK", 0, 0, 0, 0, |
6b8db2da | 63 | {mg_ignore, mg_ignore, {ms_mlock, 3}, {ms_mlock, 3}, mg_ignore, mg_ignore} |
8727cbe8 | 64 | }; |
212380e3 | 65 | struct Message bmask_msgtab = { |
7baa37a9 | 66 | "BMASK", 0, 0, 0, 0, |
212380e3 AC |
67 | {mg_ignore, mg_ignore, mg_ignore, {ms_bmask, 5}, mg_ignore, mg_ignore} |
68 | }; | |
fdd8cad9 JP |
69 | struct Message ebmask_msgtab = { |
70 | "EBMASK", 0, 0, 0, 0, | |
71 | {mg_ignore, mg_ignore, mg_ignore, {ms_ebmask, 5}, mg_ignore, mg_ignore} | |
72 | }; | |
212380e3 | 73 | |
fdd8cad9 | 74 | mapi_clist_av1 mode_clist[] = { &mode_msgtab, &tmode_msgtab, &mlock_msgtab, &bmask_msgtab, &ebmask_msgtab, NULL }; |
212380e3 | 75 | |
ee6dcb05 | 76 | DECLARE_MODULE_AV2(mode, NULL, NULL, mode_clist, NULL, NULL, NULL, NULL, mode_desc); |
212380e3 AC |
77 | |
78 | /* | |
79 | * m_mode - MODE command handler | |
212380e3 AC |
80 | * parv[1] - channel |
81 | */ | |
3c7d6fcc | 82 | static void |
428ca87b | 83 | m_mode(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) |
212380e3 AC |
84 | { |
85 | struct Channel *chptr = NULL; | |
86 | struct membership *msptr; | |
87 | int n = 2; | |
88 | const char *dest; | |
89 | int operspy = 0; | |
90 | ||
91 | dest = parv[1]; | |
92 | ||
93 | if(IsOperSpy(source_p) && *dest == '!') | |
94 | { | |
95 | dest++; | |
96 | operspy = 1; | |
97 | ||
98 | if(EmptyString(dest)) | |
99 | { | |
100 | sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), | |
101 | me.name, source_p->name, "MODE"); | |
3c7d6fcc | 102 | return; |
212380e3 AC |
103 | } |
104 | } | |
105 | ||
106 | /* Now, try to find the channel in question */ | |
107 | if(!IsChanPrefix(*dest)) | |
108 | { | |
109 | /* if here, it has to be a non-channel name */ | |
110 | user_mode(client_p, source_p, parc, parv); | |
3c7d6fcc | 111 | return; |
212380e3 AC |
112 | } |
113 | ||
114 | if(!check_channel_name(dest)) | |
115 | { | |
116 | sendto_one_numeric(source_p, ERR_BADCHANNAME, form_str(ERR_BADCHANNAME), parv[1]); | |
3c7d6fcc | 117 | return; |
212380e3 AC |
118 | } |
119 | ||
120 | chptr = find_channel(dest); | |
121 | ||
122 | if(chptr == NULL) | |
123 | { | |
124 | sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, | |
125 | form_str(ERR_NOSUCHCHANNEL), parv[1]); | |
3c7d6fcc | 126 | return; |
212380e3 AC |
127 | } |
128 | ||
129 | /* Now know the channel exists */ | |
130 | if(parc < n + 1) | |
131 | { | |
132 | if(operspy) | |
133 | report_operspy(source_p, "MODE", chptr->chname); | |
134 | ||
135 | sendto_one(source_p, form_str(RPL_CHANNELMODEIS), | |
136 | me.name, source_p->name, parv[1], | |
137 | operspy ? channel_modes(chptr, &me) : channel_modes(chptr, source_p)); | |
138 | ||
139 | sendto_one(source_p, form_str(RPL_CREATIONTIME), | |
5c01fc8b | 140 | me.name, source_p->name, parv[1], (long long)chptr->channelts); |
212380e3 AC |
141 | } |
142 | else | |
143 | { | |
144 | msptr = find_channel_membership(chptr, source_p); | |
145 | ||
212380e3 AC |
146 | set_channel_mode(client_p, source_p, chptr, msptr, parc - n, parv + n); |
147 | } | |
212380e3 AC |
148 | } |
149 | ||
3c7d6fcc | 150 | static void |
428ca87b | 151 | ms_mode(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) |
212380e3 AC |
152 | { |
153 | struct Channel *chptr; | |
154 | ||
155 | chptr = find_channel(parv[1]); | |
156 | ||
157 | if(chptr == NULL) | |
158 | { | |
159 | sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, | |
160 | form_str(ERR_NOSUCHCHANNEL), parv[1]); | |
3c7d6fcc | 161 | return; |
212380e3 AC |
162 | } |
163 | ||
164 | set_channel_mode(client_p, source_p, chptr, NULL, parc - 2, parv + 2); | |
212380e3 AC |
165 | } |
166 | ||
3c7d6fcc | 167 | static void |
428ca87b | 168 | ms_tmode(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) |
212380e3 AC |
169 | { |
170 | struct Channel *chptr = NULL; | |
171 | struct membership *msptr; | |
172 | ||
173 | /* Now, try to find the channel in question */ | |
174 | if(!IsChanPrefix(parv[2][0]) || !check_channel_name(parv[2])) | |
175 | { | |
176 | sendto_one_numeric(source_p, ERR_BADCHANNAME, form_str(ERR_BADCHANNAME), parv[2]); | |
3c7d6fcc | 177 | return; |
212380e3 AC |
178 | } |
179 | ||
180 | chptr = find_channel(parv[2]); | |
181 | ||
182 | if(chptr == NULL) | |
183 | { | |
184 | sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, | |
185 | form_str(ERR_NOSUCHCHANNEL), parv[2]); | |
3c7d6fcc | 186 | return; |
212380e3 AC |
187 | } |
188 | ||
189 | /* TS is higher, drop it. */ | |
190 | if(atol(parv[1]) > chptr->channelts) | |
3c7d6fcc | 191 | return; |
212380e3 AC |
192 | |
193 | if(IsServer(source_p)) | |
194 | { | |
195 | set_channel_mode(client_p, source_p, chptr, NULL, parc - 3, parv + 3); | |
196 | } | |
197 | else | |
198 | { | |
199 | msptr = find_channel_membership(chptr, source_p); | |
200 | ||
212380e3 AC |
201 | set_channel_mode(client_p, source_p, chptr, msptr, parc - 3, parv + 3); |
202 | } | |
212380e3 AC |
203 | } |
204 | ||
3c7d6fcc | 205 | static void |
428ca87b | 206 | ms_mlock(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) |
8727cbe8 AC |
207 | { |
208 | struct Channel *chptr = NULL; | |
209 | ||
210 | /* Now, try to find the channel in question */ | |
211 | if(!IsChanPrefix(parv[2][0]) || !check_channel_name(parv[2])) | |
212 | { | |
213 | sendto_one_numeric(source_p, ERR_BADCHANNAME, form_str(ERR_BADCHANNAME), parv[2]); | |
3c7d6fcc | 214 | return; |
8727cbe8 AC |
215 | } |
216 | ||
217 | chptr = find_channel(parv[2]); | |
218 | ||
219 | if(chptr == NULL) | |
220 | { | |
221 | sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL, | |
222 | form_str(ERR_NOSUCHCHANNEL), parv[2]); | |
3c7d6fcc | 223 | return; |
8727cbe8 AC |
224 | } |
225 | ||
226 | /* TS is higher, drop it. */ | |
227 | if(atol(parv[1]) > chptr->channelts) | |
3c7d6fcc | 228 | return; |
8727cbe8 AC |
229 | |
230 | if(IsServer(source_p)) | |
07554369 | 231 | set_channel_mlock(client_p, source_p, chptr, parv[3], true); |
8727cbe8 AC |
232 | } |
233 | ||
d1316b19 JT |
234 | static void |
235 | possibly_remove_lower_forward(struct Client *fakesource_p, int mems, | |
236 | struct Channel *chptr, rb_dlink_list *banlist, int mchar, | |
237 | const char *mask, const char *forward) | |
238 | { | |
239 | struct Ban *actualBan; | |
240 | rb_dlink_node *ptr; | |
241 | ||
242 | RB_DLINK_FOREACH(ptr, banlist->head) | |
243 | { | |
244 | actualBan = ptr->data; | |
245 | if(!irccmp(actualBan->banstr, mask) && | |
246 | (actualBan->forward == NULL || | |
247 | irccmp(actualBan->forward, forward) < 0)) | |
248 | { | |
4b1cce65 | 249 | sendto_channel_local(fakesource_p, mems, chptr, ":%s MODE %s -%c %s%s%s", |
d1316b19 JT |
250 | fakesource_p->name, |
251 | chptr->chname, | |
252 | mchar, | |
253 | actualBan->banstr, | |
254 | actualBan->forward ? "$" : "", | |
255 | actualBan->forward ? actualBan->forward : ""); | |
256 | rb_dlinkDelete(&actualBan->node, banlist); | |
257 | free_ban(actualBan); | |
258 | return; | |
259 | } | |
260 | } | |
261 | } | |
262 | ||
3c7d6fcc | 263 | static void |
fdd8cad9 | 264 | do_bmask(bool extended, struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) |
212380e3 | 265 | { |
fdd8cad9 | 266 | static char output[BUFSIZE]; |
212380e3 | 267 | static char parabuf[BUFSIZE]; |
fdd8cad9 JP |
268 | static char degrade[BUFSIZE]; |
269 | static char squitreason[120]; | |
212380e3 | 270 | struct Channel *chptr; |
fdd8cad9 | 271 | struct Ban *banptr; |
5b96d9a6 | 272 | rb_dlink_list *banlist; |
fdd8cad9 JP |
273 | char *s, *mask, *forward, *who; |
274 | char *output_ptr; | |
275 | char *param_ptr; | |
276 | char *degrade_ptr; | |
212380e3 AC |
277 | long mode_type; |
278 | int mlen; | |
279 | int plen = 0; | |
280 | int tlen; | |
281 | int arglen; | |
282 | int modecount = 0; | |
283 | int needcap = NOCAPS; | |
284 | int mems; | |
fdd8cad9 | 285 | time_t when = (long)rb_current_time(); |
212380e3 AC |
286 | struct Client *fakesource_p; |
287 | ||
288 | if(!IsChanPrefix(parv[2][0]) || !check_channel_name(parv[2])) | |
3c7d6fcc | 289 | return; |
212380e3 AC |
290 | |
291 | if((chptr = find_channel(parv[2])) == NULL) | |
3c7d6fcc | 292 | return; |
212380e3 AC |
293 | |
294 | /* TS is higher, drop it. */ | |
295 | if(atol(parv[1]) > chptr->channelts) | |
3c7d6fcc | 296 | return; |
212380e3 AC |
297 | |
298 | switch (parv[3][0]) | |
299 | { | |
300 | case 'b': | |
301 | banlist = &chptr->banlist; | |
302 | mode_type = CHFL_BAN; | |
303 | mems = ALL_MEMBERS; | |
304 | break; | |
305 | ||
306 | case 'e': | |
307 | banlist = &chptr->exceptlist; | |
308 | mode_type = CHFL_EXCEPTION; | |
309 | needcap = CAP_EX; | |
310 | mems = ONLY_CHANOPS; | |
311 | break; | |
312 | ||
313 | case 'I': | |
314 | banlist = &chptr->invexlist; | |
315 | mode_type = CHFL_INVEX; | |
316 | needcap = CAP_IE; | |
317 | mems = ONLY_CHANOPS; | |
318 | break; | |
319 | ||
320 | case 'q': | |
321 | banlist = &chptr->quietlist; | |
322 | mode_type = CHFL_QUIET; | |
323 | mems = ALL_MEMBERS; | |
324 | break; | |
325 | ||
326 | /* maybe we should just blindly propagate this? */ | |
327 | default: | |
3c7d6fcc | 328 | return; |
212380e3 AC |
329 | } |
330 | ||
331 | parabuf[0] = '\0'; | |
332 | s = LOCAL_COPY(parv[4]); | |
333 | ||
334 | /* Hide connecting server on netburst -- jilles */ | |
335 | if (ConfigServerHide.flatten_links && !HasSentEob(source_p)) | |
336 | fakesource_p = &me; | |
337 | else | |
338 | fakesource_p = source_p; | |
fdd8cad9 JP |
339 | who = fakesource_p->name; |
340 | ||
341 | mlen = sprintf(output, ":%s MODE %s +", fakesource_p->name, chptr->chname); | |
342 | output_ptr = output + mlen; | |
343 | param_ptr = parabuf; | |
344 | degrade_ptr = degrade; | |
212380e3 AC |
345 | |
346 | while(*s == ' ') | |
347 | s++; | |
348 | ||
fdd8cad9 | 349 | s = strtok(s, " "); |
212380e3 | 350 | |
212380e3 AC |
351 | while(!EmptyString(s)) |
352 | { | |
212380e3 | 353 | if(*s == ':') |
fdd8cad9 JP |
354 | { |
355 | /* ban with a leading ':' -- this will break the protocol */ | |
356 | sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, | |
357 | "Link %s dropped, invalid BMASK mask (%s)", source_p->name, s); | |
358 | snprintf(squitreason, sizeof squitreason, "Invalid BMASK mask (%s)", s); | |
359 | exit_client(client_p, client_p, client_p, squitreason); | |
360 | return; | |
361 | } | |
212380e3 AC |
362 | |
363 | tlen = strlen(s); | |
364 | ||
365 | /* I dont even want to begin parsing this.. */ | |
366 | if(tlen > MODEBUFLEN) | |
367 | break; | |
368 | ||
765d839d EM |
369 | if((forward = strchr(s+1, '$')) != NULL) |
370 | { | |
371 | *forward++ = '\0'; | |
372 | if(*forward == '\0') | |
23f6b63a | 373 | tlen--, forward = NULL; |
fea6157d JT |
374 | else |
375 | possibly_remove_lower_forward(fakesource_p, | |
376 | mems, chptr, banlist, | |
377 | parv[3][0], s, forward); | |
765d839d EM |
378 | } |
379 | ||
fdd8cad9 JP |
380 | mask = s; |
381 | if (extended) { | |
382 | when = atol(strtok(NULL, " ")); | |
383 | who = strtok(NULL, " "); | |
384 | if (who == NULL) | |
385 | { | |
386 | /* EBMASK params don't divide by 3, so we have an incomplete chunk */ | |
387 | sendto_realops_snomask(SNO_GENERAL, L_NETWIDE, | |
388 | "Link %s dropped, invalid EBMASK chunk", source_p->name); | |
389 | snprintf(squitreason, sizeof squitreason, "Invalid EBMASK chunk"); | |
390 | exit_client(client_p, client_p, client_p, squitreason); | |
391 | return; | |
392 | } | |
393 | ||
394 | arglen = sprintf(degrade_ptr, "%s ", mask); | |
395 | degrade_ptr += arglen; | |
396 | } | |
397 | ||
398 | if((banptr = add_id(fakesource_p, chptr, mask, forward, banlist, mode_type)) != NULL) | |
212380e3 | 399 | { |
fdd8cad9 JP |
400 | banptr->when = when; |
401 | rb_free(banptr->who); | |
402 | banptr->who = rb_strdup(who); | |
403 | ||
212380e3 AC |
404 | /* this new one wont fit.. */ |
405 | if(mlen + MAXMODEPARAMS + plen + tlen > BUFSIZE - 5 || | |
406 | modecount >= MAXMODEPARAMS) | |
407 | { | |
fdd8cad9 JP |
408 | *output_ptr = '\0'; |
409 | *(param_ptr - 1) = '\0'; | |
410 | sendto_channel_local(fakesource_p, mems, chptr, "%s %s", output, parabuf); | |
212380e3 | 411 | |
fdd8cad9 JP |
412 | output_ptr = output + mlen; |
413 | param_ptr = parabuf; | |
212380e3 AC |
414 | plen = modecount = 0; |
415 | } | |
416 | ||
23f6b63a JT |
417 | if (forward != NULL) |
418 | forward[-1] = '$'; | |
419 | ||
fdd8cad9 JP |
420 | *output_ptr++ = parv[3][0]; |
421 | arglen = sprintf(param_ptr, "%s ", mask); | |
422 | param_ptr += arglen; | |
212380e3 AC |
423 | plen += arglen; |
424 | modecount++; | |
425 | } | |
426 | ||
fdd8cad9 | 427 | s = strtok(NULL, " "); |
212380e3 AC |
428 | } |
429 | ||
430 | if(modecount) | |
431 | { | |
fdd8cad9 JP |
432 | *output_ptr = '\0'; |
433 | *(param_ptr - 1) = '\0'; | |
434 | sendto_channel_local(fakesource_p, mems, chptr, "%s %s", output, parabuf); | |
435 | } | |
436 | ||
437 | if (extended) { | |
438 | *(degrade_ptr - 1) = '\0'; | |
439 | sendto_server(client_p, chptr, CAP_EBMASK | CAP_TS6 | needcap, NOCAPS, ":%s EBMASK %ld %s %s :%s", | |
440 | source_p->id, (long) chptr->channelts, chptr->chname, parv[3], parv[4]); | |
441 | sendto_server(client_p, chptr, CAP_TS6 | needcap, CAP_EBMASK, ":%s BMASK %ld %s %s :%s", | |
442 | source_p->id, (long) chptr->channelts, chptr->chname, parv[3], degrade); | |
212380e3 | 443 | } |
fdd8cad9 JP |
444 | else |
445 | sendto_server(client_p, chptr, CAP_TS6 | needcap, NOCAPS, ":%s BMASK %ld %s %s :%s", | |
446 | source_p->id, (long) chptr->channelts, chptr->chname, parv[3], parv[4]); | |
447 | } | |
212380e3 | 448 | |
fdd8cad9 JP |
449 | static void |
450 | ms_bmask(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) | |
451 | { | |
452 | do_bmask(false, msgbuf_p, client_p, source_p, parc, parv); | |
212380e3 | 453 | } |
fdd8cad9 JP |
454 | static void |
455 | ms_ebmask(struct MsgBuf *msgbuf_p, struct Client *client_p, struct Client *source_p, int parc, const char *parv[]) | |
456 | { | |
457 | do_bmask(true, msgbuf_p, client_p, source_p, parc, parv); | |
458 | } | |
459 |