ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/channel_mode.c
Revision: 9234
Committed: Fri Jan 31 17:38:34 2020 UTC (5 years, 6 months ago) by michael
Content type: text/x-csrc
File size: 28881 byte(s)
Log Message:
- Extbans have been implemented. Main implementation done by Adam for p4.
  Currently supported extbans:

  Matching:

   $a:<account>   Matches users logged into a matching account.
   $c:<channel>   Matches users that are on the given channel. An additional
                  prefix of either @, %, or + can be specified to test for
                  certain channel privileges.
   $o:<class>     Matches IRC operators that have joined a class
                  matching the mask.
   $r:<realname>  Matches users with a matching realname.
   $s:<server>    Matches users that are connected to a server matching the mask.
   $u:<modes>     Matches users having the specified user modes set or not set.
   $z:<certfp>    Matches users having the given TLS certificate fingerprint.

  Acting:

   $j:<banmask>   Prevents matching users from joining the channel.
   $m:<banmask>   Blocks messages from matching users. Users with voice
                  or above are not affected.

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2020 ircd-hybrid development team
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
19 * USA
20 */
21
22 /*! \file channel_mode.c
23 * \brief Controls modes on channels.
24 * \version $Id$
25 */
26
27 #include "stdinc.h"
28 #include "list.h"
29 #include "channel.h"
30 #include "channel_mode.h"
31 #include "client.h"
32 #include "conf.h"
33 #include "hostmask.h"
34 #include "irc_string.h"
35 #include "ircd.h"
36 #include "numeric.h"
37 #include "server.h"
38 #include "send.h"
39 #include "memory.h"
40 #include "parse.h"
41 #include "extban.h"
42
43
44 static struct ChModeChange mode_changes[IRCD_BUFSIZE];
45 static unsigned int mode_count;
46 static unsigned int mode_limit; /* number of modes set other than simple */
47 static unsigned int simple_modes_mask; /* bit mask of simple modes already set */
48
49
50 /* check_string()
51 *
52 * inputs - string to check
53 * output - pointer to modified string
54 * side effects - Fixes a string so that the first white space found
55 * becomes an end of string marker (`\0`).
56 * returns the 'fixed' string or "*" if the string
57 * was NULL length or a NULL pointer.
58 */
59 static void
60 check_string(char *s)
61 {
62 char *str = s;
63
64 assert(s);
65
66 for (; *s; ++s)
67 {
68 if (IsSpace(*s))
69 {
70 *s = '\0';
71 break;
72 }
73 }
74
75 if (EmptyString(str))
76 strcpy(str, "*");
77 }
78
79 static const char *
80 get_mask(const struct Ban *ban)
81 {
82 static char buf[MODEBUFLEN];
83 const unsigned int i = extban_format(ban->extban, buf);
84
85 assert(i <= sizeof(buf));
86
87 /* Matching extbans only use ban->host */
88 if (ban->extban & extban_matching_mask())
89 strlcpy(buf + i, ban->host, sizeof(buf) - i);
90 else
91 snprintf(buf + i, sizeof(buf) - i, "%s!%s@%s", ban->name, ban->user, ban->host);
92
93 return buf;
94 }
95
96 /*
97 * Ban functions to work with mode +b/e/I
98 */
99 /* add the specified ID to the channel.
100 * -is 8/9/00
101 */
102
103 const char *
104 add_id(struct Client *client_p, struct Channel *channel, const char *banid, dlink_list *list, unsigned int type)
105 {
106 dlink_node *node;
107 char mask[MODEBUFLEN];
108 char *maskptr = mask;
109 unsigned int extbans, offset;
110
111 strlcpy(mask, banid, sizeof(mask));
112
113 if (MyClient(client_p))
114 {
115 unsigned int num_mask = dlink_list_length(&channel->banlist) +
116 dlink_list_length(&channel->exceptlist) +
117 dlink_list_length(&channel->invexlist);
118
119 /* Don't let local clients overflow the b/e/I lists */
120 if (num_mask >= ((HasCMode(channel, MODE_EXTLIMIT)) ? ConfigChannel.max_bans_large :
121 ConfigChannel.max_bans))
122 {
123 sendto_one_numeric(client_p, &me, ERR_BANLISTFULL, channel->name, banid);
124 return false;
125 }
126
127 collapse(mask);
128 }
129
130 enum extban_type etype = extban_parse(mask, &extbans, &offset);
131 maskptr += offset;
132
133 if (MyClient(client_p))
134 {
135 if (etype == EXTBAN_INVALID)
136 {
137 sendto_one_numeric(client_p, &me, ERR_INVALIDBAN, channel->name, mask);
138 return NULL;
139 }
140
141 if (etype != EXTBAN_NONE && ConfigChannel.enable_extbans == 0)
142 {
143 sendto_one_numeric(client_p, &me, ERR_INVALIDBAN, channel->name, mask);
144 return NULL;
145 }
146
147 unsigned int extban_acting = extbans & extban_acting_mask();
148 if (extban_acting)
149 {
150 const struct Extban *extban = extban_find_flag(extban_acting);
151
152 if (extban == NULL || !(extban->types & type))
153 {
154 sendto_one_numeric(client_p, &me, ERR_INVALIDBAN, channel->name, mask);
155 return NULL;
156 }
157 }
158
159 unsigned extban_matching = extbans & extban_matching_mask();
160 if (extban_matching)
161 {
162 const struct Extban *extban = extban_find_flag(extban_matching);
163
164 if (extban == NULL || !(extban->types & type))
165 {
166 sendto_one_numeric(client_p, &me, ERR_INVALIDBAN, channel->name, mask);
167 return NULL;
168 }
169 }
170 }
171
172 /* Don't allow empty bans */
173 if (EmptyString(maskptr))
174 return NULL;
175
176 struct Ban *ban = xcalloc(sizeof(*ban));
177 ban->extban = extbans;
178 ban->when = event_base->time.sec_real;
179
180 check_string(maskptr);
181
182 if (etype == EXTBAN_MATCHING)
183 /* Matching extbans have their own format, don't try to parse it */
184 strlcpy(ban->host, maskptr, sizeof(ban->host));
185 else
186 {
187 struct split_nuh_item nuh;
188
189 nuh.nuhmask = maskptr;
190 nuh.nickptr = ban->name;
191 nuh.userptr = ban->user;
192 nuh.hostptr = ban->host;
193
194 nuh.nicksize = sizeof(ban->name);
195 nuh.usersize = sizeof(ban->user);
196 nuh.hostsize = sizeof(ban->host);
197
198 split_nuh(&nuh);
199
200 ban->type = parse_netmask(ban->host, &ban->addr, &ban->bits);
201 }
202
203 if (MyClient(client_p))
204 ban->banstr_len = strlcpy(ban->banstr, get_mask(ban), sizeof(ban->banstr));
205 else
206 ban->banstr_len = strlcpy(ban->banstr, mask, sizeof(ban->banstr));
207
208 DLINK_FOREACH(node, list->head)
209 {
210 const struct Ban *tmp = node->data;
211
212 if (irccmp(tmp->banstr, ban->banstr) == 0)
213 {
214 xfree(ban);
215 return NULL;
216 }
217 }
218
219 clear_ban_cache_list(&channel->members_local);
220
221 if (IsClient(client_p))
222 snprintf(ban->who, sizeof(ban->who), "%s!%s@%s", client_p->name,
223 client_p->username, client_p->host);
224 else if (IsHidden(client_p) || ConfigServerHide.hide_servers)
225 strlcpy(ban->who, me.name, sizeof(ban->who));
226 else
227 strlcpy(ban->who, client_p->name, sizeof(ban->who));
228
229 dlinkAdd(ban, &ban->node, list);
230
231 return ban->banstr;
232 }
233
234 /*
235 * inputs - pointer to channel
236 * - pointer to ban id
237 * - type of ban, i.e. ban, exception, invex
238 * output - 0 for failure, 1 for success
239 * side effects -
240 */
241 static const char *
242 del_id(struct Client *client_p, struct Channel *channel, const char *banid, dlink_list *list, unsigned int type)
243 {
244 static char mask[MODEBUFLEN];
245 dlink_node *node;
246 assert(banid);
247
248 /* TBD: n!u@h formatting fo local clients */
249
250 DLINK_FOREACH(node, list->head)
251 {
252 struct Ban *ban = node->data;
253
254 if (irccmp(banid, ban->banstr) == 0)
255 {
256 strlcpy(mask, ban->banstr, sizeof(mask)); /* caSe might be different in 'banid' */
257 clear_ban_cache_list(&channel->members_local);
258 remove_ban(ban, list);
259
260 return mask;
261 }
262 }
263
264 return NULL;
265 }
266
267 /* channel_modes()
268 *
269 * inputs - pointer to channel
270 * - pointer to client
271 * - pointer to mode buf
272 * - pointer to parameter buf
273 * output - NONE
274 * side effects - write the "simple" list of channel modes for channel
275 * channel onto buffer mbuf with the parameters in pbuf.
276 */
277 void
278 channel_modes(const struct Channel *channel, const struct Client *client_p, char *mbuf, char *pbuf)
279 {
280 *mbuf++ = '+';
281 *pbuf = '\0';
282
283 for (const struct chan_mode *tab = cmode_tab; tab->letter; ++tab)
284 if (tab->mode && HasCMode(channel, tab->mode))
285 *mbuf++ = tab->letter;
286
287 if (channel->mode.limit)
288 {
289 *mbuf++ = 'l';
290
291 if (IsServer(client_p) || IsMember(client_p, channel))
292 pbuf += sprintf(pbuf, "%u ", channel->mode.limit);
293 }
294
295 if (channel->mode.key[0])
296 {
297 *mbuf++ = 'k';
298
299 if (IsServer(client_p) || IsMember(client_p, channel))
300 sprintf(pbuf, "%s ", channel->mode.key);
301 }
302
303 *mbuf = '\0';
304 }
305
306 /* fix_key()
307 *
308 * inputs - pointer to key to clean up
309 * output - pointer to cleaned up key
310 * side effects - input string is modified
311 *
312 * stolen from Undernet's ircd -orabidoo
313 */
314 static char *
315 fix_key(char *arg)
316 {
317 unsigned char *s, *t, c;
318
319 for (s = t = (unsigned char *)arg; (c = *s) && s - (unsigned char *)arg < KEYLEN; ++s)
320 {
321 c &= 0x7f;
322
323 if (c != ':' && c > ' ' && c != ',')
324 *t++ = c;
325 }
326
327 *t = '\0';
328 return arg;
329 }
330
331 /*
332 * inputs - pointer to channel
333 * output - none
334 * side effects - clear ban cache
335 */
336 void
337 clear_ban_cache_list(dlink_list *list)
338 {
339 dlink_node *node;
340
341 DLINK_FOREACH(node, list->head)
342 {
343 struct ChannelMember *member = node->data;
344 member->flags &= ~(CHFL_BAN_SILENCED | CHFL_BAN_CHECKED | CHFL_MUTE_CHECKED);
345 }
346 }
347
348 /*
349 * Bitmasks for various error returns that channel_mode_set should only return
350 * once per call -orabidoo
351 */
352 enum
353 {
354 SM_ERR_NOOPS = 1 << 0, /* No chan ops */
355 SM_ERR_UNKNOWN = 1 << 1,
356 SM_ERR_RPL_B = 1 << 2,
357 SM_ERR_RPL_E = 1 << 3,
358 SM_ERR_RPL_I = 1 << 4,
359 SM_ERR_NOTONCHANNEL = 1 << 5, /* Client is not on channel */
360 SM_ERR_NOTOPER = 1 << 6, /* Only irc-operators can change that mode */
361 SM_ERR_ONLYSERVER = 1 << 7 /* Only servers or services can change that mode */
362 };
363
364 /* Mode functions handle mode changes for a particular mode... */
365 static void
366 chm_nosuch(struct Client *source_p, struct Channel *channel, int parc, int *parn, char **parv,
367 int *errors, int alev, int dir, const char c, const struct chan_mode *mode)
368 {
369 if (*errors & SM_ERR_UNKNOWN)
370 return;
371
372 *errors |= SM_ERR_UNKNOWN;
373 sendto_one_numeric(source_p, &me, ERR_UNKNOWNMODE, c);
374 }
375
376 static void
377 chm_simple(struct Client *source_p, struct Channel *channel, int parc, int *parn, char **parv,
378 int *errors, int alev, int dir, const char c, const struct chan_mode *mode)
379 {
380 if (mode->only_opers)
381 {
382 if (MyClient(source_p) && !HasUMode(source_p, UMODE_OPER))
383 {
384 if (!(*errors & SM_ERR_NOTOPER))
385 sendto_one_numeric(source_p, &me, ERR_NOPRIVILEGES);
386
387 *errors |= SM_ERR_NOTOPER;
388 return;
389 }
390 }
391
392 if (mode->only_servers)
393 {
394 if (!IsServer(source_p) && !HasFlag(source_p, FLAGS_SERVICE))
395 {
396 if (!(*errors & SM_ERR_ONLYSERVER))
397 sendto_one_numeric(source_p, &me,
398 alev == CHACCESS_NOTONCHAN ? ERR_NOTONCHANNEL :
399 ERR_ONLYSERVERSCANCHANGE, channel->name);
400
401 *errors |= SM_ERR_ONLYSERVER;
402 return;
403 }
404 }
405
406 if (alev < mode->required_oplevel)
407 {
408 if (!(*errors & SM_ERR_NOOPS))
409 sendto_one_numeric(source_p, &me,
410 alev == CHACCESS_NOTONCHAN ? ERR_NOTONCHANNEL :
411 ERR_CHANOPRIVSNEEDED, channel->name);
412
413 *errors |= SM_ERR_NOOPS;
414 return;
415 }
416
417 /* If have already dealt with this simple mode, ignore it */
418 if (simple_modes_mask & mode->mode)
419 return;
420
421 simple_modes_mask |= mode->mode;
422
423 if (dir == MODE_ADD) /* setting + */
424 {
425 if (MyClient(source_p) && HasCMode(channel, mode->mode))
426 return;
427
428 AddCMode(channel, mode->mode);
429 }
430 else if (dir == MODE_DEL) /* setting - */
431 {
432 if (MyClient(source_p) && !HasCMode(channel, mode->mode))
433 return;
434
435 DelCMode(channel, mode->mode);
436 }
437
438 mode_changes[mode_count].letter = mode->letter;
439 mode_changes[mode_count].arg = NULL;
440 mode_changes[mode_count].id = NULL;
441 mode_changes[mode_count].flags = 0;
442 mode_changes[mode_count++].dir = dir;
443 }
444
445 static void
446 chm_mask(struct Client *source_p, struct Channel *channel, int parc, int *parn, char **parv,
447 int *errors, int alev, int dir, const char c, const struct chan_mode *mode)
448 {
449 const char *ret = NULL;
450 dlink_list *list;
451 enum irc_numerics rpl_list = 0, rpl_endlist = 0;
452 int errtype = 0;
453
454 switch (mode->flag)
455 {
456 case CHFL_BAN:
457 errtype = SM_ERR_RPL_B;
458 list = &channel->banlist;
459 rpl_list = RPL_BANLIST;
460 rpl_endlist = RPL_ENDOFBANLIST;
461 break;
462 case CHFL_EXCEPTION:
463 errtype = SM_ERR_RPL_E;
464 list = &channel->exceptlist;
465 rpl_list = RPL_EXCEPTLIST;
466 rpl_endlist = RPL_ENDOFEXCEPTLIST;
467 break;
468 case CHFL_INVEX:
469 errtype = SM_ERR_RPL_I;
470 list = &channel->invexlist;
471 rpl_list = RPL_INVEXLIST;
472 rpl_endlist = RPL_ENDOFINVEXLIST;
473 break;
474 default:
475 list = NULL; /* Let it crash */
476 }
477
478 if (dir == MODE_QUERY || parc <= *parn)
479 {
480 dlink_node *node;
481
482 if (*errors & errtype)
483 return;
484
485 *errors |= errtype;
486
487 DLINK_FOREACH(node, list->head)
488 {
489 const struct Ban *ban = node->data;
490
491 if (!HasCMode(channel, MODE_HIDEBMASKS) || alev >= mode->required_oplevel)
492 sendto_one_numeric(source_p, &me, rpl_list, channel->name,
493 ban->banstr, ban->who, ban->when);
494 }
495
496 sendto_one_numeric(source_p, &me, rpl_endlist, channel->name);
497 return;
498 }
499
500 if (alev < mode->required_oplevel)
501 {
502 if (!(*errors & SM_ERR_NOOPS))
503 sendto_one_numeric(source_p, &me,
504 alev == CHACCESS_NOTONCHAN ? ERR_NOTONCHANNEL :
505 ERR_CHANOPRIVSNEEDED, channel->name);
506
507 *errors |= SM_ERR_NOOPS;
508 return;
509 }
510
511 if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
512 return;
513
514 char *mask = parv[*parn];
515 ++(*parn);
516
517 if (*mask == ':' || (!MyConnect(source_p) && strchr(mask, ' ')))
518 return;
519
520 if (dir == MODE_ADD) /* setting + */
521 {
522 ret = add_id(source_p, channel, mask, list, mode->flag);
523 if (ret == NULL)
524 return;
525 }
526 else if (dir == MODE_DEL) /* setting - */
527 {
528 ret = del_id(source_p, channel, mask, list, mode->flag);
529 if (ret == NULL)
530 return;
531 }
532
533 static char buf[MAXPARA][MODEBUFLEN];
534 mask = buf[(*parn) - 1];
535 strlcpy(mask, ret, sizeof(buf[(*parn) - 1]));
536
537 mode_changes[mode_count].letter = mode->letter;
538 mode_changes[mode_count].arg = mask; /* At this point 'mask' is no longer than MODEBUFLEN */
539 mode_changes[mode_count].id = NULL;
540 if (HasCMode(channel, MODE_HIDEBMASKS))
541 mode_changes[mode_count].flags = CHFL_CHANOP | CHFL_HALFOP;
542 else
543 mode_changes[mode_count].flags = 0;
544 mode_changes[mode_count++].dir = dir;
545 }
546
547 static void
548 chm_flag(struct Client *source_p, struct Channel *channel, int parc, int *parn, char **parv,
549 int *errors, int alev, int dir, const char c, const struct chan_mode *mode)
550 {
551 struct Client *target_p;
552 struct ChannelMember *member;
553
554 if (alev < mode->required_oplevel)
555 {
556 if (!(*errors & SM_ERR_NOOPS))
557 sendto_one_numeric(source_p, &me,
558 alev == CHACCESS_NOTONCHAN ? ERR_NOTONCHANNEL :
559 ERR_CHANOPRIVSNEEDED, channel->name);
560
561 *errors |= SM_ERR_NOOPS;
562 return;
563 }
564
565 if (dir == MODE_QUERY || parc <= *parn)
566 return;
567
568 if ((target_p = find_chasing(source_p, parv[(*parn)++])) == NULL)
569 return; /* find_chasing sends ERR_NOSUCHNICK */
570
571 if ((member = find_channel_link(target_p, channel)) == NULL)
572 {
573 if (!(*errors & SM_ERR_NOTONCHANNEL))
574 sendto_one_numeric(source_p, &me, ERR_USERNOTINCHANNEL, target_p->name, channel->name);
575
576 *errors |= SM_ERR_NOTONCHANNEL;
577 return;
578 }
579
580 if (MyClient(source_p) && (++mode_limit > MAXMODEPARAMS))
581 return;
582
583 if (dir == MODE_ADD) /* setting + */
584 {
585 if (has_member_flags(member, mode->flag))
586 return; /* No redundant mode changes */
587
588 AddMemberFlag(member, mode->flag);
589 }
590 else if (dir == MODE_DEL) /* setting - */
591 {
592 if (has_member_flags(member, mode->flag) == 0)
593 return; /* No redundant mode changes */
594
595 DelMemberFlag(member, mode->flag);
596 }
597
598 mode_changes[mode_count].letter = mode->letter;
599 mode_changes[mode_count].arg = target_p->name;
600 mode_changes[mode_count].id = target_p->id;
601 mode_changes[mode_count].flags = 0;
602 mode_changes[mode_count++].dir = dir;
603 }
604
605 static void
606 chm_limit(struct Client *source_p, struct Channel *channel, int parc, int *parn, char **parv,
607 int *errors, int alev, int dir, const char c, const struct chan_mode *mode)
608 {
609 if (alev < mode->required_oplevel)
610 {
611 if (!(*errors & SM_ERR_NOOPS))
612 sendto_one_numeric(source_p, &me,
613 alev == CHACCESS_NOTONCHAN ? ERR_NOTONCHANNEL :
614 ERR_CHANOPRIVSNEEDED, channel->name);
615 *errors |= SM_ERR_NOOPS;
616 return;
617 }
618
619 if (dir == MODE_QUERY)
620 return;
621
622 if (dir == MODE_ADD && parc > *parn)
623 {
624 char *const lstr = parv[(*parn)++];
625 int limit = 0;
626
627 if (EmptyString(lstr) || (limit = atoi(lstr)) <= 0)
628 return;
629
630 sprintf(lstr, "%d", limit);
631
632 /* If somebody sets MODE #channel +ll 1 2, accept latter --fl */
633 for (unsigned int i = 0; i < mode_count; ++i)
634 if (mode_changes[i].letter == mode->letter && mode_changes[i].dir == MODE_ADD)
635 mode_changes[i].letter = 0;
636
637 mode_changes[mode_count].letter = mode->letter;
638 mode_changes[mode_count].arg = lstr;
639 mode_changes[mode_count].id = NULL;
640 mode_changes[mode_count].flags = 0;
641 mode_changes[mode_count++].dir = dir;
642
643 channel->mode.limit = limit;
644 }
645 else if (dir == MODE_DEL)
646 {
647 if (channel->mode.limit == 0)
648 return;
649
650 channel->mode.limit = 0;
651
652 mode_changes[mode_count].letter = mode->letter;
653 mode_changes[mode_count].arg = NULL;
654 mode_changes[mode_count].id = NULL;
655 mode_changes[mode_count].flags = 0;
656 mode_changes[mode_count++].dir = dir;
657 }
658 }
659
660 static void
661 chm_key(struct Client *source_p, struct Channel *channel, int parc, int *parn, char **parv,
662 int *errors, int alev, int dir, const char c, const struct chan_mode *mode)
663 {
664 if (alev < mode->required_oplevel)
665 {
666 if (!(*errors & SM_ERR_NOOPS))
667 sendto_one_numeric(source_p, &me,
668 alev == CHACCESS_NOTONCHAN ? ERR_NOTONCHANNEL :
669 ERR_CHANOPRIVSNEEDED, channel->name);
670 *errors |= SM_ERR_NOOPS;
671 return;
672 }
673
674 if (dir == MODE_QUERY)
675 return;
676
677 if (dir == MODE_ADD && parc > *parn)
678 {
679 char *const key = fix_key(parv[(*parn)++]);
680
681 if (EmptyString(key))
682 return;
683
684 assert(key[0] != ' ');
685 strlcpy(channel->mode.key, key, sizeof(channel->mode.key));
686
687 /* If somebody does MODE #channel +kk a b, accept latter --fl */
688 for (unsigned int i = 0; i < mode_count; ++i)
689 if (mode_changes[i].letter == mode->letter && mode_changes[i].dir == MODE_ADD)
690 mode_changes[i].letter = 0;
691
692 mode_changes[mode_count].letter = mode->letter;
693 mode_changes[mode_count].arg = key;
694 mode_changes[mode_count].id = NULL;
695 mode_changes[mode_count].flags = 0;
696 mode_changes[mode_count++].dir = dir;
697 }
698 else if (dir == MODE_DEL)
699 {
700 if (parc > *parn)
701 ++(*parn);
702
703 if (channel->mode.key[0] == '\0')
704 return;
705
706 channel->mode.key[0] = '\0';
707
708 mode_changes[mode_count].letter = mode->letter;
709 mode_changes[mode_count].arg = "*";
710 mode_changes[mode_count].id = NULL;
711 mode_changes[mode_count].flags = 0;
712 mode_changes[mode_count++].dir = dir;
713 }
714 }
715
716 /* get_channel_access()
717 *
718 * inputs - pointer to Client struct
719 * - pointer to Membership struct
720 * output - CHACCESS_CHANOP if we should let them have
721 * chanop level access, 0 for peon level access.
722 * side effects - NONE
723 */
724 static int
725 get_channel_access(const struct Client *source_p,
726 const struct ChannelMember *member)
727 {
728 /* Let hacked servers in for now... */
729 if (!MyClient(source_p))
730 return CHACCESS_REMOTE;
731
732 if (member == NULL)
733 return CHACCESS_NOTONCHAN;
734
735 /* Just to be sure.. */
736 assert(source_p == member->client);
737
738 if (has_member_flags(member, CHFL_CHANOP))
739 return CHACCESS_CHANOP;
740
741 if (has_member_flags(member, CHFL_HALFOP))
742 return CHACCESS_HALFOP;
743
744 return CHACCESS_PEON;
745 }
746
747 /* send_mode_changes_server()
748 * Input: the source client(source_p),
749 * the channel to send mode changes for(channel)
750 * Output: None.
751 * Side-effects: Sends the appropriate mode changes to servers.
752 *
753 */
754 static void
755 send_mode_changes_server(struct Client *source_p, struct Channel *channel)
756 {
757 char modebuf[IRCD_BUFSIZE] = "";
758 char parabuf[IRCD_BUFSIZE] = "";
759 char *parptr = parabuf;
760 unsigned int mbl = 0, pbl = 0, arglen = 0, modecount = 0, paracount = 0;
761 unsigned int dir = MODE_QUERY;
762
763 mbl = snprintf(modebuf, sizeof(modebuf), ":%s TMODE %ju %s ",
764 source_p->id, channel->creation_time, channel->name);
765
766 /* Loop the list of modes we have */
767 for (unsigned int i = 0; i < mode_count; ++i)
768 {
769 if (mode_changes[i].letter == 0)
770 continue;
771
772 const char *arg;
773 if (mode_changes[i].id)
774 arg = mode_changes[i].id;
775 else
776 arg = mode_changes[i].arg;
777
778 if (arg)
779 arglen = strlen(arg);
780 else
781 arglen = 0;
782
783 /*
784 * If we're creeping past the buf size, we need to send it and make
785 * another line for the other modes
786 */
787 if ((paracount == MAXMODEPARAMS) ||
788 ((arglen + mbl + pbl + 2 /* +2 for /r/n */ ) > IRCD_BUFSIZE))
789 {
790 if (modecount)
791 sendto_server(source_p, 0, 0, "%s %s", modebuf, parabuf);
792
793 modecount = 0;
794 paracount = 0;
795
796 mbl = snprintf(modebuf, sizeof(modebuf), ":%s TMODE %ju %s ",
797 source_p->id, channel->creation_time, channel->name);
798
799 pbl = 0;
800 parabuf[0] = '\0';
801 parptr = parabuf;
802 dir = MODE_QUERY;
803 }
804
805 if (dir != mode_changes[i].dir)
806 {
807 modebuf[mbl++] = (mode_changes[i].dir == MODE_ADD) ? '+' : '-';
808 dir = mode_changes[i].dir;
809 }
810
811 modebuf[mbl++] = mode_changes[i].letter;
812 modebuf[mbl] = '\0';
813 ++modecount;
814
815 if (arg)
816 {
817 int len = sprintf(parptr, (pbl == 0) ? "%s" : " %s", arg);
818 pbl += len;
819 parptr += len;
820 ++paracount;
821 }
822 }
823
824 if (modecount)
825 sendto_server(source_p, 0, 0, "%s %s", modebuf, parabuf);
826 }
827
828 /* void send_mode_changes(struct Client *client_p,
829 * struct Client *source_p,
830 * struct Channel *channel)
831 * Input: The client sending(client_p), the source client(source_p),
832 * the channel to send mode changes for(channel),
833 * mode change globals.
834 * Output: None.
835 * Side-effects: Sends the appropriate mode changes to other clients
836 * and propagates to servers.
837 */
838 static void
839 send_mode_changes_client(struct Client *source_p, struct Channel *channel)
840 {
841 unsigned int flags = 0;
842
843 for (unsigned int pass = 2; pass--; flags = CHFL_CHANOP | CHFL_HALFOP)
844 {
845 char modebuf[IRCD_BUFSIZE] = "";
846 char parabuf[IRCD_BUFSIZE] = "";
847 char *parptr = parabuf;
848 unsigned int mbl = 0, pbl = 0, arglen = 0, modecount = 0, paracount = 0;
849 unsigned int dir = MODE_QUERY;
850
851 if (IsClient(source_p))
852 mbl = snprintf(modebuf, sizeof(modebuf), ":%s!%s@%s MODE %s ", source_p->name,
853 source_p->username, source_p->host, channel->name);
854 else
855 mbl = snprintf(modebuf, sizeof(modebuf), ":%s MODE %s ", (IsHidden(source_p) ||
856 ConfigServerHide.hide_servers) ?
857 me.name : source_p->name, channel->name);
858
859 for (unsigned int i = 0; i < mode_count; ++i)
860 {
861 if (mode_changes[i].letter == 0 || mode_changes[i].flags != flags)
862 continue;
863
864 const char *arg = mode_changes[i].arg;
865 if (arg)
866 arglen = strlen(arg);
867 else
868 arglen = 0;
869
870 if ((paracount == MAXMODEPARAMS) ||
871 ((arglen + mbl + pbl + 2 /* +2 for /r/n */ ) > IRCD_BUFSIZE))
872 {
873 if (modecount)
874 sendto_channel_local(NULL, channel, flags, 0, 0, "%s %s", modebuf, parabuf);
875
876 modecount = 0;
877 paracount = 0;
878
879 if (IsClient(source_p))
880 mbl = snprintf(modebuf, sizeof(modebuf), ":%s!%s@%s MODE %s ", source_p->name,
881 source_p->username, source_p->host, channel->name);
882 else
883 mbl = snprintf(modebuf, sizeof(modebuf), ":%s MODE %s ", (IsHidden(source_p) ||
884 ConfigServerHide.hide_servers) ?
885 me.name : source_p->name, channel->name);
886
887 pbl = 0;
888 parabuf[0] = '\0';
889 parptr = parabuf;
890 dir = MODE_QUERY;
891 }
892
893 if (dir != mode_changes[i].dir)
894 {
895 modebuf[mbl++] = (mode_changes[i].dir == MODE_ADD) ? '+' : '-';
896 dir = mode_changes[i].dir;
897 }
898
899 modebuf[mbl++] = mode_changes[i].letter;
900 modebuf[mbl] = '\0';
901 ++modecount;
902
903 if (arg)
904 {
905 int len = sprintf(parptr, (pbl == 0) ? "%s" : " %s", arg);
906 pbl += len;
907 parptr += len;
908 ++paracount;
909 }
910 }
911
912 if (modecount)
913 sendto_channel_local(NULL, channel, flags, 0, 0, "%s %s", modebuf, parabuf);
914 }
915 }
916
917 const struct chan_mode *cmode_map[256];
918 const struct chan_mode cmode_tab[] =
919 {
920 { .letter = 'b', .flag = CHFL_BAN, .required_oplevel = CHACCESS_HALFOP, .func = chm_mask },
921 { .letter = 'c', .mode = MODE_NOCTRL, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
922 { .letter = 'e', .flag = CHFL_EXCEPTION, .required_oplevel = CHACCESS_HALFOP, .func = chm_mask },
923 { .letter = 'h', .flag = CHFL_HALFOP, .required_oplevel = CHACCESS_CHANOP, .func = chm_flag },
924 { .letter = 'i', .mode = MODE_INVITEONLY, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
925 { .letter = 'k', .func = chm_key, .required_oplevel = CHACCESS_HALFOP },
926 { .letter = 'l', .func = chm_limit, .required_oplevel = CHACCESS_HALFOP },
927 { .letter = 'm', .mode = MODE_MODERATED, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
928 { .letter = 'n', .mode = MODE_NOPRIVMSGS, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
929 { .letter = 'o', .flag = CHFL_CHANOP, .required_oplevel = CHACCESS_CHANOP, .func = chm_flag },
930 { .letter = 'p', .mode = MODE_PRIVATE, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
931 { .letter = 'r', .mode = MODE_REGISTERED, .required_oplevel = CHACCESS_REMOTE, .only_servers = true, .func = chm_simple },
932 { .letter = 's', .mode = MODE_SECRET, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
933 { .letter = 't', .mode = MODE_TOPICLIMIT, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
934 { .letter = 'u', .mode = MODE_HIDEBMASKS, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
935 { .letter = 'v', .flag = CHFL_VOICE, .required_oplevel = CHACCESS_HALFOP, .func = chm_flag },
936 { .letter = 'C', .mode = MODE_NOCTCP, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
937 { .letter = 'I', .flag = CHFL_INVEX, .required_oplevel = CHACCESS_HALFOP, .func = chm_mask },
938 { .letter = 'L', .mode = MODE_EXTLIMIT, .required_oplevel = CHACCESS_HALFOP, .only_opers = true, .func = chm_simple },
939 { .letter = 'M', .mode = MODE_MODREG, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
940 { .letter = 'N', .mode = MODE_NONICKCHANGE, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
941 { .letter = 'O', .mode = MODE_OPERONLY, .required_oplevel = CHACCESS_HALFOP, .only_opers = true, .func = chm_simple },
942 { .letter = 'R', .mode = MODE_REGONLY, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
943 { .letter = 'S', .mode = MODE_SECUREONLY, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
944 { .letter = 'T', .mode = MODE_NONOTICE, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
945 { .letter = '\0', .mode = 0 }
946 };
947
948 void
949 channel_mode_init(void)
950 {
951 for (const struct chan_mode *tab = cmode_tab; tab->letter; ++tab)
952 cmode_map[tab->letter] = tab;
953 }
954
955 /*
956 * Input: The the client this originated
957 * from, the channel, the parameter count starting at the modes,
958 * the parameters, the channel name.
959 * Output: None.
960 * Side-effects: Changes the channel membership and modes appropriately,
961 * sends the appropriate MODE messages to the appropriate
962 * clients.
963 */
964 void
965 channel_mode_set(struct Client *source_p, struct Channel *channel,
966 struct ChannelMember *member, int parc, char *parv[])
967 {
968 int dir = MODE_ADD;
969 int parn = 1;
970 int alevel = 0, errors = 0;
971
972 mode_count = 0;
973 mode_limit = 0;
974 simple_modes_mask = 0;
975
976 alevel = get_channel_access(source_p, member);
977
978 for (const char *ml = parv[0]; *ml; ++ml)
979 {
980 switch (*ml)
981 {
982 case '+':
983 dir = MODE_ADD;
984 break;
985 case '-':
986 dir = MODE_DEL;
987 break;
988 case '=':
989 dir = MODE_QUERY;
990 break;
991 default:
992 {
993 const struct chan_mode *mode = cmode_map[(unsigned char)*ml];
994
995 if (mode)
996 mode->func(source_p, channel, parc, &parn, parv, &errors, alevel, dir, *ml, mode);
997 else
998 chm_nosuch(source_p, channel, parc, &parn, parv, &errors, alevel, dir, *ml, NULL);
999 break;
1000 }
1001 }
1002 }
1003
1004 /* Bail out if we have nothing to do... */
1005 if (mode_count == 0)
1006 return;
1007
1008 send_mode_changes_client(source_p, channel);
1009 send_mode_changes_server(source_p, channel);
1010 }

Properties

Name Value
svn:eol-style native
svn:keywords Id