]> jfr.im git - solanum.git/blob - ircd/chmode.c
Add general::hidden_caps
[solanum.git] / ircd / chmode.c
1 /*
2 * Solanum: a slightly advanced ircd
3 * chmode.c: channel mode management
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 * 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
24 */
25
26 #include "stdinc.h"
27 #include "channel.h"
28 #include "client.h"
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"
42 #include "s_assert.h"
43 #include "parse.h"
44 #include "msgbuf.h"
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
63 static struct ChModeChange mode_changes[BUFSIZE];
64 static int mode_count;
65 static int mode_limit;
66 static int mode_limit_simple;
67 static int mask_pos;
68 static int removed_mask_pos;
69
70 char cflagsbuf[256];
71 char cflagsmyinfo[256];
72
73 int chmode_flags[256];
74
75 extern int h_get_channel_access;
76
77 /* OPTIMIZE ME! -- dwr */
78 void
79 construct_cflags_strings(void)
80 {
81 int i;
82 char *ptr = cflagsbuf;
83 char *ptr2 = cflagsmyinfo;
84
85 *ptr = '\0';
86 *ptr2 = '\0';
87
88 for(i = 0; i < 256; i++)
89 {
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)
97 {
98 chmode_flags[i] = chmode_table[i].mode_type;
99 }
100 else
101 {
102 chmode_flags[i] = 0;
103 }
104
105 switch (chmode_flags[i])
106 {
107 case MODE_FREETARGET:
108 case MODE_DISFORWARD:
109 if(ConfigChannel.use_forward)
110 *ptr++ = (char) i;
111 break;
112 default:
113 if(chmode_flags[i] != 0)
114 {
115 *ptr++ = (char) i;
116 }
117 }
118
119 /* Should we leave orphaned check here? -- dwr */
120 if (chmode_table[i].set_func != NULL &&
121 chmode_table[i].set_func != chm_nosuch &&
122 chmode_table[i].set_func != chm_orphaned)
123 {
124 *ptr2++ = (char) i;
125 }
126 }
127
128 *ptr++ = '\0';
129 *ptr2++ = '\0';
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 */
140 static unsigned int
141 find_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;
152 }
153
154 unsigned int
155 cflag_add(char c_, ChannelModeFunc function)
156 {
157 int c = (unsigned char)c_;
158
159 if (chmode_table[c].set_func != NULL &&
160 chmode_table[c].set_func != chm_nosuch &&
161 chmode_table[c].set_func != chm_orphaned)
162 return 0;
163
164 if (chmode_table[c].set_func == NULL || chmode_table[c].set_func == chm_nosuch)
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
173 void
174 cflag_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
183 int
184 get_channel_access(struct Client *source_p, struct Channel *chptr, struct membership *msptr, int dir, const char *modestr)
185 {
186 hook_data_channel_approval moduledata;
187
188 if(!MyClient(source_p))
189 return CHFL_CHANOP;
190
191 moduledata.client = source_p;
192 moduledata.chptr = chptr;
193 moduledata.msptr = msptr;
194 moduledata.target = NULL;
195 moduledata.approved = (msptr != NULL && is_chanop(msptr)) ? CHFL_CHANOP : CHFL_PEON;
196 moduledata.dir = dir;
197 moduledata.modestr = modestr;
198
199 call_hook(h_get_channel_access, &moduledata);
200
201 return moduledata.approved;
202 }
203
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
209 * outputs - false on failure, true on success
210 * side effects - error message sent on failure
211 */
212 static bool
213 allow_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;
227 return false;
228 }
229 if(alevel < CHFL_CHANOP)
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;
235 return false;
236 }
237 return true;
238 }
239
240 /* add_id()
241 *
242 * inputs - client, channel, id to add, type, forward
243 * outputs - false on failure, true on success
244 * side effects - given id is added to the appropriate list
245 */
246 bool
247 add_id(struct Client *source_p, struct Channel *chptr, const char *banid, const char *forward,
248 rb_dlink_list * list, long mode_type)
249 {
250 struct Ban *actualBan;
251 static char who[USERHOST_REPLYLEN];
252 char *realban = LOCAL_COPY(banid);
253 rb_dlink_node *ptr;
254
255 /* dont let local clients overflow the banlist, or set redundant
256 * bans
257 */
258 if(MyClient(source_p))
259 {
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))
261 {
262 sendto_one(source_p, form_str(ERR_BANLISTFULL),
263 me.name, source_p->name, chptr->chname, realban);
264 return false;
265 }
266
267 RB_DLINK_FOREACH(ptr, list->head)
268 {
269 actualBan = ptr->data;
270 if(mask_match(actualBan->banstr, realban))
271 return false;
272 }
273 }
274 /* dont let remotes set duplicates */
275 else
276 {
277 RB_DLINK_FOREACH(ptr, list->head)
278 {
279 actualBan = ptr->data;
280 if(!irccmp(actualBan->banstr, realban))
281 return false;
282 }
283 }
284
285
286 if(IsPerson(source_p))
287 sprintf(who, "%s!%s@%s", source_p->name, source_p->username, source_p->host);
288 else
289 rb_strlcpy(who, source_p->name, sizeof(who));
290
291 actualBan = allocate_ban(realban, who, forward);
292 actualBan->when = rb_current_time();
293
294 rb_dlinkAdd(actualBan, &actualBan->node, list);
295
296 /* invalidate the can_send() cache */
297 if(mode_type == CHFL_BAN || mode_type == CHFL_QUIET || mode_type == CHFL_EXCEPTION)
298 chptr->bants = rb_current_time();
299
300 return true;
301 }
302
303 /* del_id()
304 *
305 * inputs - channel, id to remove, type
306 * outputs - pointer to ban that was removed, if any
307 * side effects - given id is removed from the appropriate list and returned
308 */
309 struct Ban *
310 del_id(struct Channel *chptr, const char *banid, rb_dlink_list * list, long mode_type)
311 {
312 rb_dlink_node *ptr;
313 struct Ban *banptr;
314
315 if(EmptyString(banid))
316 return NULL;
317
318 RB_DLINK_FOREACH(ptr, list->head)
319 {
320 banptr = ptr->data;
321
322 if(irccmp(banid, banptr->banstr) == 0)
323 {
324 rb_dlinkDelete(&banptr->node, list);
325
326 /* invalidate the can_send() cache */
327 if(mode_type == CHFL_BAN || mode_type == CHFL_QUIET || mode_type == CHFL_EXCEPTION)
328 chptr->bants = rb_current_time();
329
330 return banptr;
331 }
332 }
333
334 return NULL;
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 */
343 static char *
344 check_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 */
373 static char *
374 pretty_mask(const char *idmask)
375 {
376 static char mask_buf[BUFSIZE];
377 int old_mask_pos;
378 const char *nick, *user, *host, *forward = NULL;
379 char *t, *at, *ex;
380 int nl, ul, hl, fl;
381 char *mask;
382 size_t masklen;
383
384 mask = LOCAL_COPY(idmask);
385 mask = check_string(mask);
386 collapse(mask);
387 masklen = strlen(mask);
388
389 nick = user = host = "*";
390 nl = ul = hl = 1;
391 fl = 0;
392
393 if((size_t) BUFSIZE - mask_pos < masklen + 5)
394 return NULL;
395
396 old_mask_pos = mask_pos;
397
398 if (*mask == '$')
399 {
400 memcpy(mask_buf + mask_pos, mask, masklen + 1);
401 mask_pos += masklen + 1;
402 t = mask_buf + old_mask_pos + 1;
403 if (*t == '!')
404 *t = '~';
405 if (*t == '~')
406 t++;
407 *t = irctolower(*t);
408 return mask_buf + old_mask_pos;
409 }
410
411 at = ex = NULL;
412 if((t = memchr(mask, '@', masklen)) != NULL)
413 {
414 at = t;
415 t++;
416 if(*t != '\0')
417 host = t, hl = strlen(t);
418
419 if((t = memchr(mask, '!', at - mask)) != NULL)
420 {
421 ex = t;
422 t++;
423 if(at != t)
424 user = t, ul = at - t;
425 if(ex != mask)
426 nick = mask, nl = ex - mask;
427 }
428 else
429 {
430 if(at != mask)
431 user = mask, ul = at - mask;
432 }
433
434 if((t = memchr(host, '!', hl)) != NULL ||
435 (t = memchr(host, '$', hl)) != NULL)
436 {
437 t++;
438 if (host + hl != t)
439 forward = t, fl = host + hl - t;
440 hl = t - 1 - host;
441 }
442 }
443 else if((t = memchr(mask, '!', masklen)) != NULL)
444 {
445 ex = t;
446 t++;
447 if(ex != mask)
448 nick = mask, nl = ex - mask;
449 if(*t != '\0')
450 user = t, ul = strlen(t);
451 }
452 else if(memchr(mask, '.', masklen) != NULL ||
453 memchr(mask, ':', masklen) != NULL)
454 {
455 host = mask, hl = masklen;
456 }
457 else
458 {
459 if(masklen > 0)
460 nick = mask, nl = masklen;
461 }
462
463 /* truncate values to max lengths */
464 if(nl > NICKLEN - 1)
465 nl = NICKLEN - 1;
466 if(ul > USERLEN)
467 ul = USERLEN;
468 if(hl > HOSTLEN)
469 hl = HOSTLEN;
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;
481 }
482 mask_buf[mask_pos++] = '\0';
483
484 return mask_buf + old_mask_pos;
485 }
486
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 */
493 static bool
494 check_forward(struct Client *source_p, struct Channel *chptr,
495 const char *forward)
496 {
497 struct Channel *targptr = NULL;
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);
504 return false;
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);
511 return false;
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);
517 return false;
518 }
519 if(MyClient(source_p) && !(targptr->mode.mode & MODE_FREETARGET))
520 {
521 if((msptr = find_channel_membership(targptr, source_p)) == NULL ||
522 get_channel_access(source_p, targptr, msptr, MODE_QUERY, NULL) < CHFL_CHANOP)
523 {
524 sendto_one(source_p, form_str(ERR_CHANOPRIVSNEEDED),
525 me.name, source_p->name, targptr->chname);
526 return false;
527 }
528 }
529 return true;
530 }
531
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 */
539 static char *
540 fix_key(char *arg)
541 {
542 unsigned char *s, *t, c;
543
544 for(s = t = (unsigned char *) arg; (c = *s); s++)
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 */
562 static char *
563 fix_key_remote(char *arg)
564 {
565 unsigned char *s, *t, c;
566
567 for(s = t = (unsigned char *) arg; (c = *s); s++)
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 */
582 void
583 chm_nosuch(struct Client *source_p, struct Channel *chptr,
584 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
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
592 void
593 chm_simple(struct Client *source_p, struct Channel *chptr,
594 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
595 {
596 if(!allow_mode_change(source_p, chptr, alevel, errors, c))
597 return;
598
599 if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
600 return;
601
602 /* setting + */
603 if((dir == MODE_ADD) && !(chptr->mode.mode & mode_type))
604 {
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
610 chptr->mode.mode |= mode_type;
611
612 mode_changes[mode_count].letter = c;
613 mode_changes[mode_count].dir = MODE_ADD;
614 mode_changes[mode_count].id = NULL;
615 mode_changes[mode_count].mems = ALL_MEMBERS;
616 mode_changes[mode_count++].arg = NULL;
617 }
618 else if((dir == MODE_DEL) && (chptr->mode.mode & mode_type))
619 {
620 chptr->mode.mode &= ~mode_type;
621
622 mode_changes[mode_count].letter = c;
623 mode_changes[mode_count].dir = MODE_DEL;
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
630 void
631 chm_orphaned(struct Client *source_p, struct Channel *chptr,
632 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
633 {
634 if(MyClient(source_p))
635 return;
636
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;
643 mode_changes[mode_count].id = NULL;
644 mode_changes[mode_count].mems = ALL_MEMBERS;
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;
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
659 void
660 chm_hidden(struct Client *source_p, struct Channel *chptr,
661 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
662 {
663 if(MyClient(source_p) && !IsOperGeneral(source_p))
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;
689 mode_changes[mode_count].id = NULL;
690 mode_changes[mode_count].mems = ONLY_OPERS;
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;
699 mode_changes[mode_count].mems = ONLY_OPERS;
700 mode_changes[mode_count].id = NULL;
701 mode_changes[mode_count++].arg = NULL;
702 }
703 }
704
705 void
706 chm_staff(struct Client *source_p, struct Channel *chptr,
707 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
708 {
709 if(MyClient(source_p) && !IsOper(source_p))
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 }
716 if(MyClient(source_p) && !HasPrivilege(source_p, "oper:cmodes"))
717 {
718 if(!(*errors & SM_ERR_NOPRIVS))
719 sendto_one(source_p, form_str(ERR_NOPRIVS), me.name,
720 source_p->name, "cmodes");
721 *errors |= SM_ERR_NOPRIVS;
722 return;
723 }
724
725 if(MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
726 return;
727
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;
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;
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
751 void
752 chm_ban(struct Client *source_p, struct Channel *chptr,
753 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
754 {
755 const char *mask;
756 char *forward;
757 rb_dlink_list *list;
758 rb_dlink_node *ptr;
759 struct Ban *banptr;
760 int errorval;
761 const char *rpl_list_p;
762 const char *rpl_endlist_p;
763 int mems;
764
765 switch (mode_type)
766 {
767 case CHFL_BAN:
768 list = &chptr->banlist;
769 errorval = SM_ERR_RPL_B;
770 rpl_list_p = form_str(RPL_BANLIST);
771 rpl_endlist_p = form_str(RPL_ENDOFBANLIST);
772 mems = ALL_MEMBERS;
773 break;
774
775 case CHFL_EXCEPTION:
776 /* if +e is disabled, allow all but +e locally */
777 if (!ConfigChannel.use_except && MyClient(source_p) && dir == MODE_ADD)
778 return;
779
780 list = &chptr->exceptlist;
781 errorval = SM_ERR_RPL_E;
782 rpl_list_p = form_str(RPL_EXCEPTLIST);
783 rpl_endlist_p = form_str(RPL_ENDOFEXCEPTLIST);
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 */
793 if (!ConfigChannel.use_invex && MyClient(source_p) && dir == MODE_ADD)
794 return;
795
796 list = &chptr->invexlist;
797 errorval = SM_ERR_RPL_I;
798 rpl_list_p = form_str(RPL_INVITELIST);
799 rpl_endlist_p = form_str(RPL_ENDOFINVITELIST);
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;
810 rpl_list_p = form_str(RPL_QUIETLIST);
811 rpl_endlist_p = form_str(RPL_ENDOFQUIETLIST);
812 mems = ALL_MEMBERS;
813 break;
814
815 default:
816 sendto_realops_snomask(SNO_GENERAL, L_ALL, "chm_ban() called with unknown type!");
817 return;
818 }
819
820 if (dir == MODE_QUERY)
821 {
822 if((*errors & errorval) != 0)
823 return;
824 *errors |= errorval;
825
826 /* non-ops cant see +eI lists.. */
827 /* note that this is still permitted if +e/+I are mlocked. */
828 if(alevel < CHFL_CHANOP && mode_type != CHFL_BAN &&
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
838 RB_DLINK_FOREACH(ptr, list->head)
839 {
840 char buf[BANLEN];
841 banptr = ptr->data;
842 if(banptr->forward)
843 snprintf(buf, sizeof(buf), "%s$%s", banptr->banstr, banptr->forward);
844 else
845 rb_strlcpy(buf, banptr->banstr, sizeof(buf));
846
847 sendto_one(source_p, rpl_list_p,
848 me.name, source_p->name, chptr->chname,
849 buf, banptr->who, banptr->when);
850 }
851 sendto_one(source_p, rpl_endlist_p, me.name, source_p->name, chptr->chname);
852 return;
853 }
854
855 if (!allow_mode_change(source_p, chptr, alevel, errors, c))
856 return;
857
858
859 if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
860 return;
861
862 /* empty ban, or starts with ':' which messes up s2s, ignore it */
863 if (EmptyString(arg) || *arg == ':')
864 return;
865
866 if (!MyClient(source_p))
867 {
868 if (strchr(arg, ' '))
869 return;
870
871 mask = arg;
872 }
873 else
874 mask = pretty_mask(arg);
875
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 */
880 if(strlen(mask) > MIN(BANLEN, MODEBUFLEN - 5))
881 {
882 sendto_one_numeric(source_p, ERR_INVALIDBAN,
883 form_str(ERR_INVALIDBAN),
884 chptr->chname, c, arg);
885 return;
886 }
887
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
899 /* if we're adding a NEW id */
900 if (dir == MODE_ADD)
901 {
902 if (*mask == '$' && MyClient(source_p))
903 {
904 if (!valid_extban(mask, source_p, chptr, mode_type))
905 {
906 sendto_one_numeric(source_p, ERR_INVALIDBAN,
907 form_str(ERR_INVALIDBAN),
908 chptr->chname, c, arg);
909 return;
910 }
911 }
912
913 /* For compatibility, only check the forward channel from
914 * local clients. Accept any forward channel from servers.
915 */
916 if (forward != NULL && MyClient(source_p))
917 {
918 /* For simplicity and future flexibility, do not
919 * allow '$' in forwarding targets.
920 */
921 if (!ConfigChannel.use_forward ||
922 strchr(forward, '$') != NULL)
923 {
924 sendto_one_numeric(source_p, ERR_INVALIDBAN,
925 form_str(ERR_INVALIDBAN),
926 chptr->chname, c, arg);
927 return;
928 }
929 /* check_forward() sends its own error message */
930 if (!check_forward(source_p, chptr, forward))
931 return;
932 /* Forwards only make sense for bans. */
933 if (mode_type != CHFL_BAN)
934 {
935 sendto_one_numeric(source_p, ERR_INVALIDBAN,
936 form_str(ERR_INVALIDBAN),
937 chptr->chname, c, arg);
938 return;
939 }
940 }
941
942 /* dont allow local clients to overflow the banlist, dont
943 * let remote servers set duplicate bans
944 */
945 if (!add_id(source_p, chptr, mask, forward, list, mode_type))
946 return;
947
948 if (forward)
949 forward[-1]= '$';
950
951 mode_changes[mode_count].letter = c;
952 mode_changes[mode_count].dir = MODE_ADD;
953 mode_changes[mode_count].mems = mems;
954 mode_changes[mode_count].id = NULL;
955 mode_changes[mode_count++].arg = mask;
956 }
957 else if (dir == MODE_DEL)
958 {
959 struct Ban *removed;
960 static char buf[BANLEN * MAXMODEPARAMS];
961 int old_removed_mask_pos = removed_mask_pos;
962 if ((removed = del_id(chptr, mask, list, mode_type)) == NULL)
963 {
964 /* mask isn't a valid ban, check arg */
965 if ((removed = del_id(chptr, arg, list, mode_type)) != NULL)
966 mask = arg;
967 }
968
969 if (removed && removed->forward)
970 removed_mask_pos += snprintf(buf + old_removed_mask_pos, sizeof(buf), "%s$%s", removed->banstr, removed->forward) + 1;
971 else
972 removed_mask_pos += rb_strlcpy(buf + old_removed_mask_pos, mask, sizeof(buf)) + 1;
973 if (removed)
974 {
975 free_ban(removed);
976 removed = NULL;
977 }
978
979 mode_changes[mode_count].letter = c;
980 mode_changes[mode_count].dir = MODE_DEL;
981 mode_changes[mode_count].mems = mems;
982 mode_changes[mode_count].id = NULL;
983 mode_changes[mode_count++].arg = buf + old_removed_mask_pos;
984 }
985 }
986
987 void
988 chm_op(struct Client *source_p, struct Channel *chptr,
989 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
990 {
991 struct membership *mstptr;
992 struct Client *targ_p;
993
994 if(!allow_mode_change(source_p, chptr, alevel, errors, c))
995 return;
996
997 /* empty nick */
998 if(EmptyString(arg))
999 {
1000 sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), "*");
1001 return;
1002 }
1003
1004 if((targ_p = find_chasing(source_p, arg, NULL)) == NULL)
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,
1015 form_str(ERR_USERNOTINCHANNEL), arg, chptr->chname);
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 {
1025 if(targ_p == source_p && mstptr->flags & CHFL_CHANOP)
1026 return;
1027
1028 mode_changes[mode_count].letter = c;
1029 mode_changes[mode_count].dir = MODE_ADD;
1030 mode_changes[mode_count].mems = ALL_MEMBERS;
1031 mode_changes[mode_count].id = targ_p->id;
1032 mode_changes[mode_count++].arg = targ_p->name;
1033
1034 mstptr->flags |= CHFL_CHANOP;
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;
1047 mode_changes[mode_count].mems = ALL_MEMBERS;
1048 mode_changes[mode_count].id = targ_p->id;
1049 mode_changes[mode_count++].arg = targ_p->name;
1050
1051 mstptr->flags &= ~CHFL_CHANOP;
1052 }
1053 }
1054
1055 void
1056 chm_voice(struct Client *source_p, struct Channel *chptr,
1057 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
1058 {
1059 struct membership *mstptr;
1060 struct Client *targ_p;
1061
1062 if(!allow_mode_change(source_p, chptr, alevel, errors, c))
1063 return;
1064
1065 /* empty nick */
1066 if(EmptyString(arg))
1067 {
1068 sendto_one_numeric(source_p, ERR_NOSUCHNICK, form_str(ERR_NOSUCHNICK), "*");
1069 return;
1070 }
1071
1072 if((targ_p = find_chasing(source_p, arg, NULL)) == NULL)
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,
1083 form_str(ERR_USERNOTINCHANNEL), arg, chptr->chname);
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;
1095 mode_changes[mode_count].mems = ALL_MEMBERS;
1096 mode_changes[mode_count].id = targ_p->id;
1097 mode_changes[mode_count++].arg = targ_p->name;
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;
1105 mode_changes[mode_count].mems = ALL_MEMBERS;
1106 mode_changes[mode_count].id = targ_p->id;
1107 mode_changes[mode_count++].arg = targ_p->name;
1108
1109 mstptr->flags &= ~CHFL_VOICE;
1110 }
1111 }
1112
1113 void
1114 chm_limit(struct Client *source_p, struct Channel *chptr,
1115 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
1116 {
1117 static char limitstr[30];
1118 int limit;
1119
1120 if (!allow_mode_change(source_p, chptr, alevel, errors, c))
1121 return;
1122
1123 if (MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
1124 return;
1125
1126 if (dir == MODE_ADD)
1127 {
1128 if (EmptyString(arg) || (limit = atoi(arg)) <= 0)
1129 return;
1130
1131 sprintf(limitstr, "%d", limit);
1132
1133 mode_changes[mode_count].letter = c;
1134 mode_changes[mode_count].dir = MODE_ADD;
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 }
1141 else if (dir == MODE_DEL)
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;
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
1156 void
1157 chm_throttle(struct Client *source_p, struct Channel *chptr,
1158 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
1159 {
1160 int joins = 0, timeslice = 0;
1161
1162 if (!allow_mode_change(source_p, chptr, alevel, errors, c))
1163 return;
1164
1165 if (MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
1166 return;
1167
1168 if (dir == MODE_ADD)
1169 {
1170 if (sscanf(arg, "%d:%d", &joins, &timeslice) < 2)
1171 return;
1172
1173 if(joins <= 0 || timeslice <= 0)
1174 return;
1175
1176 mode_changes[mode_count].letter = c;
1177 mode_changes[mode_count].dir = MODE_ADD;
1178 mode_changes[mode_count].mems = ALL_MEMBERS;
1179 mode_changes[mode_count].id = NULL;
1180 mode_changes[mode_count++].arg = arg;
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;
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
1203 void
1204 chm_forward(struct Client *source_p, struct Channel *chptr,
1205 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
1206 {
1207 /* if +f is disabled, ignore local attempts to set it */
1208 if (!ConfigChannel.use_forward && MyClient(source_p) && dir == MODE_ADD)
1209 return;
1210
1211 if (dir == MODE_QUERY)
1212 {
1213 if (!(*errors & SM_ERR_RPL_F))
1214 {
1215 if (*chptr->mode.forward == '\0')
1216 sendto_one_notice(source_p, ":%s has no forward channel", chptr->chname);
1217 else
1218 sendto_one_notice(source_p, ":%s forward channel is %s", chptr->chname, chptr->mode.forward);
1219 *errors |= SM_ERR_RPL_F;
1220 }
1221 return;
1222 }
1223
1224 #ifndef FORWARD_OPERONLY
1225 if (!allow_mode_change(source_p, chptr, alevel, errors, c))
1226 return;
1227 #else
1228 if (!IsOperGeneral(source_p) && !IsServer(source_p))
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
1237 if (MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
1238 return;
1239
1240 if (dir == MODE_ADD)
1241 {
1242 if(EmptyString(arg))
1243 return;
1244
1245 if(!check_forward(source_p, chptr, arg))
1246 return;
1247
1248 rb_strlcpy(chptr->mode.forward, arg, sizeof(chptr->mode.forward));
1249
1250 mode_changes[mode_count].letter = c;
1251 mode_changes[mode_count].dir = MODE_ADD;
1252 mode_changes[mode_count].mems =
1253 ConfigChannel.use_forward ? ALL_MEMBERS : ONLY_SERVERS;
1254 mode_changes[mode_count].id = NULL;
1255 mode_changes[mode_count++].arg = arg;
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;
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
1272 void
1273 chm_key(struct Client *source_p, struct Channel *chptr,
1274 int alevel, const char *arg, int *errors, int dir, char c, long mode_type)
1275 {
1276 char *key;
1277
1278 if (!allow_mode_change(source_p, chptr, alevel, errors, c))
1279 return;
1280
1281 if (MyClient(source_p) && (++mode_limit_simple > MAXMODES_SIMPLE))
1282 return;
1283
1284 if (dir == MODE_ADD)
1285 {
1286 key = LOCAL_COPY(arg);
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] != ' ');
1297 rb_strlcpy(chptr->mode.key, key, sizeof(chptr->mode.key));
1298
1299 mode_changes[mode_count].letter = c;
1300 mode_changes[mode_count].dir = MODE_ADD;
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
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;
1327 mode_changes[mode_count].mems = ALL_MEMBERS;
1328 mode_changes[mode_count].id = NULL;
1329 mode_changes[mode_count++].arg = "*";
1330 }
1331 }
1332
1333 /* *INDENT-OFF* */
1334 struct ChannelMode chmode_table[256] =
1335 {
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 },
1359 };
1360
1361 /* *INDENT-ON* */
1362
1363 /* set_channel_mode()
1364 *
1365 * inputs - client, source, channel, membership pointer, params
1366 * output -
1367 * side effects - channel modes/memberships are changed, MODE is issued
1368 *
1369 * Extensively modified to be hotpluggable, 03/09/06 -- nenolod
1370 */
1371 void
1372 set_channel_mode(struct Client *client_p, struct Client *source_p,
1373 struct Channel *chptr, struct membership *msptr, int parc, const char *parv[])
1374 {
1375 static char modebuf[BUFSIZE * 2]; /* paranoid case: 2 canonical chars per input char */
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;
1381 int dir = MODE_ADD;
1382 int access_dir = MODE_QUERY;
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;
1389 int flags_list[3] = { ALL_MEMBERS, ONLY_CHANOPS, ONLY_OPERS };
1390
1391 mask_pos = 0;
1392 removed_mask_pos = 0;
1393 mode_count = 0;
1394 mode_limit = 0;
1395 mode_limit_simple = 0;
1396
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
1403 struct modeset {
1404 const struct ChannelMode *cm;
1405 const char *arg;
1406 int dir;
1407 char mode;
1408 };
1409
1410 static struct modeset modesets[MAXPARA];
1411 struct modeset *ms = modesets, *mend;
1412 char canon_op = '\0';
1413
1414 mbuf = modebuf;
1415
1416 for (ml = parv[0]; *ml != 0; ml++)
1417 {
1418 c = *ml;
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:
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 */
1459 continue;
1460 }
1461
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
1471 if (effective_dir != MODE_QUERY && access_dir == MODE_QUERY)
1472 access_dir = effective_dir;
1473 if (effective_dir == MODE_QUERY && cm->flags & CHM_OPS_QUERY)
1474 access_dir = MODE_OP_QUERY;
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 }
1485 }
1486 }
1487
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
1495 if (parn < parc)
1496 {
1497 /* XXX we could reject excess params here */
1498 }
1499
1500 mend = ms;
1501
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);
1512
1513 for (ms = modesets; ms < mend; ms++)
1514 {
1515 ChannelModeFunc *set_func = ms->cm->set_func;
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
1521 /* bail out if we have nothing to do... */
1522 if (!mode_count)
1523 return;
1524
1525 if (IsServer(source_p))
1526 mlen = sprintf(modebuf, ":%s MODE %s ", fakesource_p->name, chptr->chname);
1527 else
1528 mlen = sprintf(modebuf, ":%s!%s@%s MODE %s ",
1529 source_p->name, source_p->username,
1530 source_p->host, chptr->chname);
1531
1532 for(j = 0; j < 3; j++)
1533 {
1534 int send_flags = flags = flags_list[j];
1535 const char *priv = NULL;
1536 if (flags == ONLY_OPERS)
1537 {
1538 send_flags = ALL_MEMBERS;
1539 priv = "auspex:cmodes";
1540 }
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)
1573 sendto_channel_local_priv(IsServer(source_p) ? fakesource_p : source_p,
1574 send_flags, priv, chptr, "%s %s", modebuf, parabuf);
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++;
1599 len = sprintf(pbuf, "%s ", mode_changes[i].arg);
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)
1610 sendto_channel_local_priv(IsServer(source_p) ? fakesource_p : source_p,
1611 send_flags, priv, chptr, "%s %s", modebuf, parabuf);
1612 }
1613
1614 /* only propagate modes originating locally, or if we're hubbing */
1615 if(MyClient(source_p) || rb_dlink_list_length(&serv_list) > 1)
1616 send_cap_mode_changes(client_p, source_p, chptr, mode_changes, mode_count);
1617 }
1618
1619 /* set_channel_mlock()
1620 *
1621 * inputs - client, source, channel, params
1622 * output -
1623 * side effects - channel mlock is changed / MLOCK is propagated
1624 */
1625 void
1626 set_channel_mlock(struct Client *client_p, struct Client *source_p,
1627 struct Channel *chptr, const char *newmlock, bool propagate)
1628 {
1629 rb_free(chptr->mode_lock);
1630 chptr->mode_lock = newmlock ? rb_strdup(newmlock) : NULL;
1631
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 }
1638 }