ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/branches/8.2.x/src/channel_mode.c
Revision: 10024
Committed: Sat Jan 1 10:20:46 2022 UTC (4 years, 6 months ago) by michael
Content type: text/x-csrc
File size: 29216 byte(s)
Log Message:
- Bump copyright years

File Contents

# Content
1 /*
2 * ircd-hybrid: an advanced, lightweight Internet Relay Chat Daemon (ircd)
3 *
4 * Copyright (c) 1997-2022 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, 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))
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, &me, ERR_BANLISTFULL, channel->name, banid);
124 return NULL;
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))
134 {
135 if (etype == EXTBAN_INVALID)
136 {
137 sendto_one_numeric(client, &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, &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, &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, &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))
204 ban->banstr_len = strlcpy(ban->banstr, get_mask(ban), sizeof(ban->banstr));
205 else
206 ban->banstr_len = strlcpy(ban->banstr, banid, 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))
222 snprintf(ban->who, sizeof(ban->who), "%s!%s@%s", client->name,
223 client->username, client->host);
224 else if (IsHidden(client) || ConfigServerHide.hide_servers)
225 strlcpy(ban->who, me.name, sizeof(ban->who));
226 else
227 strlcpy(ban->who, client->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, struct Channel *channel, const char *banid, dlink_list *list, unsigned int type)
243 {
244 static char mask[MODEBUFLEN];
245 dlink_node *node;
246
247 assert(banid);
248
249 /* TBD: n!u@h formatting fo local clients */
250
251 DLINK_FOREACH(node, list->head)
252 {
253 struct Ban *ban = node->data;
254
255 if (irccmp(banid, ban->banstr) == 0)
256 {
257 strlcpy(mask, ban->banstr, sizeof(mask)); /* caSe might be different in 'banid' */
258 clear_ban_cache_list(&channel->members_local);
259 remove_ban(ban, list);
260
261 return mask;
262 }
263 }
264
265 return NULL;
266 }
267
268 /* channel_modes()
269 *
270 * inputs - pointer to channel
271 * - pointer to client
272 * - pointer to mode buf
273 * - pointer to parameter buf
274 * output - NONE
275 * side effects - write the "simple" list of channel modes for channel
276 * channel onto buffer mbuf with the parameters in pbuf.
277 */
278 void
279 channel_modes(const struct Channel *channel, const struct Client *client,
280 const struct ChannelMember *member, char *mbuf, char *pbuf)
281 {
282 *mbuf++ = '+';
283 *pbuf = '\0';
284
285 for (const struct chan_mode *tab = cmode_tab; tab->letter; ++tab)
286 if (tab->mode && HasCMode(channel, tab->mode))
287 *mbuf++ = tab->letter;
288
289 if (channel->mode.limit)
290 {
291 *mbuf++ = 'l';
292
293 if (IsServer(client) || member || (member = member_find_link(client, channel)))
294 pbuf += sprintf(pbuf, "%u ", channel->mode.limit);
295 }
296
297 if (channel->mode.key[0])
298 {
299 *mbuf++ = 'k';
300
301 if (IsServer(client) || member || (member = member_find_link(client, channel)))
302 sprintf(pbuf, "%s ", channel->mode.key);
303 }
304
305 *mbuf = '\0';
306 }
307
308 /* fix_key()
309 *
310 * inputs - pointer to key to clean up
311 * output - pointer to cleaned up key
312 * side effects - input string is modified
313 *
314 * stolen from Undernet's ircd -orabidoo
315 */
316 static char *
317 fix_key(char *arg)
318 {
319 unsigned char *s = (unsigned char *)arg;
320 unsigned char *t = (unsigned char *)arg;
321
322 for (unsigned char c; (c = *s) && s - (unsigned char *)arg < KEYLEN; ++s)
323 {
324 c &= 0x7f;
325
326 if (c != ':' && c > ' ' && c != ',')
327 *t++ = c;
328 }
329
330 *t = '\0';
331 return arg;
332 }
333
334 /*
335 * inputs - pointer to channel
336 * output - none
337 * side effects - clear ban cache
338 */
339 void
340 clear_ban_cache_list(dlink_list *list)
341 {
342 dlink_node *node;
343
344 DLINK_FOREACH(node, list->head)
345 {
346 struct ChannelMember *member = node->data;
347 member->flags &= ~(CHFL_BAN_SILENCED | CHFL_BAN_CHECKED | CHFL_MUTE_CHECKED);
348 }
349 }
350
351 /*
352 * Bitmasks for various error returns that channel_mode_set should only return
353 * once per call -orabidoo
354 */
355 enum
356 {
357 SM_ERR_NOOPS = 1 << 0, /* No chan ops */
358 SM_ERR_UNKNOWN = 1 << 1,
359 SM_ERR_RPL_B = 1 << 2,
360 SM_ERR_RPL_E = 1 << 3,
361 SM_ERR_RPL_I = 1 << 4,
362 SM_ERR_NOTONCHANNEL = 1 << 5, /* Client is not on channel */
363 SM_ERR_NOTOPER = 1 << 6, /* Only irc-operators can change that mode */
364 SM_ERR_ONLYSERVER = 1 << 7 /* Only servers or services can change that mode */
365 };
366
367 /* Mode functions handle mode changes for a particular mode... */
368 static void
369 chm_nosuch(struct Client *client, struct Channel *channel, int parc, int *parn, char **parv,
370 int *errors, int alev, int dir, const char c, const struct chan_mode *mode)
371 {
372 if (*errors & SM_ERR_UNKNOWN)
373 return;
374
375 *errors |= SM_ERR_UNKNOWN;
376 sendto_one_numeric(client, &me, ERR_UNKNOWNMODE, c);
377 }
378
379 static void
380 chm_simple(struct Client *client, struct Channel *channel, int parc, int *parn, char **parv,
381 int *errors, int alev, int dir, const char c, const struct chan_mode *mode)
382 {
383 if (mode->only_opers == true)
384 {
385 if (MyClient(client) && !HasUMode(client, UMODE_OPER))
386 {
387 if (!(*errors & SM_ERR_NOTOPER))
388 sendto_one_numeric(client, &me, ERR_NOPRIVILEGES);
389
390 *errors |= SM_ERR_NOTOPER;
391 return;
392 }
393 }
394
395 if (mode->only_servers == true)
396 {
397 if (!IsServer(client) && !HasFlag(client, FLAGS_SERVICE))
398 {
399 if (!(*errors & SM_ERR_ONLYSERVER))
400 sendto_one_numeric(client, &me,
401 alev == CHACCESS_NOTONCHAN ? ERR_NOTONCHANNEL :
402 ERR_ONLYSERVERSCANCHANGE, channel->name);
403
404 *errors |= SM_ERR_ONLYSERVER;
405 return;
406 }
407 }
408
409 if (alev < mode->required_oplevel)
410 {
411 if (!(*errors & SM_ERR_NOOPS))
412 sendto_one_numeric(client, &me,
413 alev == CHACCESS_NOTONCHAN ? ERR_NOTONCHANNEL :
414 ERR_CHANOPRIVSNEEDED, channel->name);
415
416 *errors |= SM_ERR_NOOPS;
417 return;
418 }
419
420 /* If have already dealt with this simple mode, ignore it */
421 if (simple_modes_mask & mode->mode)
422 return;
423
424 simple_modes_mask |= mode->mode;
425
426 if (dir == MODE_ADD) /* setting + */
427 {
428 if (MyClient(client) && HasCMode(channel, mode->mode))
429 return;
430
431 AddCMode(channel, mode->mode);
432 }
433 else if (dir == MODE_DEL) /* setting - */
434 {
435 if (MyClient(client) && !HasCMode(channel, mode->mode))
436 return;
437
438 DelCMode(channel, mode->mode);
439 }
440
441 mode_changes[mode_count].letter = mode->letter;
442 mode_changes[mode_count].arg = NULL;
443 mode_changes[mode_count].id = NULL;
444 mode_changes[mode_count].flags = 0;
445 mode_changes[mode_count++].dir = dir;
446 }
447
448 static void
449 chm_mask(struct Client *client, struct Channel *channel, int parc, int *parn, char **parv,
450 int *errors, int alev, int dir, const char c, const struct chan_mode *mode)
451 {
452 const char *ret = NULL;
453 dlink_list *list;
454 enum irc_numerics rpl_list = 0, rpl_endlist = 0;
455 int errtype = 0;
456
457 switch (mode->flag)
458 {
459 case CHFL_BAN:
460 errtype = SM_ERR_RPL_B;
461 list = &channel->banlist;
462 rpl_list = RPL_BANLIST;
463 rpl_endlist = RPL_ENDOFBANLIST;
464 break;
465 case CHFL_EXCEPTION:
466 errtype = SM_ERR_RPL_E;
467 list = &channel->exceptlist;
468 rpl_list = RPL_EXCEPTLIST;
469 rpl_endlist = RPL_ENDOFEXCEPTLIST;
470 break;
471 case CHFL_INVEX:
472 errtype = SM_ERR_RPL_I;
473 list = &channel->invexlist;
474 rpl_list = RPL_INVEXLIST;
475 rpl_endlist = RPL_ENDOFINVEXLIST;
476 break;
477 default:
478 list = NULL; /* Let it crash */
479 }
480
481 if (dir == MODE_QUERY || parc <= *parn)
482 {
483 dlink_node *node;
484
485 if (*errors & errtype)
486 return;
487
488 *errors |= errtype;
489
490 DLINK_FOREACH(node, list->head)
491 {
492 const struct Ban *ban = node->data;
493
494 if (!HasCMode(channel, MODE_HIDEBMASKS) || alev >= mode->required_oplevel)
495 sendto_one_numeric(client, &me, rpl_list, channel->name,
496 ban->banstr, ban->who, ban->when);
497 }
498
499 sendto_one_numeric(client, &me, rpl_endlist, channel->name);
500 return;
501 }
502
503 if (alev < mode->required_oplevel)
504 {
505 if (!(*errors & SM_ERR_NOOPS))
506 sendto_one_numeric(client, &me,
507 alev == CHACCESS_NOTONCHAN ? ERR_NOTONCHANNEL :
508 ERR_CHANOPRIVSNEEDED, channel->name);
509
510 *errors |= SM_ERR_NOOPS;
511 return;
512 }
513
514 if (MyClient(client) && (++mode_limit > MAXMODEPARAMS))
515 return;
516
517 char *mask = parv[*parn];
518 ++(*parn);
519
520 if (*mask == ':' || (!MyConnect(client) && strchr(mask, ' ')))
521 return;
522
523 if (dir == MODE_ADD) /* setting + */
524 {
525 ret = add_id(client, channel, mask, list, mode->flag);
526 if (ret == NULL)
527 return;
528 }
529 else if (dir == MODE_DEL) /* setting - */
530 {
531 ret = del_id(client, channel, mask, list, mode->flag);
532 if (ret == NULL)
533 return;
534 }
535
536 static char buf[MAXPARA][MODEBUFLEN];
537 mask = buf[(*parn) - 1];
538 strlcpy(mask, ret, sizeof(buf[(*parn) - 1]));
539
540 mode_changes[mode_count].letter = mode->letter;
541 mode_changes[mode_count].arg = mask; /* At this point 'mask' is no longer than MODEBUFLEN */
542 mode_changes[mode_count].id = NULL;
543 if (HasCMode(channel, MODE_HIDEBMASKS))
544 mode_changes[mode_count].flags = CHFL_CHANOP | CHFL_HALFOP;
545 else
546 mode_changes[mode_count].flags = 0;
547 mode_changes[mode_count++].dir = dir;
548 }
549
550 static void
551 chm_flag(struct Client *client, struct Channel *channel, int parc, int *parn, char **parv,
552 int *errors, int alev, int dir, const char c, const struct chan_mode *mode)
553 {
554 if (alev < mode->required_oplevel)
555 {
556 if (!(*errors & SM_ERR_NOOPS))
557 sendto_one_numeric(client, &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 struct Client *client_target = find_chasing(client, parv[(*parn)++]);
569 if (client_target == NULL)
570 return; /* find_chasing sends ERR_NOSUCHNICK */
571
572 struct ChannelMember *member = member_find_link(client_target, channel);
573 if (member == NULL)
574 {
575 if (!(*errors & SM_ERR_NOTONCHANNEL))
576 sendto_one_numeric(client, &me, ERR_USERNOTINCHANNEL, client_target->name, channel->name);
577
578 *errors |= SM_ERR_NOTONCHANNEL;
579 return;
580 }
581
582 if (MyClient(client) && (++mode_limit > MAXMODEPARAMS))
583 return;
584
585 if (dir == MODE_ADD) /* setting + */
586 {
587 if (member_has_flags(member, mode->flag) == true)
588 return; /* No redundant mode changes */
589
590 AddMemberFlag(member, mode->flag);
591 }
592 else if (dir == MODE_DEL) /* setting - */
593 {
594 if (member_has_flags(member, mode->flag) == false)
595 return; /* No redundant mode changes */
596
597 DelMemberFlag(member, mode->flag);
598 }
599
600 mode_changes[mode_count].letter = mode->letter;
601 mode_changes[mode_count].arg = client_target->name;
602 mode_changes[mode_count].id = client_target->id;
603 mode_changes[mode_count].flags = 0;
604 mode_changes[mode_count++].dir = dir;
605 }
606
607 static void
608 chm_limit(struct Client *client, struct Channel *channel, int parc, int *parn, char **parv,
609 int *errors, int alev, int dir, const char c, const struct chan_mode *mode)
610 {
611 if (alev < mode->required_oplevel)
612 {
613 if (!(*errors & SM_ERR_NOOPS))
614 sendto_one_numeric(client, &me,
615 alev == CHACCESS_NOTONCHAN ? ERR_NOTONCHANNEL :
616 ERR_CHANOPRIVSNEEDED, channel->name);
617 *errors |= SM_ERR_NOOPS;
618 return;
619 }
620
621 if (dir == MODE_QUERY)
622 return;
623
624 if (dir == MODE_ADD && parc > *parn)
625 {
626 char *const lstr = parv[(*parn)++];
627 int limit = 0;
628
629 if (EmptyString(lstr) || (limit = atoi(lstr)) <= 0)
630 return;
631
632 sprintf(lstr, "%d", limit);
633
634 /* If somebody sets MODE #channel +ll 1 2, accept latter --fl */
635 for (unsigned int i = 0; i < mode_count; ++i)
636 if (mode_changes[i].letter == mode->letter && mode_changes[i].dir == MODE_ADD)
637 mode_changes[i].letter = 0;
638
639 mode_changes[mode_count].letter = mode->letter;
640 mode_changes[mode_count].arg = lstr;
641 mode_changes[mode_count].id = NULL;
642 mode_changes[mode_count].flags = 0;
643 mode_changes[mode_count++].dir = dir;
644
645 channel->mode.limit = limit;
646 }
647 else if (dir == MODE_DEL)
648 {
649 if (channel->mode.limit == 0)
650 return;
651
652 channel->mode.limit = 0;
653
654 mode_changes[mode_count].letter = mode->letter;
655 mode_changes[mode_count].arg = NULL;
656 mode_changes[mode_count].id = NULL;
657 mode_changes[mode_count].flags = 0;
658 mode_changes[mode_count++].dir = dir;
659 }
660 }
661
662 static void
663 chm_key(struct Client *client, struct Channel *channel, int parc, int *parn, char **parv,
664 int *errors, int alev, int dir, const char c, const struct chan_mode *mode)
665 {
666 if (alev < mode->required_oplevel)
667 {
668 if (!(*errors & SM_ERR_NOOPS))
669 sendto_one_numeric(client, &me,
670 alev == CHACCESS_NOTONCHAN ? ERR_NOTONCHANNEL :
671 ERR_CHANOPRIVSNEEDED, channel->name);
672 *errors |= SM_ERR_NOOPS;
673 return;
674 }
675
676 if (dir == MODE_QUERY)
677 return;
678
679 if (dir == MODE_ADD && parc > *parn)
680 {
681 char *const key = fix_key(parv[(*parn)++]);
682
683 if (EmptyString(key))
684 return;
685
686 assert(key[0] != ' ');
687 strlcpy(channel->mode.key, key, sizeof(channel->mode.key));
688
689 /* If somebody does MODE #channel +kk a b, accept latter --fl */
690 for (unsigned int i = 0; i < mode_count; ++i)
691 if (mode_changes[i].letter == mode->letter && mode_changes[i].dir == MODE_ADD)
692 mode_changes[i].letter = 0;
693
694 mode_changes[mode_count].letter = mode->letter;
695 mode_changes[mode_count].arg = key;
696 mode_changes[mode_count].id = NULL;
697 mode_changes[mode_count].flags = 0;
698 mode_changes[mode_count++].dir = dir;
699 }
700 else if (dir == MODE_DEL)
701 {
702 if (parc > *parn)
703 ++(*parn);
704
705 if (channel->mode.key[0] == '\0')
706 return;
707
708 channel->mode.key[0] = '\0';
709
710 mode_changes[mode_count].letter = mode->letter;
711 mode_changes[mode_count].arg = "*";
712 mode_changes[mode_count].id = NULL;
713 mode_changes[mode_count].flags = 0;
714 mode_changes[mode_count++].dir = dir;
715 }
716 }
717
718 /* get_channel_access()
719 *
720 * inputs - pointer to Client struct
721 * - pointer to Membership struct
722 * output - CHACCESS_CHANOP if we should let them have
723 * chanop level access, 0 for peon level access.
724 * side effects - NONE
725 */
726 static int
727 get_channel_access(const struct Client *client,
728 const struct ChannelMember *member)
729 {
730 /* Let hacked servers in for now... */
731 if (!MyClient(client))
732 return CHACCESS_REMOTE;
733
734 if (member == NULL)
735 return CHACCESS_NOTONCHAN;
736
737 /* Just to be sure.. */
738 assert(client == member->client);
739
740 if (member_has_flags(member, CHFL_CHANOP) == true)
741 return CHACCESS_CHANOP;
742
743 if (member_has_flags(member, CHFL_HALFOP) == true)
744 return CHACCESS_HALFOP;
745
746 return CHACCESS_PEON;
747 }
748
749 /* send_mode_changes_server()
750 * Input: the source client(client),
751 * the channel to send mode changes for(channel)
752 * Output: None.
753 * Side-effects: Sends the appropriate mode changes to servers.
754 *
755 */
756 static void
757 send_mode_changes_server(struct Client *client, struct Channel *channel)
758 {
759 char modebuf[IRCD_BUFSIZE] = "";
760 char parabuf[IRCD_BUFSIZE] = ""; /* Essential that parabuf[0] = '\0' */
761 char *parptr = parabuf;
762 unsigned int mbl = 0, pbl = 0, arglen = 0, modecount = 0, paracount = 0;
763 unsigned int dir = MODE_QUERY;
764
765 mbl = snprintf(modebuf, sizeof(modebuf), ":%s TMODE %ju %s ",
766 client->id, channel->creation_time, channel->name);
767
768 /* Loop the list of modes we have */
769 for (unsigned int i = 0; i < mode_count; ++i)
770 {
771 if (mode_changes[i].letter == 0)
772 continue;
773
774 const char *arg;
775 if (mode_changes[i].id)
776 arg = mode_changes[i].id;
777 else
778 arg = mode_changes[i].arg;
779
780 if (arg)
781 arglen = strlen(arg);
782 else
783 arglen = 0;
784
785 /*
786 * If we're creeping past the buf size, we need to send it and make
787 * another line for the other modes
788 */
789 if ((paracount == MAXMODEPARAMS) ||
790 ((arglen + mbl + pbl + 2 /* +2 for /r/n */ ) > IRCD_BUFSIZE))
791 {
792 if (modecount)
793 sendto_server(client, 0, 0, paracount == 0 ? "%s" : "%s %s", modebuf, parabuf);
794
795 modecount = 0;
796 paracount = 0;
797
798 mbl = snprintf(modebuf, sizeof(modebuf), ":%s TMODE %ju %s ",
799 client->id, channel->creation_time, channel->name);
800
801 pbl = 0;
802 parabuf[0] = '\0';
803 parptr = parabuf;
804 dir = MODE_QUERY;
805 }
806
807 if (dir != mode_changes[i].dir)
808 {
809 modebuf[mbl++] = (mode_changes[i].dir == MODE_ADD) ? '+' : '-';
810 dir = mode_changes[i].dir;
811 }
812
813 modebuf[mbl++] = mode_changes[i].letter;
814 modebuf[mbl] = '\0';
815 ++modecount;
816
817 if (arg)
818 {
819 int len = sprintf(parptr, (pbl == 0) ? "%s" : " %s", arg);
820 pbl += len;
821 parptr += len;
822 ++paracount;
823 }
824 }
825
826 if (modecount)
827 sendto_server(client, 0, 0, paracount == 0 ? "%s" : "%s %s", modebuf, parabuf);
828 }
829
830 /* void send_mode_changes(struct Client *client,
831 * struct Client *client,
832 * struct Channel *channel)
833 * Input: The client sending(client), the source client(client),
834 * the channel to send mode changes for(channel),
835 * mode change globals.
836 * Output: None.
837 * Side-effects: Sends the appropriate mode changes to other clients
838 * and propagates to servers.
839 */
840 static void
841 send_mode_changes_client(struct Client *client, struct Channel *channel)
842 {
843 unsigned int flags = 0;
844
845 for (unsigned int pass = 2; pass--; flags = CHFL_CHANOP | CHFL_HALFOP)
846 {
847 char modebuf[IRCD_BUFSIZE] = "";
848 char parabuf[IRCD_BUFSIZE] = ""; /* Essential that parabuf[0] = '\0' */
849 char *parptr = parabuf;
850 unsigned int mbl = 0, pbl = 0, arglen = 0, modecount = 0, paracount = 0;
851 unsigned int dir = MODE_QUERY;
852
853 if (IsClient(client))
854 mbl = snprintf(modebuf, sizeof(modebuf), ":%s!%s@%s MODE %s ", client->name,
855 client->username, client->host, channel->name);
856 else
857 mbl = snprintf(modebuf, sizeof(modebuf), ":%s MODE %s ", (IsHidden(client) ||
858 ConfigServerHide.hide_servers) ?
859 me.name : client->name, channel->name);
860
861 for (unsigned int i = 0; i < mode_count; ++i)
862 {
863 if (mode_changes[i].letter == 0 || mode_changes[i].flags != flags)
864 continue;
865
866 const char *arg = mode_changes[i].arg;
867 if (arg)
868 arglen = strlen(arg);
869 else
870 arglen = 0;
871
872 if ((paracount == MAXMODEPARAMS) ||
873 ((arglen + mbl + pbl + 2 /* +2 for /r/n */ ) > IRCD_BUFSIZE))
874 {
875 if (modecount)
876 sendto_channel_local(NULL, channel, flags, 0, 0, paracount == 0 ? "%s" : "%s %s", modebuf, parabuf);
877
878 modecount = 0;
879 paracount = 0;
880
881 if (IsClient(client))
882 mbl = snprintf(modebuf, sizeof(modebuf), ":%s!%s@%s MODE %s ", client->name,
883 client->username, client->host, channel->name);
884 else
885 mbl = snprintf(modebuf, sizeof(modebuf), ":%s MODE %s ", (IsHidden(client) ||
886 ConfigServerHide.hide_servers) ?
887 me.name : client->name, channel->name);
888
889 pbl = 0;
890 parabuf[0] = '\0';
891 parptr = parabuf;
892 dir = MODE_QUERY;
893 }
894
895 if (dir != mode_changes[i].dir)
896 {
897 modebuf[mbl++] = (mode_changes[i].dir == MODE_ADD) ? '+' : '-';
898 dir = mode_changes[i].dir;
899 }
900
901 modebuf[mbl++] = mode_changes[i].letter;
902 modebuf[mbl] = '\0';
903 ++modecount;
904
905 if (arg)
906 {
907 int len = sprintf(parptr, (pbl == 0) ? "%s" : " %s", arg);
908 pbl += len;
909 parptr += len;
910 ++paracount;
911 }
912 }
913
914 if (modecount)
915 sendto_channel_local(NULL, channel, flags, 0, 0, paracount == 0 ? "%s" : "%s %s", modebuf, parabuf);
916 }
917 }
918
919 const struct chan_mode *cmode_map[256];
920 const struct chan_mode cmode_tab[] =
921 {
922 { .letter = 'b', .flag = CHFL_BAN, .required_oplevel = CHACCESS_HALFOP, .func = chm_mask },
923 { .letter = 'c', .mode = MODE_NOCTRL, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
924 { .letter = 'e', .flag = CHFL_EXCEPTION, .required_oplevel = CHACCESS_HALFOP, .func = chm_mask },
925 { .letter = 'h', .flag = CHFL_HALFOP, .required_oplevel = CHACCESS_CHANOP, .func = chm_flag },
926 { .letter = 'i', .mode = MODE_INVITEONLY, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
927 { .letter = 'k', .func = chm_key, .required_oplevel = CHACCESS_HALFOP },
928 { .letter = 'l', .func = chm_limit, .required_oplevel = CHACCESS_HALFOP },
929 { .letter = 'm', .mode = MODE_MODERATED, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
930 { .letter = 'n', .mode = MODE_NOPRIVMSGS, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
931 { .letter = 'o', .flag = CHFL_CHANOP, .required_oplevel = CHACCESS_CHANOP, .func = chm_flag },
932 { .letter = 'p', .mode = MODE_PRIVATE, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
933 { .letter = 'r', .mode = MODE_REGISTERED, .required_oplevel = CHACCESS_REMOTE, .only_servers = true, .func = chm_simple },
934 { .letter = 's', .mode = MODE_SECRET, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
935 { .letter = 't', .mode = MODE_TOPICLIMIT, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
936 { .letter = 'u', .mode = MODE_HIDEBMASKS, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
937 { .letter = 'v', .flag = CHFL_VOICE, .required_oplevel = CHACCESS_HALFOP, .func = chm_flag },
938 { .letter = 'C', .mode = MODE_NOCTCP, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
939 { .letter = 'I', .flag = CHFL_INVEX, .required_oplevel = CHACCESS_HALFOP, .func = chm_mask },
940 { .letter = 'K', .mode = MODE_NOKNOCK, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
941 { .letter = 'L', .mode = MODE_EXTLIMIT, .required_oplevel = CHACCESS_HALFOP, .only_opers = true, .func = chm_simple },
942 { .letter = 'M', .mode = MODE_MODREG, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
943 { .letter = 'N', .mode = MODE_NONICKCHANGE, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
944 { .letter = 'O', .mode = MODE_OPERONLY, .required_oplevel = CHACCESS_HALFOP, .only_opers = true, .func = chm_simple },
945 { .letter = 'R', .mode = MODE_REGONLY, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
946 { .letter = 'S', .mode = MODE_SECUREONLY, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
947 { .letter = 'T', .mode = MODE_NONOTICE, .required_oplevel = CHACCESS_HALFOP, .func = chm_simple },
948 { .letter = '\0', .mode = 0 }
949 };
950
951 void
952 channel_mode_init(void)
953 {
954 for (const struct chan_mode *tab = cmode_tab; tab->letter; ++tab)
955 cmode_map[tab->letter] = tab;
956 }
957
958 /*
959 * Input: The the client this originated
960 * from, the channel, the parameter count starting at the modes,
961 * the parameters, the channel name.
962 * Output: None.
963 * Side-effects: Changes the channel membership and modes appropriately,
964 * sends the appropriate MODE messages to the appropriate
965 * clients.
966 */
967 void
968 channel_mode_set(struct Client *client, struct Channel *channel,
969 struct ChannelMember *member, int parc, char *parv[])
970 {
971 int dir = MODE_ADD;
972 int parn = 1;
973 int alevel = 0, errors = 0;
974
975 mode_count = 0;
976 mode_limit = 0;
977 simple_modes_mask = 0;
978
979 alevel = get_channel_access(client, member);
980
981 for (const char *ml = parv[0]; *ml; ++ml)
982 {
983 switch (*ml)
984 {
985 case '+':
986 dir = MODE_ADD;
987 break;
988 case '-':
989 dir = MODE_DEL;
990 break;
991 case '=':
992 dir = MODE_QUERY;
993 break;
994 default:
995 {
996 const struct chan_mode *mode = cmode_map[(unsigned char)*ml];
997
998 if (mode)
999 mode->func(client, channel, parc, &parn, parv, &errors, alevel, dir, *ml, mode);
1000 else
1001 chm_nosuch(client, channel, parc, &parn, parv, &errors, alevel, dir, *ml, NULL);
1002 break;
1003 }
1004 }
1005 }
1006
1007 /* Bail out if we have nothing to do... */
1008 if (mode_count == 0)
1009 return;
1010
1011 send_mode_changes_client(client, channel);
1012 send_mode_changes_server(client, channel);
1013 }

Properties

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