2 * IRC - Internet Relay Chat, ircd/channel.c
3 * Copyright (C) 1990 Jarkko Oikarinen and
4 * University of Oulu, Co Center
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 1, or (at your option)
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.
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., 675 Mass Ave, Cambridge, MA 02139, USA.
21 * @brief Channel management and maintenance
28 #include "destruct_event.h"
31 #include "ircd_alloc.h"
32 #include "ircd_chattr.h"
33 #include "ircd_defs.h"
34 #include "ircd_features.h"
36 #include "ircd_reply.h"
37 #include "ircd_snprintf.h"
38 #include "ircd_string.h"
45 #include "querycmds.h"
56 /* #include <assert.h> -- Now using assert in ircd_log.h */
61 /** Linked list containing the full list of all channels */
62 struct Channel* GlobalChannelList = 0;
64 /** Number of struct Membership*'s allocated */
65 static unsigned int membershipAllocCount;
66 /** Freelist for struct Membership*'s */
67 static struct Membership* membershipFreeList;
68 /** Freelist for struct Ban*'s */
69 static struct Ban* free_bans;
70 /** Number of ban structures allocated. */
71 static size_t bans_alloc;
72 /** Number of ban structures in use. */
73 static size_t bans_inuse;
76 /** return the length (>=0) of a chain of links.
77 * @param lp pointer to the start of the linked list
78 * @return the number of items in the list
80 static int list_length(struct SLink *lp)
84 for (; lp; lp = lp->next)
90 /** Set the mask for a ban, checking for IP masks.
91 * @param[in,out] ban Ban structure to modify.
92 * @param[in] banstr Mask to ban.
95 set_ban_mask(struct Ban *ban, const char *banstr)
98 assert(banstr != NULL);
99 ircd_strncpy(ban->banstr, banstr, sizeof(ban->banstr) - 1);
100 sep = strrchr(banstr, '@');
102 ban->nu_len = sep - banstr;
103 if (ipmask_parse(sep + 1, &ban->address, &ban->addrbits))
104 ban->flags |= BAN_IPMASK;
108 /** Allocate a new Ban structure.
109 * @param[in] banstr Ban mask to use.
110 * @return Newly allocated ban.
113 make_ban(const char *banstr)
118 free_bans = free_bans->next;
120 else if (!(ban = MyMalloc(sizeof(*ban))))
125 memset(ban, 0, sizeof(*ban));
126 set_ban_mask(ban, banstr);
130 /** Deallocate a ban structure.
131 * @param[in] ban Ban to deallocate.
134 free_ban(struct Ban *ban)
136 ban->next = free_bans;
141 /** Report ban usage to \a cptr.
142 * @param[in] cptr Client requesting information.
144 void bans_send_meminfo(struct Client *cptr)
148 for (num_free = 0, ban = free_bans; ban; ban = ban->next)
150 send_reply(cptr, SND_EXPLICIT | RPL_STATSDEBUG, ":Bans: inuse %zu(%zu) free %zu alloc %zu",
151 bans_inuse, bans_inuse * sizeof(*ban), num_free, bans_alloc);
154 /** return the struct Membership* that represents a client on a channel
155 * This function finds a struct Membership* which holds the state about
156 * a client on a specific channel. The code is smart enough to iterate
157 * over the channels a user is in, or the users in a channel to find the
158 * user depending on which is likely to be more efficient.
160 * @param chptr pointer to the channel struct
161 * @param cptr pointer to the client struct
163 * @returns pointer to the struct Membership representing this client on
164 * this channel. Returns NULL if the client is not on the channel.
165 * Returns NULL if the client is actually a server.
166 * @see find_channel_member()
168 struct Membership* find_member_link(struct Channel* chptr, const struct Client* cptr)
170 struct Membership *m;
174 /* Servers don't have member links */
175 if (IsServer(cptr)||IsMe(cptr))
178 /* +k users are typically on a LOT of channels. So we iterate over who
179 * is in the channel. X/W are +k and are in about 5800 channels each.
180 * however there are typically no more than 1000 people in a channel
183 if (IsChannelService(cptr)) {
186 assert(m->channel == chptr);
192 /* Users on the other hand aren't allowed on more than 15 channels. 50%
193 * of users that are on channels are on 2 or less, 95% are on 7 or less,
194 * and 99% are on 10 or less.
197 m = (cli_user(cptr))->channel;
199 assert(m->user == cptr);
200 if (m->channel == chptr)
208 /** Find the client structure for a nick name (user)
209 * Find the client structure for a nick name (user)
210 * using history mechanism if necessary. If the client is not found, an error
211 * message (NO SUCH NICK) is generated. If the client was found
212 * through the history, chasing will be 1 and otherwise 0.
214 * This function was used extensively in the P09 days, and since we now have
215 * numeric nicks is no longer quite as important.
217 * @param sptr Pointer to the client that has requested the search
218 * @param user a string representing the client to be found
219 * @param chasing a variable set to 0 if the user was found directly,
221 * @returns a pointer the client, or NULL if the client wasn't found.
223 struct Client* find_chasing(struct Client* sptr, const char* user, int* chasing)
225 struct Client* who = FindClient(user);
232 if (!(who = get_history(user, feature_int(FEAT_KILLCHASETIMELIMIT)))) {
233 send_reply(sptr, ERR_NOSUCHNICK, user);
241 /** Decrement the count of users, and free if empty.
242 * Subtract one user from channel i (and free channel * block, if channel
245 * @param chptr The channel to subtract one from.
247 * @returns true (1) if channel still has members.
248 * false (0) if the channel is now empty.
250 int sub1_from_channel(struct Channel* chptr)
252 if (chptr->users > 1) /* Can be 0, called for an empty channel too */
254 assert(0 != chptr->members);
262 * Also channels without Apass set need to be kept alive,
263 * otherwise Bad Guys(tm) would be able to takeover
264 * existing channels too easily, and then set an Apass!
265 * However, if a channel without Apass becomes empty
266 * then we try to be kind to them and remove possible
269 chptr->mode.mode &= ~MODE_INVITEONLY;
270 chptr->mode.limit = 0;
272 * We do NOT reset a possible key or bans because when
273 * the 'channel owners' can't get in because of a key
274 * or ban then apparently there was a fight/takeover
275 * on the channel and we want them to contact IRC opers
276 * who then will educate them on the use of Apass/Upass.
278 if (!chptr->mode.apass[0]) /* If no Apass, reset all modes. */
280 struct Ban *link, *next;
281 chptr->mode.mode = 0;
282 *chptr->mode.key = '\0';
283 while (chptr->invites)
284 del_invite(chptr->invites->value.cptr, chptr);
285 for (link = chptr->banlist; link; link = next) {
289 chptr->banlist = NULL;
291 #if 1 /* Temporary code */
292 /* Immediately destruct empty -A channels if ZANNELS is FALSE.
293 When OPLEVELS is true, ZANNELS should be TRUE too. Test for
294 that error. This is done to avoid the DESTRUCT message to
295 occur, which is necessary on a network with mixed versions
296 of 2.10.12.x, with x < 04 *and* 2.10.11 servers. Because
297 servers prior to 2.10.12.04 can cause a BURST message outside
298 the normal net.burst as a result of a DESTRUCT message, and
299 2.10.11 SQUIT servers when they do that. */
300 if (!(feature_bool(FEAT_ZANNELS) || feature_bool(FEAT_OPLEVELS)))
302 destruct_channel(chptr);
307 if (TStime() - chptr->creationtime < 172800) /* Channel younger than 48 hours? */
308 schedule_destruct_event_1m(chptr); /* Get rid of it in approximately 4-5 minutes */
310 schedule_destruct_event_48h(chptr); /* Get rid of it in approximately 48 hours */
315 /** Destroy an empty channel
316 * This function destroys an empty channel, removing it from hashtables,
317 * and removing any resources it may have consumed.
319 * @param chptr The channel to destroy
321 * @returns 0 (success)
323 * FIXME: Change to return void, this function never fails.
325 int destruct_channel(struct Channel* chptr)
327 struct Ban *ban, *next;
329 assert(0 == chptr->members);
332 * Now, find all invite links from channel structure
334 while (chptr->invites)
335 del_invite(chptr->invites->value.cptr, chptr);
337 for (ban = chptr->banlist; ban; ban = next)
343 chptr->prev->next = chptr->next;
345 GlobalChannelList = chptr->next;
347 chptr->next->prev = chptr->prev;
349 --UserStats.channels;
351 * make sure that channel actually got removed from hash table
353 assert(chptr->hnext == chptr);
358 /** returns Membership * if a person is joined and not a zombie
360 * @param chptr Channel
361 * @returns pointer to the client's struct Membership * on the channel if that
362 * user is a full member of the channel, or NULL otherwise.
364 * @see find_member_link()
366 struct Membership* find_channel_member(struct Client* cptr, struct Channel* chptr)
368 struct Membership* member;
371 member = find_member_link(chptr, cptr);
372 return (member && !IsZombie(member)) ? member : 0;
375 /** Searches for a ban from a ban list that matches a user.
376 * @param[in] cptr The client to test.
377 * @param[in] banlist The list of bans to test.
378 * @return Pointer to a matching ban, or NULL if none exit.
380 struct Ban *find_ban(struct Client *cptr, struct Ban *banlist)
382 char nu[NICKLEN + USERLEN + 2];
383 char tmphost[HOSTLEN + 1];
384 char iphost[SOCKIPLEN + 1];
389 /* Build nick!user and alternate host names. */
390 ircd_snprintf(0, nu, sizeof(nu), "%s!%s",
391 cli_name(cptr), cli_user(cptr)->username);
392 ircd_ntoa_r(iphost, &cli_ip(cptr));
393 if (!IsAccount(cptr))
395 else if (HasHiddenHost(cptr))
396 sr = cli_user(cptr)->realhost;
399 ircd_snprintf(0, tmphost, HOSTLEN, "%s.%s",
400 cli_user(cptr)->account, feature_str(FEAT_HIDDEN_HOST));
404 /* Walk through ban list. */
405 for (found = NULL; banlist; banlist = banlist->next) {
407 /* If we have found a positive ban already, only consider exceptions. */
408 if (found && !(banlist->flags & BAN_EXCEPTION))
410 /* Compare nick!user portion of ban. */
411 banlist->banstr[banlist->nu_len] = '\0';
412 res = match(banlist->banstr, nu);
413 banlist->banstr[banlist->nu_len] = '@';
416 /* Compare host portion of ban. */
417 hostmask = banlist->banstr + banlist->nu_len + 1;
418 if (!((banlist->flags & BAN_IPMASK)
419 && ipmask_check(&cli_ip(cptr), &banlist->address, banlist->addrbits))
420 && match(hostmask, cli_user(cptr)->host)
421 && !(sr && !match(hostmask, sr)))
423 /* If an exception matches, no ban can match. */
424 if (banlist->flags & BAN_EXCEPTION)
426 /* Otherwise, remember this ban but keep searching for an exception. */
433 * This function returns true if the user is banned on the said channel.
434 * This function will check the ban cache if applicable, otherwise will
435 * do the comparisons and cache the result.
437 * @param[in] member The Membership to test for banned-ness.
438 * @return Non-zero if the member is banned, zero if not.
440 static int is_banned(struct Membership* member)
442 if (IsBanValid(member))
443 return IsBanned(member);
446 if (find_ban(member->user, member->channel->banlist)) {
455 /** add a user to a channel.
456 * adds a user to a channel by adding another link to the channels member
459 * @param chptr The channel to add to.
460 * @param who The user to add.
461 * @param flags The flags the user gets initially.
462 * @param oplevel The oplevel the user starts with.
464 void add_user_to_channel(struct Channel* chptr, struct Client* who,
465 unsigned int flags, int oplevel)
472 struct Membership* member = membershipFreeList;
474 membershipFreeList = member->next_member;
476 member = (struct Membership*) MyMalloc(sizeof(struct Membership));
477 ++membershipAllocCount;
482 member->channel = chptr;
483 member->status = flags;
484 SetOpLevel(member, oplevel);
486 member->next_member = chptr->members;
487 if (member->next_member)
488 member->next_member->prev_member = member;
489 member->prev_member = 0;
490 chptr->members = member;
492 member->next_channel = (cli_user(who))->channel;
493 if (member->next_channel)
494 member->next_channel->prev_channel = member;
495 member->prev_channel = 0;
496 (cli_user(who))->channel = member;
498 if (chptr->destruct_event)
499 remove_destruct_event(chptr);
501 ++((cli_user(who))->joined);
505 /** Remove a person from a channel, given their Membership*
507 * @param member A member of a channel.
509 * @returns true if there are more people in the channel.
511 static int remove_member_from_channel(struct Membership* member)
513 struct Channel* chptr;
515 chptr = member->channel;
517 * unlink channel member list
519 if (member->next_member)
520 member->next_member->prev_member = member->prev_member;
521 if (member->prev_member)
522 member->prev_member->next_member = member->next_member;
524 member->channel->members = member->next_member;
527 * If this is the last delayed-join user, may have to clear WASDELJOINS.
529 if (IsDelayedJoin(member))
530 CheckDelayedJoins(chptr);
533 * unlink client channel list
535 if (member->next_channel)
536 member->next_channel->prev_channel = member->prev_channel;
537 if (member->prev_channel)
538 member->prev_channel->next_channel = member->next_channel;
540 (cli_user(member->user))->channel = member->next_channel;
542 --(cli_user(member->user))->joined;
544 member->next_member = membershipFreeList;
545 membershipFreeList = member;
547 return sub1_from_channel(chptr);
550 /** Check if all the remaining members on the channel are zombies
552 * @returns False if the channel has any non zombie members, True otherwise.
555 static int channel_all_zombies(struct Channel* chptr)
557 struct Membership* member;
559 for (member = chptr->members; member; member = member->next_member) {
560 if (!IsZombie(member))
567 /** Remove a user from a channel
568 * This is the generic entry point for removing a user from a channel, this
569 * function will remove the client from the channel, and destroy the channel
570 * if there are no more normal users left.
572 * @param cptr The client
573 * @param chptr The channel
575 void remove_user_from_channel(struct Client* cptr, struct Channel* chptr)
578 struct Membership* member;
581 if ((member = find_member_link(chptr, cptr))) {
582 if (remove_member_from_channel(member)) {
583 if (channel_all_zombies(chptr)) {
585 * XXX - this looks dangerous but isn't if we got the referential
586 * integrity right for channels
588 while (remove_member_from_channel(chptr->members))
595 /** Remove a user from all channels they are on.
597 * This function removes a user from all channels they are on.
599 * @param cptr The client to remove.
601 void remove_user_from_all_channels(struct Client* cptr)
603 struct Membership* chan;
605 assert(0 != cli_user(cptr));
607 while ((chan = (cli_user(cptr))->channel))
608 remove_user_from_channel(cptr, chan->channel);
611 /** Check if this user is a legitimate chanop
613 * @param cptr Client to check
614 * @param chptr Channel to check
616 * @returns True if the user is a chanop (And not a zombie), False otherwise.
619 int is_chan_op(struct Client *cptr, struct Channel *chptr)
621 struct Membership* member;
623 if ((member = find_member_link(chptr, cptr)))
624 return (!IsZombie(member) && IsChanOp(member));
629 /** Check if a user is a Zombie on a specific channel.
631 * @param cptr The client to check.
632 * @param chptr The channel to check.
634 * @returns True if the client (cptr) is a zombie on the channel (chptr),
639 int is_zombie(struct Client *cptr, struct Channel *chptr)
641 struct Membership* member;
645 if ((member = find_member_link(chptr, cptr)))
646 return IsZombie(member);
650 /** Returns if a user has voice on a channel.
652 * @param cptr The client
653 * @param chptr The channel
655 * @returns True if the client (cptr) is voiced on (chptr) and is not a zombie.
658 int has_voice(struct Client* cptr, struct Channel* chptr)
660 struct Membership* member;
663 if ((member = find_member_link(chptr, cptr)))
664 return (!IsZombie(member) && HasVoice(member));
669 /** Can this member send to a channel
671 * A user can speak on a channel iff:
673 * <li> They didn't use the Apass to gain ops.
674 * <li> They are op'd or voice'd.
675 * <li> You aren't banned.
676 * <li> The channel isn't +m
677 * <li> The channel isn't +n or you are on the channel.
680 * This function will optionally reveal a user on a delayed join channel if
681 * they are allowed to send to the channel.
683 * @param member The membership of the user
684 * @param reveal If true, the user will be "revealed" on a delayed
687 * @returns True if the client can speak on the channel.
689 int member_can_send_to_channel(struct Membership* member, int reveal)
693 /* Do not check for users on other servers: This should be a
694 * temporary desynch, or maybe they are on an older server, but
695 * we do not want to send ERR_CANNOTSENDTOCHAN more than once.
697 if (!MyUser(member->user))
699 if (IsDelayedJoin(member) && reveal)
700 RevealDelayedJoin(member);
704 /* Discourage using the Apass to get op. They should use the Upass. */
705 if (IsChannelManager(member) && member->channel->mode.apass[0])
708 /* If you have voice or ops, you can speak. */
709 if (IsVoicedOrOpped(member))
713 * If it's moderated, and you aren't a privileged user, you can't
716 if (member->channel->mode.mode & MODE_MODERATED)
719 /* If only logged in users may join and you're not one, you can't speak. */
720 if (member->channel->mode.mode & MODE_REGONLY && !IsAccount(member->user))
723 /* If you're banned then you can't speak either. */
724 if (is_banned(member))
727 if (IsDelayedJoin(member) && reveal)
728 RevealDelayedJoin(member);
733 /** Check if a client can send to a channel.
735 * Has the added check over member_can_send_to_channel() of servers can
738 * @param cptr The client to check
739 * @param chptr The channel to check
740 * @param reveal If the user should be revealed (see
741 * member_can_send_to_channel())
743 * @returns true if the client is allowed to speak on the channel, false
746 * @see member_can_send_to_channel()
748 int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr, int reveal)
750 struct Membership *member;
753 * Servers can always speak on channels.
758 member = find_channel_member(cptr, chptr);
761 * You can't speak if you're off channel, and it is +n (no external messages)
765 if ((chptr->mode.mode & (MODE_NOPRIVMSGS|MODE_MODERATED)) ||
766 ((chptr->mode.mode & MODE_REGONLY) && !IsAccount(cptr)))
769 return !find_ban(cptr, chptr->banlist);
771 return member_can_send_to_channel(member, reveal);
774 /** Returns the name of a channel that prevents the user from changing nick.
775 * if a member and not (opped or voiced) and (banned or moderated), return
776 * the name of the first channel banned on.
778 * @param cptr The client
780 * @returns the name of the first channel banned on, or NULL if the user
783 const char* find_no_nickchange_channel(struct Client* cptr)
786 struct Membership* member;
787 for (member = (cli_user(cptr))->channel; member;
788 member = member->next_channel) {
789 if (IsVoicedOrOpped(member))
791 if ((member->channel->mode.mode & MODE_MODERATED)
792 || (member->channel->mode.mode & MODE_REGONLY && !IsAccount(cptr))
793 || is_banned(member))
794 return member->channel->chname;
801 /** Fill mbuf/pbuf with modes from chptr
802 * write the "simple" list of channel modes for channel chptr onto buffer mbuf
803 * with the parameters in pbuf as visible by cptr.
805 * This function will hide keys from non-op'd, non-server clients.
807 * @param cptr The client to generate the mode for.
808 * @param mbuf The buffer to write the modes into.
809 * @param pbuf The buffer to write the mode parameters into.
810 * @param buflen The length of the buffers.
811 * @param chptr The channel to get the modes from.
812 * @param member The membership of this client on this channel (or NULL
813 * if this client isn't on this channel)
816 void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
817 struct Channel *chptr, struct Membership *member)
819 int previous_parameter = 0;
826 if (chptr->mode.mode & MODE_SECRET)
828 else if (chptr->mode.mode & MODE_PRIVATE)
830 if (chptr->mode.mode & MODE_MODERATED)
832 if (chptr->mode.mode & MODE_TOPICLIMIT)
834 if (chptr->mode.mode & MODE_INVITEONLY)
836 if (chptr->mode.mode & MODE_NOPRIVMSGS)
838 if (chptr->mode.mode & MODE_REGONLY)
840 if (chptr->mode.mode & MODE_DELJOINS)
842 else if (MyUser(cptr) && (chptr->mode.mode & MODE_WASDELJOINS))
844 if (chptr->mode.limit) {
846 ircd_snprintf(0, pbuf, buflen, "%u", chptr->mode.limit);
847 previous_parameter = 1;
850 if (*chptr->mode.key) {
852 if (previous_parameter)
854 if (is_chan_op(cptr, chptr) || IsServer(cptr)) {
855 strcat(pbuf, chptr->mode.key);
858 previous_parameter = 1;
860 if (*chptr->mode.apass) {
862 if (previous_parameter)
864 if (IsServer(cptr)) {
865 strcat(pbuf, chptr->mode.apass);
868 previous_parameter = 1;
870 if (*chptr->mode.upass) {
872 if (previous_parameter)
874 if (IsServer(cptr) || (member && IsChanOp(member) && OpLevel(member) == 0)) {
875 strcat(pbuf, chptr->mode.upass);
882 /** Compare two members oplevel
884 * @param mp1 Pointer to a pointer to a membership
885 * @param mp2 Pointer to a pointer to a membership
887 * @returns 0 if equal, -1 if mp1 is lower, +1 otherwise.
891 int compare_member_oplevel(const void *mp1, const void *mp2)
893 struct Membership const* member1 = *(struct Membership const**)mp1;
894 struct Membership const* member2 = *(struct Membership const**)mp2;
895 if (member1->oplevel == member2->oplevel)
897 return (member1->oplevel < member2->oplevel) ? -1 : 1;
900 /* send "cptr" a full list of the modes for channel chptr.
902 * Sends a BURST line to cptr, bursting all the modes for the channel.
904 * @param cptr Client pointer
905 * @param chptr Channel pointer
907 void send_channel_modes(struct Client *cptr, struct Channel *chptr)
909 /* The order in which modes are generated is now mandatory */
910 static unsigned int current_flags[4] =
911 { 0, CHFL_VOICE, CHFL_CHANOP, CHFL_CHANOP | CHFL_VOICE };
917 struct Membership* member;
919 char modebuf[MODEBUFLEN];
920 char parabuf[MODEBUFLEN];
922 int number_of_ops = 0;
923 int opped_members_index = 0;
924 struct Membership** opped_members = NULL;
925 int last_oplevel = 0;
926 int feat_oplevels = (chptr->mode.apass[0]) != '\0';
931 if (IsLocalChannel(chptr->chname))
934 member = chptr->members;
935 lp2 = chptr->banlist;
937 *modebuf = *parabuf = '\0';
938 channel_modes(cptr, modebuf, parabuf, sizeof(parabuf), chptr, 0);
940 for (first = 1; full; first = 0) /* Loop for multiple messages */
942 full = 0; /* Assume by default we get it
943 all in one message */
945 /* (Continued) prefix: "<Y> B <channel> <TS>" */
946 /* is there any better way we can do this? */
947 mb = msgq_make(&me, "%C " TOK_BURST " %H %Tu", &me, chptr,
948 chptr->creationtime);
950 if (first && modebuf[1]) /* Add simple modes (Aiklmnpstu)
953 /* prefix: "<Y> B <channel> <TS>[ <modes>[ <params>]]" */
954 msgq_append(&me, mb, " %s", modebuf);
957 msgq_append(&me, mb, " %s", parabuf);
961 * Attach nicks, comma separated " nick[:modes],nick[:modes],..."
963 * First find all opless members.
964 * Run 2 times over all members, to group the members with
965 * and without voice together.
966 * Then run 2 times over all opped members (which are ordered
967 * by op-level) to also group voice and non-voice together.
969 for (first = 1; flag_cnt < 4; new_mode = 1, ++flag_cnt)
973 if (flag_cnt < 2 && IsChanOp(member))
976 * The first loop (to find all non-voice/op), we count the ops.
977 * The second loop (to find all voiced non-ops), store the ops
978 * in a dynamic array.
983 opped_members[opped_members_index++] = member;
985 /* Only handle the members with the flags that we are interested in. */
986 if ((member->status & CHFL_VOICED_OR_OPPED) == current_flags[flag_cnt])
988 if (msgq_bufleft(mb) < NUMNICKLEN + 3 + MAXOPLEVELDIGITS)
989 /* The 3 + MAXOPLEVELDIGITS is a possible ",:v999". */
991 full = 1; /* Make sure we continue after
993 /* Ensure the new BURST line contains the current
994 * ":mode", except when there is no mode yet. */
995 new_mode = (flag_cnt > 0) ? 1 : 0;
996 break; /* Do not add this member to this message */
998 msgq_append(&me, mb, "%c%C", first ? ' ' : ',', member->user);
999 first = 0; /* From now on, use commas to add new nicks */
1002 * Do we have a nick with a new mode ?
1003 * Or are we starting a new BURST line?
1008 * This means we are at the _first_ member that has only
1009 * voice, or the first member that has only ops, or the
1010 * first member that has voice and ops (so we get here
1011 * at most three times, plus once for every start of
1012 * a continued BURST line where only these modes is current.
1013 * In the two cases where the current mode includes ops,
1014 * we need to add the _absolute_ value of the oplevel to the mode.
1016 char tbuf[3 + MAXOPLEVELDIGITS] = ":";
1019 if (HasVoice(member)) /* flag_cnt == 1 or 3 */
1021 if (IsChanOp(member)) /* flag_cnt == 2 or 3 */
1023 /* append the absolute value of the oplevel */
1025 loc += ircd_snprintf(0, tbuf + loc, sizeof(tbuf) - loc, "%u", last_oplevel = member->oplevel);
1030 msgq_append(&me, mb, tbuf);
1033 else if (feat_oplevels && flag_cnt > 1 && last_oplevel != member->oplevel)
1036 * This can't be the first member of a (continued) BURST
1037 * message because then either flag_cnt == 0 or new_mode == 1
1038 * Now we need to append the incremental value of the oplevel.
1040 char tbuf[2 + MAXOPLEVELDIGITS];
1041 ircd_snprintf(0, tbuf, sizeof(tbuf), ":%u", member->oplevel - last_oplevel);
1042 last_oplevel = member->oplevel;
1043 msgq_append(&me, mb, tbuf);
1046 /* Go to the next `member'. */
1048 member = member->next_member;
1050 member = opped_members[++opped_members_index];
1055 /* Point `member' at the start of the list again. */
1058 member = chptr->members;
1059 /* Now, after one loop, we know the number of ops and can
1060 * allocate the dynamic array with pointer to the ops. */
1061 opped_members = (struct Membership**)
1062 MyMalloc((number_of_ops + 1) * sizeof(struct Membership*));
1063 opped_members[number_of_ops] = NULL; /* Needed for loop termination */
1067 /* At the end of the second loop, sort the opped members with
1068 * increasing op-level, so that we will output them in the
1069 * correct order (and all op-level increments stay positive) */
1071 qsort(opped_members, number_of_ops,
1072 sizeof(struct Membership*), compare_member_oplevel);
1073 /* The third and fourth loop run only over the opped members. */
1074 member = opped_members[(opped_members_index = 0)];
1077 } /* loop over 0,+v,+o,+ov */
1081 /* Attach all bans, space separated " :%ban ban ..." */
1082 for (first = 2; lp2; lp2 = lp2->next)
1084 len = strlen(lp2->banstr);
1085 if (msgq_bufleft(mb) < len + 1 + first)
1086 /* The +1 stands for the added ' '.
1087 * The +first stands for the added ":%".
1093 msgq_append(&me, mb, " %s%s", first ? ":%" : "",
1099 send_buffer(cptr, mb, 0); /* Send this message */
1101 } /* Continue when there was something
1102 that didn't fit (full==1) */
1104 MyFree(opped_members);
1105 if (feature_bool(FEAT_TOPIC_BURST) && (chptr->topic[0] != '\0'))
1106 sendcmdto_one(&me, CMD_TOPIC, cptr, "%H %Tu %Tu :%s", chptr,
1107 chptr->creationtime, chptr->topic_time, chptr->topic);
1110 /** Canonify a mask.
1113 * @author Carlo Wood (Run),
1116 * When the nick is longer then NICKLEN, it is cut off (its an error of course).
1117 * When the user name or host name are too long (USERLEN and HOSTLEN
1118 * respectively) then they are cut off at the start with a '*'.
1120 * The following transformations are made:
1122 * 1) xxx -> nick!*@*
1123 * 2) xxx.xxx -> *!*\@host
1124 * 3) xxx\!yyy -> nick!user\@*
1125 * 4) xxx\@yyy -> *!user\@host
1126 * 5) xxx!yyy\@zzz -> nick!user\@host
1128 * @param mask The uncanonified mask.
1129 * @returns The updated mask in a static buffer.
1131 char *pretty_mask(char *mask)
1133 static char star[2] = { '*', 0 };
1134 static char retmask[NICKLEN + USERLEN + HOSTLEN + 3];
1135 char *last_dot = NULL;
1138 /* Case 1: default */
1143 /* Do a _single_ pass through the characters of the mask: */
1144 for (ptr = mask; *ptr; ++ptr)
1148 /* Case 3 or 5: Found first '!' (without finding a '@' yet) */
1152 else if (*ptr == '@')
1154 /* Case 4: Found last '@' (without finding a '!' yet) */
1159 else if (*ptr == '.' || *ptr == ':')
1161 /* Case 2: Found character specific to IP or hostname (without
1162 * finding a '!' or '@' yet) */
1172 /* Case 4 or 5: Found last '@' */
1178 if (user == star && last_dot)
1188 char *nick_end = (user != star) ? user - 1 : ptr;
1189 if (nick_end - nick > NICKLEN)
1195 char *user_end = (host != star) ? host - 1 : ptr;
1196 if (user_end - user > USERLEN)
1198 user = user_end - USERLEN;
1203 if (host != star && ptr - host > HOSTLEN)
1205 host = ptr - HOSTLEN;
1208 ircd_snprintf(0, retmask, sizeof(retmask), "%s!%s@%s", nick, user, host);
1212 /** send a banlist to a client for a channel
1214 * @param cptr Client to send the banlist to.
1215 * @param chptr Channel whose banlist to send.
1217 static void send_ban_list(struct Client* cptr, struct Channel* chptr)
1224 for (lp = chptr->banlist; lp; lp = lp->next)
1225 send_reply(cptr, RPL_BANLIST, chptr->chname, lp->banstr,
1228 send_reply(cptr, RPL_ENDOFBANLIST, chptr->chname);
1231 /** Remove bells and commas from channel name
1233 * @param cn Channel name to clean, modified in place.
1235 void clean_channelname(char *cn)
1239 for (i = 0; cn[i]; i++) {
1240 if (i >= IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN))
1241 || !IsChannelChar(cn[i])) {
1245 if (IsChannelLower(cn[i])) {
1246 cn[i] = ToLower(cn[i]);
1252 if ((unsigned char)(cn[i]) == 0xd0)
1253 cn[i] = (char) 0xf0;
1259 /** Get a channel block, creating if necessary.
1260 * Get Channel block for chname (and allocate a new channel
1261 * block, if it didn't exists before).
1263 * @param cptr Client joining the channel.
1264 * @param chname The name of the channel to join.
1265 * @param flag set to CGT_CREATE to create the channel if it doesn't
1268 * @returns NULL if the channel is invalid, doesn't exist and CGT_CREATE
1269 * wasn't specified or a pointer to the channel structure
1271 struct Channel *get_channel(struct Client *cptr, char *chname, ChannelGetType flag)
1273 struct Channel *chptr;
1276 if (EmptyString(chname))
1279 len = strlen(chname);
1280 if (MyUser(cptr) && len > CHANNELLEN)
1283 *(chname + CHANNELLEN) = '\0';
1285 if ((chptr = FindChannel(chname)))
1287 if (flag == CGT_CREATE)
1289 chptr = (struct Channel*) MyMalloc(sizeof(struct Channel) + len);
1291 ++UserStats.channels;
1292 memset(chptr, 0, sizeof(struct Channel));
1293 strcpy(chptr->chname, chname);
1294 if (GlobalChannelList)
1295 GlobalChannelList->prev = chptr;
1297 chptr->next = GlobalChannelList;
1298 chptr->creationtime = MyUser(cptr) ? TStime() : (time_t) 0;
1299 GlobalChannelList = chptr;
1305 /** invite a user to a channel.
1307 * Adds an invite for a user to a channel. Limits the number of invites
1308 * to FEAT_MAXCHANNELSPERUSER. Does not sent notification to the user.
1310 * @param cptr The client to be invited.
1311 * @param chptr The channel to be invited to.
1313 void add_invite(struct Client *cptr, struct Channel *chptr)
1315 struct SLink *inv, **tmp;
1317 del_invite(cptr, chptr);
1319 * Delete last link in chain if the list is max length
1321 assert(list_length((cli_user(cptr))->invited) == (cli_user(cptr))->invites);
1322 if ((cli_user(cptr))->invites >= feature_int(FEAT_MAXCHANNELSPERUSER))
1323 del_invite(cptr, (cli_user(cptr))->invited->value.chptr);
1325 * Add client to channel invite list
1328 inv->value.cptr = cptr;
1329 inv->next = chptr->invites;
1330 chptr->invites = inv;
1332 * Add channel to the end of the client invite list
1334 for (tmp = &((cli_user(cptr))->invited); *tmp; tmp = &((*tmp)->next));
1336 inv->value.chptr = chptr;
1339 (cli_user(cptr))->invites++;
1342 /** Delete an invite
1343 * Delete Invite block from channel invite list and client invite list
1345 * @param cptr Client pointer
1346 * @param chptr Channel pointer
1348 void del_invite(struct Client *cptr, struct Channel *chptr)
1350 struct SLink **inv, *tmp;
1352 for (inv = &(chptr->invites); (tmp = *inv); inv = &tmp->next)
1353 if (tmp->value.cptr == cptr)
1358 (cli_user(cptr))->invites--;
1362 for (inv = &((cli_user(cptr))->invited); (tmp = *inv); inv = &tmp->next)
1363 if (tmp->value.chptr == chptr)
1372 /** @page zombie Explanation of Zombies
1376 * A channel member is turned into a zombie when he is kicked from a
1377 * channel but his server has not acknowledged the kick. Servers that
1378 * see the member as a zombie can accept actions he performed before
1379 * being kicked, without allowing chanop operations from outsiders or
1380 * desyncing the network.
1388 * X --a--> A --b--> B --d--> D
1393 * Where `who' is being KICK-ed by a "KICK" message received by server 'A'
1394 * via 'a', or on server 'B' via either 'b' or 'c', or on server D via 'd'.
1396 * a) On server A : set CHFL_ZOMBIE for `who' (lp) and pass on the KICK.
1397 * Remove the user immediately when no users are left on the channel.
1398 * b) On server B : remove the user (who/lp) from the channel, send a
1399 * PART upstream (to A) and pass on the KICK.
1400 * c) KICKed by `client'; On server B : remove the user (who/lp) from the
1401 * channel, and pass on the KICK.
1402 * d) On server D : remove the user (who/lp) from the channel, and pass on
1406 * - Setting the ZOMBIE flag never hurts, we either remove the
1407 * client after that or we don't.
1408 * - The KICK message was already passed on, as should be in all cases.
1409 * - `who' is removed in all cases except case a) when users are left.
1410 * - A PART is only sent upstream in case b).
1416 * 1 --- 2 --- 3 --- 4 --- 5
1421 * We also need to turn 'who' into a zombie on servers 1 and 6,
1422 * because a KICK from 'who' (kicking someone else in that direction)
1423 * can arrive there afterward - which should not be bounced itself.
1424 * Therefore case a) also applies for servers 1 and 6.
1429 /** Turn a user on a channel into a zombie
1430 * This function turns a user into a zombie (see \ref zombie)
1432 * @param member The structure representing this user on this channel.
1433 * @param who The client that is being kicked.
1434 * @param cptr The connection the kick came from.
1435 * @param sptr The client that is doing the kicking.
1436 * @param chptr The channel the user is being kicked from.
1438 void make_zombie(struct Membership* member, struct Client* who,
1439 struct Client* cptr, struct Client* sptr, struct Channel* chptr)
1441 assert(0 != member);
1446 /* Default for case a): */
1449 /* Case b) or c) ?: */
1450 if (MyUser(who)) /* server 4 */
1452 if (IsServer(cptr)) /* Case b) ? */
1453 sendcmdto_one(who, CMD_PART, cptr, "%H", chptr);
1454 remove_user_from_channel(who, chptr);
1457 if (cli_from(who) == cptr) /* True on servers 1, 5 and 6 */
1459 struct Client *acptr = IsServer(sptr) ? sptr : (cli_user(sptr))->server;
1460 for (; acptr != &me; acptr = (cli_serv(acptr))->up)
1461 if (acptr == (cli_user(who))->server) /* Case d) (server 5) */
1463 remove_user_from_channel(who, chptr);
1468 /* Case a) (servers 1, 2, 3 and 6) */
1469 if (channel_all_zombies(chptr))
1470 remove_user_from_channel(who, chptr);
1472 /* XXX Can't actually call Debug here; if the channel is all zombies,
1473 * chptr will no longer exist when we get here.
1474 Debug((DEBUG_INFO, "%s is now a zombie on %s", who->name, chptr->chname));
1478 /** returns the number of zombies on a channel
1479 * @param chptr Channel to count zombies in.
1481 * @returns The number of zombies on the channel.
1483 int number_of_zombies(struct Channel *chptr)
1485 struct Membership* member;
1489 for (member = chptr->members; member; member = member->next_member) {
1490 if (IsZombie(member))
1496 /** Concatenate some strings together.
1497 * This helper function builds an argument string in strptr, consisting
1498 * of the original string, a space, and str1 and str2 concatenated (if,
1499 * of course, str2 is not NULL)
1501 * @param strptr The buffer to concatenate into
1502 * @param strptr_i modified offset to the position to modify
1503 * @param str1 The string to concatenate from.
1504 * @param str2 The second string to contatenate from.
1505 * @param c Charactor to separate the string from str1 and str2.
1508 build_string(char *strptr, int *strptr_i, const char *str1,
1509 const char *str2, char c)
1512 strptr[(*strptr_i)++] = c;
1515 strptr[(*strptr_i)++] = *(str1++);
1519 strptr[(*strptr_i)++] = *(str2++);
1521 strptr[(*strptr_i)] = '\0';
1524 /** Flush out the modes
1525 * This is the workhorse of our ModeBuf suite; this actually generates the
1526 * output MODE commands, HACK notices, or whatever. It's pretty complicated.
1528 * @param mbuf The mode buffer to flush
1529 * @param all If true, flush all modes, otherwise leave partial modes in the
1535 modebuf_flush_int(struct ModeBuf *mbuf, int all)
1537 /* we only need the flags that don't take args right now */
1538 static int flags[] = {
1539 /* MODE_CHANOP, 'o', */
1540 /* MODE_VOICE, 'v', */
1543 MODE_MODERATED, 'm',
1544 MODE_TOPICLIMIT, 't',
1545 MODE_INVITEONLY, 'i',
1546 MODE_NOPRIVMSGS, 'n',
1549 /* MODE_KEY, 'k', */
1550 /* MODE_BAN, 'b', */
1552 /* MODE_APASS, 'A', */
1553 /* MODE_UPASS, 'U', */
1556 static int local_flags[] = {
1557 MODE_WASDELJOINS, 'd',
1563 struct Client *app_source; /* where the MODE appears to come from */
1565 char addbuf[20], addbuf_local[20]; /* accumulates +psmtin, etc. */
1566 int addbuf_i = 0, addbuf_local_i = 0;
1567 char rembuf[20], rembuf_local[20]; /* accumulates -psmtin, etc. */
1568 int rembuf_i = 0, rembuf_local_i = 0;
1569 char *bufptr; /* we make use of indirection to simplify the code */
1572 char addstr[BUFSIZE]; /* accumulates MODE parameters to add */
1574 char remstr[BUFSIZE]; /* accumulates MODE parameters to remove */
1576 char *strptr; /* more indirection to simplify the code */
1579 int totalbuflen = BUFSIZE - 200; /* fuzz factor -- don't overrun buffer! */
1582 char limitbuf[20]; /* convert limits to strings */
1584 unsigned int limitdel = MODE_LIMIT;
1588 /* If the ModeBuf is empty, we have nothing to do */
1589 if (mbuf->mb_add == 0 && mbuf->mb_rem == 0 && mbuf->mb_count == 0)
1592 /* Ok, if we were given the OPMODE flag, or its a server, hide the source.
1594 if (mbuf->mb_dest & MODEBUF_DEST_OPMODE || IsServer(mbuf->mb_source) || IsMe(mbuf->mb_source))
1597 app_source = mbuf->mb_source;
1600 * Account for user we're bouncing; we have to get it in on the first
1601 * bounced MODE, or we could have problems
1603 if (mbuf->mb_dest & MODEBUF_DEST_DEOP)
1604 totalbuflen -= 6; /* numeric nick == 5, plus one space */
1606 /* Calculate the simple flags */
1607 for (flag_p = flags; flag_p[0]; flag_p += 2) {
1608 if (*flag_p & mbuf->mb_add)
1609 addbuf[addbuf_i++] = flag_p[1];
1610 else if (*flag_p & mbuf->mb_rem)
1611 rembuf[rembuf_i++] = flag_p[1];
1614 /* Some flags may be for local display only. */
1615 for (flag_p = local_flags; flag_p[0]; flag_p += 2) {
1616 if (*flag_p & mbuf->mb_add)
1617 addbuf_local[addbuf_local_i++] = flag_p[1];
1618 else if (*flag_p & mbuf->mb_rem)
1619 rembuf_local[rembuf_local_i++] = flag_p[1];
1622 /* Now go through the modes with arguments... */
1623 for (i = 0; i < mbuf->mb_count; i++) {
1624 if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
1626 bufptr_i = &addbuf_i;
1629 bufptr_i = &rembuf_i;
1632 if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE)) {
1633 tmp = strlen(cli_name(MB_CLIENT(mbuf, i)));
1635 if ((totalbuflen - IRCD_MAX(9, tmp)) <= 0) /* don't overflow buffer */
1636 MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1638 bufptr[(*bufptr_i)++] = MB_TYPE(mbuf, i) & MODE_CHANOP ? 'o' : 'v';
1639 totalbuflen -= IRCD_MAX(9, tmp) + 1;
1641 } else if (MB_TYPE(mbuf, i) & (MODE_BAN | MODE_APASS | MODE_UPASS)) {
1642 tmp = strlen(MB_STRING(mbuf, i));
1644 if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
1645 MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1648 switch(MB_TYPE(mbuf, i) & (MODE_BAN | MODE_APASS | MODE_UPASS))
1660 bufptr[(*bufptr_i)++] = mode_char;
1661 totalbuflen -= tmp + 1;
1663 } else if (MB_TYPE(mbuf, i) & MODE_KEY) {
1664 tmp = (mbuf->mb_dest & MODEBUF_DEST_NOKEY ? 1 :
1665 strlen(MB_STRING(mbuf, i)));
1667 if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
1668 MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1670 bufptr[(*bufptr_i)++] = 'k';
1671 totalbuflen -= tmp + 1;
1673 } else if (MB_TYPE(mbuf, i) & MODE_LIMIT) {
1674 /* if it's a limit, we also format the number */
1675 ircd_snprintf(0, limitbuf, sizeof(limitbuf), "%u", MB_UINT(mbuf, i));
1677 tmp = strlen(limitbuf);
1679 if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
1680 MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1682 bufptr[(*bufptr_i)++] = 'l';
1683 totalbuflen -= tmp + 1;
1688 /* terminate the mode strings */
1689 addbuf[addbuf_i] = '\0';
1690 rembuf[rembuf_i] = '\0';
1691 addbuf_local[addbuf_local_i] = '\0';
1692 rembuf_local[rembuf_local_i] = '\0';
1694 /* If we're building a user visible MODE or HACK... */
1695 if (mbuf->mb_dest & (MODEBUF_DEST_CHANNEL | MODEBUF_DEST_HACK2 |
1696 MODEBUF_DEST_HACK3 | MODEBUF_DEST_HACK4 |
1697 MODEBUF_DEST_LOG)) {
1698 /* Set up the parameter strings */
1704 for (i = 0; i < mbuf->mb_count; i++) {
1705 if (MB_TYPE(mbuf, i) & MODE_SAVE)
1708 if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
1710 strptr_i = &addstr_i;
1713 strptr_i = &remstr_i;
1716 /* deal with clients... */
1717 if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE))
1718 build_string(strptr, strptr_i, cli_name(MB_CLIENT(mbuf, i)), 0, ' ');
1720 /* deal with bans... */
1721 else if (MB_TYPE(mbuf, i) & MODE_BAN)
1722 build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
1724 /* deal with keys... */
1725 else if (MB_TYPE(mbuf, i) & MODE_KEY)
1726 build_string(strptr, strptr_i, mbuf->mb_dest & MODEBUF_DEST_NOKEY ?
1727 "*" : MB_STRING(mbuf, i), 0, ' ');
1729 /* deal with invisible passwords */
1730 else if (MB_TYPE(mbuf, i) & (MODE_APASS | MODE_UPASS))
1731 build_string(strptr, strptr_i, "*", 0, ' ');
1734 * deal with limit; note we cannot include the limit parameter if we're
1737 else if ((MB_TYPE(mbuf, i) & (MODE_ADD | MODE_LIMIT)) ==
1738 (MODE_ADD | MODE_LIMIT))
1739 build_string(strptr, strptr_i, limitbuf, 0, ' ');
1742 /* send the messages off to their destination */
1743 if (mbuf->mb_dest & MODEBUF_DEST_HACK2)
1744 sendto_opmask_butone(0, SNO_HACK2, "HACK(2): %s MODE %s %s%s%s%s%s%s "
1746 cli_name(feature_bool(FEAT_HIS_SNOTICES) ?
1747 mbuf->mb_source : app_source),
1748 mbuf->mb_channel->chname,
1749 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1750 addbuf, remstr, addstr,
1751 mbuf->mb_channel->creationtime);
1753 if (mbuf->mb_dest & MODEBUF_DEST_HACK3)
1754 sendto_opmask_butone(0, SNO_HACK3, "BOUNCE or HACK(3): %s MODE %s "
1755 "%s%s%s%s%s%s [%Tu]",
1756 cli_name(feature_bool(FEAT_HIS_SNOTICES) ?
1757 mbuf->mb_source : app_source),
1758 mbuf->mb_channel->chname, rembuf_i ? "-" : "",
1759 rembuf, addbuf_i ? "+" : "", addbuf, remstr, addstr,
1760 mbuf->mb_channel->creationtime);
1762 if (mbuf->mb_dest & MODEBUF_DEST_HACK4)
1763 sendto_opmask_butone(0, SNO_HACK4, "HACK(4): %s MODE %s %s%s%s%s%s%s "
1765 cli_name(feature_bool(FEAT_HIS_SNOTICES) ?
1766 mbuf->mb_source : app_source),
1767 mbuf->mb_channel->chname,
1768 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1769 addbuf, remstr, addstr,
1770 mbuf->mb_channel->creationtime);
1772 if (mbuf->mb_dest & MODEBUF_DEST_LOG)
1773 log_write(LS_OPERMODE, L_INFO, LOG_NOSNOTICE,
1774 "%#C OPMODE %H %s%s%s%s%s%s", mbuf->mb_source,
1775 mbuf->mb_channel, rembuf_i ? "-" : "", rembuf,
1776 addbuf_i ? "+" : "", addbuf, remstr, addstr);
1778 if (mbuf->mb_dest & MODEBUF_DEST_CHANNEL)
1779 sendcmdto_channel_butserv_butone(app_source, CMD_MODE, mbuf->mb_channel, NULL, 0,
1780 "%H %s%s%s%s%s%s%s%s", mbuf->mb_channel,
1781 rembuf_i || rembuf_local_i ? "-" : "",
1782 rembuf, rembuf_local,
1783 addbuf_i || addbuf_local_i ? "+" : "",
1784 addbuf, addbuf_local,
1788 /* Now are we supposed to propagate to other servers? */
1789 if (mbuf->mb_dest & MODEBUF_DEST_SERVER) {
1790 /* set up parameter string */
1797 * limit is supressed if we're removing it; we have to figure out which
1798 * direction is the direction for it to be removed, though...
1800 limitdel |= (mbuf->mb_dest & MODEBUF_DEST_BOUNCE) ? MODE_DEL : MODE_ADD;
1802 for (i = 0; i < mbuf->mb_count; i++) {
1803 if (MB_TYPE(mbuf, i) & MODE_SAVE)
1806 if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
1808 strptr_i = &addstr_i;
1811 strptr_i = &remstr_i;
1814 /* if we're changing oplevels we know the oplevel, pass it on */
1815 if (mbuf->mb_channel->mode.apass[0]
1816 && (MB_TYPE(mbuf, i) & MODE_CHANOP)
1817 && MB_OPLEVEL(mbuf, i) < MAXOPLEVEL)
1818 *strptr_i += ircd_snprintf(0, strptr + *strptr_i, BUFSIZE - *strptr_i,
1820 NumNick(MB_CLIENT(mbuf, i)),
1821 MB_OPLEVEL(mbuf, i));
1823 /* deal with other modes that take clients */
1824 else if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE))
1825 build_string(strptr, strptr_i, NumNick(MB_CLIENT(mbuf, i)), ' ');
1827 /* deal with modes that take strings */
1828 else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN | MODE_APASS | MODE_UPASS))
1829 build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
1832 * deal with the limit. Logic here is complicated; if HACK2 is set,
1833 * we're bouncing the mode, so sense is reversed, and we have to
1834 * include the original limit if it looks like it's being removed
1836 else if ((MB_TYPE(mbuf, i) & limitdel) == limitdel)
1837 build_string(strptr, strptr_i, limitbuf, 0, ' ');
1840 /* we were told to deop the source */
1841 if (mbuf->mb_dest & MODEBUF_DEST_DEOP) {
1842 addbuf[addbuf_i++] = 'o'; /* remember, sense is reversed */
1843 addbuf[addbuf_i] = '\0'; /* terminate the string... */
1844 build_string(addstr, &addstr_i, NumNick(mbuf->mb_source), ' ');
1846 /* mark that we've done this, so we don't do it again */
1847 mbuf->mb_dest &= ~MODEBUF_DEST_DEOP;
1850 if (mbuf->mb_dest & MODEBUF_DEST_OPMODE) {
1851 /* If OPMODE was set, we're propagating the mode as an OPMODE message */
1852 sendcmdto_serv_butone(mbuf->mb_source, CMD_OPMODE, mbuf->mb_connect,
1853 "%H %s%s%s%s%s%s", mbuf->mb_channel,
1854 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1855 addbuf, remstr, addstr);
1856 } else if (mbuf->mb_dest & MODEBUF_DEST_BOUNCE) {
1858 * If HACK2 was set, we're bouncing; we send the MODE back to the
1859 * connection we got it from with the senses reversed and a TS of 0;
1862 sendcmdto_one(&me, CMD_MODE, mbuf->mb_connect, "%H %s%s%s%s%s%s %Tu",
1863 mbuf->mb_channel, addbuf_i ? "-" : "", addbuf,
1864 rembuf_i ? "+" : "", rembuf, addstr, remstr,
1865 mbuf->mb_channel->creationtime);
1868 * We're propagating a normal MODE command to the rest of the network;
1869 * we send the actual channel TS unless this is a HACK3 or a HACK4
1871 if (IsServer(mbuf->mb_source))
1872 sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
1873 "%H %s%s%s%s%s%s %Tu", mbuf->mb_channel,
1874 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1875 addbuf, remstr, addstr,
1876 (mbuf->mb_dest & MODEBUF_DEST_HACK4) ? 0 :
1877 mbuf->mb_channel->creationtime);
1879 sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
1880 "%H %s%s%s%s%s%s", mbuf->mb_channel,
1881 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1882 addbuf, remstr, addstr);
1886 /* We've drained the ModeBuf... */
1891 /* reinitialize the mode-with-arg slots */
1892 for (i = 0; i < MAXMODEPARAMS; i++) {
1893 /* If we saved any, pack them down */
1894 if (MB_TYPE(mbuf, i) & MODE_SAVE) {
1895 mbuf->mb_modeargs[mbuf->mb_count] = mbuf->mb_modeargs[i];
1896 MB_TYPE(mbuf, mbuf->mb_count) &= ~MODE_SAVE; /* don't save anymore */
1898 if (mbuf->mb_count++ == i) /* don't overwrite our hard work */
1900 } else if (MB_TYPE(mbuf, i) & MODE_FREE)
1901 MyFree(MB_STRING(mbuf, i)); /* free string if needed */
1903 MB_TYPE(mbuf, i) = 0;
1904 MB_UINT(mbuf, i) = 0;
1907 /* If we're supposed to flush it all, do so--all hail tail recursion */
1908 if (all && mbuf->mb_count)
1909 return modebuf_flush_int(mbuf, 1);
1914 /** Initialise a modebuf
1915 * This routine just initializes a ModeBuf structure with the information
1916 * needed and the options given.
1918 * @param mbuf The mode buffer to initialise.
1919 * @param source The client that is performing the mode.
1921 * @param chan The channel that the mode is being performed upon.
1925 modebuf_init(struct ModeBuf *mbuf, struct Client *source,
1926 struct Client *connect, struct Channel *chan, unsigned int dest)
1931 assert(0 != source);
1935 if (IsLocalChannel(chan->chname)) dest &= ~MODEBUF_DEST_SERVER;
1939 mbuf->mb_source = source;
1940 mbuf->mb_connect = connect;
1941 mbuf->mb_channel = chan;
1942 mbuf->mb_dest = dest;
1945 /* clear each mode-with-parameter slot */
1946 for (i = 0; i < MAXMODEPARAMS; i++) {
1947 MB_TYPE(mbuf, i) = 0;
1948 MB_UINT(mbuf, i) = 0;
1952 /** Append a new mode to a modebuf
1953 * This routine simply adds modes to be added or deleted; do a binary OR
1954 * with either MODE_ADD or MODE_DEL
1956 * @param mbuf Mode buffer
1957 * @param mode MODE_ADD or MODE_DEL OR'd with MODE_PRIVATE etc.
1960 modebuf_mode(struct ModeBuf *mbuf, unsigned int mode)
1963 assert(0 != (mode & (MODE_ADD | MODE_DEL)));
1965 mode &= (MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET | MODE_MODERATED |
1966 MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS | MODE_REGONLY |
1967 MODE_DELJOINS | MODE_WASDELJOINS);
1969 if (!(mode & ~(MODE_ADD | MODE_DEL))) /* don't add empty modes... */
1972 if (mode & MODE_ADD) {
1973 mbuf->mb_rem &= ~mode;
1974 mbuf->mb_add |= mode;
1976 mbuf->mb_add &= ~mode;
1977 mbuf->mb_rem |= mode;
1981 /** Append a mode that takes an int argument to the modebuf
1983 * This routine adds a mode to be added or deleted that takes a unsigned
1984 * int parameter; mode may *only* be the relevant mode flag ORed with one
1985 * of MODE_ADD or MODE_DEL
1987 * @param mbuf The mode buffer to append to.
1988 * @param mode The mode to append.
1989 * @param uint The argument to the mode.
1992 modebuf_mode_uint(struct ModeBuf *mbuf, unsigned int mode, unsigned int uint)
1995 assert(0 != (mode & (MODE_ADD | MODE_DEL)));
1997 if (mode == (MODE_LIMIT | ((mbuf->mb_dest & MODEBUF_DEST_BOUNCE) ? MODE_ADD : MODE_DEL))) {
1998 mbuf->mb_rem |= mode;
2001 MB_TYPE(mbuf, mbuf->mb_count) = mode;
2002 MB_UINT(mbuf, mbuf->mb_count) = uint;
2004 /* when we've reached the maximal count, flush the buffer */
2005 if (++mbuf->mb_count >=
2006 (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
2007 modebuf_flush_int(mbuf, 0);
2010 /** append a string mode
2011 * This routine adds a mode to be added or deleted that takes a string
2012 * parameter; mode may *only* be the relevant mode flag ORed with one of
2013 * MODE_ADD or MODE_DEL
2015 * @param mbuf The mode buffer to append to.
2016 * @param mode The mode to append.
2017 * @param string The string parameter to append.
2018 * @param free If the string should be free'd later.
2021 modebuf_mode_string(struct ModeBuf *mbuf, unsigned int mode, char *string,
2025 assert(0 != (mode & (MODE_ADD | MODE_DEL)));
2027 MB_TYPE(mbuf, mbuf->mb_count) = mode | (free ? MODE_FREE : 0);
2028 MB_STRING(mbuf, mbuf->mb_count) = string;
2030 /* when we've reached the maximal count, flush the buffer */
2031 if (++mbuf->mb_count >=
2032 (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
2033 modebuf_flush_int(mbuf, 0);
2036 /** Append a mode on a client to a modebuf.
2037 * This routine adds a mode to be added or deleted that takes a client
2038 * parameter; mode may *only* be the relevant mode flag ORed with one of
2039 * MODE_ADD or MODE_DEL
2041 * @param mbuf The modebuf to append the mode to.
2042 * @param mode The mode to append.
2043 * @param client The client argument to append.
2044 * @param oplevel The oplevel the user had or will have
2047 modebuf_mode_client(struct ModeBuf *mbuf, unsigned int mode,
2048 struct Client *client, int oplevel)
2051 assert(0 != (mode & (MODE_ADD | MODE_DEL)));
2053 MB_TYPE(mbuf, mbuf->mb_count) = mode;
2054 MB_CLIENT(mbuf, mbuf->mb_count) = client;
2055 MB_OPLEVEL(mbuf, mbuf->mb_count) = oplevel;
2057 /* when we've reached the maximal count, flush the buffer */
2058 if (++mbuf->mb_count >=
2059 (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
2060 modebuf_flush_int(mbuf, 0);
2063 /** The exported binding for modebuf_flush()
2065 * @param mbuf The mode buffer to flush.
2067 * @see modebuf_flush_int()
2070 modebuf_flush(struct ModeBuf *mbuf)
2072 struct Membership *memb;
2074 /* Check if MODE_WASDELJOINS should be set */
2075 if (!(mbuf->mb_channel->mode.mode & (MODE_DELJOINS | MODE_WASDELJOINS))
2076 && (mbuf->mb_rem & MODE_DELJOINS)) {
2077 for (memb = mbuf->mb_channel->members; memb; memb = memb->next_member) {
2078 if (IsDelayedJoin(memb)) {
2079 mbuf->mb_channel->mode.mode |= MODE_WASDELJOINS;
2080 mbuf->mb_add |= MODE_WASDELJOINS;
2081 mbuf->mb_rem &= ~MODE_WASDELJOINS;
2087 return modebuf_flush_int(mbuf, 1);
2090 /* This extracts the simple modes contained in mbuf
2092 * @param mbuf The mode buffer to extract the modes from.
2093 * @param buf The string buffer to write the modes into.
2096 modebuf_extract(struct ModeBuf *mbuf, char *buf)
2098 static int flags[] = {
2099 /* MODE_CHANOP, 'o', */
2100 /* MODE_VOICE, 'v', */
2103 MODE_MODERATED, 'm',
2104 MODE_TOPICLIMIT, 't',
2105 MODE_INVITEONLY, 'i',
2106 MODE_NOPRIVMSGS, 'n',
2110 /* MODE_BAN, 'b', */
2117 int i, bufpos = 0, len;
2119 char *key = 0, limitbuf[20];
2120 char *apass = 0, *upass = 0;
2129 for (i = 0; i < mbuf->mb_count; i++) { /* find keys and limits */
2130 if (MB_TYPE(mbuf, i) & MODE_ADD) {
2131 add |= MB_TYPE(mbuf, i) & (MODE_KEY | MODE_LIMIT | MODE_APASS | MODE_UPASS);
2133 if (MB_TYPE(mbuf, i) & MODE_KEY) /* keep strings */
2134 key = MB_STRING(mbuf, i);
2135 else if (MB_TYPE(mbuf, i) & MODE_LIMIT)
2136 ircd_snprintf(0, limitbuf, sizeof(limitbuf), "%u", MB_UINT(mbuf, i));
2137 else if (MB_TYPE(mbuf, i) & MODE_UPASS)
2138 upass = MB_STRING(mbuf, i);
2139 else if (MB_TYPE(mbuf, i) & MODE_APASS)
2140 apass = MB_STRING(mbuf, i);
2147 buf[bufpos++] = '+'; /* start building buffer */
2149 for (flag_p = flags; flag_p[0]; flag_p += 2)
2151 buf[bufpos++] = flag_p[1];
2153 for (i = 0, len = bufpos; i < len; i++) {
2155 build_string(buf, &bufpos, key, 0, ' ');
2156 else if (buf[i] == 'l')
2157 build_string(buf, &bufpos, limitbuf, 0, ' ');
2158 else if (buf[i] == 'U')
2159 build_string(buf, &bufpos, upass, 0, ' ');
2160 else if (buf[i] == 'A')
2161 build_string(buf, &bufpos, apass, 0, ' ');
2169 /** Simple function to invalidate bans
2171 * This function sets all bans as being valid.
2173 * @param chan The channel to operate on.
2176 mode_ban_invalidate(struct Channel *chan)
2178 struct Membership *member;
2180 for (member = chan->members; member; member = member->next_member)
2181 ClearBanValid(member);
2184 /** Simple function to drop invite structures
2186 * Remove all the invites on the channel.
2188 * @param chan Channel to remove invites from.
2192 mode_invite_clear(struct Channel *chan)
2194 while (chan->invites)
2195 del_invite(chan->invites->value.cptr, chan);
2198 /* What we've done for mode_parse so far... */
2199 #define DONE_LIMIT 0x01 /**< We've set the limit */
2200 #define DONE_KEY 0x02 /**< We've set the key */
2201 #define DONE_BANLIST 0x04 /**< We've sent the ban list */
2202 #define DONE_NOTOPER 0x08 /**< We've sent a "Not oper" error */
2203 #define DONE_BANCLEAN 0x10 /**< We've cleaned bans... */
2204 #define DONE_UPASS 0x20 /**< We've set user pass */
2205 #define DONE_APASS 0x40 /**< We've set admin pass */
2208 struct ModeBuf *mbuf;
2209 struct Client *cptr;
2210 struct Client *sptr;
2211 struct Channel *chptr;
2212 struct Membership *member;
2223 struct Ban banlist[MAXPARA];
2226 unsigned short oplevel;
2227 struct Client *client;
2228 } cli_change[MAXPARA];
2231 /** Helper function to send "Not oper" or "Not member" messages
2232 * Here's a helper function to deal with sending along "Not oper" or
2233 * "Not member" messages
2235 * @param state Parsing State object
2238 send_notoper(struct ParseState *state)
2240 if (state->done & DONE_NOTOPER)
2243 send_reply(state->sptr, (state->flags & MODE_PARSE_NOTOPER) ?
2244 ERR_CHANOPRIVSNEEDED : ERR_NOTONCHANNEL, state->chptr->chname);
2246 state->done |= DONE_NOTOPER;
2250 * Helper function to convert limits
2252 * @param state Parsing state object.
2256 mode_parse_limit(struct ParseState *state, int *flag_p)
2258 unsigned int t_limit;
2260 if (state->dir == MODE_ADD) { /* convert arg only if adding limit */
2261 if (MyUser(state->sptr) && state->max_args <= 0) /* too many args? */
2264 if (state->parc <= 0) { /* warn if not enough args */
2265 if (MyUser(state->sptr))
2266 need_more_params(state->sptr, "MODE +l");
2270 t_limit = strtoul(state->parv[state->args_used++], 0, 10); /* grab arg */
2274 if ((int)t_limit<0) /* don't permit a negative limit */
2277 if (!(state->flags & MODE_PARSE_WIPEOUT) &&
2278 (!t_limit || t_limit == state->chptr->mode.limit))
2281 t_limit = state->chptr->mode.limit;
2283 /* If they're not an oper, they can't change modes */
2284 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2285 send_notoper(state);
2289 /* Can't remove a limit that's not there */
2290 if (state->dir == MODE_DEL && !state->chptr->mode.limit)
2293 /* Skip if this is a burst and a lower limit than this is set already */
2294 if ((state->flags & MODE_PARSE_BURST) &&
2295 (state->chptr->mode.mode & flag_p[0]) &&
2296 (state->chptr->mode.limit < t_limit))
2299 if (state->done & DONE_LIMIT) /* allow limit to be set only once */
2301 state->done |= DONE_LIMIT;
2306 modebuf_mode_uint(state->mbuf, state->dir | flag_p[0], t_limit);
2308 if (state->flags & MODE_PARSE_SET) { /* set the limit */
2309 if (state->dir & MODE_ADD) {
2310 state->chptr->mode.mode |= flag_p[0];
2311 state->chptr->mode.limit = t_limit;
2313 state->chptr->mode.mode &= ~flag_p[0];
2314 state->chptr->mode.limit = 0;
2319 /** Helper function to clean key-like parameters. */
2325 while (*s > ' ' && *s != ':' && *s != ',' && t_len--)
2331 * Helper function to convert keys
2334 mode_parse_key(struct ParseState *state, int *flag_p)
2338 if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2341 if (state->parc <= 0) { /* warn if not enough args */
2342 if (MyUser(state->sptr))
2343 need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +k" :
2348 t_str = state->parv[state->args_used++]; /* grab arg */
2352 /* If they're not an oper, they can't change modes */
2353 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2354 send_notoper(state);
2358 if (state->done & DONE_KEY) /* allow key to be set only once */
2360 state->done |= DONE_KEY;
2362 /* clean up the key string */
2364 if (!*t_str || *t_str == ':') { /* warn if empty */
2365 if (MyUser(state->sptr))
2366 need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +k" :
2374 /* Skip if this is a burst, we have a key already and the new key is
2375 * after the old one alphabetically */
2376 if ((state->flags & MODE_PARSE_BURST) &&
2377 *(state->chptr->mode.key) &&
2378 ircd_strcmp(state->chptr->mode.key, t_str) <= 0)
2381 /* can't add a key if one is set, nor can one remove the wrong key */
2382 if (!(state->flags & MODE_PARSE_FORCE))
2383 if ((state->dir == MODE_ADD && *state->chptr->mode.key) ||
2384 (state->dir == MODE_DEL &&
2385 ircd_strcmp(state->chptr->mode.key, t_str))) {
2386 send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
2390 if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
2391 !ircd_strcmp(state->chptr->mode.key, t_str))
2392 return; /* no key change */
2394 if (state->flags & MODE_PARSE_BOUNCE) {
2395 if (*state->chptr->mode.key) /* reset old key */
2396 modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
2397 state->chptr->mode.key, 0);
2398 else /* remove new bogus key */
2399 modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
2400 } else /* send new key */
2401 modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
2403 if (state->flags & MODE_PARSE_SET) {
2404 if (state->dir == MODE_DEL) /* remove the old key */
2405 *state->chptr->mode.key = '\0';
2406 else if (!state->chptr->mode.key[0]
2407 || ircd_strcmp(t_str, state->chptr->mode.key) < 0)
2408 ircd_strncpy(state->chptr->mode.key, t_str, KEYLEN);
2413 * Helper function to convert user passes
2416 mode_parse_upass(struct ParseState *state, int *flag_p)
2420 if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2423 if (state->parc <= 0) { /* warn if not enough args */
2424 if (MyUser(state->sptr))
2425 need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +U" :
2430 t_str = state->parv[state->args_used++]; /* grab arg */
2434 /* If they're not an oper, they can't change modes */
2435 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2436 send_notoper(state);
2440 /* If a non-service user is trying to force it, refuse. */
2441 if (state->flags & MODE_PARSE_FORCE && MyUser(state->sptr)
2442 && !HasPriv(state->sptr, PRIV_APASS_OPMODE)) {
2443 send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
2444 state->chptr->chname);
2448 /* If they are not the channel manager, they are not allowed to change it */
2449 if (MyUser(state->sptr) && !(state->flags & MODE_PARSE_FORCE || IsChannelManager(state->member))) {
2450 if (*state->chptr->mode.apass) {
2451 send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
2452 state->chptr->chname);
2454 send_reply(state->sptr, ERR_NOMANAGER, state->chptr->chname,
2455 (TStime() - state->chptr->creationtime < 172800) ?
2456 "approximately 4-5 minutes" : "approximately 48 hours");
2461 if (state->done & DONE_UPASS) /* allow upass to be set only once */
2463 state->done |= DONE_UPASS;
2465 /* clean up the upass string */
2467 if (!*t_str || *t_str == ':') { /* warn if empty */
2468 if (MyUser(state->sptr))
2469 need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +U" :
2477 if (!(state->flags & MODE_PARSE_FORCE)) {
2478 /* can't add the upass while apass is not set */
2479 if (state->dir == MODE_ADD && !*state->chptr->mode.apass) {
2480 send_reply(state->sptr, ERR_UPASSNOTSET, state->chptr->chname, state->chptr->chname);
2483 /* cannot set a +U password that is the same as +A */
2484 if (state->dir == MODE_ADD && !ircd_strcmp(state->chptr->mode.apass, t_str)) {
2485 send_reply(state->sptr, ERR_UPASS_SAME_APASS, state->chptr->chname);
2488 /* can't add a upass if one is set, nor can one remove the wrong upass */
2489 if ((state->dir == MODE_ADD && *state->chptr->mode.upass) ||
2490 (state->dir == MODE_DEL &&
2491 ircd_strcmp(state->chptr->mode.upass, t_str))) {
2492 send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
2497 if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
2498 !ircd_strcmp(state->chptr->mode.upass, t_str))
2499 return; /* no upass change */
2501 if (state->flags & MODE_PARSE_BOUNCE) {
2502 if (*state->chptr->mode.upass) /* reset old upass */
2503 modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
2504 state->chptr->mode.upass, 0);
2505 else /* remove new bogus upass */
2506 modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
2507 } else /* send new upass */
2508 modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
2510 if (state->flags & MODE_PARSE_SET) {
2511 if (state->dir == MODE_DEL) /* remove the old upass */
2512 *state->chptr->mode.upass = '\0';
2513 else if (state->chptr->mode.upass[0] == '\0'
2514 || ircd_strcmp(t_str, state->chptr->mode.upass) < 0)
2515 ircd_strncpy(state->chptr->mode.upass, t_str, KEYLEN);
2520 * Helper function to convert admin passes
2523 mode_parse_apass(struct ParseState *state, int *flag_p)
2525 struct Membership *memb;
2528 if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2531 if (state->parc <= 0) { /* warn if not enough args */
2532 if (MyUser(state->sptr))
2533 need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +A" :
2538 t_str = state->parv[state->args_used++]; /* grab arg */
2542 /* If they're not an oper, they can't change modes */
2543 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2544 send_notoper(state);
2548 if (MyUser(state->sptr)) {
2549 if (state->flags & MODE_PARSE_FORCE) {
2550 /* If an unprivileged oper is trying to force it, refuse. */
2551 if (!HasPriv(state->sptr, PRIV_APASS_OPMODE)) {
2552 send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
2553 state->chptr->chname);
2557 /* If they are not the channel manager, they are not allowed to change it. */
2558 if (!IsChannelManager(state->member)) {
2559 if (*state->chptr->mode.apass) {
2560 send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
2561 state->chptr->chname);
2563 send_reply(state->sptr, ERR_NOMANAGER, state->chptr->chname,
2564 (TStime() - state->chptr->creationtime < 172800) ?
2565 "approximately 4-5 minutes" : "approximately 48 hours");
2569 /* Can't remove the Apass while Upass is still set. */
2570 if (state->dir == MODE_DEL && *state->chptr->mode.upass) {
2571 send_reply(state->sptr, ERR_UPASSSET, state->chptr->chname, state->chptr->chname);
2574 /* Can't add an Apass if one is set, nor can one remove the wrong Apass. */
2575 if ((state->dir == MODE_ADD && *state->chptr->mode.apass) ||
2576 (state->dir == MODE_DEL && ircd_strcmp(state->chptr->mode.apass, t_str))) {
2577 send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
2582 /* Forbid removing the Apass if the channel is older than 48 hours
2583 * unless an oper is doing it. */
2584 if (TStime() - state->chptr->creationtime >= 172800
2585 && state->dir == MODE_DEL
2586 && !IsAnOper(state->sptr)) {
2587 send_reply(state->sptr, ERR_CHANSECURED, state->chptr->chname);
2592 if (state->done & DONE_APASS) /* allow apass to be set only once */
2594 state->done |= DONE_APASS;
2596 /* clean up the apass string */
2598 if (!*t_str || *t_str == ':') { /* warn if empty */
2599 if (MyUser(state->sptr))
2600 need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +A" :
2608 if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
2609 !ircd_strcmp(state->chptr->mode.apass, t_str))
2610 return; /* no apass change */
2612 if (state->flags & MODE_PARSE_BOUNCE) {
2613 if (*state->chptr->mode.apass) /* reset old apass */
2614 modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
2615 state->chptr->mode.apass, 0);
2616 else /* remove new bogus apass */
2617 modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
2618 } else /* send new apass */
2619 modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
2621 if (state->flags & MODE_PARSE_SET) {
2622 if (state->dir == MODE_ADD) { /* set the new apass */
2623 /* Only accept the new apass if there is no current apass
2624 * (e.g. when a user sets it) or the new one is "less" than the
2625 * old (for resolving conflicts during burst).
2627 if (state->chptr->mode.apass[0] == '\0'
2628 || ircd_strcmp(t_str, state->chptr->mode.apass) < 0)
2629 ircd_strncpy(state->chptr->mode.apass, t_str, KEYLEN);
2630 /* Make it VERY clear to the user that this is a one-time password */
2631 if (MyUser(state->sptr)) {
2632 send_reply(state->sptr, RPL_APASSWARN_SET, state->chptr->mode.apass);
2633 send_reply(state->sptr, RPL_APASSWARN_SECRET, state->chptr->chname,
2634 state->chptr->mode.apass);
2636 /* Give the channel manager level 0 ops.
2637 There should not be tested for IsChannelManager here because
2638 on the local server it is impossible to set the apass if one
2639 isn't a channel manager and remote servers might need to sync
2640 the oplevel here: when someone creates a channel (and becomes
2641 channel manager) during a net.break, and only sets the Apass
2642 after the net rejoined, they will have oplevel MAXOPLEVEL on
2643 all remote servers. */
2645 SetOpLevel(state->member, 0);
2646 } else { /* remove the old apass */
2647 *state->chptr->mode.apass = '\0';
2648 /* Clear Upass so that there is never a Upass set when a zannel is burst. */
2649 *state->chptr->mode.upass = '\0';
2650 if (MyUser(state->sptr))
2651 send_reply(state->sptr, RPL_APASSWARN_CLEAR);
2652 /* Revert everyone to MAXOPLEVEL. */
2653 for (memb = state->chptr->members; memb; memb = memb->next_member) {
2654 if (memb->status & MODE_CHANOP)
2655 SetOpLevel(memb, MAXOPLEVEL);
2661 /** Compare one ban's extent to another.
2662 * This works very similarly to mmatch() but it knows about CIDR masks
2663 * and ban exceptions. If both bans are CIDR-based, compare their
2664 * address bits; otherwise, use mmatch().
2665 * @param[in] old_ban One ban.
2666 * @param[in] new_ban Another ban.
2667 * @return Zero if \a old_ban is a superset of \a new_ban, non-zero otherwise.
2670 bmatch(struct Ban *old_ban, struct Ban *new_ban)
2673 assert(old_ban != NULL);
2674 assert(new_ban != NULL);
2675 /* A ban is never treated as a superset of an exception. */
2676 if (!(old_ban->flags & BAN_EXCEPTION)
2677 && (new_ban->flags & BAN_EXCEPTION))
2679 /* If either is not an address mask, match the text masks. */
2680 if ((old_ban->flags & new_ban->flags & BAN_IPMASK) == 0)
2681 return mmatch(old_ban->banstr, new_ban->banstr);
2682 /* If the old ban has a longer prefix than new, it cannot be a superset. */
2683 if (old_ban->addrbits > new_ban->addrbits)
2685 /* Compare the masks before the hostname part. */
2686 old_ban->banstr[old_ban->nu_len] = new_ban->banstr[new_ban->nu_len] = '\0';
2687 res = mmatch(old_ban->banstr, new_ban->banstr);
2688 old_ban->banstr[old_ban->nu_len] = new_ban->banstr[new_ban->nu_len] = '@';
2691 /* Compare the addresses. */
2692 return !ipmask_check(&new_ban->address, &old_ban->address, old_ban->addrbits);
2695 /** Add a ban from a ban list and mark bans that should be removed
2696 * because they overlap.
2698 * There are three invariants for a ban list. First, no ban may be
2699 * more specific than another ban. Second, no exception may be more
2700 * specific than another exception. Finally, no ban may be more
2701 * specific than any exception.
2703 * @param[in,out] banlist Pointer to head of list.
2704 * @param[in] newban Ban (or exception) to add (or remove).
2705 * @param[in] do_free If non-zero, free \a newban on failure.
2706 * @return Zero if \a newban could be applied, non-zero if not.
2708 int apply_ban(struct Ban **banlist, struct Ban *newban, int do_free)
2713 assert(newban->flags & (BAN_ADD|BAN_DEL));
2714 if (newban->flags & BAN_ADD) {
2716 /* If a less specific entry is found, fail. */
2717 for (ban = *banlist; ban; ban = ban->next) {
2718 if (!bmatch(ban, newban)) {
2723 if (!(ban->flags & (BAN_OVERLAPPED|BAN_DEL))) {
2725 totlen += strlen(ban->banstr);
2728 /* Mark more specific entries and add this one to the end of the list. */
2729 while ((ban = *banlist) != NULL) {
2730 if (!bmatch(newban, ban)) {
2731 ban->flags |= BAN_OVERLAPPED | BAN_DEL;
2733 banlist = &ban->next;
2737 } else if (newban->flags & BAN_DEL) {
2738 size_t remove_count = 0;
2739 /* Mark more specific entries. */
2740 for (ban = *banlist; ban; ban = ban->next) {
2741 if (!bmatch(newban, ban)) {
2742 ban->flags |= BAN_OVERLAPPED | BAN_DEL;
2748 /* If no matches were found, fail. */
2759 * Helper function to convert bans
2762 mode_parse_ban(struct ParseState *state, int *flag_p)
2765 struct Ban *ban, *newban;
2767 if (state->parc <= 0) { /* Not enough args, send ban list */
2768 if (MyUser(state->sptr) && !(state->done & DONE_BANLIST)) {
2769 send_ban_list(state->sptr, state->chptr);
2770 state->done |= DONE_BANLIST;
2776 if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2779 t_str = state->parv[state->args_used++]; /* grab arg */
2783 /* If they're not an oper, they can't change modes */
2784 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2785 send_notoper(state);
2789 if ((s = strchr(t_str, ' ')))
2792 if (!*t_str || *t_str == ':') { /* warn if empty */
2793 if (MyUser(state->sptr))
2794 need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +b" :
2799 /* Clear all ADD/DEL/OVERLAPPED flags from ban list. */
2800 if (!(state->done & DONE_BANCLEAN)) {
2801 for (ban = state->chptr->banlist; ban; ban = ban->next)
2802 ban->flags &= ~(BAN_ADD | BAN_DEL | BAN_OVERLAPPED);
2803 state->done |= DONE_BANCLEAN;
2806 /* remember the ban for the moment... */
2807 newban = state->banlist + (state->numbans++);
2809 newban->flags = ((state->dir == MODE_ADD) ? BAN_ADD : BAN_DEL)
2810 | (*flag_p == MODE_BAN ? 0 : BAN_EXCEPTION);
2811 set_ban_mask(newban, collapse(pretty_mask(t_str)));
2812 ircd_strncpy(newban->who, IsUser(state->sptr) ? cli_name(state->sptr) : "*", NICKLEN);
2813 newban->when = TStime();
2814 apply_ban(&state->chptr->banlist, newban, 0);
2818 * This is the bottom half of the ban processor
2821 mode_process_bans(struct ParseState *state)
2823 struct Ban *ban, *newban, *prevban, *nextban;
2829 for (prevban = 0, ban = state->chptr->banlist; ban; ban = nextban) {
2831 banlen = strlen(ban->banstr);
2833 nextban = ban->next;
2835 if ((ban->flags & (BAN_DEL | BAN_ADD)) == (BAN_DEL | BAN_ADD)) {
2837 prevban->next = 0; /* Break the list; ban isn't a real ban */
2839 state->chptr->banlist = 0;
2845 } else if (ban->flags & BAN_DEL) { /* Deleted a ban? */
2847 DupString(bandup, ban->banstr);
2848 modebuf_mode_string(state->mbuf, MODE_DEL | MODE_BAN,
2851 if (state->flags & MODE_PARSE_SET) { /* Ok, make it take effect */
2852 if (prevban) /* clip it out of the list... */
2853 prevban->next = ban->next;
2855 state->chptr->banlist = ban->next;
2862 continue; /* next ban; keep prevban like it is */
2864 ban->flags &= BAN_IPMASK; /* unset other flags */
2865 } else if (ban->flags & BAN_ADD) { /* adding a ban? */
2867 prevban->next = 0; /* Break the list; ban isn't a real ban */
2869 state->chptr->banlist = 0;
2871 /* If we're supposed to ignore it, do so. */
2872 if (ban->flags & BAN_OVERLAPPED &&
2873 !(state->flags & MODE_PARSE_BOUNCE)) {
2877 if (state->flags & MODE_PARSE_SET && MyUser(state->sptr) &&
2878 (len > (feature_int(FEAT_AVBANLEN) * feature_int(FEAT_MAXBANS)) ||
2879 count > feature_int(FEAT_MAXBANS))) {
2880 send_reply(state->sptr, ERR_BANLISTFULL, state->chptr->chname,
2886 /* add the ban to the buffer */
2887 DupString(bandup, ban->banstr);
2888 modebuf_mode_string(state->mbuf, MODE_ADD | MODE_BAN,
2891 if (state->flags & MODE_PARSE_SET) { /* create a new ban */
2892 newban = make_ban(ban->banstr);
2893 strcpy(newban->who, ban->who);
2894 newban->when = ban->when;
2895 newban->flags = ban->flags & BAN_IPMASK;
2897 newban->next = state->chptr->banlist; /* and link it in */
2898 state->chptr->banlist = newban;
2907 } /* for (prevban = 0, ban = state->chptr->banlist; ban; ban = nextban) { */
2909 if (changed) /* if we changed the ban list, we must invalidate the bans */
2910 mode_ban_invalidate(state->chptr);
2914 * Helper function to process client changes
2917 mode_parse_client(struct ParseState *state, int *flag_p)
2921 struct Client *acptr;
2922 struct Membership *member;
2923 int oplevel = MAXOPLEVEL + 1;
2927 if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2930 if (state->parc <= 0) /* return if not enough args */
2933 t_str = state->parv[state->args_used++]; /* grab arg */
2937 /* If they're not an oper, they can't change modes */
2938 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2939 send_notoper(state);
2943 if (MyUser(state->sptr)) {
2944 colon = strchr(t_str, ':');
2945 if (colon != NULL) {
2947 req_oplevel = atoi(colon);
2948 if (!(state->flags & MODE_PARSE_FORCE)
2950 && (req_oplevel < OpLevel(state->member)
2951 || (req_oplevel == OpLevel(state->member)
2952 && OpLevel(state->member) < MAXOPLEVEL)
2953 || req_oplevel > MAXOPLEVEL))
2954 send_reply(state->sptr, ERR_NOTLOWEROPLEVEL,
2955 t_str, state->chptr->chname,
2956 OpLevel(state->member), req_oplevel, "op",
2957 OpLevel(state->member) == req_oplevel ? "the same" : "a higher");
2958 else if (req_oplevel <= MAXOPLEVEL)
2959 oplevel = req_oplevel;
2961 /* find client we're manipulating */
2962 acptr = find_chasing(state->sptr, t_str, NULL);
2964 if (t_str[5] == ':') {
2966 oplevel = atoi(t_str + 6);
2968 acptr = findNUser(t_str);
2972 return; /* find_chasing() already reported an error to the user */
2974 for (i = 0; i < MAXPARA; i++) /* find an element to stick them in */
2975 if (!state->cli_change[i].flag || (state->cli_change[i].client == acptr &&
2976 state->cli_change[i].flag & flag_p[0]))
2977 break; /* found a slot */
2979 /* If we are going to bounce this deop, mark the correct oplevel. */
2980 if (state->flags & MODE_PARSE_BOUNCE
2981 && state->dir == MODE_DEL
2982 && flag_p[0] == MODE_CHANOP
2983 && (member = find_member_link(state->chptr, acptr)))
2984 oplevel = OpLevel(member);
2986 /* Store what we're doing to them */
2987 state->cli_change[i].flag = state->dir | flag_p[0];
2988 state->cli_change[i].oplevel = oplevel;
2989 state->cli_change[i].client = acptr;
2993 * Helper function to process the changed client list
2996 mode_process_clients(struct ParseState *state)
2999 struct Membership *member;
3001 for (i = 0; state->cli_change[i].flag; i++) {
3002 assert(0 != state->cli_change[i].client);
3004 /* look up member link */
3005 if (!(member = find_member_link(state->chptr,
3006 state->cli_change[i].client)) ||
3007 (MyUser(state->sptr) && IsZombie(member))) {
3008 if (MyUser(state->sptr))
3009 send_reply(state->sptr, ERR_USERNOTINCHANNEL,
3010 cli_name(state->cli_change[i].client),
3011 state->chptr->chname);
3015 if ((state->cli_change[i].flag & MODE_ADD &&
3016 (state->cli_change[i].flag & member->status)) ||
3017 (state->cli_change[i].flag & MODE_DEL &&
3018 !(state->cli_change[i].flag & member->status)))
3019 continue; /* no change made, don't do anything */
3021 /* see if the deop is allowed */
3022 if ((state->cli_change[i].flag & (MODE_DEL | MODE_CHANOP)) ==
3023 (MODE_DEL | MODE_CHANOP)) {
3024 /* prevent +k users from being deopped */
3025 if (IsChannelService(state->cli_change[i].client)) {
3026 if (state->flags & MODE_PARSE_FORCE) /* it was forced */
3027 sendto_opmask_butone(0, SNO_HACK4, "Deop of +k user on %H by %s",
3029 (IsServer(state->sptr) ? cli_name(state->sptr) :
3030 cli_name((cli_user(state->sptr))->server)));
3032 else if (MyUser(state->sptr) && state->flags & MODE_PARSE_SET) {
3033 send_reply(state->sptr, ERR_ISCHANSERVICE,
3034 cli_name(state->cli_change[i].client),
3035 state->chptr->chname);
3040 /* check deop for local user */
3041 if (MyUser(state->sptr)) {
3043 /* don't allow local opers to be deopped on local channels */
3044 if (state->cli_change[i].client != state->sptr &&
3045 IsLocalChannel(state->chptr->chname) &&
3046 HasPriv(state->cli_change[i].client, PRIV_DEOP_LCHAN)) {
3047 send_reply(state->sptr, ERR_ISOPERLCHAN,
3048 cli_name(state->cli_change[i].client),
3049 state->chptr->chname);
3053 /* Forbid deopping other members with an oplevel less than
3054 * one's own level, and other members with an oplevel the same
3055 * as one's own unless both are at MAXOPLEVEL. */
3056 if (state->sptr != state->cli_change[i].client
3058 && ((OpLevel(member) < OpLevel(state->member))
3059 || (OpLevel(member) == OpLevel(state->member)
3060 && OpLevel(member) < MAXOPLEVEL))) {
3061 int equal = (OpLevel(member) == OpLevel(state->member));
3062 send_reply(state->sptr, ERR_NOTLOWEROPLEVEL,
3063 cli_name(state->cli_change[i].client),
3064 state->chptr->chname,
3065 OpLevel(state->member), OpLevel(member),
3066 "deop", equal ? "the same" : "a higher");
3072 /* set op-level of member being opped */
3073 if ((state->cli_change[i].flag & (MODE_ADD | MODE_CHANOP)) ==
3074 (MODE_ADD | MODE_CHANOP)) {
3075 /* If a valid oplevel was specified, use it.
3076 * Otherwise, if being opped by an outsider, get MAXOPLEVEL.
3077 * Otherwise, if not an apass channel, or state->member has
3078 * MAXOPLEVEL, get oplevel MAXOPLEVEL.
3079 * Otherwise, get state->member's oplevel+1.
3081 if (state->cli_change[i].oplevel <= MAXOPLEVEL)
3082 SetOpLevel(member, state->cli_change[i].oplevel);
3083 else if (!state->member)
3084 SetOpLevel(member, MAXOPLEVEL);
3085 else if (!state->chptr->mode.apass[0] || OpLevel(state->member) == MAXOPLEVEL)
3086 SetOpLevel(member, MAXOPLEVEL);
3088 SetOpLevel(member, OpLevel(state->member) + 1);
3091 /* actually effect the change */
3092 if (state->flags & MODE_PARSE_SET) {
3093 if (state->cli_change[i].flag & MODE_ADD) {
3094 if (IsDelayedJoin(member))
3095 RevealDelayedJoin(member);
3096 member->status |= (state->cli_change[i].flag &
3097 (MODE_CHANOP | MODE_VOICE));
3098 if (state->cli_change[i].flag & MODE_CHANOP)
3099 ClearDeopped(member);
3101 member->status &= ~(state->cli_change[i].flag &
3102 (MODE_CHANOP | MODE_VOICE));
3105 /* accumulate the change */
3106 modebuf_mode_client(state->mbuf, state->cli_change[i].flag,
3107 state->cli_change[i].client,
3108 state->cli_change[i].oplevel);
3109 } /* for (i = 0; state->cli_change[i].flags; i++) */
3113 * Helper function to process the simple modes
3116 mode_parse_mode(struct ParseState *state, int *flag_p)
3118 /* If they're not an oper, they can't change modes */
3119 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
3120 send_notoper(state);
3127 if (state->dir == MODE_ADD) {
3128 state->add |= flag_p[0];
3129 state->del &= ~flag_p[0];
3131 if (flag_p[0] & MODE_SECRET) {
3132 state->add &= ~MODE_PRIVATE;
3133 state->del |= MODE_PRIVATE;
3134 } else if (flag_p[0] & MODE_PRIVATE) {
3135 state->add &= ~MODE_SECRET;
3136 state->del |= MODE_SECRET;
3138 if (flag_p[0] & MODE_DELJOINS) {
3139 state->add &= ~MODE_WASDELJOINS;
3140 state->del |= MODE_WASDELJOINS;
3143 state->add &= ~flag_p[0];
3144 state->del |= flag_p[0];
3147 assert(0 == (state->add & state->del));
3148 assert((MODE_SECRET | MODE_PRIVATE) !=
3149 (state->add & (MODE_SECRET | MODE_PRIVATE)));
3153 * This routine is intended to parse MODE or OPMODE commands and effect the
3154 * changes (or just build the bounce buffer). We pass the starting offset
3158 mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
3159 struct Channel *chptr, int parc, char *parv[], unsigned int flags,
3160 struct Membership* member)
3162 static int chan_flags[] = {
3167 MODE_MODERATED, 'm',
3168 MODE_TOPICLIMIT, 't',
3169 MODE_INVITEONLY, 'i',
3170 MODE_NOPRIVMSGS, 'n',
3184 unsigned int t_mode;
3186 struct ParseState state;
3197 state.chptr = chptr;
3198 state.member = member;
3201 state.flags = flags;
3202 state.dir = MODE_ADD;
3206 state.args_used = 0;
3207 state.max_args = MAXMODEPARAMS;
3210 for (i = 0; i < MAXPARA; i++) { /* initialize ops/voices arrays */
3211 state.banlist[i].next = 0;
3212 state.banlist[i].who[0] = '\0';
3213 state.banlist[i].when = 0;
3214 state.banlist[i].flags = 0;
3215 state.cli_change[i].flag = 0;
3216 state.cli_change[i].client = 0;
3219 modestr = state.parv[state.args_used++];
3223 for (; *modestr; modestr++) {
3224 for (flag_p = chan_flags; flag_p[0]; flag_p += 2) /* look up flag */
3225 if (flag_p[1] == *modestr)
3228 if (!flag_p[0]) { /* didn't find it? complain and continue */
3229 if (MyUser(state.sptr))
3230 send_reply(state.sptr, ERR_UNKNOWNMODE, *modestr);
3235 case '+': /* switch direction to MODE_ADD */
3236 case '-': /* switch direction to MODE_DEL */
3237 state.dir = flag_p[0];
3240 case 'l': /* deal with limits */
3241 mode_parse_limit(&state, flag_p);
3244 case 'k': /* deal with keys */
3245 mode_parse_key(&state, flag_p);
3248 case 'A': /* deal with Admin passes */
3249 if (IsServer(cptr) || feature_bool(FEAT_OPLEVELS))
3250 mode_parse_apass(&state, flag_p);
3253 case 'U': /* deal with user passes */
3254 if (IsServer(cptr) || feature_bool(FEAT_OPLEVELS))
3255 mode_parse_upass(&state, flag_p);
3258 case 'b': /* deal with bans */
3259 mode_parse_ban(&state, flag_p);
3262 case 'o': /* deal with ops/voice */
3264 mode_parse_client(&state, flag_p);
3267 default: /* deal with other modes */
3268 mode_parse_mode(&state, flag_p);
3270 } /* switch (*modestr) */
3271 } /* for (; *modestr; modestr++) */
3273 if (state.flags & MODE_PARSE_BURST)
3274 break; /* don't interpret any more arguments */
3276 if (state.parc > 0) { /* process next argument in string */
3277 modestr = state.parv[state.args_used++];
3281 if (IsServer(state.sptr) && !state.parc && IsDigit(*modestr)) {
3284 if (!(state.flags & MODE_PARSE_SET)) /* don't set earlier TS if */
3285 break; /* we're then going to bounce the mode! */
3287 recv_ts = atoi(modestr);
3289 if (recv_ts && recv_ts < state.chptr->creationtime)
3290 state.chptr->creationtime = recv_ts; /* respect earlier TS */
3292 break; /* break out of while loop */
3293 } else if (state.flags & MODE_PARSE_STRICT ||
3294 (MyUser(state.sptr) && state.max_args <= 0)) {
3295 state.parc++; /* we didn't actually gobble the argument */
3297 break; /* break out of while loop */
3300 } /* while (*modestr) */
3303 * the rest of the function finishes building resultant MODEs; if the
3304 * origin isn't a member or an oper, skip it.
3306 if (!state.mbuf || state.flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER))
3307 return state.args_used; /* tell our parent how many args we gobbled */
3309 t_mode = state.chptr->mode.mode;
3311 if (state.del & t_mode) { /* delete any modes to be deleted... */
3312 modebuf_mode(state.mbuf, MODE_DEL | (state.del & t_mode));
3314 t_mode &= ~state.del;
3316 if (state.add & ~t_mode) { /* add any modes to be added... */
3317 modebuf_mode(state.mbuf, MODE_ADD | (state.add & ~t_mode));
3319 t_mode |= state.add;
3322 if (state.flags & MODE_PARSE_SET) { /* set the channel modes */
3323 if ((state.chptr->mode.mode & MODE_INVITEONLY) &&
3324 !(t_mode & MODE_INVITEONLY))
3325 mode_invite_clear(state.chptr);
3327 state.chptr->mode.mode = t_mode;
3330 if (state.flags & MODE_PARSE_WIPEOUT) {
3331 if (state.chptr->mode.limit && !(state.done & DONE_LIMIT))
3332 modebuf_mode_uint(state.mbuf, MODE_DEL | MODE_LIMIT,
3333 state.chptr->mode.limit);
3334 if (*state.chptr->mode.key && !(state.done & DONE_KEY))
3335 modebuf_mode_string(state.mbuf, MODE_DEL | MODE_KEY,
3336 state.chptr->mode.key, 0);
3337 if (*state.chptr->mode.upass && !(state.done & DONE_UPASS))
3338 modebuf_mode_string(state.mbuf, MODE_DEL | MODE_UPASS,
3339 state.chptr->mode.upass, 0);
3340 if (*state.chptr->mode.apass && !(state.done & DONE_APASS))
3341 modebuf_mode_string(state.mbuf, MODE_DEL | MODE_APASS,
3342 state.chptr->mode.apass, 0);
3345 if (state.done & DONE_BANCLEAN) /* process bans */
3346 mode_process_bans(&state);
3348 /* process client changes */
3349 if (state.cli_change[0].flag)
3350 mode_process_clients(&state);
3352 return state.args_used; /* tell our parent how many args we gobbled */
3356 * Initialize a join buffer
3359 joinbuf_init(struct JoinBuf *jbuf, struct Client *source,
3360 struct Client *connect, unsigned int type, char *comment,
3366 assert(0 != source);
3367 assert(0 != connect);
3369 jbuf->jb_source = source; /* just initialize struct JoinBuf */
3370 jbuf->jb_connect = connect;
3371 jbuf->jb_type = type;
3372 jbuf->jb_comment = comment;
3373 jbuf->jb_create = create;
3375 jbuf->jb_strlen = (((type == JOINBUF_TYPE_JOIN ||
3376 type == JOINBUF_TYPE_PART ||
3377 type == JOINBUF_TYPE_PARTALL) ?
3378 STARTJOINLEN : STARTCREATELEN) +
3379 (comment ? strlen(comment) + 2 : 0));
3381 for (i = 0; i < MAXJOINARGS; i++)
3382 jbuf->jb_channels[i] = 0;
3386 * Add a channel to the join buffer
3389 joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
3397 sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect, "0");
3401 is_local = IsLocalChannel(chan->chname);
3403 if (jbuf->jb_type == JOINBUF_TYPE_PART ||
3404 jbuf->jb_type == JOINBUF_TYPE_PARTALL) {
3405 struct Membership *member = find_member_link(chan, jbuf->jb_source);
3406 if (IsUserParting(member))
3408 SetUserParting(member);
3410 /* Send notification to channel */
3411 if (!(flags & (CHFL_ZOMBIE | CHFL_DELAYED)))
3412 sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_PART, chan, NULL, 0,
3413 (flags & CHFL_BANNED || !jbuf->jb_comment) ?
3414 ":%H" : "%H :%s", chan, jbuf->jb_comment);
3415 else if (MyUser(jbuf->jb_source))
3416 sendcmdto_one(jbuf->jb_source, CMD_PART, jbuf->jb_source,
3417 (flags & CHFL_BANNED || !jbuf->jb_comment) ?
3418 ":%H" : "%H :%s", chan, jbuf->jb_comment);
3419 /* XXX: Shouldn't we send a PART here anyway? */
3420 /* to users on the channel? Why? From their POV, the user isn't on
3421 * the channel anymore anyway. We don't send to servers until below,
3422 * when we gang all the channel parts together. Note that this is
3423 * exactly the same logic, albeit somewhat more concise, as was in
3424 * the original m_part.c */
3426 if (jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
3427 is_local) /* got to remove user here */
3428 remove_user_from_channel(jbuf->jb_source, chan);
3430 int oplevel = !chan->mode.apass[0] ? MAXOPLEVEL
3431 : (flags & CHFL_CHANNEL_MANAGER) ? 0
3433 /* Add user to channel */
3434 if ((chan->mode.mode & MODE_DELJOINS) && !(flags & CHFL_VOICED_OR_OPPED))
3435 add_user_to_channel(chan, jbuf->jb_source, flags | CHFL_DELAYED, oplevel);
3437 add_user_to_channel(chan, jbuf->jb_source, flags, oplevel);
3439 /* send JOIN notification to all servers (CREATE is sent later). */
3440 if (jbuf->jb_type != JOINBUF_TYPE_CREATE && !is_local)
3441 sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect,
3442 "%H %Tu", chan, chan->creationtime);
3444 if (!((chan->mode.mode & MODE_DELJOINS) && !(flags & CHFL_VOICED_OR_OPPED))) {
3445 /* Send the notification to the channel */
3446 sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_JOIN, chan, NULL, 0, "%H", chan);
3448 /* send an op, too, if needed */
3449 if (flags & CHFL_CHANOP && (oplevel < MAXOPLEVEL || !MyUser(jbuf->jb_source)))
3450 sendcmdto_channel_butserv_butone((chan->mode.apass[0] ? &his : jbuf->jb_source),
3451 CMD_MODE, chan, NULL, 0, "%H +o %C",
3452 chan, jbuf->jb_source);
3453 } else if (MyUser(jbuf->jb_source))
3454 sendcmdto_one(jbuf->jb_source, CMD_JOIN, jbuf->jb_source, ":%H", chan);
3457 if (jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
3458 jbuf->jb_type == JOINBUF_TYPE_JOIN || is_local)
3459 return; /* don't send to remote */
3461 /* figure out if channel name will cause buffer to be overflowed */
3462 len = chan ? strlen(chan->chname) + 1 : 2;
3463 if (jbuf->jb_strlen + len > BUFSIZE)
3464 joinbuf_flush(jbuf);
3466 /* add channel to list of channels to send and update counts */
3467 jbuf->jb_channels[jbuf->jb_count++] = chan;
3468 jbuf->jb_strlen += len;
3470 /* if we've used up all slots, flush */
3471 if (jbuf->jb_count >= MAXJOINARGS)
3472 joinbuf_flush(jbuf);
3476 * Flush the channel list to remote servers
3479 joinbuf_flush(struct JoinBuf *jbuf)
3481 char chanlist[BUFSIZE];
3485 if (!jbuf->jb_count || jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
3486 jbuf->jb_type == JOINBUF_TYPE_JOIN)
3487 return 0; /* no joins to process */
3489 for (i = 0; i < jbuf->jb_count; i++) { /* build channel list */
3490 build_string(chanlist, &chanlist_i,
3491 jbuf->jb_channels[i] ? jbuf->jb_channels[i]->chname : "0", 0,
3492 i == 0 ? '\0' : ',');
3493 if (JOINBUF_TYPE_PART == jbuf->jb_type)
3494 /* Remove user from channel */
3495 remove_user_from_channel(jbuf->jb_source, jbuf->jb_channels[i]);
3497 jbuf->jb_channels[i] = 0; /* mark slot empty */
3500 jbuf->jb_count = 0; /* reset base counters */
3501 jbuf->jb_strlen = ((jbuf->jb_type == JOINBUF_TYPE_PART ?
3502 STARTJOINLEN : STARTCREATELEN) +
3503 (jbuf->jb_comment ? strlen(jbuf->jb_comment) + 2 : 0));
3505 /* and send the appropriate command */
3506 switch (jbuf->jb_type) {
3507 case JOINBUF_TYPE_CREATE:
3508 sendcmdto_serv_butone(jbuf->jb_source, CMD_CREATE, jbuf->jb_connect,
3509 "%s %Tu", chanlist, jbuf->jb_create);
3512 case JOINBUF_TYPE_PART:
3513 sendcmdto_serv_butone(jbuf->jb_source, CMD_PART, jbuf->jb_connect,
3514 jbuf->jb_comment ? "%s :%s" : "%s", chanlist,
3522 /* Returns TRUE (1) if client is invited, FALSE (0) if not */
3523 int IsInvited(struct Client* cptr, const void* chptr)
3527 for (lp = (cli_user(cptr))->invited; lp; lp = lp->next)
3528 if (lp->value.chptr == chptr)
3533 /* RevealDelayedJoin: sends a join for a hidden user */
3535 void RevealDelayedJoin(struct Membership *member)
3537 ClearDelayedJoin(member);
3538 sendcmdto_channel_butserv_butone(member->user, CMD_JOIN, member->channel, member->user, 0, ":%H",
3540 CheckDelayedJoins(member->channel);
3543 /* CheckDelayedJoins: checks and clear +d if necessary */
3545 void CheckDelayedJoins(struct Channel *chan)
3547 struct Membership *memb2;
3549 if (chan->mode.mode & MODE_WASDELJOINS) {
3550 for (memb2=chan->members;memb2;memb2=memb2->next_member)
3551 if (IsDelayedJoin(memb2))
3556 chan->mode.mode &= ~MODE_WASDELJOINS;
3557 sendcmdto_channel_butserv_butone(&his, CMD_MODE, chan, NULL, 0,