]> jfr.im git - solanum.git/blame - ircd/chmode.c
Resolve shfit/reduce conflict in timespec production (#54)
[solanum.git] / ircd / chmode.c
CommitLineData
25ea5d2f 1/*
a6f63a82 2 * Solanum: a slightly advanced ircd
25ea5d2f
EM
3 * chmode.c: channel mode management
4 *
55abcbb2
KB
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
25ea5d2f
EM
8 * Copyright (C) 2005-2006 charybdis development team
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 *
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
23 * USA
25ea5d2f
EM
24 */
25
26#include "stdinc.h"
27#include "channel.h"
28#include "client.h"
25ea5d2f
EM
29#include "hash.h"
30#include "hook.h"
31#include "match.h"
32#include "ircd.h"
33#include "numeric.h"
34#include "s_serv.h" /* captab */
35#include "s_user.h"
36#include "send.h"
37#include "whowas.h"
38#include "s_conf.h" /* ConfigFileEntry, ConfigChannel */
39#include "s_newconf.h"
40#include "logger.h"
41#include "chmode.h"
77d3d2db 42#include "s_assert.h"
b870a5f8 43#include "parse.h"
04952c32 44#include "msgbuf.h"
25ea5d2f
EM
45
46/* bitmasks for error returns, so we send once per call */
47#define SM_ERR_NOTS 0x00000001 /* No TS on channel */
48#define SM_ERR_NOOPS 0x00000002 /* No chan ops */
49#define SM_ERR_UNKNOWN 0x00000004
50#define SM_ERR_RPL_C 0x00000008
51#define SM_ERR_RPL_B 0x00000010
52#define SM_ERR_RPL_E 0x00000020
53#define SM_ERR_NOTONCHANNEL 0x00000040 /* Not on channel */
54#define SM_ERR_RPL_I 0x00000100
55#define SM_ERR_RPL_D 0x00000200
56#define SM_ERR_NOPRIVS 0x00000400
57#define SM_ERR_RPL_Q 0x00000800
58#define SM_ERR_RPL_F 0x00001000
59#define SM_ERR_MLOCK 0x00002000
60
61#define MAXMODES_SIMPLE 46 /* a-zA-Z except bqeIov */
62
63static struct ChModeChange mode_changes[BUFSIZE];
64static int mode_count;
65static int mode_limit;
66static int mode_limit_simple;
67static int mask_pos;
68static int removed_mask_pos;
69
70char cflagsbuf[256];
71char cflagsmyinfo[256];
72
73int chmode_flags[256];
acdf71d9 74
749d8c11
AC
75extern int h_get_channel_access;
76
9bec26cc 77/* OPTIMIZE ME! -- dwr */
efccc22c 78void
19716b9f 79construct_cflags_strings(void)
efccc22c
VY
80{
81 int i;
c18cb68b
VY
82 char *ptr = cflagsbuf;
83 char *ptr2 = cflagsmyinfo;
55abcbb2 84
c18cb68b
VY
85 *ptr = '\0';
86 *ptr2 = '\0';
efccc22c
VY
87
88 for(i = 0; i < 256; i++)
89 {
b5c8d52d
EK
90 if (chmode_table[i].set_func != chm_ban &&
91 chmode_table[i].set_func != chm_forward &&
92 chmode_table[i].set_func != chm_throttle &&
93 chmode_table[i].set_func != chm_key &&
94 chmode_table[i].set_func != chm_limit &&
95 chmode_table[i].set_func != chm_op &&
96 chmode_table[i].set_func != chm_voice)
efccc22c
VY
97 {
98 chmode_flags[i] = chmode_table[i].mode_type;
99 }
100 else
101 {
102 chmode_flags[i] = 0;
103 }
55abcbb2 104
c18cb68b
VY
105 switch (chmode_flags[i])
106 {
c55b2782 107 case MODE_FREETARGET:
73d0f900 108 case MODE_DISFORWARD:
2da6f6eb
JT
109 if(ConfigChannel.use_forward)
110 *ptr++ = (char) i;
111 break;
c18cb68b
VY
112 default:
113 if(chmode_flags[i] != 0)
114 {
115 *ptr++ = (char) i;
116 }
117 }
55abcbb2 118
c18cb68b 119 /* Should we leave orphaned check here? -- dwr */
b5c8d52d
EK
120 if (chmode_table[i].set_func != NULL &&
121 chmode_table[i].set_func != chm_nosuch &&
122 chmode_table[i].set_func != chm_orphaned)
c18cb68b
VY
123 {
124 *ptr2++ = (char) i;
125 }
efccc22c 126 }
55abcbb2 127
c18cb68b
VY
128 *ptr++ = '\0';
129 *ptr2++ = '\0';
46d59e11
VY
130}
131
132/*
133 * find_umode_slot
134 *
135 * inputs - NONE
136 * outputs - an available cflag bitmask or
137 * 0 if no cflags are available
138 * side effects - NONE
139 */
19716b9f 140static unsigned int
46d59e11
VY
141find_cflag_slot(void)
142{
143 unsigned int all_cflags = 0, my_cflag = 0, i;
144
145 for (i = 0; i < 256; i++)
146 all_cflags |= chmode_flags[i];
147
148 for (my_cflag = 1; my_cflag && (all_cflags & my_cflag);
149 my_cflag <<= 1);
150
151 return my_cflag;
efccc22c
VY
152}
153
19716b9f
JT
154unsigned int
155cflag_add(char c_, ChannelModeFunc function)
156{
157 int c = (unsigned char)c_;
158
b5c8d52d
EK
159 if (chmode_table[c].set_func != NULL &&
160 chmode_table[c].set_func != chm_nosuch &&
19716b9f
JT
161 chmode_table[c].set_func != chm_orphaned)
162 return 0;
163
b5c8d52d 164 if (chmode_table[c].set_func == NULL || chmode_table[c].set_func == chm_nosuch)
19716b9f
JT
165 chmode_table[c].mode_type = find_cflag_slot();
166 if (chmode_table[c].mode_type == 0)
167 return 0;
168 chmode_table[c].set_func = function;
169 construct_cflags_strings();
170 return chmode_table[c].mode_type;
171}
172
173void
174cflag_orphan(char c_)
175{
176 int c = (unsigned char)c_;
177
178 s_assert(chmode_flags[c] != 0);
179 chmode_table[c].set_func = chm_orphaned;
180 construct_cflags_strings();
181}
182
3c52f289 183int
3ee43bcf 184get_channel_access(struct Client *source_p, struct Channel *chptr, struct membership *msptr, int dir, const char *modestr)
212380e3 185{
749d8c11
AC
186 hook_data_channel_approval moduledata;
187
188 if(!MyClient(source_p))
212380e3
AC
189 return CHFL_CHANOP;
190
749d8c11 191 moduledata.client = source_p;
3ee43bcf 192 moduledata.chptr = chptr;
749d8c11
AC
193 moduledata.msptr = msptr;
194 moduledata.target = NULL;
b4e3861b 195 moduledata.approved = (msptr != NULL && is_chanop(msptr)) ? CHFL_CHANOP : CHFL_PEON;
202d4966 196 moduledata.dir = dir;
b870a5f8 197 moduledata.modestr = modestr;
8aabb973 198
749d8c11
AC
199 call_hook(h_get_channel_access, &moduledata);
200
201 return moduledata.approved;
212380e3
AC
202}
203
f3b3ad0b
JT
204/* allow_mode_change()
205 *
206 * Checks if mlock and chanops permit a mode change.
207 *
208 * inputs - client, channel, access level, errors pointer, mode char
9aa639ed 209 * outputs - false on failure, true on success
f3b3ad0b
JT
210 * side effects - error message sent on failure
211 */
9aa639ed 212static bool
f3b3ad0b
JT
213allow_mode_change(struct Client *source_p, struct Channel *chptr, int alevel,
214 int *errors, char c)
215{
216 /* If this mode char is locked, don't allow local users to change it. */
217 if (MyClient(source_p) && chptr->mode_lock && strchr(chptr->mode_lock, c))
218 {
219 if (!(*errors & SM_ERR_MLOCK))
220 sendto_one_numeric(source_p,
221 ERR_MLOCKRESTRICTED,
222 form_str(ERR_MLOCKRESTRICTED),
223 chptr->chname,
224 c,
225 chptr->mode_lock);
226 *errors |= SM_ERR_MLOCK;
9aa639ed 227 return false;
f3b3ad0b 228 }
1046ac77 229 if(alevel < CHFL_CHANOP)
f3b3ad0b
JT
230 {
231 if(!(*errors & SM_ERR_NOOPS))
232 sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
233 me.name, source_p->name, chptr->chname);
234 *errors |= SM_ERR_NOOPS;
9aa639ed 235 return false;
f3b3ad0b 236 }
9aa639ed 237 return true;
f3b3ad0b
JT
238}
239
212380e3
AC
240/* add_id()
241 *
765d839d 242 * inputs - client, channel, id to add, type, forward
a383180a 243 * outputs - false on failure, true on success
212380e3
AC
244 * side effects - given id is added to the appropriate list
245 */
a383180a 246bool
765d839d 247add_id(struct Client *source_p, struct Channel *chptr, const char *banid, const char *forward,
330fc5c1 248 rb_dlink_list * list, long mode_type)
212380e3
AC
249{
250 struct Ban *actualBan;
83294285 251 static char who[USERHOST_REPLYLEN];
212380e3 252 char *realban = LOCAL_COPY(banid);
330fc5c1 253 rb_dlink_node *ptr;
212380e3
AC
254
255 /* dont let local clients overflow the banlist, or set redundant
256 * bans
257 */
258 if(MyClient(source_p))
259 {
d8f0b5d7 260 if((rb_dlink_list_length(&chptr->banlist) + rb_dlink_list_length(&chptr->exceptlist) + rb_dlink_list_length(&chptr->invexlist) + rb_dlink_list_length(&chptr->quietlist)) >= (unsigned long)((chptr->mode.mode & MODE_EXLIMIT) ? ConfigChannel.max_bans_large : ConfigChannel.max_bans))
212380e3
AC
261 {
262 sendto_one(source_p, form_str(ERR_BANLISTFULL),
263 me.name, source_p->name, chptr->chname, realban);
a383180a 264 return false;
212380e3
AC
265 }
266
5cefa1d6 267 RB_DLINK_FOREACH(ptr, list->head)
212380e3
AC
268 {
269 actualBan = ptr->data;
fd488ac1 270 if(mask_match(actualBan->banstr, realban))
a383180a 271 return false;
212380e3
AC
272 }
273 }
274 /* dont let remotes set duplicates */
275 else
276 {
5cefa1d6 277 RB_DLINK_FOREACH(ptr, list->head)
212380e3
AC
278 {
279 actualBan = ptr->data;
280 if(!irccmp(actualBan->banstr, realban))
a383180a 281 return false;
212380e3
AC
282 }
283 }
284
285
286 if(IsPerson(source_p))
5203cba5 287 sprintf(who, "%s!%s@%s", source_p->name, source_p->username, source_p->host);
212380e3 288 else
f427c8b0 289 rb_strlcpy(who, source_p->name, sizeof(who));
212380e3 290
765d839d 291 actualBan = allocate_ban(realban, who, forward);
e3354945 292 actualBan->when = rb_current_time();
212380e3 293
330fc5c1 294 rb_dlinkAdd(actualBan, &actualBan->node, list);
212380e3
AC
295
296 /* invalidate the can_send() cache */
297 if(mode_type == CHFL_BAN || mode_type == CHFL_QUIET || mode_type == CHFL_EXCEPTION)
b5f3e5e5 298 chptr->bants = rb_current_time();
212380e3 299
a383180a 300 return true;
212380e3
AC
301}
302
303/* del_id()
304 *
305 * inputs - channel, id to remove, type
765d839d
EM
306 * outputs - pointer to ban that was removed, if any
307 * side effects - given id is removed from the appropriate list and returned
212380e3 308 */
765d839d 309struct Ban *
330fc5c1 310del_id(struct Channel *chptr, const char *banid, rb_dlink_list * list, long mode_type)
212380e3 311{
330fc5c1 312 rb_dlink_node *ptr;
212380e3
AC
313 struct Ban *banptr;
314
315 if(EmptyString(banid))
765d839d 316 return NULL;
212380e3 317
5cefa1d6 318 RB_DLINK_FOREACH(ptr, list->head)
212380e3
AC
319 {
320 banptr = ptr->data;
321
322 if(irccmp(banid, banptr->banstr) == 0)
323 {
330fc5c1 324 rb_dlinkDelete(&banptr->node, list);
212380e3
AC
325
326 /* invalidate the can_send() cache */
327 if(mode_type == CHFL_BAN || mode_type == CHFL_QUIET || mode_type == CHFL_EXCEPTION)
b5f3e5e5 328 chptr->bants = rb_current_time();
212380e3 329
765d839d 330 return banptr;
212380e3
AC
331 }
332 }
333
765d839d 334 return NULL;
212380e3
AC
335}
336
337/* check_string()
338 *
339 * input - string to check
340 * output - pointer to 'fixed' string, or "*" if empty
341 * side effects - any white space found becomes \0
342 */
343static char *
344check_string(char *s)
345{
346 char *str = s;
347 static char splat[] = "*";
348 if(!(s && *s))
349 return splat;
350
351 for(; *s; ++s)
352 {
353 if(IsSpace(*s))
354 {
355 *s = '\0';
356 break;
357 }
358 }
359 return str;
360}
361
362/* pretty_mask()
363 *
364 * inputs - mask to pretty
365 * outputs - better version of the mask
366 * side effects - mask is chopped to limits, and transformed:
367 * x!y@z => x!y@z
368 * y@z => *!y@z
369 * x!y => x!y@*
370 * x => x!*@*
371 * z.d => *!*@z.d
372 */
373static char *
374pretty_mask(const char *idmask)
375{
376 static char mask_buf[BUFSIZE];
377 int old_mask_pos;
f4e893b5 378 const char *nick, *user, *host, *forward = NULL;
74b2fb72 379 char *t, *at, *ex;
f4e893b5 380 int nl, ul, hl, fl;
212380e3 381 char *mask;
74b2fb72 382 size_t masklen;
212380e3
AC
383
384 mask = LOCAL_COPY(idmask);
385 mask = check_string(mask);
386 collapse(mask);
74b2fb72 387 masklen = strlen(mask);
212380e3 388
f4e893b5 389 nick = user = host = "*";
74b2fb72
JT
390 nl = ul = hl = 1;
391 fl = 0;
212380e3 392
74b2fb72 393 if((size_t) BUFSIZE - mask_pos < masklen + 5)
212380e3
AC
394 return NULL;
395
396 old_mask_pos = mask_pos;
397
398 if (*mask == '$')
399 {
74b2fb72
JT
400 memcpy(mask_buf + mask_pos, mask, masklen + 1);
401 mask_pos += masklen + 1;
212380e3
AC
402 t = mask_buf + old_mask_pos + 1;
403 if (*t == '!')
404 *t = '~';
405 if (*t == '~')
406 t++;
7e6b5384 407 *t = irctolower(*t);
212380e3
AC
408 return mask_buf + old_mask_pos;
409 }
410
74b2fb72
JT
411 at = ex = NULL;
412 if((t = memchr(mask, '@', masklen)) != NULL)
212380e3
AC
413 {
414 at = t;
74b2fb72 415 t++;
212380e3 416 if(*t != '\0')
74b2fb72 417 host = t, hl = strlen(t);
212380e3 418
74b2fb72 419 if((t = memchr(mask, '!', at - mask)) != NULL)
212380e3
AC
420 {
421 ex = t;
74b2fb72
JT
422 t++;
423 if(at != t)
424 user = t, ul = at - t;
425 if(ex != mask)
426 nick = mask, nl = ex - mask;
212380e3
AC
427 }
428 else
429 {
74b2fb72
JT
430 if(at != mask)
431 user = mask, ul = at - mask;
212380e3 432 }
765d839d 433
74b2fb72
JT
434 if((t = memchr(host, '!', hl)) != NULL ||
435 (t = memchr(host, '$', hl)) != NULL)
765d839d 436 {
74b2fb72
JT
437 t++;
438 if (host + hl != t)
439 forward = t, fl = host + hl - t;
440 hl = t - 1 - host;
765d839d 441 }
212380e3 442 }
74b2fb72 443 else if((t = memchr(mask, '!', masklen)) != NULL)
212380e3
AC
444 {
445 ex = t;
74b2fb72
JT
446 t++;
447 if(ex != mask)
448 nick = mask, nl = ex - mask;
212380e3 449 if(*t != '\0')
74b2fb72 450 user = t, ul = strlen(t);
212380e3 451 }
74b2fb72
JT
452 else if(memchr(mask, '.', masklen) != NULL ||
453 memchr(mask, ':', masklen) != NULL)
212380e3 454 {
74b2fb72 455 host = mask, hl = masklen;
212380e3
AC
456 }
457 else
458 {
74b2fb72
JT
459 if(masklen > 0)
460 nick = mask, nl = masklen;
212380e3
AC
461 }
462
463 /* truncate values to max lengths */
f4e893b5
JT
464 if(nl > NICKLEN - 1)
465 nl = NICKLEN - 1;
f4e893b5
JT
466 if(ul > USERLEN)
467 ul = USERLEN;
f4e893b5
JT
468 if(hl > HOSTLEN)
469 hl = HOSTLEN;
f4e893b5
JT
470 if(fl > CHANNELLEN)
471 fl = CHANNELLEN;
472
473 memcpy(mask_buf + mask_pos, nick, nl), mask_pos += nl;
474 mask_buf[mask_pos++] = '!';
475 memcpy(mask_buf + mask_pos, user, ul), mask_pos += ul;
476 mask_buf[mask_pos++] = '@';
477 memcpy(mask_buf + mask_pos, host, hl), mask_pos += hl;
478 if (forward) {
479 mask_buf[mask_pos++] = '$';
480 memcpy(mask_buf + mask_pos, forward, fl), mask_pos += fl;
212380e3 481 }
f4e893b5 482 mask_buf[mask_pos++] = '\0';
212380e3 483
212380e3
AC
484 return mask_buf + old_mask_pos;
485}
486
0c730321
JT
487/* check_forward()
488 *
489 * input - client, channel to set mode on, target channel name
490 * output - true if forwarding should be allowed
491 * side effects - numeric sent if not allowed
492 */
e0a9b5d3 493static bool
0c730321
JT
494check_forward(struct Client *source_p, struct Channel *chptr,
495 const char *forward)
496{
73d759ae 497 struct Channel *targptr = NULL;
0c730321
JT
498 struct membership *msptr;
499
500 if(!check_channel_name(forward) ||
501 (MyClient(source_p) && (strlen(forward) > LOC_CHANNELLEN || hash_find_resv(forward))))
502 {
503 sendto_one_numeric(source_p, ERR_BADCHANNAME, form_str(ERR_BADCHANNAME), forward);
e0a9b5d3 504 return false;
0c730321
JT
505 }
506 /* don't forward to inconsistent target -- jilles */
507 if(chptr->chname[0] == '#' && forward[0] == '&')
508 {
509 sendto_one_numeric(source_p, ERR_BADCHANNAME,
510 form_str(ERR_BADCHANNAME), forward);
e0a9b5d3 511 return false;
0c730321
JT
512 }
513 if(MyClient(source_p) && (targptr = find_channel(forward)) == NULL)
514 {
515 sendto_one_numeric(source_p, ERR_NOSUCHCHANNEL,
516 form_str(ERR_NOSUCHCHANNEL), forward);
e0a9b5d3 517 return false;
0c730321
JT
518 }
519 if(MyClient(source_p) && !(targptr->mode.mode & MODE_FREETARGET))
520 {
521 if((msptr = find_channel_membership(targptr, source_p)) == NULL ||
1046ac77 522 get_channel_access(source_p, targptr, msptr, MODE_QUERY, NULL) < CHFL_CHANOP)
0c730321
JT
523 {
524 sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
525 me.name, source_p->name, targptr->chname);
e0a9b5d3 526 return false;
0c730321
JT
527 }
528 }
e0a9b5d3 529 return true;
0c730321
JT
530}
531
212380e3
AC
532/* fix_key()
533 *
534 * input - key to fix
535 * output - the same key, fixed
536 * side effects - anything below ascii 13 is discarded, ':' discarded,
537 * high ascii is dropped to lower half of ascii table
538 */
539static char *
540fix_key(char *arg)
541{
4b11f391 542 unsigned char *s, *t, c;
212380e3 543
4b11f391 544 for(s = t = (unsigned char *) arg; (c = *s); s++)
212380e3
AC
545 {
546 c &= 0x7f;
547 if(c != ':' && c != ',' && c > ' ')
548 *t++ = c;
549 }
550
551 *t = '\0';
552 return arg;
553}
554
555/* fix_key_remote()
556 *
557 * input - key to fix
558 * ouput - the same key, fixed
559 * side effects - high ascii dropped to lower half of table,
560 * CR/LF/':' are dropped
561 */
562static char *
563fix_key_remote(char *arg)
564{
4b11f391 565 unsigned char *s, *t, c;
212380e3 566
4b11f391 567 for(s = t = (unsigned char *) arg; (c = *s); s++)
212380e3
AC
568 {
569 c &= 0x7f;
570 if((c != 0x0a) && (c != ':') && (c != ',') && (c != 0x0d) && (c != ' '))
571 *t++ = c;
572 }
573
574 *t = '\0';
575 return arg;
576}
577
578/* chm_*()
579 *
580 * The handlers for each specific mode.
581 */
582void
583chm_nosuch(struct Client *source_p, struct Channel *chptr,
04952c32 584 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
212380e3
AC
585{
586 if(*errors & SM_ERR_UNKNOWN)
587 return;
588 *errors |= SM_ERR_UNKNOWN;
589 sendto_one(source_p, form_str(ERR_UNKNOWNMODE), me.name, source_p->name, c);
590}
591
592void
593chm_simple(struct Client *source_p, struct Channel *chptr,
04952c32 594 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
212380e3 595{
f3b3ad0b 596 if(!allow_mode_change(source_p, chptr, alevel, errors, c))
212380e3 597 return;
212380e3 598
84c9a8c7 599 if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
212380e3
AC
600 return;
601
602 /* setting + */
78e6b731 603 if((dir == MODE_ADD) && !(chptr->mode.mode & mode_type))
212380e3 604 {
2da6f6eb
JT
605 /* if +f is disabled, ignore an attempt to set +QF locally */
606 if(!ConfigChannel.use_forward && MyClient(source_p) &&
607 (c == 'Q' || c == 'F'))
608 return;
609
212380e3
AC
610 chptr->mode.mode |= mode_type;
611
612 mode_changes[mode_count].letter = c;
613 mode_changes[mode_count].dir = MODE_ADD;
212380e3
AC
614 mode_changes[mode_count].id = NULL;
615 mode_changes[mode_count].mems = ALL_MEMBERS;
cbed45a2
VY
616 mode_changes[mode_count++].arg = NULL;
617 }
78e6b731 618 else if((dir == MODE_DEL) && (chptr->mode.mode & mode_type))
cbed45a2
VY
619 {
620 chptr->mode.mode &= ~mode_type;
621
622 mode_changes[mode_count].letter = c;
623 mode_changes[mode_count].dir = MODE_DEL;
cbed45a2
VY
624 mode_changes[mode_count].mems = ALL_MEMBERS;
625 mode_changes[mode_count].id = NULL;
626 mode_changes[mode_count++].arg = NULL;
627 }
628}
629
630void
631chm_orphaned(struct Client *source_p, struct Channel *chptr,
04952c32 632 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
cbed45a2
VY
633{
634 if(MyClient(source_p))
635 return;
55abcbb2 636
cbed45a2
VY
637 if((dir == MODE_ADD) && !(chptr->mode.mode & mode_type))
638 {
639 chptr->mode.mode |= mode_type;
640
641 mode_changes[mode_count].letter = c;
642 mode_changes[mode_count].dir = MODE_ADD;
cbed45a2
VY
643 mode_changes[mode_count].id = NULL;
644 mode_changes[mode_count].mems = ALL_MEMBERS;
212380e3
AC
645 mode_changes[mode_count++].arg = NULL;
646 }
647 else if((dir == MODE_DEL) && (chptr->mode.mode & mode_type))
648 {
649 chptr->mode.mode &= ~mode_type;
650
651 mode_changes[mode_count].letter = c;
652 mode_changes[mode_count].dir = MODE_DEL;
212380e3
AC
653 mode_changes[mode_count].mems = ALL_MEMBERS;
654 mode_changes[mode_count].id = NULL;
655 mode_changes[mode_count++].arg = NULL;
656 }
657}
658
be29ec79
AC
659void
660chm_hidden(struct Client *source_p, struct Channel *chptr,
04952c32 661 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
be29ec79 662{
07697336 663 if(MyClient(source_p) && !IsOperGeneral(source_p))
be29ec79
AC
664 {
665 if(!(*errors & SM_ERR_NOPRIVS))
666 sendto_one_numeric(source_p, ERR_NOPRIVILEGES, form_str(ERR_NOPRIVILEGES));
667 *errors |= SM_ERR_NOPRIVS;
668 return;
669 }
670 if(MyClient(source_p) && !IsOperAdmin(source_p))
671 {
672 if(!(*errors & SM_ERR_NOPRIVS))
673 sendto_one(source_p, form_str(ERR_NOPRIVS), me.name,
674 source_p->name, "admin");
675 *errors |= SM_ERR_NOPRIVS;
676 return;
677 }
678
679 if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
680 return;
681
682 /* setting + */
683 if((dir == MODE_ADD) && !(chptr->mode.mode & mode_type))
684 {
685 chptr->mode.mode |= mode_type;
686
687 mode_changes[mode_count].letter = c;
688 mode_changes[mode_count].dir = MODE_ADD;
be29ec79
AC
689 mode_changes[mode_count].id = NULL;
690 mode_changes[mode_count].mems = ONLY_OPERS;
be29ec79
AC
691 mode_changes[mode_count++].arg = NULL;
692 }
693 else if((dir == MODE_DEL) && (chptr->mode.mode & mode_type))
694 {
695 chptr->mode.mode &= ~mode_type;
696
697 mode_changes[mode_count].letter = c;
698 mode_changes[mode_count].dir = MODE_DEL;
be29ec79
AC
699 mode_changes[mode_count].mems = ONLY_OPERS;
700 mode_changes[mode_count].id = NULL;
be29ec79
AC
701 mode_changes[mode_count++].arg = NULL;
702 }
703}
704
212380e3
AC
705void
706chm_staff(struct Client *source_p, struct Channel *chptr,
04952c32 707 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
212380e3 708{
07697336 709 if(MyClient(source_p) && !IsOper(source_p))
212380e3
AC
710 {
711 if(!(*errors & SM_ERR_NOPRIVS))
712 sendto_one_numeric(source_p, ERR_NOPRIVILEGES, form_str(ERR_NOPRIVILEGES));
713 *errors |= SM_ERR_NOPRIVS;
714 return;
715 }
80303ab7 716 if(MyClient(source_p) && !HasPrivilege(source_p, "oper:cmodes"))
1ef5b430
JT
717 {
718 if(!(*errors & SM_ERR_NOPRIVS))
719 sendto_one(source_p, form_str(ERR_NOPRIVS), me.name,
80303ab7 720 source_p->name, "cmodes");
1ef5b430
JT
721 *errors |= SM_ERR_NOPRIVS;
722 return;
723 }
212380e3 724
84c9a8c7
JT
725 if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
726 return;
727
212380e3
AC
728 /* setting + */
729 if((dir == MODE_ADD) && !(chptr->mode.mode & mode_type))
730 {
731 chptr->mode.mode |= mode_type;
732
733 mode_changes[mode_count].letter = c;
734 mode_changes[mode_count].dir = MODE_ADD;
212380e3
AC
735 mode_changes[mode_count].id = NULL;
736 mode_changes[mode_count].mems = ALL_MEMBERS;
737 mode_changes[mode_count++].arg = NULL;
738 }
739 else if((dir == MODE_DEL) && (chptr->mode.mode & mode_type))
740 {
741 chptr->mode.mode &= ~mode_type;
742
743 mode_changes[mode_count].letter = c;
744 mode_changes[mode_count].dir = MODE_DEL;
212380e3
AC
745 mode_changes[mode_count].mems = ALL_MEMBERS;
746 mode_changes[mode_count].id = NULL;
747 mode_changes[mode_count++].arg = NULL;
748 }
749}
750
751void
752chm_ban(struct Client *source_p, struct Channel *chptr,
04952c32 753 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
212380e3 754{
04952c32 755 const char *mask;
cea0689e 756 char *forward;
330fc5c1
AC
757 rb_dlink_list *list;
758 rb_dlink_node *ptr;
212380e3
AC
759 struct Ban *banptr;
760 int errorval;
6f7b36d5
AC
761 const char *rpl_list_p;
762 const char *rpl_endlist_p;
212380e3
AC
763 int mems;
764
765 switch (mode_type)
766 {
767 case CHFL_BAN:
768 list = &chptr->banlist;
769 errorval = SM_ERR_RPL_B;
6f7b36d5
AC
770 rpl_list_p = form_str(RPL_BANLIST);
771 rpl_endlist_p = form_str(RPL_ENDOFBANLIST);
212380e3 772 mems = ALL_MEMBERS;
212380e3
AC
773 break;
774
775 case CHFL_EXCEPTION:
776 /* if +e is disabled, allow all but +e locally */
04952c32 777 if (!ConfigChannel.use_except && MyClient(source_p) && dir == MODE_ADD)
212380e3
AC
778 return;
779
780 list = &chptr->exceptlist;
781 errorval = SM_ERR_RPL_E;
6f7b36d5
AC
782 rpl_list_p = form_str(RPL_EXCEPTLIST);
783 rpl_endlist_p = form_str(RPL_ENDOFEXCEPTLIST);
212380e3
AC
784
785 if(ConfigChannel.use_except || (dir == MODE_DEL))
786 mems = ONLY_CHANOPS;
787 else
788 mems = ONLY_SERVERS;
789 break;
790
791 case CHFL_INVEX:
792 /* if +I is disabled, allow all but +I locally */
04952c32 793 if (!ConfigChannel.use_invex && MyClient(source_p) && dir == MODE_ADD)
212380e3
AC
794 return;
795
796 list = &chptr->invexlist;
797 errorval = SM_ERR_RPL_I;
6f7b36d5
AC
798 rpl_list_p = form_str(RPL_INVITELIST);
799 rpl_endlist_p = form_str(RPL_ENDOFINVITELIST);
212380e3
AC
800
801 if(ConfigChannel.use_invex || (dir == MODE_DEL))
802 mems = ONLY_CHANOPS;
803 else
804 mems = ONLY_SERVERS;
805 break;
806
807 case CHFL_QUIET:
808 list = &chptr->quietlist;
809 errorval = SM_ERR_RPL_Q;
6f7b36d5
AC
810 rpl_list_p = form_str(RPL_QUIETLIST);
811 rpl_endlist_p = form_str(RPL_ENDOFQUIETLIST);
212380e3 812 mems = ALL_MEMBERS;
212380e3
AC
813 break;
814
815 default:
816 sendto_realops_snomask(SNO_GENERAL, L_ALL, "chm_ban() called with unknown type!");
817 return;
212380e3
AC
818 }
819
04952c32 820 if (dir == MODE_QUERY)
212380e3
AC
821 {
822 if((*errors & errorval) != 0)
823 return;
824 *errors |= errorval;
825
826 /* non-ops cant see +eI lists.. */
f3b3ad0b 827 /* note that this is still permitted if +e/+I are mlocked. */
1046ac77 828 if(alevel < CHFL_CHANOP && mode_type != CHFL_BAN &&
212380e3
AC
829 mode_type != CHFL_QUIET)
830 {
831 if(!(*errors & SM_ERR_NOOPS))
832 sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
833 me.name, source_p->name, chptr->chname);
834 *errors |= SM_ERR_NOOPS;
835 return;
836 }
837
5cefa1d6 838 RB_DLINK_FOREACH(ptr, list->head)
212380e3 839 {
765d839d 840 char buf[BANLEN];
212380e3 841 banptr = ptr->data;
765d839d 842 if(banptr->forward)
5203cba5 843 snprintf(buf, sizeof(buf), "%s$%s", banptr->banstr, banptr->forward);
765d839d
EM
844 else
845 rb_strlcpy(buf, banptr->banstr, sizeof(buf));
846
6f7b36d5 847 sendto_one(source_p, rpl_list_p,
212380e3 848 me.name, source_p->name, chptr->chname,
765d839d 849 buf, banptr->who, banptr->when);
212380e3 850 }
6f7b36d5 851 sendto_one(source_p, rpl_endlist_p, me.name, source_p->name, chptr->chname);
212380e3
AC
852 return;
853 }
854
04952c32 855 if (!allow_mode_change(source_p, chptr, alevel, errors, c))
212380e3 856 return;
f3b3ad0b 857
212380e3 858
04952c32 859 if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
212380e3
AC
860 return;
861
212380e3 862 /* empty ban, or starts with ':' which messes up s2s, ignore it */
04952c32 863 if (EmptyString(arg) || *arg == ':')
212380e3
AC
864 return;
865
04952c32 866 if (!MyClient(source_p))
212380e3 867 {
04952c32 868 if (strchr(arg, ' '))
212380e3
AC
869 return;
870
04952c32 871 mask = arg;
212380e3
AC
872 }
873 else
04952c32 874 mask = pretty_mask(arg);
212380e3 875
1aa35c8a
JT
876 /* we'd have problems parsing this, hyb6 does it too
877 * also make sure it will always fit on a line with channel
878 * name etc.
879 */
79435744 880 if(strlen(mask) > MIN(BANLEN, MODEBUFLEN - 5))
be0365e1
JT
881 {
882 sendto_one_numeric(source_p, ERR_INVALIDBAN,
883 form_str(ERR_INVALIDBAN),
04952c32 884 chptr->chname, c, arg);
1aa35c8a 885 return;
be0365e1 886 }
1aa35c8a 887
765d839d
EM
888 /* Look for a $ after the first character.
889 * As the first character, it marks an extban; afterwards
890 * it delimits a forward channel.
891 */
892 if ((forward = strchr(mask+1, '$')) != NULL)
893 {
894 *forward++ = '\0';
895 if (*forward == '\0')
896 forward = NULL;
897 }
898
212380e3 899 /* if we're adding a NEW id */
04952c32 900 if (dir == MODE_ADD)
212380e3
AC
901 {
902 if (*mask == '$' && MyClient(source_p))
903 {
904 if (!valid_extban(mask, source_p, chptr, mode_type))
be0365e1
JT
905 {
906 sendto_one_numeric(source_p, ERR_INVALIDBAN,
907 form_str(ERR_INVALIDBAN),
04952c32 908 chptr->chname, c, arg);
212380e3 909 return;
be0365e1 910 }
212380e3
AC
911 }
912
e1dc9e54
JT
913 /* For compatibility, only check the forward channel from
914 * local clients. Accept any forward channel from servers.
915 */
04952c32 916 if (forward != NULL && MyClient(source_p))
e1dc9e54 917 {
f2edb2be
JT
918 /* For simplicity and future flexibility, do not
919 * allow '$' in forwarding targets.
920 */
04952c32 921 if (!ConfigChannel.use_forward ||
be0365e1
JT
922 strchr(forward, '$') != NULL)
923 {
924 sendto_one_numeric(source_p, ERR_INVALIDBAN,
925 form_str(ERR_INVALIDBAN),
04952c32 926 chptr->chname, c, arg);
be0365e1
JT
927 return;
928 }
929 /* check_forward() sends its own error message */
04952c32 930 if (!check_forward(source_p, chptr, forward))
f2edb2be 931 return;
5efa7ef6 932 /* Forwards only make sense for bans. */
04952c32 933 if (mode_type != CHFL_BAN)
be0365e1
JT
934 {
935 sendto_one_numeric(source_p, ERR_INVALIDBAN,
936 form_str(ERR_INVALIDBAN),
04952c32 937 chptr->chname, c, arg);
5efa7ef6 938 return;
be0365e1 939 }
e1dc9e54 940 }
2da6f6eb 941
212380e3
AC
942 /* dont allow local clients to overflow the banlist, dont
943 * let remote servers set duplicate bans
944 */
04952c32 945 if (!add_id(source_p, chptr, mask, forward, list, mode_type))
212380e3
AC
946 return;
947
04952c32 948 if (forward)
765d839d
EM
949 forward[-1]= '$';
950
212380e3
AC
951 mode_changes[mode_count].letter = c;
952 mode_changes[mode_count].dir = MODE_ADD;
212380e3
AC
953 mode_changes[mode_count].mems = mems;
954 mode_changes[mode_count].id = NULL;
955 mode_changes[mode_count++].arg = mask;
956 }
04952c32 957 else if (dir == MODE_DEL)
212380e3 958 {
765d839d
EM
959 struct Ban *removed;
960 static char buf[BANLEN * MAXMODEPARAMS];
961 int old_removed_mask_pos = removed_mask_pos;
04952c32 962 if ((removed = del_id(chptr, mask, list, mode_type)) == NULL)
212380e3 963 {
04952c32
EK
964 /* mask isn't a valid ban, check arg */
965 if ((removed = del_id(chptr, arg, list, mode_type)) != NULL)
966 mask = arg;
212380e3
AC
967 }
968
04952c32 969 if (removed && removed->forward)
5203cba5 970 removed_mask_pos += snprintf(buf + old_removed_mask_pos, sizeof(buf), "%s$%s", removed->banstr, removed->forward) + 1;
765d839d 971 else
3e910a18 972 removed_mask_pos += rb_strlcpy(buf + old_removed_mask_pos, mask, sizeof(buf)) + 1;
04952c32 973 if (removed)
765d839d
EM
974 {
975 free_ban(removed);
976 removed = NULL;
977 }
978
212380e3
AC
979 mode_changes[mode_count].letter = c;
980 mode_changes[mode_count].dir = MODE_DEL;
212380e3
AC
981 mode_changes[mode_count].mems = mems;
982 mode_changes[mode_count].id = NULL;
765d839d 983 mode_changes[mode_count++].arg = buf + old_removed_mask_pos;
212380e3
AC
984 }
985}
986
987void
988chm_op(struct Client *source_p, struct Channel *chptr,
04952c32 989 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
212380e3
AC
990{
991 struct membership *mstptr;
212380e3
AC
992 struct Client *targ_p;
993
f3b3ad0b 994 if(!allow_mode_change(source_p, chptr, alevel, errors, c))
212380e3 995 return;
212380e3 996
212380e3 997 /* empty nick */
04952c32 998 if(EmptyString(arg))
212380e3
AC
999 {
1000 sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), "*");
1001 return;
1002 }
1003
04952c32 1004 if((targ_p = find_chasing(source_p, arg, NULL)) == NULL)
212380e3
AC
1005 {
1006 return;
1007 }
1008
1009 mstptr = find_channel_membership(chptr, targ_p);
1010
1011 if(mstptr == NULL)
1012 {
1013 if(!(*errors & SM_ERR_NOTONCHANNEL) && MyClient(source_p))
1014 sendto_one_numeric(source_p, ERR_USERNOTINCHANNEL,
04952c32 1015 form_str(ERR_USERNOTINCHANNEL), arg, chptr->chname);
212380e3
AC
1016 *errors |= SM_ERR_NOTONCHANNEL;
1017 return;
1018 }
1019
1020 if(MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
1021 return;
1022
1023 if(dir == MODE_ADD)
1024 {
fa0e2152
JT
1025 if(targ_p == source_p && mstptr->flags & CHFL_CHANOP)
1026 return;
1027
212380e3
AC
1028 mode_changes[mode_count].letter = c;
1029 mode_changes[mode_count].dir = MODE_ADD;
212380e3
AC
1030 mode_changes[mode_count].mems = ALL_MEMBERS;
1031 mode_changes[mode_count].id = targ_p->id;
0f8db055 1032 mode_changes[mode_count++].arg = targ_p->name;
212380e3
AC
1033
1034 mstptr->flags |= CHFL_CHANOP;
212380e3
AC
1035 }
1036 else
1037 {
1038 if(MyClient(source_p) && IsService(targ_p))
1039 {
1040 sendto_one(source_p, form_str(ERR_ISCHANSERVICE),
1041 me.name, source_p->name, targ_p->name, chptr->chname);
1042 return;
1043 }
1044
1045 mode_changes[mode_count].letter = c;
1046 mode_changes[mode_count].dir = MODE_DEL;
212380e3
AC
1047 mode_changes[mode_count].mems = ALL_MEMBERS;
1048 mode_changes[mode_count].id = targ_p->id;
0f8db055 1049 mode_changes[mode_count++].arg = targ_p->name;
212380e3
AC
1050
1051 mstptr->flags &= ~CHFL_CHANOP;
1052 }
1053}
1054
1055void
1056chm_voice(struct Client *source_p, struct Channel *chptr,
04952c32 1057 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
212380e3
AC
1058{
1059 struct membership *mstptr;
212380e3
AC
1060 struct Client *targ_p;
1061
f3b3ad0b 1062 if(!allow_mode_change(source_p, chptr, alevel, errors, c))
212380e3 1063 return;
212380e3 1064
212380e3 1065 /* empty nick */
04952c32 1066 if(EmptyString(arg))
212380e3
AC
1067 {
1068 sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), "*");
1069 return;
1070 }
1071
04952c32 1072 if((targ_p = find_chasing(source_p, arg, NULL)) == NULL)
212380e3
AC
1073 {
1074 return;
1075 }
1076
1077 mstptr = find_channel_membership(chptr, targ_p);
1078
1079 if(mstptr == NULL)
1080 {
1081 if(!(*errors & SM_ERR_NOTONCHANNEL) && MyClient(source_p))
1082 sendto_one_numeric(source_p, ERR_USERNOTINCHANNEL,
04952c32 1083 form_str(ERR_USERNOTINCHANNEL), arg, chptr->chname);
212380e3
AC
1084 *errors |= SM_ERR_NOTONCHANNEL;
1085 return;
1086 }
1087
1088 if(MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
1089 return;
1090
1091 if(dir == MODE_ADD)
1092 {
1093 mode_changes[mode_count].letter = c;
1094 mode_changes[mode_count].dir = MODE_ADD;
212380e3
AC
1095 mode_changes[mode_count].mems = ALL_MEMBERS;
1096 mode_changes[mode_count].id = targ_p->id;
0f8db055 1097 mode_changes[mode_count++].arg = targ_p->name;
212380e3
AC
1098
1099 mstptr->flags |= CHFL_VOICE;
1100 }
1101 else
1102 {
1103 mode_changes[mode_count].letter = 'v';
1104 mode_changes[mode_count].dir = MODE_DEL;
212380e3
AC
1105 mode_changes[mode_count].mems = ALL_MEMBERS;
1106 mode_changes[mode_count].id = targ_p->id;
0f8db055 1107 mode_changes[mode_count++].arg = targ_p->name;
212380e3
AC
1108
1109 mstptr->flags &= ~CHFL_VOICE;
1110 }
1111}
1112
1113void
1114chm_limit(struct Client *source_p, struct Channel *chptr,
04952c32 1115 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
212380e3 1116{
212380e3
AC
1117 static char limitstr[30];
1118 int limit;
1119
04952c32 1120 if (!allow_mode_change(source_p, chptr, alevel, errors, c))
212380e3
AC
1121 return;
1122
04952c32 1123 if (MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
84c9a8c7
JT
1124 return;
1125
04952c32 1126 if (dir == MODE_ADD)
212380e3 1127 {
04952c32 1128 if (EmptyString(arg) || (limit = atoi(arg)) <= 0)
212380e3
AC
1129 return;
1130
5203cba5 1131 sprintf(limitstr, "%d", limit);
212380e3
AC
1132
1133 mode_changes[mode_count].letter = c;
1134 mode_changes[mode_count].dir = MODE_ADD;
212380e3
AC
1135 mode_changes[mode_count].mems = ALL_MEMBERS;
1136 mode_changes[mode_count].id = NULL;
1137 mode_changes[mode_count++].arg = limitstr;
1138
1139 chptr->mode.limit = limit;
1140 }
04952c32 1141 else if (dir == MODE_DEL)
212380e3
AC
1142 {
1143 if(!chptr->mode.limit)
1144 return;
1145
1146 chptr->mode.limit = 0;
1147
1148 mode_changes[mode_count].letter = c;
1149 mode_changes[mode_count].dir = MODE_DEL;
212380e3
AC
1150 mode_changes[mode_count].mems = ALL_MEMBERS;
1151 mode_changes[mode_count].id = NULL;
1152 mode_changes[mode_count++].arg = NULL;
1153 }
1154}
1155
1156void
1157chm_throttle(struct Client *source_p, struct Channel *chptr,
04952c32 1158 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
212380e3
AC
1159{
1160 int joins = 0, timeslice = 0;
1161
04952c32 1162 if (!allow_mode_change(source_p, chptr, alevel, errors, c))
212380e3
AC
1163 return;
1164
04952c32 1165 if (MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
84c9a8c7
JT
1166 return;
1167
04952c32 1168 if (dir == MODE_ADD)
212380e3 1169 {
04952c32 1170 if (sscanf(arg, "%d:%d", &joins, &timeslice) < 2)
2daf1813 1171 return;
212380e3 1172
90552e21 1173 if(joins <= 0 || timeslice <= 0)
212380e3
AC
1174 return;
1175
1176 mode_changes[mode_count].letter = c;
1177 mode_changes[mode_count].dir = MODE_ADD;
212380e3
AC
1178 mode_changes[mode_count].mems = ALL_MEMBERS;
1179 mode_changes[mode_count].id = NULL;
04952c32 1180 mode_changes[mode_count++].arg = arg;
212380e3
AC
1181
1182 chptr->mode.join_num = joins;
1183 chptr->mode.join_time = timeslice;
1184 }
1185 else if(dir == MODE_DEL)
1186 {
1187 if(!chptr->mode.join_num)
1188 return;
1189
1190 chptr->mode.join_num = 0;
1191 chptr->mode.join_time = 0;
1192 chptr->join_count = 0;
1193 chptr->join_delta = 0;
1194
1195 mode_changes[mode_count].letter = c;
1196 mode_changes[mode_count].dir = MODE_DEL;
212380e3
AC
1197 mode_changes[mode_count].mems = ALL_MEMBERS;
1198 mode_changes[mode_count].id = NULL;
1199 mode_changes[mode_count++].arg = NULL;
1200 }
1201}
1202
1203void
1204chm_forward(struct Client *source_p, struct Channel *chptr,
04952c32 1205 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
212380e3 1206{
2da6f6eb 1207 /* if +f is disabled, ignore local attempts to set it */
04952c32 1208 if (!ConfigChannel.use_forward && MyClient(source_p) && dir == MODE_ADD)
2da6f6eb
JT
1209 return;
1210
04952c32 1211 if (dir == MODE_QUERY)
212380e3
AC
1212 {
1213 if (!(*errors & SM_ERR_RPL_F))
1214 {
1215 if (*chptr->mode.forward == '\0')
5366977b 1216 sendto_one_notice(source_p, ":%s has no forward channel", chptr->chname);
212380e3 1217 else
5366977b 1218 sendto_one_notice(source_p, ":%s forward channel is %s", chptr->chname, chptr->mode.forward);
212380e3
AC
1219 *errors |= SM_ERR_RPL_F;
1220 }
1221 return;
1222 }
1223
1224#ifndef FORWARD_OPERONLY
04952c32 1225 if (!allow_mode_change(source_p, chptr, alevel, errors, c))
212380e3 1226 return;
212380e3 1227#else
04952c32 1228 if (!IsOperGeneral(source_p) && !IsServer(source_p))
212380e3
AC
1229 {
1230 if(!(*errors & SM_ERR_NOPRIVS))
1231 sendto_one_numeric(source_p, ERR_NOPRIVILEGES, form_str(ERR_NOPRIVILEGES));
1232 *errors |= SM_ERR_NOPRIVS;
1233 return;
1234 }
1235#endif
1236
04952c32 1237 if (MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
84c9a8c7
JT
1238 return;
1239
04952c32 1240 if (dir == MODE_ADD)
212380e3 1241 {
04952c32 1242 if(EmptyString(arg))
212380e3 1243 return;
0c730321 1244
04952c32 1245 if(!check_forward(source_p, chptr, arg))
212380e3 1246 return;
212380e3 1247
04952c32 1248 rb_strlcpy(chptr->mode.forward, arg, sizeof(chptr->mode.forward));
212380e3
AC
1249
1250 mode_changes[mode_count].letter = c;
1251 mode_changes[mode_count].dir = MODE_ADD;
2da6f6eb
JT
1252 mode_changes[mode_count].mems =
1253 ConfigChannel.use_forward ? ALL_MEMBERS : ONLY_SERVERS;
212380e3 1254 mode_changes[mode_count].id = NULL;
04952c32 1255 mode_changes[mode_count++].arg = arg;
212380e3
AC
1256 }
1257 else if(dir == MODE_DEL)
1258 {
1259 if(!(*chptr->mode.forward))
1260 return;
1261
1262 *chptr->mode.forward = '\0';
1263
1264 mode_changes[mode_count].letter = c;
1265 mode_changes[mode_count].dir = MODE_DEL;
212380e3
AC
1266 mode_changes[mode_count].mems = ALL_MEMBERS;
1267 mode_changes[mode_count].id = NULL;
1268 mode_changes[mode_count++].arg = NULL;
1269 }
1270}
1271
1272void
1273chm_key(struct Client *source_p, struct Channel *chptr,
04952c32 1274 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
212380e3
AC
1275{
1276 char *key;
1277
04952c32 1278 if (!allow_mode_change(source_p, chptr, alevel, errors, c))
212380e3 1279 return;
212380e3 1280
04952c32 1281 if (MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
212380e3
AC
1282 return;
1283
04952c32 1284 if (dir == MODE_ADD)
212380e3 1285 {
04952c32 1286 key = LOCAL_COPY(arg);
212380e3
AC
1287
1288 if(MyClient(source_p))
1289 fix_key(key);
1290 else
1291 fix_key_remote(key);
1292
1293 if(EmptyString(key))
1294 return;
1295
1296 s_assert(key[0] != ' ');
f427c8b0 1297 rb_strlcpy(chptr->mode.key, key, sizeof(chptr->mode.key));
212380e3
AC
1298
1299 mode_changes[mode_count].letter = c;
1300 mode_changes[mode_count].dir = MODE_ADD;
212380e3
AC
1301 mode_changes[mode_count].mems = ALL_MEMBERS;
1302 mode_changes[mode_count].id = NULL;
1303 mode_changes[mode_count++].arg = chptr->mode.key;
1304 }
1305 else if(dir == MODE_DEL)
1306 {
1307 static char splat[] = "*";
1308 int i;
1309
212380e3
AC
1310 if(!(*chptr->mode.key))
1311 return;
1312
1313 /* hack time. when we get a +k-k mode, the +k arg is
1314 * chptr->mode.key, which the -k sets to \0, so hunt for a
1315 * +k when we get a -k, and set the arg to splat. --anfl
1316 */
1317 for(i = 0; i < mode_count; i++)
1318 {
1319 if(mode_changes[i].letter == 'k' && mode_changes[i].dir == MODE_ADD)
1320 mode_changes[i].arg = splat;
1321 }
1322
1323 *chptr->mode.key = 0;
1324
1325 mode_changes[mode_count].letter = c;
1326 mode_changes[mode_count].dir = MODE_DEL;
212380e3
AC
1327 mode_changes[mode_count].mems = ALL_MEMBERS;
1328 mode_changes[mode_count].id = NULL;
1329 mode_changes[mode_count++].arg = "*";
1330 }
1331}
1332
212380e3
AC
1333/* *INDENT-OFF* */
1334struct ChannelMode chmode_table[256] =
1335{
04952c32
EK
1336 ['F'] = {chm_simple, MODE_FREETARGET, 0 },
1337 ['I'] = {chm_ban, CHFL_INVEX, CHM_QUERYABLE | CHM_OPS_QUERY },
1338 ['L'] = {chm_staff, MODE_EXLIMIT, 0 },
1339 ['P'] = {chm_staff, MODE_PERMANENT, 0 },
1340 ['Q'] = {chm_simple, MODE_DISFORWARD, 0 },
1341 ['b'] = {chm_ban, CHFL_BAN, CHM_QUERYABLE },
1342 ['e'] = {chm_ban, CHFL_EXCEPTION, CHM_QUERYABLE | CHM_OPS_QUERY },
1343 ['f'] = {chm_forward, 0, CHM_ARG_SET | CHM_CAN_QUERY }, /* weird because it's nonstandard and violates isupport */
1344 ['g'] = {chm_simple, MODE_FREEINVITE, 0 },
1345 ['i'] = {chm_simple, MODE_INVITEONLY, 0 },
1346 ['j'] = {chm_throttle, 0, CHM_ARG_SET },
1347 ['k'] = {chm_key, 0, CHM_QUERYABLE },
1348 ['l'] = {chm_limit, 0, CHM_ARG_SET },
1349 ['m'] = {chm_simple, MODE_MODERATED, 0 },
1350 ['n'] = {chm_simple, MODE_NOPRIVMSGS, 0 },
1351 ['o'] = {chm_op, 0, CHM_ARGS },
1352 ['p'] = {chm_simple, MODE_PRIVATE, 0 },
1353 ['q'] = {chm_ban, CHFL_QUIET, CHM_QUERYABLE },
1354 ['r'] = {chm_simple, MODE_REGONLY, 0 },
1355 ['s'] = {chm_simple, MODE_SECRET, 0 },
1356 ['t'] = {chm_simple, MODE_TOPICLIMIT, 0 },
1357 ['v'] = {chm_voice, 0, CHM_ARGS },
1358 ['z'] = {chm_simple, MODE_OPMODERATE, 0 },
212380e3
AC
1359};
1360
1361/* *INDENT-ON* */
1362
1363/* set_channel_mode()
1364 *
1365 * inputs - client, source, channel, membership pointer, params
55abcbb2 1366 * output -
212380e3
AC
1367 * side effects - channel modes/memberships are changed, MODE is issued
1368 *
1369 * Extensively modified to be hotpluggable, 03/09/06 -- nenolod
1370 */
1371void
1372set_channel_mode(struct Client *client_p, struct Client *source_p,
1373 struct Channel *chptr, struct membership *msptr, int parc, const char *parv[])
1374{
047b56e7 1375 static char modebuf[BUFSIZE * 2]; /* paranoid case: 2 canonical chars per input char */
212380e3
AC
1376 static char parabuf[BUFSIZE];
1377 char *mbuf;
1378 char *pbuf;
1379 int cur_len, mlen, paralen, paracount, arglen, len;
1380 int i, j, flags;
04952c32
EK
1381 int dir = MODE_ADD;
1382 int access_dir = MODE_QUERY;
212380e3
AC
1383 int parn = 1;
1384 int errors = 0;
1385 int alevel;
1386 const char *ml = parv[0];
1387 char c;
1388 struct Client *fakesource_p;
be29ec79 1389 int flags_list[3] = { ALL_MEMBERS, ONLY_CHANOPS, ONLY_OPERS };
212380e3
AC
1390
1391 mask_pos = 0;
765d839d 1392 removed_mask_pos = 0;
212380e3
AC
1393 mode_count = 0;
1394 mode_limit = 0;
84c9a8c7 1395 mode_limit_simple = 0;
212380e3 1396
212380e3
AC
1397 /* Hide connecting server on netburst -- jilles */
1398 if (ConfigServerHide.flatten_links && IsServer(source_p) && !has_id(source_p) && !HasSentEob(source_p))
1399 fakesource_p = &me;
1400 else
1401 fakesource_p = source_p;
1402
04952c32
EK
1403 struct modeset {
1404 const struct ChannelMode *cm;
1405 const char *arg;
1406 int dir;
1407 char mode;
1408 };
202d4966 1409
04952c32
EK
1410 static struct modeset modesets[MAXPARA];
1411 struct modeset *ms = modesets, *mend;
047b56e7
EK
1412 char canon_op = '\0';
1413
1414 mbuf = modebuf;
04952c32
EK
1415
1416 for (ml = parv[0]; *ml != 0; ml++)
212380e3 1417 {
04952c32 1418 c = *ml;
212380e3
AC
1419 switch (c)
1420 {
1421 case '+':
1422 dir = MODE_ADD;
1423 break;
1424 case '-':
1425 dir = MODE_DEL;
1426 break;
1427 case '=':
1428 dir = MODE_QUERY;
1429 break;
1430 default:
04952c32
EK
1431 {
1432 int effective_dir = dir;
1433 const struct ChannelMode *cm = &chmode_table[(unsigned char) c];
1434 bool use_arg = dir == MODE_ADD ? cm->flags & CHM_ARG_SET :
1435 dir == MODE_DEL ? cm->flags & CHM_ARG_DEL :
1436 false;
1437 if (cm->set_func == NULL || cm->set_func == chm_nosuch)
1438 {
1439 sendto_one(source_p, form_str(ERR_UNKNOWNMODE), me.name, source_p->name, c);
1440 return;
1441 }
1442 if (use_arg && parn >= parc)
1443 {
1444 if (!(cm->flags & CHM_CAN_QUERY))
1445 {
1446 sendto_one(source_p, form_str(ERR_NEEDMOREPARAMS), me.name, source_p->name, "MODE");
1447 return;
1448 }
1449 effective_dir = MODE_QUERY;
1450 use_arg = false;
1451 }
1452
1453 if (effective_dir == MODE_QUERY && !(cm->flags & CHM_CAN_QUERY))
1454 {
1455 /* XXX this currently replicates traditional behaviour and just
1456 * does nothing for a query on a mode with no query. would it be
1457 * good to send an error here?
1458 */
047b56e7 1459 continue;
04952c32
EK
1460 }
1461
047b56e7
EK
1462 char op = effective_dir == MODE_ADD ? '+' :
1463 effective_dir == MODE_DEL ? '-' :
1464 '=';
1465
1466 if (op != canon_op)
1467 *mbuf++ = canon_op = op;
1468
1469 *mbuf++ = c;
1470
04952c32
EK
1471 if (effective_dir != MODE_QUERY && access_dir == MODE_QUERY)
1472 access_dir = effective_dir;
ea41b24f
EK
1473 if (effective_dir == MODE_QUERY && cm->flags & CHM_OPS_QUERY)
1474 access_dir = MODE_OP_QUERY;
04952c32
EK
1475
1476 ms->cm = cm;
1477 ms->dir = effective_dir;
1478 if (use_arg)
1479 ms->arg = parv[parn++];
1480 else
1481 ms->arg = NULL;
1482 ms->mode = c;
1483 ms++;
1484 }
212380e3
AC
1485 }
1486 }
1487
047b56e7
EK
1488 /* this will happen on something like MODE +-=++-.
1489 * we'd have caught that with the if !mode_count
1490 * later on, but this saves an override notice
1491 */
1492 if (ms == modesets)
1493 return;
1494
04952c32
EK
1495 if (parn < parc)
1496 {
1497 /* XXX we could reject excess params here */
1498 }
1499
1500 mend = ms;
1501
047b56e7
EK
1502 if (parn > 1)
1503 {
1504 strcpy(mbuf, " ");
1505 rb_strlcat(modebuf, reconstruct_parv(parn - 1, parv + 1), sizeof modebuf);
1506 }
1507 else
1508 {
1509 *mbuf = '\0';
1510 }
1511 alevel = get_channel_access(source_p, chptr, msptr, access_dir, modebuf);
04952c32
EK
1512
1513 for (ms = modesets; ms < mend; ms++)
1514 {
92c6e47b 1515 ChannelModeFunc *set_func = ms->cm->set_func;
04952c32
EK
1516 if (set_func == NULL)
1517 set_func = chm_nosuch;
1518 set_func(fakesource_p, chptr, alevel, ms->arg, &errors, ms->dir, ms->mode, ms->cm->mode_type);
1519 }
1520
212380e3 1521 /* bail out if we have nothing to do... */
047b56e7 1522 if (!mode_count)
212380e3
AC
1523 return;
1524
047b56e7 1525 if (IsServer(source_p))
5203cba5 1526 mlen = sprintf(modebuf, ":%s MODE %s ", fakesource_p->name, chptr->chname);
212380e3 1527 else
5203cba5 1528 mlen = sprintf(modebuf, ":%s!%s@%s MODE %s ",
212380e3
AC
1529 source_p->name, source_p->username,
1530 source_p->host, chptr->chname);
1531
87c44482 1532 for(j = 0; j < 3; j++)
212380e3 1533 {
d3fd88a4 1534 int send_flags = flags = flags_list[j];
f5d60bb5 1535 const char *priv = NULL;
d3fd88a4
EK
1536 if (flags == ONLY_OPERS)
1537 {
1538 send_flags = ALL_MEMBERS;
1539 priv = "auspex:cmodes";
1540 }
212380e3
AC
1541 cur_len = mlen;
1542 mbuf = modebuf + mlen;
1543 pbuf = parabuf;
1544 parabuf[0] = '\0';
1545 paracount = paralen = 0;
1546 dir = MODE_QUERY;
1547
1548 for(i = 0; i < mode_count; i++)
1549 {
1550 if(mode_changes[i].letter == 0 || mode_changes[i].mems != flags)
1551 continue;
1552
1553 if(mode_changes[i].arg != NULL)
1554 {
1555 arglen = strlen(mode_changes[i].arg);
1556
1557 if(arglen > MODEBUFLEN - 5)
1558 continue;
1559 }
1560 else
1561 arglen = 0;
1562
1563 /* if we're creeping over MAXMODEPARAMSSERV, or over
1564 * bufsize (4 == +/-,modechar,two spaces) send now.
1565 */
1566 if(mode_changes[i].arg != NULL &&
1567 ((paracount == MAXMODEPARAMSSERV) ||
1568 ((cur_len + paralen + arglen + 4) > (BUFSIZE - 3))))
1569 {
1570 *mbuf = '\0';
1571
1572 if(cur_len > mlen)
d3fd88a4
EK
1573 sendto_channel_local_priv(IsServer(source_p) ? fakesource_p : source_p,
1574 send_flags, priv, chptr, "%s %s", modebuf, parabuf);
212380e3
AC
1575 else
1576 continue;
1577
1578 paracount = paralen = 0;
1579 cur_len = mlen;
1580 mbuf = modebuf + mlen;
1581 pbuf = parabuf;
1582 parabuf[0] = '\0';
1583 dir = MODE_QUERY;
1584 }
1585
1586 if(dir != mode_changes[i].dir)
1587 {
1588 *mbuf++ = (mode_changes[i].dir == MODE_ADD) ? '+' : '-';
1589 cur_len++;
1590 dir = mode_changes[i].dir;
1591 }
1592
1593 *mbuf++ = mode_changes[i].letter;
1594 cur_len++;
1595
1596 if(mode_changes[i].arg != NULL)
1597 {
1598 paracount++;
5203cba5 1599 len = sprintf(pbuf, "%s ", mode_changes[i].arg);
212380e3
AC
1600 pbuf += len;
1601 paralen += len;
1602 }
1603 }
1604
1605 if(paralen && parabuf[paralen - 1] == ' ')
1606 parabuf[paralen - 1] = '\0';
1607
1608 *mbuf = '\0';
1609 if(cur_len > mlen)
d3fd88a4
EK
1610 sendto_channel_local_priv(IsServer(source_p) ? fakesource_p : source_p,
1611 send_flags, priv, chptr, "%s %s", modebuf, parabuf);
212380e3
AC
1612 }
1613
1614 /* only propagate modes originating locally, or if we're hubbing */
330fc5c1 1615 if(MyClient(source_p) || rb_dlink_list_length(&serv_list) > 1)
212380e3
AC
1616 send_cap_mode_changes(client_p, source_p, chptr, mode_changes, mode_count);
1617}
8727cbe8
AC
1618
1619/* set_channel_mlock()
1620 *
1621 * inputs - client, source, channel, params
55abcbb2 1622 * output -
8727cbe8
AC
1623 * side effects - channel mlock is changed / MLOCK is propagated
1624 */
1625void
1626set_channel_mlock(struct Client *client_p, struct Client *source_p,
07554369 1627 struct Channel *chptr, const char *newmlock, bool propagate)
8727cbe8 1628{
78e6b731 1629 rb_free(chptr->mode_lock);
6b8db2da 1630 chptr->mode_lock = newmlock ? rb_strdup(newmlock) : NULL;
8727cbe8 1631
6fb6bd15
AC
1632 if (propagate)
1633 {
1634 sendto_server(client_p, NULL, CAP_TS6 | CAP_MLOCK, NOCAPS, ":%s MLOCK %ld %s :%s",
1635 source_p->id, (long) chptr->channelts, chptr->chname,
1636 chptr->mode_lock ? chptr->mode_lock : "");
1637 }
8727cbe8 1638}