ViewVC Help
View File | Revision Log | Show Annotations | View Changeset | Root Listing
root/svn/ircd-hybrid/trunk/src/channel_mode.c
Revision: 9598
Committed: Sat Sep 5 10:10:00 2020 UTC (4 years, 11 months ago) by michael
Content type: text/x-csrc
File size: 29024 byte(s)
Log Message:
- Style corrections; reformatting

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

Properties

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