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: 10039
Committed: Tue May 24 12:58:37 2022 UTC (22 months, 3 weeks ago) by michael
Content type: text/x-csrc
File size: 30722 byte(s)
Log Message:
- channel_mode.c: documentation

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

Properties

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