]>
jfr.im git - irc/quakenet/snircd.git/blob - ircd/channel.c
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
22 * @version $Id: channel.c,v 1.155.2.15 2006/04/04 23:09:47 entrope Exp $
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 /* Immediately destruct empty -A channels if not using apass. */
292 if (!feature_bool(FEAT_OPLEVELS
))
294 destruct_channel(chptr
);
298 if (TStime() - chptr
->creationtime
< 172800) /* Channel younger than 48 hours? */
299 schedule_destruct_event_1m(chptr
); /* Get rid of it in approximately 4-5 minutes */
301 schedule_destruct_event_48h(chptr
); /* Get rid of it in approximately 48 hours */
306 /** Destroy an empty channel
307 * This function destroys an empty channel, removing it from hashtables,
308 * and removing any resources it may have consumed.
310 * @param chptr The channel to destroy
312 * @returns 0 (success)
314 * FIXME: Change to return void, this function never fails.
316 int destruct_channel(struct Channel
* chptr
)
318 struct Ban
*ban
, *next
;
320 assert(0 == chptr
->members
);
323 * Now, find all invite links from channel structure
325 while (chptr
->invites
)
326 del_invite(chptr
->invites
->value
.cptr
, chptr
);
328 for (ban
= chptr
->banlist
; ban
; ban
= next
)
334 chptr
->prev
->next
= chptr
->next
;
336 GlobalChannelList
= chptr
->next
;
338 chptr
->next
->prev
= chptr
->prev
;
340 --UserStats
.channels
;
342 * make sure that channel actually got removed from hash table
344 assert(chptr
->hnext
== chptr
);
349 /** returns Membership * if a person is joined and not a zombie
351 * @param chptr Channel
352 * @returns pointer to the client's struct Membership * on the channel if that
353 * user is a full member of the channel, or NULL otherwise.
355 * @see find_member_link()
357 struct Membership
* find_channel_member(struct Client
* cptr
, struct Channel
* chptr
)
359 struct Membership
* member
;
362 member
= find_member_link(chptr
, cptr
);
363 return (member
&& !IsZombie(member
)) ? member
: 0;
366 /** Searches for a ban from a ban list that matches a user.
367 * @param[in] cptr The client to test.
368 * @param[in] banlist The list of bans to test.
369 * @return Pointer to a matching ban, or NULL if none exit.
371 struct Ban
*find_ban(struct Client
*cptr
, struct Ban
*banlist
)
373 char nu
[NICKLEN
+ USERLEN
+ 2];
374 char tmphost
[HOSTLEN
+ 1];
375 char iphost
[SOCKIPLEN
+ 1];
380 /* Build nick!user and alternate host names. */
381 ircd_snprintf(0, nu
, sizeof(nu
), "%s!%s",
382 cli_name(cptr
), cli_user(cptr
)->username
);
383 ircd_ntoa_r(iphost
, &cli_ip(cptr
));
384 if (!IsAccount(cptr
))
386 else if (HasHiddenHost(cptr
) || HasSetHost(cptr
))
387 sr
= cli_user(cptr
)->realhost
;
390 ircd_snprintf(0, tmphost
, HOSTLEN
, "%s.%s",
391 cli_user(cptr
)->account
, feature_str(FEAT_HIDDEN_HOST
));
395 /* Walk through ban list. */
396 for (found
= NULL
; banlist
; banlist
= banlist
->next
) {
398 /* If we have found a positive ban already, only consider exceptions. */
399 if (found
&& !(banlist
->flags
& BAN_EXCEPTION
))
401 /* Compare nick!user portion of ban. */
402 banlist
->banstr
[banlist
->nu_len
] = '\0';
403 res
= match(banlist
->banstr
, nu
);
404 banlist
->banstr
[banlist
->nu_len
] = '@';
407 /* Compare host portion of ban. */
408 hostmask
= banlist
->banstr
+ banlist
->nu_len
+ 1;
409 if (!((banlist
->flags
& BAN_IPMASK
)
410 && ipmask_check(&cli_ip(cptr
), &banlist
->address
, banlist
->addrbits
))
411 && match(hostmask
, cli_user(cptr
)->host
)
412 && !(sr
&& !match(hostmask
, sr
)))
414 /* If an exception matches, no ban can match. */
415 if (banlist
->flags
& BAN_EXCEPTION
)
417 /* Otherwise, remember this ban but keep searching for an exception. */
424 * This function returns true if the user is banned on the said channel.
425 * This function will check the ban cache if applicable, otherwise will
426 * do the comparisons and cache the result.
428 * @param[in] member The Membership to test for banned-ness.
429 * @return Non-zero if the member is banned, zero if not.
431 static int is_banned(struct Membership
* member
)
433 if (IsBanValid(member
))
434 return IsBanned(member
);
437 if (find_ban(member
->user
, member
->channel
->banlist
)) {
446 /** add a user to a channel.
447 * adds a user to a channel by adding another link to the channels member
450 * @param chptr The channel to add to.
451 * @param who The user to add.
452 * @param flags The flags the user gets initially.
453 * @param oplevel The oplevel the user starts with.
455 void add_user_to_channel(struct Channel
* chptr
, struct Client
* who
,
456 unsigned int flags
, int oplevel
)
463 struct Membership
* member
= membershipFreeList
;
465 membershipFreeList
= member
->next_member
;
467 member
= (struct Membership
*) MyMalloc(sizeof(struct Membership
));
468 ++membershipAllocCount
;
473 member
->channel
= chptr
;
474 member
->status
= flags
;
475 SetOpLevel(member
, oplevel
);
477 member
->next_member
= chptr
->members
;
478 if (member
->next_member
)
479 member
->next_member
->prev_member
= member
;
480 member
->prev_member
= 0;
481 chptr
->members
= member
;
483 member
->next_channel
= (cli_user(who
))->channel
;
484 if (member
->next_channel
)
485 member
->next_channel
->prev_channel
= member
;
486 member
->prev_channel
= 0;
487 (cli_user(who
))->channel
= member
;
489 if (chptr
->destruct_event
)
490 remove_destruct_event(chptr
);
492 ++((cli_user(who
))->joined
);
496 /** Remove a person from a channel, given their Membership*
498 * @param member A member of a channel.
500 * @returns true if there are more people in the channel.
502 static int remove_member_from_channel(struct Membership
* member
)
504 struct Channel
* chptr
;
506 chptr
= member
->channel
;
508 * unlink channel member list
510 if (member
->next_member
)
511 member
->next_member
->prev_member
= member
->prev_member
;
512 if (member
->prev_member
)
513 member
->prev_member
->next_member
= member
->next_member
;
515 member
->channel
->members
= member
->next_member
;
518 * If this is the last delayed-join user, may have to clear WASDELJOINS.
520 if (IsDelayedJoin(member
))
521 CheckDelayedJoins(chptr
);
524 * unlink client channel list
526 if (member
->next_channel
)
527 member
->next_channel
->prev_channel
= member
->prev_channel
;
528 if (member
->prev_channel
)
529 member
->prev_channel
->next_channel
= member
->next_channel
;
531 (cli_user(member
->user
))->channel
= member
->next_channel
;
533 --(cli_user(member
->user
))->joined
;
535 member
->next_member
= membershipFreeList
;
536 membershipFreeList
= member
;
538 return sub1_from_channel(chptr
);
541 /** Check if all the remaining members on the channel are zombies
543 * @returns False if the channel has any non zombie members, True otherwise.
546 static int channel_all_zombies(struct Channel
* chptr
)
548 struct Membership
* member
;
550 for (member
= chptr
->members
; member
; member
= member
->next_member
) {
551 if (!IsZombie(member
))
558 /** Remove a user from a channel
559 * This is the generic entry point for removing a user from a channel, this
560 * function will remove the client from the channel, and destroy the channel
561 * if there are no more normal users left.
563 * @param cptr The client
564 * @param chptr The channel
566 void remove_user_from_channel(struct Client
* cptr
, struct Channel
* chptr
)
569 struct Membership
* member
;
572 if ((member
= find_member_link(chptr
, cptr
))) {
573 if (remove_member_from_channel(member
)) {
574 if (channel_all_zombies(chptr
)) {
576 * XXX - this looks dangerous but isn't if we got the referential
577 * integrity right for channels
579 while (remove_member_from_channel(chptr
->members
))
586 /** Remove a user from all channels they are on.
588 * This function removes a user from all channels they are on.
590 * @param cptr The client to remove.
592 void remove_user_from_all_channels(struct Client
* cptr
)
594 struct Membership
* chan
;
596 assert(0 != cli_user(cptr
));
598 while ((chan
= (cli_user(cptr
))->channel
))
599 remove_user_from_channel(cptr
, chan
->channel
);
602 /** Check if this user is a legitimate chanop
604 * @param cptr Client to check
605 * @param chptr Channel to check
607 * @returns True if the user is a chanop (And not a zombie), False otherwise.
610 int is_chan_op(struct Client
*cptr
, struct Channel
*chptr
)
612 struct Membership
* member
;
614 if ((member
= find_member_link(chptr
, cptr
)))
615 return (!IsZombie(member
) && IsChanOp(member
));
620 /** Check if a user is a Zombie on a specific channel.
622 * @param cptr The client to check.
623 * @param chptr The channel to check.
625 * @returns True if the client (cptr) is a zombie on the channel (chptr),
630 int is_zombie(struct Client
*cptr
, struct Channel
*chptr
)
632 struct Membership
* member
;
636 if ((member
= find_member_link(chptr
, cptr
)))
637 return IsZombie(member
);
641 /** Returns if a user has voice on a channel.
643 * @param cptr The client
644 * @param chptr The channel
646 * @returns True if the client (cptr) is voiced on (chptr) and is not a zombie.
649 int has_voice(struct Client
* cptr
, struct Channel
* chptr
)
651 struct Membership
* member
;
654 if ((member
= find_member_link(chptr
, cptr
)))
655 return (!IsZombie(member
) && HasVoice(member
));
660 /** Can this member send to a channel
662 * A user can speak on a channel iff:
664 * <li> They didn't use the Apass to gain ops.
665 * <li> They are op'd or voice'd.
666 * <li> You aren't banned.
667 * <li> The channel isn't +m
668 * <li> The channel isn't +n or you are on the channel.
671 * This function will optionally reveal a user on a delayed join channel if
672 * they are allowed to send to the channel.
674 * @param member The membership of the user
675 * @param reveal If true, the user will be "revealed" on a delayed
678 * @returns True if the client can speak on the channel.
680 int member_can_send_to_channel(struct Membership
* member
, int reveal
)
684 /* Do not check for users on other servers: This should be a
685 * temporary desynch, or maybe they are on an older server, but
686 * we do not want to send ERR_CANNOTSENDTOCHAN more than once.
688 if (!MyUser(member
->user
))
690 if (IsDelayedJoin(member
) && reveal
)
691 RevealDelayedJoin(member
);
695 /* Discourage using the Apass to get op. They should use the Upass. */
696 if (IsChannelManager(member
) && member
->channel
->mode
.apass
[0])
699 /* If you have voice or ops, you can speak. */
700 if (IsVoicedOrOpped(member
))
704 * If it's moderated, and you aren't a privileged user, you can't
707 if (member
->channel
->mode
.mode
& MODE_MODERATED
)
710 /* If only logged in users may join and you're not one, you can't speak. */
711 if (member
->channel
->mode
.mode
& MODE_REGONLY
&& !IsAccount(member
->user
))
714 /* If you're banned then you can't speak either. */
715 if (is_banned(member
))
718 if (IsDelayedJoin(member
) && reveal
)
719 RevealDelayedJoin(member
);
724 /** Check if a client can send to a channel.
726 * Has the added check over member_can_send_to_channel() of servers can
729 * @param cptr The client to check
730 * @param chptr The channel to check
731 * @param reveal If the user should be revealed (see
732 * member_can_send_to_channel())
734 * @returns true if the client is allowed to speak on the channel, false
737 * @see member_can_send_to_channel()
739 int client_can_send_to_channel(struct Client
*cptr
, struct Channel
*chptr
, int reveal
)
741 struct Membership
*member
;
744 * Servers can always speak on channels.
746 if (IsServer(cptr
) || IsXtraOp(cptr
))
749 member
= find_channel_member(cptr
, chptr
);
752 * You can't speak if you're off channel, and it is +n (no external messages)
756 if ((chptr
->mode
.mode
& (MODE_NOPRIVMSGS
|MODE_MODERATED
)) ||
757 ((chptr
->mode
.mode
& MODE_REGONLY
) && !IsAccount(cptr
)))
760 return !find_ban(cptr
, chptr
->banlist
);
762 return member_can_send_to_channel(member
, reveal
);
765 /** Returns the name of a channel that prevents the user from changing nick.
766 * if a member and not (opped or voiced) and (banned or moderated), return
767 * the name of the first channel banned on.
769 * @param cptr The client
771 * @returns the name of the first channel banned on, or NULL if the user
774 const char* find_no_nickchange_channel(struct Client
* cptr
)
777 struct Membership
* member
;
778 for (member
= (cli_user(cptr
))->channel
; member
;
779 member
= member
->next_channel
) {
780 if (IsVoicedOrOpped(member
))
782 if ((member
->channel
->mode
.mode
& MODE_MODERATED
)
783 || (member
->channel
->mode
.mode
& MODE_REGONLY
&& !IsAccount(cptr
))
784 || is_banned(member
))
785 return member
->channel
->chname
;
792 /** Fill mbuf/pbuf with modes from chptr
793 * write the "simple" list of channel modes for channel chptr onto buffer mbuf
794 * with the parameters in pbuf as visible by cptr.
796 * This function will hide keys from non-op'd, non-server clients.
798 * @param cptr The client to generate the mode for.
799 * @param mbuf The buffer to write the modes into.
800 * @param pbuf The buffer to write the mode parameters into.
801 * @param buflen The length of the buffers.
802 * @param chptr The channel to get the modes from.
803 * @param member The membership of this client on this channel (or NULL
804 * if this client isn't on this channel)
807 void channel_modes(struct Client
*cptr
, char *mbuf
, char *pbuf
, int buflen
,
808 struct Channel
*chptr
, struct Membership
*member
)
810 int previous_parameter
= 0;
817 if (chptr
->mode
.mode
& MODE_SECRET
)
819 else if (chptr
->mode
.mode
& MODE_PRIVATE
)
821 if (chptr
->mode
.mode
& MODE_MODERATED
)
823 if (chptr
->mode
.mode
& MODE_TOPICLIMIT
)
825 if (chptr
->mode
.mode
& MODE_INVITEONLY
)
827 if (chptr
->mode
.mode
& MODE_NOPRIVMSGS
)
829 if (chptr
->mode
.mode
& MODE_REGONLY
)
831 if (chptr
->mode
.mode
& MODE_NOCOLOUR
)
833 if (chptr
->mode
.mode
& MODE_NOCTCP
)
835 if (chptr
->mode
.mode
& MODE_NONOTICE
)
837 if (chptr
->mode
.mode
& MODE_NOQUITPARTS
)
839 if (chptr
->mode
.mode
& MODE_DELJOINS
)
841 else if (MyUser(cptr
) && (chptr
->mode
.mode
& MODE_WASDELJOINS
))
843 if (chptr
->mode
.limit
) {
845 ircd_snprintf(0, pbuf
, buflen
, "%u", chptr
->mode
.limit
);
846 previous_parameter
= 1;
849 if (*chptr
->mode
.key
) {
851 if (previous_parameter
)
853 if (is_chan_op(cptr
, chptr
) || IsServer(cptr
) || IsOper(cptr
)) {
854 strcat(pbuf
, chptr
->mode
.key
);
857 previous_parameter
= 1;
859 if (*chptr
->mode
.apass
) {
861 if (previous_parameter
)
863 if (IsServer(cptr
)) {
864 strcat(pbuf
, chptr
->mode
.apass
);
867 previous_parameter
= 1;
869 if (*chptr
->mode
.upass
) {
871 if (previous_parameter
)
873 if (IsServer(cptr
) || (member
&& IsChanOp(member
) && OpLevel(member
) == 0)) {
874 strcat(pbuf
, chptr
->mode
.upass
);
881 /** Compare two members oplevel
883 * @param mp1 Pointer to a pointer to a membership
884 * @param mp2 Pointer to a pointer to a membership
886 * @returns 0 if equal, -1 if mp1 is lower, +1 otherwise.
890 int compare_member_oplevel(const void *mp1
, const void *mp2
)
892 struct Membership
const* member1
= *(struct Membership
const**)mp1
;
893 struct Membership
const* member2
= *(struct Membership
const**)mp2
;
894 if (member1
->oplevel
== member2
->oplevel
)
896 return (member1
->oplevel
< member2
->oplevel
) ? -1 : 1;
899 /* send "cptr" a full list of the modes for channel chptr.
901 * Sends a BURST line to cptr, bursting all the modes for the channel.
903 * @param cptr Client pointer
904 * @param chptr Channel pointer
906 void send_channel_modes(struct Client
*cptr
, struct Channel
*chptr
)
908 /* The order in which modes are generated is now mandatory */
909 static unsigned int current_flags
[4] =
910 { 0, CHFL_VOICE
, CHFL_CHANOP
, CHFL_CHANOP
| CHFL_VOICE
};
916 struct Membership
* member
;
918 char modebuf
[MODEBUFLEN
];
919 char parabuf
[MODEBUFLEN
];
921 int number_of_ops
= 0;
922 int opped_members_index
= 0;
923 struct Membership
** opped_members
= NULL
;
924 int last_oplevel
= 0;
925 int feat_oplevels
= (chptr
->mode
.apass
[0]) != '\0';
930 if (IsLocalChannel(chptr
->chname
))
933 member
= chptr
->members
;
934 lp2
= chptr
->banlist
;
936 *modebuf
= *parabuf
= '\0';
937 channel_modes(cptr
, modebuf
, parabuf
, sizeof(parabuf
), chptr
, 0);
939 for (first
= 1; full
; first
= 0) /* Loop for multiple messages */
941 full
= 0; /* Assume by default we get it
942 all in one message */
944 /* (Continued) prefix: "<Y> B <channel> <TS>" */
945 /* is there any better way we can do this? */
946 mb
= msgq_make(&me
, "%C " TOK_BURST
" %H %Tu", &me
, chptr
,
947 chptr
->creationtime
);
949 if (first
&& modebuf
[1]) /* Add simple modes (Aiklmnpstu)
952 /* prefix: "<Y> B <channel> <TS>[ <modes>[ <params>]]" */
953 msgq_append(&me
, mb
, " %s", modebuf
);
956 msgq_append(&me
, mb
, " %s", parabuf
);
960 * Attach nicks, comma separated " nick[:modes],nick[:modes],..."
962 * First find all opless members.
963 * Run 2 times over all members, to group the members with
964 * and without voice together.
965 * Then run 2 times over all opped members (which are ordered
966 * by op-level) to also group voice and non-voice together.
968 for (first
= 1; flag_cnt
< 4; new_mode
= 1, ++flag_cnt
)
972 if (flag_cnt
< 2 && IsChanOp(member
))
975 * The first loop (to find all non-voice/op), we count the ops.
976 * The second loop (to find all voiced non-ops), store the ops
977 * in a dynamic array.
982 opped_members
[opped_members_index
++] = member
;
984 /* Only handle the members with the flags that we are interested in. */
985 if ((member
->status
& CHFL_VOICED_OR_OPPED
) == current_flags
[flag_cnt
])
987 if (msgq_bufleft(mb
) < NUMNICKLEN
+ 3 + MAXOPLEVELDIGITS
)
988 /* The 3 + MAXOPLEVELDIGITS is a possible ",:v999". */
990 full
= 1; /* Make sure we continue after
992 /* Ensure the new BURST line contains the current
993 * ":mode", except when there is no mode yet. */
994 new_mode
= (flag_cnt
> 0) ? 1 : 0;
995 break; /* Do not add this member to this message */
997 msgq_append(&me
, mb
, "%c%C", first
? ' ' : ',', member
->user
);
998 first
= 0; /* From now on, use commas to add new nicks */
1001 * Do we have a nick with a new mode ?
1002 * Or are we starting a new BURST line?
1007 * This means we are at the _first_ member that has only
1008 * voice, or the first member that has only ops, or the
1009 * first member that has voice and ops (so we get here
1010 * at most three times, plus once for every start of
1011 * a continued BURST line where only these modes is current.
1012 * In the two cases where the current mode includes ops,
1013 * we need to add the _absolute_ value of the oplevel to the mode.
1015 char tbuf
[3 + MAXOPLEVELDIGITS
] = ":";
1018 if (HasVoice(member
)) /* flag_cnt == 1 or 3 */
1020 if (IsChanOp(member
)) /* flag_cnt == 2 or 3 */
1022 /* append the absolute value of the oplevel */
1024 loc
+= ircd_snprintf(0, tbuf
+ loc
, sizeof(tbuf
) - loc
, "%u", last_oplevel
= member
->oplevel
);
1029 msgq_append(&me
, mb
, tbuf
);
1032 else if (feat_oplevels
&& flag_cnt
> 1 && last_oplevel
!= member
->oplevel
)
1035 * This can't be the first member of a (continued) BURST
1036 * message because then either flag_cnt == 0 or new_mode == 1
1037 * Now we need to append the incremental value of the oplevel.
1039 char tbuf
[2 + MAXOPLEVELDIGITS
];
1040 ircd_snprintf(0, tbuf
, sizeof(tbuf
), ":%u", member
->oplevel
- last_oplevel
);
1041 last_oplevel
= member
->oplevel
;
1042 msgq_append(&me
, mb
, tbuf
);
1045 /* Go to the next `member'. */
1047 member
= member
->next_member
;
1049 member
= opped_members
[++opped_members_index
];
1054 /* Point `member' at the start of the list again. */
1057 member
= chptr
->members
;
1058 /* Now, after one loop, we know the number of ops and can
1059 * allocate the dynamic array with pointer to the ops. */
1060 opped_members
= (struct Membership
**)
1061 MyMalloc((number_of_ops
+ 1) * sizeof(struct Membership
*));
1062 opped_members
[number_of_ops
] = NULL
; /* Needed for loop termination */
1066 /* At the end of the second loop, sort the opped members with
1067 * increasing op-level, so that we will output them in the
1068 * correct order (and all op-level increments stay positive) */
1070 qsort(opped_members
, number_of_ops
,
1071 sizeof(struct Membership
*), compare_member_oplevel
);
1072 /* The third and fourth loop run only over the opped members. */
1073 member
= opped_members
[(opped_members_index
= 0)];
1076 } /* loop over 0,+v,+o,+ov */
1080 /* Attach all bans, space separated " :%ban ban ..." */
1081 for (first
= 2; lp2
; lp2
= lp2
->next
)
1083 len
= strlen(lp2
->banstr
);
1084 if (msgq_bufleft(mb
) < len
+ 1 + first
)
1085 /* The +1 stands for the added ' '.
1086 * The +first stands for the added ":%".
1092 msgq_append(&me
, mb
, " %s%s", first
? ":%" : "",
1098 send_buffer(cptr
, mb
, 0); /* Send this message */
1100 } /* Continue when there was something
1101 that didn't fit (full==1) */
1103 MyFree(opped_members
);
1104 if (feature_bool(FEAT_TOPIC_BURST
) && (chptr
->topic
[0] != '\0'))
1105 sendcmdto_one(&me
, CMD_TOPIC
, cptr
, "%H %Tu %Tu :%s", chptr
,
1106 chptr
->creationtime
, chptr
->topic_time
, chptr
->topic
);
1109 /** Canonify a mask.
1112 * @author Carlo Wood (Run),
1115 * When the nick is longer then NICKLEN, it is cut off (its an error of course).
1116 * When the user name or host name are too long (USERLEN and HOSTLEN
1117 * respectively) then they are cut off at the start with a '*'.
1119 * The following transformations are made:
1121 * 1) xxx -> nick!*@*
1122 * 2) xxx.xxx -> *!*\@host
1123 * 3) xxx\!yyy -> nick!user\@*
1124 * 4) xxx\@yyy -> *!user\@host
1125 * 5) xxx!yyy\@zzz -> nick!user\@host
1127 * @param mask The uncanonified mask.
1128 * @returns The updated mask in a static buffer.
1130 char *pretty_mask(char *mask
)
1132 static char star
[2] = { '*', 0 };
1133 static char retmask
[NICKLEN
+ USERLEN
+ HOSTLEN
+ 3];
1134 char *last_dot
= NULL
;
1137 /* Case 1: default */
1142 /* Do a _single_ pass through the characters of the mask: */
1143 for (ptr
= mask
; *ptr
; ++ptr
)
1147 /* Case 3 or 5: Found first '!' (without finding a '@' yet) */
1151 else if (*ptr
== '@')
1153 /* Case 4: Found last '@' (without finding a '!' yet) */
1158 else if (*ptr
== '.' || *ptr
== ':')
1160 /* Case 2: Found character specific to IP or hostname (without
1161 * finding a '!' or '@' yet) */
1171 /* Case 4 or 5: Found last '@' */
1177 if (user
== star
&& last_dot
)
1187 char *nick_end
= (user
!= star
) ? user
- 1 : ptr
;
1188 if (nick_end
- nick
> NICKLEN
)
1194 char *user_end
= (host
!= star
) ? host
- 1 : ptr
;
1195 if (user_end
- user
> USERLEN
)
1197 user
= user_end
- USERLEN
;
1202 if (host
!= star
&& ptr
- host
> HOSTLEN
)
1204 host
= ptr
- HOSTLEN
;
1207 ircd_snprintf(0, retmask
, sizeof(retmask
), "%s!%s@%s", nick
, user
, host
);
1211 /** send a banlist to a client for a channel
1213 * @param cptr Client to send the banlist to.
1214 * @param chptr Channel whose banlist to send.
1216 static void send_ban_list(struct Client
* cptr
, struct Channel
* chptr
)
1223 for (lp
= chptr
->banlist
; lp
; lp
= lp
->next
)
1224 send_reply(cptr
, RPL_BANLIST
, chptr
->chname
, lp
->banstr
,
1227 send_reply(cptr
, RPL_ENDOFBANLIST
, chptr
->chname
);
1230 /** Remove bells and commas from channel name
1232 * @param cn Channel name to clean, modified in place.
1234 void clean_channelname(char *cn
)
1238 for (i
= 0; cn
[i
]; i
++) {
1239 if (i
>= IRCD_MIN(CHANNELLEN
, feature_int(FEAT_CHANNELLEN
))
1240 || !IsChannelChar(cn
[i
])) {
1244 if (IsChannelLower(cn
[i
])) {
1245 cn
[i
] = ToLower(cn
[i
]);
1251 if ((unsigned char)(cn
[i
]) == 0xd0)
1252 cn
[i
] = (char) 0xf0;
1258 /** Get a channel block, creating if necessary.
1259 * Get Channel block for chname (and allocate a new channel
1260 * block, if it didn't exists before).
1262 * @param cptr Client joining the channel.
1263 * @param chname The name of the channel to join.
1264 * @param flag set to CGT_CREATE to create the channel if it doesn't
1267 * @returns NULL if the channel is invalid, doesn't exist and CGT_CREATE
1268 * wasn't specified or a pointer to the channel structure
1270 struct Channel
*get_channel(struct Client
*cptr
, char *chname
, ChannelGetType flag
)
1272 struct Channel
*chptr
;
1275 if (EmptyString(chname
))
1278 len
= strlen(chname
);
1279 if (MyUser(cptr
) && len
> CHANNELLEN
)
1282 *(chname
+ CHANNELLEN
) = '\0';
1284 if ((chptr
= FindChannel(chname
)))
1286 if (flag
== CGT_CREATE
)
1288 chptr
= (struct Channel
*) MyMalloc(sizeof(struct Channel
) + len
);
1290 ++UserStats
.channels
;
1291 memset(chptr
, 0, sizeof(struct Channel
));
1292 strcpy(chptr
->chname
, chname
);
1293 if (GlobalChannelList
)
1294 GlobalChannelList
->prev
= chptr
;
1296 chptr
->next
= GlobalChannelList
;
1297 chptr
->creationtime
= MyUser(cptr
) ? TStime() : (time_t) 0;
1298 GlobalChannelList
= chptr
;
1304 int SetAutoChanModes(struct Channel
*chptr
)
1306 static int chan_flags
[] = {
1309 MODE_MODERATED
, 'm',
1310 MODE_TOPICLIMIT
, 't',
1311 MODE_INVITEONLY
, 'i',
1312 MODE_NOPRIVMSGS
, 'n',
1318 MODE_NOQUITPARTS
, 'u'
1321 unsigned int *flag_p
;
1322 unsigned int t_mode
;
1323 const char *modestr
;
1329 if (!feature_bool(FEAT_AUTOCHANMODES
) || !feature_str(FEAT_AUTOCHANMODES_LIST
) || strlen(feature_str(FEAT_AUTOCHANMODES_LIST
)) <= 1)
1332 modestr
= feature_str(FEAT_AUTOCHANMODES_LIST
);
1334 for (; *modestr
; modestr
++) {
1335 for (flag_p
= chan_flags
; flag_p
[0]; flag_p
+= 2) /* look up flag */
1336 if (flag_p
[1] == *modestr
)
1339 if (!flag_p
[0]) /* didn't find it */
1342 t_mode
|= flag_p
[0];
1344 } /* for (; *modestr; modestr++) { */
1347 chptr
->mode
.mode
= t_mode
;
1351 /** invite a user to a channel.
1353 * Adds an invite for a user to a channel. Limits the number of invites
1354 * to FEAT_MAXCHANNELSPERUSER. Does not sent notification to the user.
1356 * @param cptr The client to be invited.
1357 * @param chptr The channel to be invited to.
1359 void add_invite(struct Client
*cptr
, struct Channel
*chptr
)
1361 struct SLink
*inv
, **tmp
;
1363 del_invite(cptr
, chptr
);
1365 * Delete last link in chain if the list is max length
1367 assert(list_length((cli_user(cptr
))->invited
) == (cli_user(cptr
))->invites
);
1368 if ((cli_user(cptr
))->invites
>= feature_int(FEAT_MAXCHANNELSPERUSER
))
1369 del_invite(cptr
, (cli_user(cptr
))->invited
->value
.chptr
);
1371 * Add client to channel invite list
1374 inv
->value
.cptr
= cptr
;
1375 inv
->next
= chptr
->invites
;
1376 chptr
->invites
= inv
;
1378 * Add channel to the end of the client invite list
1380 for (tmp
= &((cli_user(cptr
))->invited
); *tmp
; tmp
= &((*tmp
)->next
));
1382 inv
->value
.chptr
= chptr
;
1385 (cli_user(cptr
))->invites
++;
1388 /** Delete an invite
1389 * Delete Invite block from channel invite list and client invite list
1391 * @param cptr Client pointer
1392 * @param chptr Channel pointer
1394 void del_invite(struct Client
*cptr
, struct Channel
*chptr
)
1396 struct SLink
**inv
, *tmp
;
1398 for (inv
= &(chptr
->invites
); (tmp
= *inv
); inv
= &tmp
->next
)
1399 if (tmp
->value
.cptr
== cptr
)
1404 (cli_user(cptr
))->invites
--;
1408 for (inv
= &((cli_user(cptr
))->invited
); (tmp
= *inv
); inv
= &tmp
->next
)
1409 if (tmp
->value
.chptr
== chptr
)
1418 /** @page zombie Explanation of Zombies
1422 * A channel member is turned into a zombie when he is kicked from a
1423 * channel but his server has not acknowledged the kick. Servers that
1424 * see the member as a zombie can accept actions he performed before
1425 * being kicked, without allowing chanop operations from outsiders or
1426 * desyncing the network.
1434 * X --a--> A --b--> B --d--> D
1439 * Where `who' is being KICK-ed by a "KICK" message received by server 'A'
1440 * via 'a', or on server 'B' via either 'b' or 'c', or on server D via 'd'.
1442 * a) On server A : set CHFL_ZOMBIE for `who' (lp) and pass on the KICK.
1443 * Remove the user immediately when no users are left on the channel.
1444 * b) On server B : remove the user (who/lp) from the channel, send a
1445 * PART upstream (to A) and pass on the KICK.
1446 * c) KICKed by `client'; On server B : remove the user (who/lp) from the
1447 * channel, and pass on the KICK.
1448 * d) On server D : remove the user (who/lp) from the channel, and pass on
1452 * - Setting the ZOMBIE flag never hurts, we either remove the
1453 * client after that or we don't.
1454 * - The KICK message was already passed on, as should be in all cases.
1455 * - `who' is removed in all cases except case a) when users are left.
1456 * - A PART is only sent upstream in case b).
1462 * 1 --- 2 --- 3 --- 4 --- 5
1467 * We also need to turn 'who' into a zombie on servers 1 and 6,
1468 * because a KICK from 'who' (kicking someone else in that direction)
1469 * can arrive there afterward - which should not be bounced itself.
1470 * Therefore case a) also applies for servers 1 and 6.
1475 /** Turn a user on a channel into a zombie
1476 * This function turns a user into a zombie (see \ref zombie)
1478 * @param member The structure representing this user on this channel.
1479 * @param who The client that is being kicked.
1480 * @param cptr The connection the kick came from.
1481 * @param sptr The client that is doing the kicking.
1482 * @param chptr The channel the user is being kicked from.
1484 void make_zombie(struct Membership
* member
, struct Client
* who
,
1485 struct Client
* cptr
, struct Client
* sptr
, struct Channel
* chptr
)
1487 assert(0 != member
);
1492 /* Default for case a): */
1495 /* Case b) or c) ?: */
1496 if (MyUser(who
)) /* server 4 */
1498 if (IsServer(cptr
)) /* Case b) ? */
1499 sendcmdto_one(who
, CMD_PART
, cptr
, "%H", chptr
);
1500 remove_user_from_channel(who
, chptr
);
1503 if (cli_from(who
) == cptr
) /* True on servers 1, 5 and 6 */
1505 struct Client
*acptr
= IsServer(sptr
) ? sptr
: (cli_user(sptr
))->server
;
1506 for (; acptr
!= &me
; acptr
= (cli_serv(acptr
))->up
)
1507 if (acptr
== (cli_user(who
))->server
) /* Case d) (server 5) */
1509 remove_user_from_channel(who
, chptr
);
1514 /* Case a) (servers 1, 2, 3 and 6) */
1515 if (channel_all_zombies(chptr
))
1516 remove_user_from_channel(who
, chptr
);
1518 /* XXX Can't actually call Debug here; if the channel is all zombies,
1519 * chptr will no longer exist when we get here.
1520 Debug((DEBUG_INFO, "%s is now a zombie on %s", who->name, chptr->chname));
1524 /** returns the number of zombies on a channel
1525 * @param chptr Channel to count zombies in.
1527 * @returns The number of zombies on the channel.
1529 int number_of_zombies(struct Channel
*chptr
)
1531 struct Membership
* member
;
1535 for (member
= chptr
->members
; member
; member
= member
->next_member
) {
1536 if (IsZombie(member
))
1542 /** Concatenate some strings together.
1543 * This helper function builds an argument string in strptr, consisting
1544 * of the original string, a space, and str1 and str2 concatenated (if,
1545 * of course, str2 is not NULL)
1547 * @param strptr The buffer to concatenate into
1548 * @param strptr_i modified offset to the position to modify
1549 * @param str1 The string to concatenate from.
1550 * @param str2 The second string to contatenate from.
1551 * @param c Charactor to separate the string from str1 and str2.
1554 build_string(char *strptr
, int *strptr_i
, const char *str1
,
1555 const char *str2
, char c
)
1558 strptr
[(*strptr_i
)++] = c
;
1561 strptr
[(*strptr_i
)++] = *(str1
++);
1565 strptr
[(*strptr_i
)++] = *(str2
++);
1567 strptr
[(*strptr_i
)] = '\0';
1570 /** Flush out the modes
1571 * This is the workhorse of our ModeBuf suite; this actually generates the
1572 * output MODE commands, HACK notices, or whatever. It's pretty complicated.
1574 * @param mbuf The mode buffer to flush
1575 * @param all If true, flush all modes, otherwise leave partial modes in the
1581 modebuf_flush_int(struct ModeBuf
*mbuf
, int all
)
1583 /* we only need the flags that don't take args right now */
1584 static int flags
[] = {
1585 /* MODE_CHANOP, 'o', */
1586 /* MODE_VOICE, 'v', */
1589 MODE_MODERATED
, 'm',
1590 MODE_TOPICLIMIT
, 't',
1591 MODE_INVITEONLY
, 'i',
1592 MODE_NOPRIVMSGS
, 'n',
1595 /* MODE_KEY, 'k', */
1596 /* MODE_BAN, 'b', */
1598 /* MODE_APASS, 'A', */
1599 /* MODE_UPASS, 'U', */
1600 MODE_NOQUITPARTS
, 'u',
1606 static int local_flags
[] = {
1607 MODE_WASDELJOINS
, 'd',
1613 struct Client
*app_source
; /* where the MODE appears to come from */
1615 char addbuf
[20], addbuf_local
[20]; /* accumulates +psmtin, etc. */
1616 int addbuf_i
= 0, addbuf_local_i
= 0;
1617 char rembuf
[20], rembuf_local
[20]; /* accumulates -psmtin, etc. */
1618 int rembuf_i
= 0, rembuf_local_i
= 0;
1619 char *bufptr
; /* we make use of indirection to simplify the code */
1622 char addstr
[BUFSIZE
]; /* accumulates MODE parameters to add */
1624 char remstr
[BUFSIZE
]; /* accumulates MODE parameters to remove */
1626 char *strptr
; /* more indirection to simplify the code */
1629 int totalbuflen
= BUFSIZE
- 200; /* fuzz factor -- don't overrun buffer! */
1632 char limitbuf
[20]; /* convert limits to strings */
1634 unsigned int limitdel
= MODE_LIMIT
;
1638 /* If the ModeBuf is empty, we have nothing to do */
1639 if (mbuf
->mb_add
== 0 && mbuf
->mb_rem
== 0 && mbuf
->mb_count
== 0)
1642 /* Ok, if we were given the OPMODE flag, or its a server, hide the source.
1644 if (feature_bool(FEAT_HIS_MODEWHO
) &&
1645 (mbuf
->mb_dest
& MODEBUF_DEST_OPMODE
||
1646 IsServer(mbuf
->mb_source
) ||
1647 IsMe(mbuf
->mb_source
)))
1650 app_source
= mbuf
->mb_source
;
1653 * Account for user we're bouncing; we have to get it in on the first
1654 * bounced MODE, or we could have problems
1656 if (mbuf
->mb_dest
& MODEBUF_DEST_DEOP
)
1657 totalbuflen
-= 6; /* numeric nick == 5, plus one space */
1659 /* Calculate the simple flags */
1660 for (flag_p
= flags
; flag_p
[0]; flag_p
+= 2) {
1661 if (*flag_p
& mbuf
->mb_add
)
1662 addbuf
[addbuf_i
++] = flag_p
[1];
1663 else if (*flag_p
& mbuf
->mb_rem
)
1664 rembuf
[rembuf_i
++] = flag_p
[1];
1667 /* Some flags may be for local display only. */
1668 for (flag_p
= local_flags
; flag_p
[0]; flag_p
+= 2) {
1669 if (*flag_p
& mbuf
->mb_add
)
1670 addbuf_local
[addbuf_local_i
++] = flag_p
[1];
1671 else if (*flag_p
& mbuf
->mb_rem
)
1672 rembuf_local
[rembuf_local_i
++] = flag_p
[1];
1675 /* Now go through the modes with arguments... */
1676 for (i
= 0; i
< mbuf
->mb_count
; i
++) {
1677 if (MB_TYPE(mbuf
, i
) & MODE_ADD
) { /* adding or removing? */
1679 bufptr_i
= &addbuf_i
;
1682 bufptr_i
= &rembuf_i
;
1685 if (MB_TYPE(mbuf
, i
) & (MODE_CHANOP
| MODE_VOICE
)) {
1686 tmp
= strlen(cli_name(MB_CLIENT(mbuf
, i
)));
1688 if ((totalbuflen
- IRCD_MAX(9, tmp
)) <= 0) /* don't overflow buffer */
1689 MB_TYPE(mbuf
, i
) |= MODE_SAVE
; /* save for later */
1691 bufptr
[(*bufptr_i
)++] = MB_TYPE(mbuf
, i
) & MODE_CHANOP
? 'o' : 'v';
1692 totalbuflen
-= IRCD_MAX(9, tmp
) + 1;
1694 } else if (MB_TYPE(mbuf
, i
) & (MODE_BAN
| MODE_APASS
| MODE_UPASS
)) {
1695 tmp
= strlen(MB_STRING(mbuf
, i
));
1697 if ((totalbuflen
- tmp
) <= 0) /* don't overflow buffer */
1698 MB_TYPE(mbuf
, i
) |= MODE_SAVE
; /* save for later */
1701 switch(MB_TYPE(mbuf
, i
) & (MODE_BAN
| MODE_APASS
| MODE_UPASS
))
1713 bufptr
[(*bufptr_i
)++] = mode_char
;
1714 totalbuflen
-= tmp
+ 1;
1716 } else if (MB_TYPE(mbuf
, i
) & MODE_KEY
) {
1717 tmp
= (mbuf
->mb_dest
& MODEBUF_DEST_NOKEY
? 1 :
1718 strlen(MB_STRING(mbuf
, i
)));
1720 if ((totalbuflen
- tmp
) <= 0) /* don't overflow buffer */
1721 MB_TYPE(mbuf
, i
) |= MODE_SAVE
; /* save for later */
1723 bufptr
[(*bufptr_i
)++] = 'k';
1724 totalbuflen
-= tmp
+ 1;
1726 } else if (MB_TYPE(mbuf
, i
) & MODE_LIMIT
) {
1727 /* if it's a limit, we also format the number */
1728 ircd_snprintf(0, limitbuf
, sizeof(limitbuf
), "%u", MB_UINT(mbuf
, i
));
1730 tmp
= strlen(limitbuf
);
1732 if ((totalbuflen
- tmp
) <= 0) /* don't overflow buffer */
1733 MB_TYPE(mbuf
, i
) |= MODE_SAVE
; /* save for later */
1735 bufptr
[(*bufptr_i
)++] = 'l';
1736 totalbuflen
-= tmp
+ 1;
1741 /* terminate the mode strings */
1742 addbuf
[addbuf_i
] = '\0';
1743 rembuf
[rembuf_i
] = '\0';
1744 addbuf_local
[addbuf_local_i
] = '\0';
1745 rembuf_local
[rembuf_local_i
] = '\0';
1747 /* If we're building a user visible MODE or HACK... */
1748 if (mbuf
->mb_dest
& (MODEBUF_DEST_CHANNEL
| MODEBUF_DEST_HACK2
|
1749 MODEBUF_DEST_HACK3
| MODEBUF_DEST_HACK4
|
1750 MODEBUF_DEST_LOG
)) {
1751 /* Set up the parameter strings */
1757 for (i
= 0; i
< mbuf
->mb_count
; i
++) {
1758 if (MB_TYPE(mbuf
, i
) & MODE_SAVE
)
1761 if (MB_TYPE(mbuf
, i
) & MODE_ADD
) { /* adding or removing? */
1763 strptr_i
= &addstr_i
;
1766 strptr_i
= &remstr_i
;
1769 /* deal with clients... */
1770 if (MB_TYPE(mbuf
, i
) & (MODE_CHANOP
| MODE_VOICE
))
1771 build_string(strptr
, strptr_i
, cli_name(MB_CLIENT(mbuf
, i
)), 0, ' ');
1773 /* deal with bans... */
1774 else if (MB_TYPE(mbuf
, i
) & MODE_BAN
)
1775 build_string(strptr
, strptr_i
, MB_STRING(mbuf
, i
), 0, ' ');
1777 /* deal with keys... */
1778 else if (MB_TYPE(mbuf
, i
) & MODE_KEY
)
1779 build_string(strptr
, strptr_i
, mbuf
->mb_dest
& MODEBUF_DEST_NOKEY
?
1780 "*" : MB_STRING(mbuf
, i
), 0, ' ');
1782 /* deal with invisible passwords */
1783 else if (MB_TYPE(mbuf
, i
) & (MODE_APASS
| MODE_UPASS
))
1784 build_string(strptr
, strptr_i
, "*", 0, ' ');
1787 * deal with limit; note we cannot include the limit parameter if we're
1790 else if ((MB_TYPE(mbuf
, i
) & (MODE_ADD
| MODE_LIMIT
)) ==
1791 (MODE_ADD
| MODE_LIMIT
))
1792 build_string(strptr
, strptr_i
, limitbuf
, 0, ' ');
1795 /* send the messages off to their destination */
1796 if (mbuf
->mb_dest
& MODEBUF_DEST_HACK2
)
1797 sendto_opmask_butone(0, SNO_HACK2
, "HACK(2): %s MODE %s %s%s%s%s%s%s "
1799 cli_name(feature_bool(FEAT_HIS_SNOTICES
) ?
1800 mbuf
->mb_source
: app_source
),
1801 mbuf
->mb_channel
->chname
,
1802 rembuf_i
? "-" : "", rembuf
, addbuf_i
? "+" : "",
1803 addbuf
, remstr
, addstr
,
1804 mbuf
->mb_channel
->creationtime
);
1806 if (mbuf
->mb_dest
& MODEBUF_DEST_HACK3
)
1807 sendto_opmask_butone(0, SNO_HACK3
, "BOUNCE or HACK(3): %s MODE %s "
1808 "%s%s%s%s%s%s [%Tu]",
1809 cli_name(feature_bool(FEAT_HIS_SNOTICES
) ?
1810 mbuf
->mb_source
: app_source
),
1811 mbuf
->mb_channel
->chname
, rembuf_i
? "-" : "",
1812 rembuf
, addbuf_i
? "+" : "", addbuf
, remstr
, addstr
,
1813 mbuf
->mb_channel
->creationtime
);
1815 if (mbuf
->mb_dest
& MODEBUF_DEST_HACK4
)
1816 sendto_opmask_butone(0, SNO_HACK4
, "HACK(4): %s MODE %s %s%s%s%s%s%s "
1818 cli_name(feature_bool(FEAT_HIS_SNOTICES
) ?
1819 mbuf
->mb_source
: app_source
),
1820 mbuf
->mb_channel
->chname
,
1821 rembuf_i
? "-" : "", rembuf
, addbuf_i
? "+" : "",
1822 addbuf
, remstr
, addstr
,
1823 mbuf
->mb_channel
->creationtime
);
1825 if (mbuf
->mb_dest
& MODEBUF_DEST_LOG
)
1826 log_write(LS_OPERMODE
, L_INFO
, LOG_NOSNOTICE
,
1827 "%#C OPMODE %H %s%s%s%s%s%s", mbuf
->mb_source
,
1828 mbuf
->mb_channel
, rembuf_i
? "-" : "", rembuf
,
1829 addbuf_i
? "+" : "", addbuf
, remstr
, addstr
);
1831 if (mbuf
->mb_dest
& MODEBUF_DEST_CHANNEL
)
1832 sendcmdto_channel_butserv_butone(app_source
, CMD_MODE
, mbuf
->mb_channel
, NULL
, 0,
1833 "%H %s%s%s%s%s%s%s%s", mbuf
->mb_channel
,
1834 rembuf_i
|| rembuf_local_i
? "-" : "",
1835 rembuf
, rembuf_local
,
1836 addbuf_i
|| addbuf_local_i
? "+" : "",
1837 addbuf
, addbuf_local
,
1841 /* Now are we supposed to propagate to other servers? */
1842 if (mbuf
->mb_dest
& MODEBUF_DEST_SERVER
) {
1843 /* set up parameter string */
1850 * limit is supressed if we're removing it; we have to figure out which
1851 * direction is the direction for it to be removed, though...
1853 limitdel
|= (mbuf
->mb_dest
& MODEBUF_DEST_BOUNCE
) ? MODE_DEL
: MODE_ADD
;
1855 for (i
= 0; i
< mbuf
->mb_count
; i
++) {
1856 if (MB_TYPE(mbuf
, i
) & MODE_SAVE
)
1859 if (MB_TYPE(mbuf
, i
) & MODE_ADD
) { /* adding or removing? */
1861 strptr_i
= &addstr_i
;
1864 strptr_i
= &remstr_i
;
1867 /* if we're changing oplevels and we know the oplevel, pass it on */
1868 if (mbuf
->mb_channel
->mode
.apass
[0]
1869 && (MB_TYPE(mbuf
, i
) & MODE_CHANOP
)
1870 && MB_OPLEVEL(mbuf
, i
) < MAXOPLEVEL
)
1871 *strptr_i
+= ircd_snprintf(0, strptr
+ *strptr_i
, BUFSIZE
- *strptr_i
,
1873 NumNick(MB_CLIENT(mbuf
, i
)),
1874 MB_OPLEVEL(mbuf
, i
));
1876 /* deal with other modes that take clients */
1877 else if (MB_TYPE(mbuf
, i
) & (MODE_CHANOP
| MODE_VOICE
))
1878 build_string(strptr
, strptr_i
, NumNick(MB_CLIENT(mbuf
, i
)), ' ');
1880 /* deal with modes that take strings */
1881 else if (MB_TYPE(mbuf
, i
) & (MODE_KEY
| MODE_BAN
| MODE_APASS
| MODE_UPASS
))
1882 build_string(strptr
, strptr_i
, MB_STRING(mbuf
, i
), 0, ' ');
1885 * deal with the limit. Logic here is complicated; if HACK2 is set,
1886 * we're bouncing the mode, so sense is reversed, and we have to
1887 * include the original limit if it looks like it's being removed
1889 else if ((MB_TYPE(mbuf
, i
) & limitdel
) == limitdel
)
1890 build_string(strptr
, strptr_i
, limitbuf
, 0, ' ');
1893 /* we were told to deop the source */
1894 if (mbuf
->mb_dest
& MODEBUF_DEST_DEOP
) {
1895 addbuf
[addbuf_i
++] = 'o'; /* remember, sense is reversed */
1896 addbuf
[addbuf_i
] = '\0'; /* terminate the string... */
1897 build_string(addstr
, &addstr_i
, NumNick(mbuf
->mb_source
), ' ');
1899 /* mark that we've done this, so we don't do it again */
1900 mbuf
->mb_dest
&= ~MODEBUF_DEST_DEOP
;
1903 if (mbuf
->mb_dest
& MODEBUF_DEST_OPMODE
) {
1904 /* If OPMODE was set, we're propagating the mode as an OPMODE message */
1905 sendcmdto_serv_butone(mbuf
->mb_source
, CMD_OPMODE
, mbuf
->mb_connect
,
1906 "%H %s%s%s%s%s%s", mbuf
->mb_channel
,
1907 rembuf_i
? "-" : "", rembuf
, addbuf_i
? "+" : "",
1908 addbuf
, remstr
, addstr
);
1909 } else if (mbuf
->mb_dest
& MODEBUF_DEST_BOUNCE
) {
1911 * If HACK2 was set, we're bouncing; we send the MODE back to
1912 * the connection we got it from with the senses reversed and
1913 * the proper TS; origin is us
1915 sendcmdto_one(&me
, CMD_MODE
, mbuf
->mb_connect
, "%H %s%s%s%s%s%s %Tu",
1916 mbuf
->mb_channel
, addbuf_i
? "-" : "", addbuf
,
1917 rembuf_i
? "+" : "", rembuf
, addstr
, remstr
,
1918 mbuf
->mb_channel
->creationtime
);
1921 * We're propagating a normal (or HACK3 or HACK4) MODE command
1922 * to the rest of the network. We send the actual channel TS.
1924 sendcmdto_serv_butone(mbuf
->mb_source
, CMD_MODE
, mbuf
->mb_connect
,
1925 "%H %s%s%s%s%s%s %Tu", mbuf
->mb_channel
,
1926 rembuf_i
? "-" : "", rembuf
, addbuf_i
? "+" : "",
1927 addbuf
, remstr
, addstr
,
1928 mbuf
->mb_channel
->creationtime
);
1932 /* We've drained the ModeBuf... */
1937 /* reinitialize the mode-with-arg slots */
1938 for (i
= 0; i
< MAXMODEPARAMS
; i
++) {
1939 /* If we saved any, pack them down */
1940 if (MB_TYPE(mbuf
, i
) & MODE_SAVE
) {
1941 mbuf
->mb_modeargs
[mbuf
->mb_count
] = mbuf
->mb_modeargs
[i
];
1942 MB_TYPE(mbuf
, mbuf
->mb_count
) &= ~MODE_SAVE
; /* don't save anymore */
1944 if (mbuf
->mb_count
++ == i
) /* don't overwrite our hard work */
1946 } else if (MB_TYPE(mbuf
, i
) & MODE_FREE
)
1947 MyFree(MB_STRING(mbuf
, i
)); /* free string if needed */
1949 MB_TYPE(mbuf
, i
) = 0;
1950 MB_UINT(mbuf
, i
) = 0;
1953 /* If we're supposed to flush it all, do so--all hail tail recursion */
1954 if (all
&& mbuf
->mb_count
)
1955 return modebuf_flush_int(mbuf
, 1);
1960 /** Initialise a modebuf
1961 * This routine just initializes a ModeBuf structure with the information
1962 * needed and the options given.
1964 * @param mbuf The mode buffer to initialise.
1965 * @param source The client that is performing the mode.
1967 * @param chan The channel that the mode is being performed upon.
1971 modebuf_init(struct ModeBuf
*mbuf
, struct Client
*source
,
1972 struct Client
*connect
, struct Channel
*chan
, unsigned int dest
)
1977 assert(0 != source
);
1981 if (IsLocalChannel(chan
->chname
)) dest
&= ~MODEBUF_DEST_SERVER
;
1985 mbuf
->mb_source
= source
;
1986 mbuf
->mb_connect
= connect
;
1987 mbuf
->mb_channel
= chan
;
1988 mbuf
->mb_dest
= dest
;
1991 /* clear each mode-with-parameter slot */
1992 for (i
= 0; i
< MAXMODEPARAMS
; i
++) {
1993 MB_TYPE(mbuf
, i
) = 0;
1994 MB_UINT(mbuf
, i
) = 0;
1998 /** Append a new mode to a modebuf
1999 * This routine simply adds modes to be added or deleted; do a binary OR
2000 * with either MODE_ADD or MODE_DEL
2002 * @param mbuf Mode buffer
2003 * @param mode MODE_ADD or MODE_DEL OR'd with MODE_PRIVATE etc.
2006 modebuf_mode(struct ModeBuf
*mbuf
, unsigned int mode
)
2009 assert(0 != (mode
& (MODE_ADD
| MODE_DEL
)));
2011 mode
&= (MODE_ADD
| MODE_DEL
| MODE_PRIVATE
| MODE_SECRET
| MODE_MODERATED
|
2012 MODE_TOPICLIMIT
| MODE_INVITEONLY
| MODE_NOPRIVMSGS
| MODE_REGONLY
|
2013 MODE_DELJOINS
| MODE_WASDELJOINS
| MODE_NOQUITPARTS
| MODE_NOCOLOUR
|
2014 MODE_NOCTCP
| MODE_NONOTICE
);
2016 if (!(mode
& ~(MODE_ADD
| MODE_DEL
))) /* don't add empty modes... */
2019 if (mode
& MODE_ADD
) {
2020 mbuf
->mb_rem
&= ~mode
;
2021 mbuf
->mb_add
|= mode
;
2023 mbuf
->mb_add
&= ~mode
;
2024 mbuf
->mb_rem
|= mode
;
2028 /** Append a mode that takes an int argument to the modebuf
2030 * This routine adds a mode to be added or deleted that takes a unsigned
2031 * int parameter; mode may *only* be the relevant mode flag ORed with one
2032 * of MODE_ADD or MODE_DEL
2034 * @param mbuf The mode buffer to append to.
2035 * @param mode The mode to append.
2036 * @param uint The argument to the mode.
2039 modebuf_mode_uint(struct ModeBuf
*mbuf
, unsigned int mode
, unsigned int uint
)
2042 assert(0 != (mode
& (MODE_ADD
| MODE_DEL
)));
2044 if (mode
== (MODE_LIMIT
| ((mbuf
->mb_dest
& MODEBUF_DEST_BOUNCE
) ? MODE_ADD
: MODE_DEL
))) {
2045 mbuf
->mb_rem
|= mode
;
2048 MB_TYPE(mbuf
, mbuf
->mb_count
) = mode
;
2049 MB_UINT(mbuf
, mbuf
->mb_count
) = uint
;
2051 /* when we've reached the maximal count, flush the buffer */
2052 if (++mbuf
->mb_count
>=
2053 (MAXMODEPARAMS
- (mbuf
->mb_dest
& MODEBUF_DEST_DEOP
? 1 : 0)))
2054 modebuf_flush_int(mbuf
, 0);
2057 /** append a string mode
2058 * This routine adds a mode to be added or deleted that takes a string
2059 * parameter; mode may *only* be the relevant mode flag ORed with one of
2060 * MODE_ADD or MODE_DEL
2062 * @param mbuf The mode buffer to append to.
2063 * @param mode The mode to append.
2064 * @param string The string parameter to append.
2065 * @param free If the string should be free'd later.
2068 modebuf_mode_string(struct ModeBuf
*mbuf
, unsigned int mode
, char *string
,
2072 assert(0 != (mode
& (MODE_ADD
| MODE_DEL
)));
2074 MB_TYPE(mbuf
, mbuf
->mb_count
) = mode
| (free
? MODE_FREE
: 0);
2075 MB_STRING(mbuf
, mbuf
->mb_count
) = string
;
2077 /* when we've reached the maximal count, flush the buffer */
2078 if (++mbuf
->mb_count
>=
2079 (MAXMODEPARAMS
- (mbuf
->mb_dest
& MODEBUF_DEST_DEOP
? 1 : 0)))
2080 modebuf_flush_int(mbuf
, 0);
2083 /** Append a mode on a client to a modebuf.
2084 * This routine adds a mode to be added or deleted that takes a client
2085 * parameter; mode may *only* be the relevant mode flag ORed with one of
2086 * MODE_ADD or MODE_DEL
2088 * @param mbuf The modebuf to append the mode to.
2089 * @param mode The mode to append.
2090 * @param client The client argument to append.
2091 * @param oplevel The oplevel the user had or will have
2094 modebuf_mode_client(struct ModeBuf
*mbuf
, unsigned int mode
,
2095 struct Client
*client
, int oplevel
)
2098 assert(0 != (mode
& (MODE_ADD
| MODE_DEL
)));
2100 MB_TYPE(mbuf
, mbuf
->mb_count
) = mode
;
2101 MB_CLIENT(mbuf
, mbuf
->mb_count
) = client
;
2102 MB_OPLEVEL(mbuf
, mbuf
->mb_count
) = oplevel
;
2104 /* when we've reached the maximal count, flush the buffer */
2105 if (++mbuf
->mb_count
>=
2106 (MAXMODEPARAMS
- (mbuf
->mb_dest
& MODEBUF_DEST_DEOP
? 1 : 0)))
2107 modebuf_flush_int(mbuf
, 0);
2110 /** The exported binding for modebuf_flush()
2112 * @param mbuf The mode buffer to flush.
2114 * @see modebuf_flush_int()
2117 modebuf_flush(struct ModeBuf
*mbuf
)
2119 struct Membership
*memb
;
2121 /* Check if MODE_WASDELJOINS should be set */
2122 if (!(mbuf
->mb_channel
->mode
.mode
& (MODE_DELJOINS
| MODE_WASDELJOINS
))
2123 && (mbuf
->mb_rem
& MODE_DELJOINS
)) {
2124 for (memb
= mbuf
->mb_channel
->members
; memb
; memb
= memb
->next_member
) {
2125 if (IsDelayedJoin(memb
)) {
2126 mbuf
->mb_channel
->mode
.mode
|= MODE_WASDELJOINS
;
2127 mbuf
->mb_add
|= MODE_WASDELJOINS
;
2128 mbuf
->mb_rem
&= ~MODE_WASDELJOINS
;
2134 return modebuf_flush_int(mbuf
, 1);
2137 /* This extracts the simple modes contained in mbuf
2139 * @param mbuf The mode buffer to extract the modes from.
2140 * @param buf The string buffer to write the modes into.
2143 modebuf_extract(struct ModeBuf
*mbuf
, char *buf
)
2145 static int flags
[] = {
2146 /* MODE_CHANOP, 'o', */
2147 /* MODE_VOICE, 'v', */
2150 MODE_MODERATED
, 'm',
2151 MODE_TOPICLIMIT
, 't',
2152 MODE_INVITEONLY
, 'i',
2153 MODE_NOPRIVMSGS
, 'n',
2157 /* MODE_BAN, 'b', */
2161 MODE_NOQUITPARTS
, 'u',
2168 int i
, bufpos
= 0, len
;
2170 char *key
= 0, limitbuf
[20];
2171 char *apass
= 0, *upass
= 0;
2180 for (i
= 0; i
< mbuf
->mb_count
; i
++) { /* find keys and limits */
2181 if (MB_TYPE(mbuf
, i
) & MODE_ADD
) {
2182 add
|= MB_TYPE(mbuf
, i
) & (MODE_KEY
| MODE_LIMIT
| MODE_APASS
| MODE_UPASS
);
2184 if (MB_TYPE(mbuf
, i
) & MODE_KEY
) /* keep strings */
2185 key
= MB_STRING(mbuf
, i
);
2186 else if (MB_TYPE(mbuf
, i
) & MODE_LIMIT
)
2187 ircd_snprintf(0, limitbuf
, sizeof(limitbuf
), "%u", MB_UINT(mbuf
, i
));
2188 else if (MB_TYPE(mbuf
, i
) & MODE_UPASS
)
2189 upass
= MB_STRING(mbuf
, i
);
2190 else if (MB_TYPE(mbuf
, i
) & MODE_APASS
)
2191 apass
= MB_STRING(mbuf
, i
);
2198 buf
[bufpos
++] = '+'; /* start building buffer */
2200 for (flag_p
= flags
; flag_p
[0]; flag_p
+= 2)
2202 buf
[bufpos
++] = flag_p
[1];
2204 for (i
= 0, len
= bufpos
; i
< len
; i
++) {
2206 build_string(buf
, &bufpos
, key
, 0, ' ');
2207 else if (buf
[i
] == 'l')
2208 build_string(buf
, &bufpos
, limitbuf
, 0, ' ');
2209 else if (buf
[i
] == 'U')
2210 build_string(buf
, &bufpos
, upass
, 0, ' ');
2211 else if (buf
[i
] == 'A')
2212 build_string(buf
, &bufpos
, apass
, 0, ' ');
2220 /** Simple function to invalidate bans
2222 * This function sets all bans as being valid.
2224 * @param chan The channel to operate on.
2227 mode_ban_invalidate(struct Channel
*chan
)
2229 struct Membership
*member
;
2231 for (member
= chan
->members
; member
; member
= member
->next_member
)
2232 ClearBanValid(member
);
2235 /** Simple function to drop invite structures
2237 * Remove all the invites on the channel.
2239 * @param chan Channel to remove invites from.
2243 mode_invite_clear(struct Channel
*chan
)
2245 while (chan
->invites
)
2246 del_invite(chan
->invites
->value
.cptr
, chan
);
2249 /* What we've done for mode_parse so far... */
2250 #define DONE_LIMIT 0x01 /**< We've set the limit */
2251 #define DONE_KEY 0x02 /**< We've set the key */
2252 #define DONE_BANLIST 0x04 /**< We've sent the ban list */
2253 #define DONE_NOTOPER 0x08 /**< We've sent a "Not oper" error */
2254 #define DONE_BANCLEAN 0x10 /**< We've cleaned bans... */
2255 #define DONE_UPASS 0x20 /**< We've set user pass */
2256 #define DONE_APASS 0x40 /**< We've set admin pass */
2259 struct ModeBuf
*mbuf
;
2260 struct Client
*cptr
;
2261 struct Client
*sptr
;
2262 struct Channel
*chptr
;
2263 struct Membership
*member
;
2274 struct Ban banlist
[MAXPARA
];
2277 unsigned short oplevel
;
2278 struct Client
*client
;
2279 } cli_change
[MAXPARA
];
2282 /** Helper function to send "Not oper" or "Not member" messages
2283 * Here's a helper function to deal with sending along "Not oper" or
2284 * "Not member" messages
2286 * @param state Parsing State object
2289 send_notoper(struct ParseState
*state
)
2291 if (state
->done
& DONE_NOTOPER
)
2294 send_reply(state
->sptr
, (state
->flags
& MODE_PARSE_NOTOPER
) ?
2295 ERR_CHANOPRIVSNEEDED
: ERR_NOTONCHANNEL
, state
->chptr
->chname
);
2297 state
->done
|= DONE_NOTOPER
;
2301 * Helper function to convert limits
2303 * @param state Parsing state object.
2307 mode_parse_limit(struct ParseState
*state
, int *flag_p
)
2309 unsigned int t_limit
;
2311 if (state
->dir
== MODE_ADD
) { /* convert arg only if adding limit */
2312 if (MyUser(state
->sptr
) && state
->max_args
<= 0) /* too many args? */
2315 if (state
->parc
<= 0) { /* warn if not enough args */
2316 if (MyUser(state
->sptr
))
2317 need_more_params(state
->sptr
, "MODE +l");
2321 t_limit
= strtoul(state
->parv
[state
->args_used
++], 0, 10); /* grab arg */
2325 if ((int)t_limit
<0) /* don't permit a negative limit */
2328 if (!(state
->flags
& MODE_PARSE_WIPEOUT
) &&
2329 (!t_limit
|| t_limit
== state
->chptr
->mode
.limit
))
2332 t_limit
= state
->chptr
->mode
.limit
;
2334 /* If they're not an oper, they can't change modes */
2335 if (state
->flags
& (MODE_PARSE_NOTOPER
| MODE_PARSE_NOTMEMBER
)) {
2336 send_notoper(state
);
2340 /* Can't remove a limit that's not there */
2341 if (state
->dir
== MODE_DEL
&& !state
->chptr
->mode
.limit
)
2344 /* Skip if this is a burst and a lower limit than this is set already */
2345 if ((state
->flags
& MODE_PARSE_BURST
) &&
2346 (state
->chptr
->mode
.mode
& flag_p
[0]) &&
2347 (state
->chptr
->mode
.limit
< t_limit
))
2350 if (state
->done
& DONE_LIMIT
) /* allow limit to be set only once */
2352 state
->done
|= DONE_LIMIT
;
2357 modebuf_mode_uint(state
->mbuf
, state
->dir
| flag_p
[0], t_limit
);
2359 if (state
->flags
& MODE_PARSE_SET
) { /* set the limit */
2360 if (state
->dir
& MODE_ADD
) {
2361 state
->chptr
->mode
.mode
|= flag_p
[0];
2362 state
->chptr
->mode
.limit
= t_limit
;
2364 state
->chptr
->mode
.mode
&= ~flag_p
[0];
2365 state
->chptr
->mode
.limit
= 0;
2370 /** Helper function to clean key-like parameters. */
2376 while (*s
> ' ' && *s
!= ':' && *s
!= ',' && t_len
--)
2382 * Helper function to convert keys
2385 mode_parse_key(struct ParseState
*state
, int *flag_p
)
2389 if (MyUser(state
->sptr
) && state
->max_args
<= 0) /* drop if too many args */
2392 if (state
->parc
<= 0) { /* warn if not enough args */
2393 if (MyUser(state
->sptr
))
2394 need_more_params(state
->sptr
, state
->dir
== MODE_ADD
? "MODE +k" :
2399 t_str
= state
->parv
[state
->args_used
++]; /* grab arg */
2403 /* If they're not an oper, they can't change modes */
2404 if (state
->flags
& (MODE_PARSE_NOTOPER
| MODE_PARSE_NOTMEMBER
)) {
2405 send_notoper(state
);
2409 if (state
->done
& DONE_KEY
) /* allow key to be set only once */
2411 state
->done
|= DONE_KEY
;
2413 /* clean up the key string */
2415 if (!*t_str
|| *t_str
== ':') { /* warn if empty */
2416 if (MyUser(state
->sptr
))
2417 need_more_params(state
->sptr
, state
->dir
== MODE_ADD
? "MODE +k" :
2425 /* Skip if this is a burst, we have a key already and the new key is
2426 * after the old one alphabetically */
2427 if ((state
->flags
& MODE_PARSE_BURST
) &&
2428 *(state
->chptr
->mode
.key
) &&
2429 ircd_strcmp(state
->chptr
->mode
.key
, t_str
) <= 0)
2432 /* can't add a key if one is set, nor can one remove the wrong key */
2433 if (!(state
->flags
& MODE_PARSE_FORCE
))
2434 if ((state
->dir
== MODE_ADD
&& *state
->chptr
->mode
.key
) ||
2435 (state
->dir
== MODE_DEL
&&
2436 ircd_strcmp(state
->chptr
->mode
.key
, t_str
))) {
2437 send_reply(state
->sptr
, ERR_KEYSET
, state
->chptr
->chname
);
2441 if (!(state
->flags
& MODE_PARSE_WIPEOUT
) && state
->dir
== MODE_ADD
&&
2442 !ircd_strcmp(state
->chptr
->mode
.key
, t_str
))
2443 return; /* no key change */
2445 if (state
->flags
& MODE_PARSE_BOUNCE
) {
2446 if (*state
->chptr
->mode
.key
) /* reset old key */
2447 modebuf_mode_string(state
->mbuf
, MODE_DEL
| flag_p
[0],
2448 state
->chptr
->mode
.key
, 0);
2449 else /* remove new bogus key */
2450 modebuf_mode_string(state
->mbuf
, MODE_ADD
| flag_p
[0], t_str
, 0);
2451 } else /* send new key */
2452 modebuf_mode_string(state
->mbuf
, state
->dir
| flag_p
[0], t_str
, 0);
2454 if (state
->flags
& MODE_PARSE_SET
) {
2455 if (state
->dir
== MODE_DEL
) /* remove the old key */
2456 *state
->chptr
->mode
.key
= '\0';
2458 ircd_strncpy(state
->chptr
->mode
.key
, t_str
, KEYLEN
);
2463 * Helper function to convert user passes
2466 mode_parse_upass(struct ParseState
*state
, int *flag_p
)
2470 if (MyUser(state
->sptr
) && state
->max_args
<= 0) /* drop if too many args */
2473 if (state
->parc
<= 0) { /* warn if not enough args */
2474 if (MyUser(state
->sptr
))
2475 need_more_params(state
->sptr
, state
->dir
== MODE_ADD
? "MODE +U" :
2480 t_str
= state
->parv
[state
->args_used
++]; /* grab arg */
2484 /* If they're not an oper, they can't change modes */
2485 if (state
->flags
& (MODE_PARSE_NOTOPER
| MODE_PARSE_NOTMEMBER
)) {
2486 send_notoper(state
);
2490 /* If a non-service user is trying to force it, refuse. */
2491 if (state
->flags
& MODE_PARSE_FORCE
&& MyUser(state
->sptr
)
2492 && !HasPriv(state
->sptr
, PRIV_APASS_OPMODE
)) {
2493 send_reply(state
->sptr
, ERR_NOTMANAGER
, state
->chptr
->chname
,
2494 state
->chptr
->chname
);
2498 /* If they are not the channel manager, they are not allowed to change it */
2499 if (MyUser(state
->sptr
) && !(state
->flags
& MODE_PARSE_FORCE
|| IsChannelManager(state
->member
))) {
2500 if (*state
->chptr
->mode
.apass
) {
2501 send_reply(state
->sptr
, ERR_NOTMANAGER
, state
->chptr
->chname
,
2502 state
->chptr
->chname
);
2504 send_reply(state
->sptr
, ERR_NOMANAGER
, state
->chptr
->chname
,
2505 (TStime() - state
->chptr
->creationtime
< 172800) ?
2506 "approximately 4-5 minutes" : "approximately 48 hours");
2511 if (state
->done
& DONE_UPASS
) /* allow upass to be set only once */
2513 state
->done
|= DONE_UPASS
;
2515 /* clean up the upass string */
2517 if (!*t_str
|| *t_str
== ':') { /* warn if empty */
2518 if (MyUser(state
->sptr
))
2519 need_more_params(state
->sptr
, state
->dir
== MODE_ADD
? "MODE +U" :
2527 if (!(state
->flags
& MODE_PARSE_FORCE
)) {
2528 /* can't add the upass while apass is not set */
2529 if (state
->dir
== MODE_ADD
&& !*state
->chptr
->mode
.apass
) {
2530 send_reply(state
->sptr
, ERR_UPASSNOTSET
, state
->chptr
->chname
, state
->chptr
->chname
);
2533 /* cannot set a +U password that is the same as +A */
2534 if (state
->dir
== MODE_ADD
&& !ircd_strcmp(state
->chptr
->mode
.apass
, t_str
)) {
2535 send_reply(state
->sptr
, ERR_UPASS_SAME_APASS
, state
->chptr
->chname
);
2538 /* can't add a upass if one is set, nor can one remove the wrong upass */
2539 if ((state
->dir
== MODE_ADD
&& *state
->chptr
->mode
.upass
) ||
2540 (state
->dir
== MODE_DEL
&&
2541 ircd_strcmp(state
->chptr
->mode
.upass
, t_str
))) {
2542 send_reply(state
->sptr
, ERR_KEYSET
, state
->chptr
->chname
);
2547 if (!(state
->flags
& MODE_PARSE_WIPEOUT
) && state
->dir
== MODE_ADD
&&
2548 !ircd_strcmp(state
->chptr
->mode
.upass
, t_str
))
2549 return; /* no upass change */
2551 /* Skip if this is a burst, we have a Upass already and the new Upass is
2552 * after the old one alphabetically */
2553 if ((state
->flags
& MODE_PARSE_BURST
) &&
2554 *(state
->chptr
->mode
.upass
) &&
2555 ircd_strcmp(state
->chptr
->mode
.upass
, t_str
) <= 0)
2558 if (state
->flags
& MODE_PARSE_BOUNCE
) {
2559 if (*state
->chptr
->mode
.upass
) /* reset old upass */
2560 modebuf_mode_string(state
->mbuf
, MODE_DEL
| flag_p
[0],
2561 state
->chptr
->mode
.upass
, 0);
2562 else /* remove new bogus upass */
2563 modebuf_mode_string(state
->mbuf
, MODE_ADD
| flag_p
[0], t_str
, 0);
2564 } else /* send new upass */
2565 modebuf_mode_string(state
->mbuf
, state
->dir
| flag_p
[0], t_str
, 0);
2567 if (state
->flags
& MODE_PARSE_SET
) {
2568 if (state
->dir
== MODE_DEL
) /* remove the old upass */
2569 *state
->chptr
->mode
.upass
= '\0';
2571 ircd_strncpy(state
->chptr
->mode
.upass
, t_str
, KEYLEN
);
2576 * Helper function to convert admin passes
2579 mode_parse_apass(struct ParseState
*state
, int *flag_p
)
2581 struct Membership
*memb
;
2584 if (MyUser(state
->sptr
) && state
->max_args
<= 0) /* drop if too many args */
2587 if (state
->parc
<= 0) { /* warn if not enough args */
2588 if (MyUser(state
->sptr
))
2589 need_more_params(state
->sptr
, state
->dir
== MODE_ADD
? "MODE +A" :
2594 t_str
= state
->parv
[state
->args_used
++]; /* grab arg */
2598 /* If they're not an oper, they can't change modes */
2599 if (state
->flags
& (MODE_PARSE_NOTOPER
| MODE_PARSE_NOTMEMBER
)) {
2600 send_notoper(state
);
2604 if (MyUser(state
->sptr
)) {
2605 if (state
->flags
& MODE_PARSE_FORCE
) {
2606 /* If an unprivileged oper is trying to force it, refuse. */
2607 if (!HasPriv(state
->sptr
, PRIV_APASS_OPMODE
)) {
2608 send_reply(state
->sptr
, ERR_NOTMANAGER
, state
->chptr
->chname
,
2609 state
->chptr
->chname
);
2613 /* If they are not the channel manager, they are not allowed to change it. */
2614 if (!IsChannelManager(state
->member
)) {
2615 if (*state
->chptr
->mode
.apass
) {
2616 send_reply(state
->sptr
, ERR_NOTMANAGER
, state
->chptr
->chname
,
2617 state
->chptr
->chname
);
2619 send_reply(state
->sptr
, ERR_NOMANAGER
, state
->chptr
->chname
,
2620 (TStime() - state
->chptr
->creationtime
< 172800) ?
2621 "approximately 4-5 minutes" : "approximately 48 hours");
2625 /* Can't remove the Apass while Upass is still set. */
2626 if (state
->dir
== MODE_DEL
&& *state
->chptr
->mode
.upass
) {
2627 send_reply(state
->sptr
, ERR_UPASSSET
, state
->chptr
->chname
, state
->chptr
->chname
);
2630 /* Can't add an Apass if one is set, nor can one remove the wrong Apass. */
2631 if ((state
->dir
== MODE_ADD
&& *state
->chptr
->mode
.apass
) ||
2632 (state
->dir
== MODE_DEL
&& ircd_strcmp(state
->chptr
->mode
.apass
, t_str
))) {
2633 send_reply(state
->sptr
, ERR_KEYSET
, state
->chptr
->chname
);
2638 /* Forbid removing the Apass if the channel is older than 48 hours
2639 * unless an oper is doing it. */
2640 if (TStime() - state
->chptr
->creationtime
>= 172800
2641 && state
->dir
== MODE_DEL
2642 && !IsAnOper(state
->sptr
)) {
2643 send_reply(state
->sptr
, ERR_CHANSECURED
, state
->chptr
->chname
);
2648 if (state
->done
& DONE_APASS
) /* allow apass to be set only once */
2650 state
->done
|= DONE_APASS
;
2652 /* clean up the apass string */
2654 if (!*t_str
|| *t_str
== ':') { /* warn if empty */
2655 if (MyUser(state
->sptr
))
2656 need_more_params(state
->sptr
, state
->dir
== MODE_ADD
? "MODE +A" :
2664 if (!(state
->flags
& MODE_PARSE_WIPEOUT
) && state
->dir
== MODE_ADD
&&
2665 !ircd_strcmp(state
->chptr
->mode
.apass
, t_str
))
2666 return; /* no apass change */
2668 /* Skip if this is a burst, we have an Apass already and the new Apass is
2669 * after the old one alphabetically */
2670 if ((state
->flags
& MODE_PARSE_BURST
) &&
2671 *(state
->chptr
->mode
.apass
) &&
2672 ircd_strcmp(state
->chptr
->mode
.apass
, t_str
) <= 0)
2675 if (state
->flags
& MODE_PARSE_BOUNCE
) {
2676 if (*state
->chptr
->mode
.apass
) /* reset old apass */
2677 modebuf_mode_string(state
->mbuf
, MODE_DEL
| flag_p
[0],
2678 state
->chptr
->mode
.apass
, 0);
2679 else /* remove new bogus apass */
2680 modebuf_mode_string(state
->mbuf
, MODE_ADD
| flag_p
[0], t_str
, 0);
2681 } else /* send new apass */
2682 modebuf_mode_string(state
->mbuf
, state
->dir
| flag_p
[0], t_str
, 0);
2684 if (state
->flags
& MODE_PARSE_SET
) {
2685 if (state
->dir
== MODE_ADD
) { /* set the new apass */
2686 /* Only accept the new apass if there is no current apass or
2687 * this is a BURST. */
2688 if (state
->chptr
->mode
.apass
[0] == '\0' ||
2689 (state
->flags
& MODE_PARSE_BURST
))
2690 ircd_strncpy(state
->chptr
->mode
.apass
, t_str
, KEYLEN
);
2691 /* Make it VERY clear to the user that this is a one-time password */
2692 if (MyUser(state
->sptr
)) {
2693 send_reply(state
->sptr
, RPL_APASSWARN_SET
, state
->chptr
->mode
.apass
);
2694 send_reply(state
->sptr
, RPL_APASSWARN_SECRET
, state
->chptr
->chname
,
2695 state
->chptr
->mode
.apass
);
2697 /* Give the channel manager level 0 ops.
2698 There should not be tested for IsChannelManager here because
2699 on the local server it is impossible to set the apass if one
2700 isn't a channel manager and remote servers might need to sync
2701 the oplevel here: when someone creates a channel (and becomes
2702 channel manager) during a net.break, and only sets the Apass
2703 after the net rejoined, they will have oplevel MAXOPLEVEL on
2704 all remote servers. */
2706 SetOpLevel(state
->member
, 0);
2707 } else { /* remove the old apass */
2708 *state
->chptr
->mode
.apass
= '\0';
2709 /* Clear Upass so that there is never a Upass set when a zannel is burst. */
2710 *state
->chptr
->mode
.upass
= '\0';
2711 if (MyUser(state
->sptr
))
2712 send_reply(state
->sptr
, RPL_APASSWARN_CLEAR
);
2713 /* Revert everyone to MAXOPLEVEL. */
2714 for (memb
= state
->chptr
->members
; memb
; memb
= memb
->next_member
) {
2715 if (memb
->status
& MODE_CHANOP
)
2716 SetOpLevel(memb
, MAXOPLEVEL
);
2722 /** Compare one ban's extent to another.
2723 * This works very similarly to mmatch() but it knows about CIDR masks
2724 * and ban exceptions. If both bans are CIDR-based, compare their
2725 * address bits; otherwise, use mmatch().
2726 * @param[in] old_ban One ban.
2727 * @param[in] new_ban Another ban.
2728 * @return Zero if \a old_ban is a superset of \a new_ban, non-zero otherwise.
2731 bmatch(struct Ban
*old_ban
, struct Ban
*new_ban
)
2734 assert(old_ban
!= NULL
);
2735 assert(new_ban
!= NULL
);
2736 /* A ban is never treated as a superset of an exception. */
2737 if (!(old_ban
->flags
& BAN_EXCEPTION
)
2738 && (new_ban
->flags
& BAN_EXCEPTION
))
2740 /* If either is not an address mask, match the text masks. */
2741 if ((old_ban
->flags
& new_ban
->flags
& BAN_IPMASK
) == 0)
2742 return mmatch(old_ban
->banstr
, new_ban
->banstr
);
2743 /* If the old ban has a longer prefix than new, it cannot be a superset. */
2744 if (old_ban
->addrbits
> new_ban
->addrbits
)
2746 /* Compare the masks before the hostname part. */
2747 old_ban
->banstr
[old_ban
->nu_len
] = new_ban
->banstr
[new_ban
->nu_len
] = '\0';
2748 res
= mmatch(old_ban
->banstr
, new_ban
->banstr
);
2749 old_ban
->banstr
[old_ban
->nu_len
] = new_ban
->banstr
[new_ban
->nu_len
] = '@';
2752 /* If the old ban's mask mismatches, cannot be a superset. */
2753 if (!ipmask_check(&new_ban
->address
, &old_ban
->address
, old_ban
->addrbits
))
2755 /* Otherwise it depends on whether the old ban's text is a superset
2757 return mmatch(old_ban
->banstr
, new_ban
->banstr
);
2760 /** Add a ban from a ban list and mark bans that should be removed
2761 * because they overlap.
2763 * There are three invariants for a ban list. First, no ban may be
2764 * more specific than another ban. Second, no exception may be more
2765 * specific than another exception. Finally, no ban may be more
2766 * specific than any exception.
2768 * @param[in,out] banlist Pointer to head of list.
2769 * @param[in] newban Ban (or exception) to add (or remove).
2770 * @param[in] do_free If non-zero, free \a newban on failure.
2771 * @return Zero if \a newban could be applied, non-zero if not.
2773 int apply_ban(struct Ban
**banlist
, struct Ban
*newban
, int do_free
)
2778 assert(newban
->flags
& (BAN_ADD
|BAN_DEL
));
2779 if (newban
->flags
& BAN_ADD
) {
2781 /* If a less specific entry is found, fail. */
2782 for (ban
= *banlist
; ban
; ban
= ban
->next
) {
2783 if (!bmatch(ban
, newban
)) {
2788 if (!(ban
->flags
& (BAN_OVERLAPPED
|BAN_DEL
))) {
2790 totlen
+= strlen(ban
->banstr
);
2793 /* Mark more specific entries and add this one to the end of the list. */
2794 while ((ban
= *banlist
) != NULL
) {
2795 if (!bmatch(newban
, ban
)) {
2796 ban
->flags
|= BAN_OVERLAPPED
| BAN_DEL
;
2798 banlist
= &ban
->next
;
2802 } else if (newban
->flags
& BAN_DEL
) {
2803 size_t remove_count
= 0;
2804 /* Mark more specific entries. */
2805 for (ban
= *banlist
; ban
; ban
= ban
->next
) {
2806 if (!bmatch(newban
, ban
)) {
2807 ban
->flags
|= BAN_OVERLAPPED
| BAN_DEL
;
2813 /* If no matches were found, fail. */
2824 * Helper function to convert bans
2827 mode_parse_ban(struct ParseState
*state
, int *flag_p
)
2830 struct Ban
*ban
, *newban
;
2832 if (state
->parc
<= 0) { /* Not enough args, send ban list */
2833 if (MyUser(state
->sptr
) && !(state
->done
& DONE_BANLIST
)) {
2834 send_ban_list(state
->sptr
, state
->chptr
);
2835 state
->done
|= DONE_BANLIST
;
2841 if (MyUser(state
->sptr
) && state
->max_args
<= 0) /* drop if too many args */
2844 t_str
= state
->parv
[state
->args_used
++]; /* grab arg */
2848 /* If they're not an oper, they can't change modes */
2849 if (state
->flags
& (MODE_PARSE_NOTOPER
| MODE_PARSE_NOTMEMBER
)) {
2850 send_notoper(state
);
2854 if ((s
= strchr(t_str
, ' ')))
2857 if (!*t_str
|| *t_str
== ':') { /* warn if empty */
2858 if (MyUser(state
->sptr
))
2859 need_more_params(state
->sptr
, state
->dir
== MODE_ADD
? "MODE +b" :
2864 /* Clear all ADD/DEL/OVERLAPPED flags from ban list. */
2865 if (!(state
->done
& DONE_BANCLEAN
)) {
2866 for (ban
= state
->chptr
->banlist
; ban
; ban
= ban
->next
)
2867 ban
->flags
&= ~(BAN_ADD
| BAN_DEL
| BAN_OVERLAPPED
);
2868 state
->done
|= DONE_BANCLEAN
;
2871 /* remember the ban for the moment... */
2872 newban
= state
->banlist
+ (state
->numbans
++);
2874 newban
->flags
= ((state
->dir
== MODE_ADD
) ? BAN_ADD
: BAN_DEL
)
2875 | (*flag_p
== MODE_BAN
? 0 : BAN_EXCEPTION
);
2876 set_ban_mask(newban
, collapse(pretty_mask(t_str
)));
2877 ircd_strncpy(newban
->who
, IsUser(state
->sptr
) ? cli_name(state
->sptr
) : "*", NICKLEN
);
2878 newban
->when
= TStime();
2879 apply_ban(&state
->chptr
->banlist
, newban
, 0);
2883 * This is the bottom half of the ban processor
2886 mode_process_bans(struct ParseState
*state
)
2888 struct Ban
*ban
, *newban
, *prevban
, *nextban
;
2894 for (prevban
= 0, ban
= state
->chptr
->banlist
; ban
; ban
= nextban
) {
2896 banlen
= strlen(ban
->banstr
);
2898 nextban
= ban
->next
;
2900 if ((ban
->flags
& (BAN_DEL
| BAN_ADD
)) == (BAN_DEL
| BAN_ADD
)) {
2902 prevban
->next
= 0; /* Break the list; ban isn't a real ban */
2904 state
->chptr
->banlist
= 0;
2910 } else if (ban
->flags
& BAN_DEL
) { /* Deleted a ban? */
2912 DupString(bandup
, ban
->banstr
);
2913 modebuf_mode_string(state
->mbuf
, MODE_DEL
| MODE_BAN
,
2916 if (state
->flags
& MODE_PARSE_SET
) { /* Ok, make it take effect */
2917 if (prevban
) /* clip it out of the list... */
2918 prevban
->next
= ban
->next
;
2920 state
->chptr
->banlist
= ban
->next
;
2927 continue; /* next ban; keep prevban like it is */
2929 ban
->flags
&= BAN_IPMASK
; /* unset other flags */
2930 } else if (ban
->flags
& BAN_ADD
) { /* adding a ban? */
2932 prevban
->next
= 0; /* Break the list; ban isn't a real ban */
2934 state
->chptr
->banlist
= 0;
2936 /* If we're supposed to ignore it, do so. */
2937 if (ban
->flags
& BAN_OVERLAPPED
&&
2938 !(state
->flags
& MODE_PARSE_BOUNCE
)) {
2942 if (state
->flags
& MODE_PARSE_SET
&& MyUser(state
->sptr
) &&
2943 (len
> (feature_int(FEAT_AVBANLEN
) * feature_int(FEAT_MAXBANS
)) ||
2944 count
> feature_int(FEAT_MAXBANS
))) {
2945 send_reply(state
->sptr
, ERR_BANLISTFULL
, state
->chptr
->chname
,
2951 /* add the ban to the buffer */
2952 DupString(bandup
, ban
->banstr
);
2953 modebuf_mode_string(state
->mbuf
, MODE_ADD
| MODE_BAN
,
2956 if (state
->flags
& MODE_PARSE_SET
) { /* create a new ban */
2957 newban
= make_ban(ban
->banstr
);
2958 strcpy(newban
->who
, ban
->who
);
2959 newban
->when
= ban
->when
;
2960 newban
->flags
= ban
->flags
& BAN_IPMASK
;
2962 newban
->next
= state
->chptr
->banlist
; /* and link it in */
2963 state
->chptr
->banlist
= newban
;
2972 } /* for (prevban = 0, ban = state->chptr->banlist; ban; ban = nextban) { */
2974 if (changed
) /* if we changed the ban list, we must invalidate the bans */
2975 mode_ban_invalidate(state
->chptr
);
2979 * Helper function to process client changes
2982 mode_parse_client(struct ParseState
*state
, int *flag_p
)
2986 struct Client
*acptr
;
2987 struct Membership
*member
;
2988 int oplevel
= MAXOPLEVEL
+ 1;
2992 if (MyUser(state
->sptr
) && state
->max_args
<= 0) /* drop if too many args */
2995 if (state
->parc
<= 0) /* return if not enough args */
2998 t_str
= state
->parv
[state
->args_used
++]; /* grab arg */
3002 /* If they're not an oper, they can't change modes */
3003 if (state
->flags
& (MODE_PARSE_NOTOPER
| MODE_PARSE_NOTMEMBER
)) {
3004 send_notoper(state
);
3008 if (MyUser(state
->sptr
)) {
3009 colon
= strchr(t_str
, ':');
3010 if (colon
!= NULL
) {
3012 req_oplevel
= atoi(colon
);
3013 if (!(state
->flags
& MODE_PARSE_FORCE
)
3015 && (req_oplevel
< OpLevel(state
->member
)
3016 || (req_oplevel
== OpLevel(state
->member
)
3017 && OpLevel(state
->member
) < MAXOPLEVEL
)
3018 || req_oplevel
> MAXOPLEVEL
))
3019 send_reply(state
->sptr
, ERR_NOTLOWEROPLEVEL
,
3020 t_str
, state
->chptr
->chname
,
3021 OpLevel(state
->member
), req_oplevel
, "op",
3022 OpLevel(state
->member
) == req_oplevel
? "the same" : "a higher");
3023 else if (req_oplevel
<= MAXOPLEVEL
)
3024 oplevel
= req_oplevel
;
3026 /* find client we're manipulating */
3027 acptr
= find_chasing(state
->sptr
, t_str
, NULL
);
3029 if (t_str
[5] == ':') {
3031 oplevel
= atoi(t_str
+ 6);
3033 acptr
= findNUser(t_str
);
3037 return; /* find_chasing() already reported an error to the user */
3039 for (i
= 0; i
< MAXPARA
; i
++) /* find an element to stick them in */
3040 if (!state
->cli_change
[i
].flag
|| (state
->cli_change
[i
].client
== acptr
&&
3041 state
->cli_change
[i
].flag
& flag_p
[0]))
3042 break; /* found a slot */
3044 /* If we are going to bounce this deop, mark the correct oplevel. */
3045 if (state
->flags
& MODE_PARSE_BOUNCE
3046 && state
->dir
== MODE_DEL
3047 && flag_p
[0] == MODE_CHANOP
3048 && (member
= find_member_link(state
->chptr
, acptr
)))
3049 oplevel
= OpLevel(member
);
3051 /* Store what we're doing to them */
3052 state
->cli_change
[i
].flag
= state
->dir
| flag_p
[0];
3053 state
->cli_change
[i
].oplevel
= oplevel
;
3054 state
->cli_change
[i
].client
= acptr
;
3058 * Helper function to process the changed client list
3061 mode_process_clients(struct ParseState
*state
)
3064 struct Membership
*member
;
3066 for (i
= 0; state
->cli_change
[i
].flag
; i
++) {
3067 assert(0 != state
->cli_change
[i
].client
);
3069 /* look up member link */
3070 if (!(member
= find_member_link(state
->chptr
,
3071 state
->cli_change
[i
].client
)) ||
3072 (MyUser(state
->sptr
) && IsZombie(member
))) {
3073 if (MyUser(state
->sptr
))
3074 send_reply(state
->sptr
, ERR_USERNOTINCHANNEL
,
3075 cli_name(state
->cli_change
[i
].client
),
3076 state
->chptr
->chname
);
3080 if ((state
->cli_change
[i
].flag
& MODE_ADD
&&
3081 (state
->cli_change
[i
].flag
& member
->status
)) ||
3082 (state
->cli_change
[i
].flag
& MODE_DEL
&&
3083 !(state
->cli_change
[i
].flag
& member
->status
)))
3084 continue; /* no change made, don't do anything */
3086 /* see if the deop is allowed */
3087 if ((state
->cli_change
[i
].flag
& (MODE_DEL
| MODE_CHANOP
)) ==
3088 (MODE_DEL
| MODE_CHANOP
)) {
3089 /* prevent +k users from being deopped */
3092 * Allow +X'ed users to mess with +k'ed.
3095 if ((IsChannelService(state
->cli_change
[i
].client
) && IsService(cli_user(state
->cli_change
[i
].client
)->server
)) || (IsChannelService(state
->cli_change
[i
].client
) && !IsXtraOp(state
->sptr
))) {
3096 if (state
->flags
& MODE_PARSE_FORCE
) /* it was forced */
3097 sendto_opmask_butone(0, SNO_HACK4
, "Deop of +k user on %H by %s",
3099 (IsServer(state
->sptr
) ? cli_name(state
->sptr
) :
3100 cli_name((cli_user(state
->sptr
))->server
)));
3102 else if (MyUser(state
->sptr
) && state
->flags
& MODE_PARSE_SET
&& (state
->sptr
!= state
->cli_change
[i
].client
)) {
3103 if(IsService(cli_user(state
->cli_change
[i
].client
)->server
) && IsChannelService(state
->cli_change
[i
].client
)){
3104 send_reply(state
->sptr
, ERR_ISREALSERVICE
,
3105 cli_name(state
->cli_change
[i
].client
),
3106 state
->chptr
->chname
);
3108 send_reply(state
->sptr
, ERR_ISCHANSERVICE
,
3109 cli_name(state
->cli_change
[i
].client
),
3110 state
->chptr
->chname
);
3117 /* check deop for local user */
3118 if (MyUser(state
->sptr
)) {
3120 /* don't allow local opers to be deopped on local channels */
3121 if (state
->cli_change
[i
].client
!= state
->sptr
&&
3122 IsLocalChannel(state
->chptr
->chname
) &&
3123 HasPriv(state
->cli_change
[i
].client
, PRIV_DEOP_LCHAN
)) {
3124 send_reply(state
->sptr
, ERR_ISOPERLCHAN
,
3125 cli_name(state
->cli_change
[i
].client
),
3126 state
->chptr
->chname
);
3130 /* Forbid deopping other members with an oplevel less than
3131 * one's own level, and other members with an oplevel the same
3132 * as one's own unless both are at MAXOPLEVEL. */
3133 if (state
->sptr
!= state
->cli_change
[i
].client
3135 && ((OpLevel(member
) < OpLevel(state
->member
))
3136 || (OpLevel(member
) == OpLevel(state
->member
)
3137 && OpLevel(member
) < MAXOPLEVEL
))) {
3138 int equal
= (OpLevel(member
) == OpLevel(state
->member
));
3139 send_reply(state
->sptr
, ERR_NOTLOWEROPLEVEL
,
3140 cli_name(state
->cli_change
[i
].client
),
3141 state
->chptr
->chname
,
3142 OpLevel(state
->member
), OpLevel(member
),
3143 "deop", equal
? "the same" : "a higher");
3149 /* set op-level of member being opped */
3150 if ((state
->cli_change
[i
].flag
& (MODE_ADD
| MODE_CHANOP
)) ==
3151 (MODE_ADD
| MODE_CHANOP
)) {
3152 /* If a valid oplevel was specified, use it.
3153 * Otherwise, if being opped by an outsider, get MAXOPLEVEL.
3154 * Otherwise, if not an apass channel, or state->member has
3155 * MAXOPLEVEL, get oplevel MAXOPLEVEL.
3156 * Otherwise, get state->member's oplevel+1.
3158 if (state
->cli_change
[i
].oplevel
<= MAXOPLEVEL
)
3159 SetOpLevel(member
, state
->cli_change
[i
].oplevel
);
3160 else if (!state
->member
)
3161 SetOpLevel(member
, MAXOPLEVEL
);
3162 else if (!state
->chptr
->mode
.apass
[0] || OpLevel(state
->member
) == MAXOPLEVEL
)
3163 SetOpLevel(member
, MAXOPLEVEL
);
3165 SetOpLevel(member
, OpLevel(state
->member
) + 1);
3168 /* actually effect the change */
3169 if (state
->flags
& MODE_PARSE_SET
) {
3170 if (state
->cli_change
[i
].flag
& MODE_ADD
) {
3171 if (IsDelayedJoin(member
) && !IsZombie(member
))
3172 RevealDelayedJoin(member
);
3173 member
->status
|= (state
->cli_change
[i
].flag
&
3174 (MODE_CHANOP
| MODE_VOICE
));
3175 if (state
->cli_change
[i
].flag
& MODE_CHANOP
)
3176 ClearDeopped(member
);
3178 member
->status
&= ~(state
->cli_change
[i
].flag
&
3179 (MODE_CHANOP
| MODE_VOICE
));
3182 /* accumulate the change */
3183 modebuf_mode_client(state
->mbuf
, state
->cli_change
[i
].flag
,
3184 state
->cli_change
[i
].client
,
3185 state
->cli_change
[i
].oplevel
);
3186 } /* for (i = 0; state->cli_change[i].flags; i++) */
3190 * Helper function to process the simple modes
3193 mode_parse_mode(struct ParseState
*state
, int *flag_p
)
3195 /* If they're not an oper, they can't change modes */
3196 if (state
->flags
& (MODE_PARSE_NOTOPER
| MODE_PARSE_NOTMEMBER
)) {
3197 send_notoper(state
);
3204 if (state
->dir
== MODE_ADD
) {
3205 state
->add
|= flag_p
[0];
3206 state
->del
&= ~flag_p
[0];
3208 if (flag_p
[0] & MODE_SECRET
) {
3209 state
->add
&= ~MODE_PRIVATE
;
3210 state
->del
|= MODE_PRIVATE
;
3211 } else if (flag_p
[0] & MODE_PRIVATE
) {
3212 state
->add
&= ~MODE_SECRET
;
3213 state
->del
|= MODE_SECRET
;
3215 if (flag_p
[0] & MODE_DELJOINS
) {
3216 state
->add
&= ~MODE_WASDELJOINS
;
3217 state
->del
|= MODE_WASDELJOINS
;
3220 state
->add
&= ~flag_p
[0];
3221 state
->del
|= flag_p
[0];
3224 assert(0 == (state
->add
& state
->del
));
3225 assert((MODE_SECRET
| MODE_PRIVATE
) !=
3226 (state
->add
& (MODE_SECRET
| MODE_PRIVATE
)));
3230 * This routine is intended to parse MODE or OPMODE commands and effect the
3231 * changes (or just build the bounce buffer). We pass the starting offset
3235 mode_parse(struct ModeBuf
*mbuf
, struct Client
*cptr
, struct Client
*sptr
,
3236 struct Channel
*chptr
, int parc
, char *parv
[], unsigned int flags
,
3237 struct Membership
* member
)
3239 static int chan_flags
[] = {
3244 MODE_MODERATED
, 'm',
3245 MODE_TOPICLIMIT
, 't',
3246 MODE_INVITEONLY
, 'i',
3247 MODE_NOPRIVMSGS
, 'n',
3255 MODE_NOQUITPARTS
, 'u',
3265 unsigned int t_mode
;
3267 struct ParseState state
;
3278 state
.chptr
= chptr
;
3279 state
.member
= member
;
3282 state
.flags
= flags
;
3283 state
.dir
= MODE_ADD
;
3287 state
.args_used
= 0;
3288 state
.max_args
= MAXMODEPARAMS
;
3291 for (i
= 0; i
< MAXPARA
; i
++) { /* initialize ops/voices arrays */
3292 state
.banlist
[i
].next
= 0;
3293 state
.banlist
[i
].who
[0] = '\0';
3294 state
.banlist
[i
].when
= 0;
3295 state
.banlist
[i
].flags
= 0;
3296 state
.cli_change
[i
].flag
= 0;
3297 state
.cli_change
[i
].client
= 0;
3300 modestr
= state
.parv
[state
.args_used
++];
3304 for (; *modestr
; modestr
++) {
3305 for (flag_p
= chan_flags
; flag_p
[0]; flag_p
+= 2) /* look up flag */
3306 if (flag_p
[1] == *modestr
)
3309 if (!flag_p
[0]) { /* didn't find it? complain and continue */
3310 if (MyUser(state
.sptr
))
3311 send_reply(state
.sptr
, ERR_UNKNOWNMODE
, *modestr
);
3316 case '+': /* switch direction to MODE_ADD */
3317 case '-': /* switch direction to MODE_DEL */
3318 state
.dir
= flag_p
[0];
3321 case 'l': /* deal with limits */
3322 mode_parse_limit(&state
, flag_p
);
3325 case 'k': /* deal with keys */
3326 mode_parse_key(&state
, flag_p
);
3329 case 'A': /* deal with Admin passes */
3330 if (IsServer(cptr
) || feature_bool(FEAT_OPLEVELS
))
3331 mode_parse_apass(&state
, flag_p
);
3334 case 'U': /* deal with user passes */
3335 if (IsServer(cptr
) || feature_bool(FEAT_OPLEVELS
))
3336 mode_parse_upass(&state
, flag_p
);
3339 case 'b': /* deal with bans */
3340 mode_parse_ban(&state
, flag_p
);
3343 case 'o': /* deal with ops/voice */
3345 mode_parse_client(&state
, flag_p
);
3348 default: /* deal with other modes */
3349 mode_parse_mode(&state
, flag_p
);
3351 } /* switch (*modestr) */
3352 } /* for (; *modestr; modestr++) */
3354 if (state
.flags
& MODE_PARSE_BURST
)
3355 break; /* don't interpret any more arguments */
3357 if (state
.parc
> 0) { /* process next argument in string */
3358 modestr
= state
.parv
[state
.args_used
++];
3362 if (IsServer(state
.cptr
) && !state
.parc
&& IsDigit(*modestr
)) {
3365 if (!(state
.flags
& MODE_PARSE_SET
)) /* don't set earlier TS if */
3366 break; /* we're then going to bounce the mode! */
3368 recv_ts
= atoi(modestr
);
3370 if (recv_ts
&& recv_ts
< state
.chptr
->creationtime
)
3371 state
.chptr
->creationtime
= recv_ts
; /* respect earlier TS */
3372 else if (recv_ts
> state
.chptr
->creationtime
) {
3373 struct Client
*sserv
;
3375 /* Check whether the originating server has fully processed
3376 * the burst to it. */
3378 if (!IsServer(sserv
))
3379 sserv
= cli_user(sserv
)->server
;
3380 if (IsBurstOrBurstAck(sserv
)) {
3381 /* This is a legal but unusual case; the source server
3382 * probably just has not processed the BURST for this
3383 * channel. It SHOULD wipe out all its modes soon, so
3384 * silently ignore the mode change rather than send a
3385 * bounce that could desync modes from our side (that
3386 * have already been sent).
3388 state
.mbuf
->mb_add
= 0;
3389 state
.mbuf
->mb_rem
= 0;
3390 state
.mbuf
->mb_count
= 0;
3391 return state
.args_used
;
3393 /* Server is desynced; bounce the mode and deop the source
3395 state
.mbuf
->mb_dest
&= ~MODEBUF_DEST_CHANNEL
;
3396 state
.mbuf
->mb_dest
|= MODEBUF_DEST_BOUNCE
| MODEBUF_DEST_HACK2
;
3397 if (!IsServer(state
.cptr
))
3398 state
.mbuf
->mb_dest
|= MODEBUF_DEST_DEOP
;
3402 break; /* break out of while loop */
3403 } else if (state
.flags
& MODE_PARSE_STRICT
||
3404 (MyUser(state
.sptr
) && state
.max_args
<= 0)) {
3405 state
.parc
++; /* we didn't actually gobble the argument */
3407 break; /* break out of while loop */
3410 } /* while (*modestr) */
3413 * the rest of the function finishes building resultant MODEs; if the
3414 * origin isn't a member or an oper, skip it.
3416 if (!state
.mbuf
|| state
.flags
& (MODE_PARSE_NOTOPER
| MODE_PARSE_NOTMEMBER
))
3417 return state
.args_used
; /* tell our parent how many args we gobbled */
3419 t_mode
= state
.chptr
->mode
.mode
;
3421 if (state
.del
& t_mode
) { /* delete any modes to be deleted... */
3422 modebuf_mode(state
.mbuf
, MODE_DEL
| (state
.del
& t_mode
));
3424 t_mode
&= ~state
.del
;
3426 if (state
.add
& ~t_mode
) { /* add any modes to be added... */
3427 modebuf_mode(state
.mbuf
, MODE_ADD
| (state
.add
& ~t_mode
));
3429 t_mode
|= state
.add
;
3432 if (state
.flags
& MODE_PARSE_SET
) { /* set the channel modes */
3433 if ((state
.chptr
->mode
.mode
& MODE_INVITEONLY
) &&
3434 !(t_mode
& MODE_INVITEONLY
))
3435 mode_invite_clear(state
.chptr
);
3437 state
.chptr
->mode
.mode
= t_mode
;
3440 if (state
.flags
& MODE_PARSE_WIPEOUT
) {
3441 if (state
.chptr
->mode
.limit
&& !(state
.done
& DONE_LIMIT
))
3442 modebuf_mode_uint(state
.mbuf
, MODE_DEL
| MODE_LIMIT
,
3443 state
.chptr
->mode
.limit
);
3444 if (*state
.chptr
->mode
.key
&& !(state
.done
& DONE_KEY
))
3445 modebuf_mode_string(state
.mbuf
, MODE_DEL
| MODE_KEY
,
3446 state
.chptr
->mode
.key
, 0);
3447 if (*state
.chptr
->mode
.upass
&& !(state
.done
& DONE_UPASS
))
3448 modebuf_mode_string(state
.mbuf
, MODE_DEL
| MODE_UPASS
,
3449 state
.chptr
->mode
.upass
, 0);
3450 if (*state
.chptr
->mode
.apass
&& !(state
.done
& DONE_APASS
))
3451 modebuf_mode_string(state
.mbuf
, MODE_DEL
| MODE_APASS
,
3452 state
.chptr
->mode
.apass
, 0);
3455 if (state
.done
& DONE_BANCLEAN
) /* process bans */
3456 mode_process_bans(&state
);
3458 /* process client changes */
3459 if (state
.cli_change
[0].flag
)
3460 mode_process_clients(&state
);
3462 return state
.args_used
; /* tell our parent how many args we gobbled */
3466 * Initialize a join buffer
3469 joinbuf_init(struct JoinBuf
*jbuf
, struct Client
*source
,
3470 struct Client
*connect
, unsigned int type
, char *comment
,
3476 assert(0 != source
);
3477 assert(0 != connect
);
3479 jbuf
->jb_source
= source
; /* just initialize struct JoinBuf */
3480 jbuf
->jb_connect
= connect
;
3481 jbuf
->jb_type
= type
;
3482 jbuf
->jb_comment
= comment
;
3483 jbuf
->jb_create
= create
;
3485 jbuf
->jb_strlen
= (((type
== JOINBUF_TYPE_JOIN
||
3486 type
== JOINBUF_TYPE_PART
||
3487 type
== JOINBUF_TYPE_PARTALL
) ?
3488 STARTJOINLEN
: STARTCREATELEN
) +
3489 (comment
? strlen(comment
) + 2 : 0));
3491 for (i
= 0; i
< MAXJOINARGS
; i
++)
3492 jbuf
->jb_channels
[i
] = 0;
3496 * Add a channel to the join buffer
3499 joinbuf_join(struct JoinBuf
*jbuf
, struct Channel
*chan
, unsigned int flags
)
3507 sendcmdto_serv_butone(jbuf
->jb_source
, CMD_JOIN
, jbuf
->jb_connect
, "0");
3511 is_local
= IsLocalChannel(chan
->chname
);
3513 if (jbuf
->jb_type
== JOINBUF_TYPE_PART
||
3514 jbuf
->jb_type
== JOINBUF_TYPE_PARTALL
) {
3515 struct Membership
*member
= find_member_link(chan
, jbuf
->jb_source
);
3516 if (IsUserParting(member
))
3518 SetUserParting(member
);
3520 /* Send notification to channel */
3521 if (!(flags
& (CHFL_ZOMBIE
| CHFL_DELAYED
)))
3522 sendcmdto_channel_butserv_butone(jbuf
->jb_source
, CMD_PART
, chan
, NULL
, 0,
3523 ((flags
& CHFL_BANNED
) || ((chan
->mode
.mode
& MODE_NOQUITPARTS
)
3524 && !IsChannelService(member
->user
)) || !jbuf
->jb_comment
) ?
3525 "%H" : "%H :%s", chan
, jbuf
->jb_comment
);
3526 else if (MyUser(jbuf
->jb_source
))
3527 sendcmdto_one(jbuf
->jb_source
, CMD_PART
, jbuf
->jb_source
,
3528 ((flags
& CHFL_BANNED
) || (chan
->mode
.mode
& MODE_NOQUITPARTS
)
3529 || !jbuf
->jb_comment
) ?
3530 ":%H" : "%H :%s", chan
, jbuf
->jb_comment
);
3531 /* XXX: Shouldn't we send a PART here anyway? */
3532 /* to users on the channel? Why? From their POV, the user isn't on
3533 * the channel anymore anyway. We don't send to servers until below,
3534 * when we gang all the channel parts together. Note that this is
3535 * exactly the same logic, albeit somewhat more concise, as was in
3536 * the original m_part.c */
3538 if (jbuf
->jb_type
== JOINBUF_TYPE_PARTALL
||
3539 is_local
) /* got to remove user here */
3540 remove_user_from_channel(jbuf
->jb_source
, chan
);
3542 int oplevel
= !chan
->mode
.apass
[0] ? MAXOPLEVEL
3543 : (flags
& CHFL_CHANNEL_MANAGER
) ? 0
3545 /* Add user to channel */
3546 if ((chan
->mode
.mode
& MODE_DELJOINS
) && !(flags
& CHFL_VOICED_OR_OPPED
))
3547 add_user_to_channel(chan
, jbuf
->jb_source
, flags
| CHFL_DELAYED
, oplevel
);
3549 add_user_to_channel(chan
, jbuf
->jb_source
, flags
, oplevel
);
3551 /* send JOIN notification to all servers (CREATE is sent later). */
3552 if (jbuf
->jb_type
!= JOINBUF_TYPE_CREATE
&& !is_local
)
3553 sendcmdto_serv_butone(jbuf
->jb_source
, CMD_JOIN
, jbuf
->jb_connect
,
3554 "%H %Tu", chan
, chan
->creationtime
);
3556 if (!((chan
->mode
.mode
& MODE_DELJOINS
) && !(flags
& CHFL_VOICED_OR_OPPED
))) {
3557 /* Send the notification to the channel */
3558 sendcmdto_channel_butserv_butone(jbuf
->jb_source
, CMD_JOIN
, chan
, NULL
, 0, "%H", chan
);
3560 /* send an op, too, if needed */
3561 if (flags
& CHFL_CHANOP
&& (oplevel
< MAXOPLEVEL
|| !MyUser(jbuf
->jb_source
)))
3562 sendcmdto_channel_butserv_butone((chan
->mode
.apass
[0] ? &his
: jbuf
->jb_source
),
3563 CMD_MODE
, chan
, NULL
, 0, "%H +o %C",
3564 chan
, jbuf
->jb_source
);
3565 } else if (MyUser(jbuf
->jb_source
))
3566 sendcmdto_one(jbuf
->jb_source
, CMD_JOIN
, jbuf
->jb_source
, ":%H", chan
);
3569 if (jbuf
->jb_type
== JOINBUF_TYPE_PARTALL
||
3570 jbuf
->jb_type
== JOINBUF_TYPE_JOIN
|| is_local
)
3571 return; /* don't send to remote */
3573 /* figure out if channel name will cause buffer to be overflowed */
3574 len
= chan
? strlen(chan
->chname
) + 1 : 2;
3575 if (jbuf
->jb_strlen
+ len
> BUFSIZE
)
3576 joinbuf_flush(jbuf
);
3578 /* add channel to list of channels to send and update counts */
3579 jbuf
->jb_channels
[jbuf
->jb_count
++] = chan
;
3580 jbuf
->jb_strlen
+= len
;
3582 /* if we've used up all slots, flush */
3583 if (jbuf
->jb_count
>= MAXJOINARGS
)
3584 joinbuf_flush(jbuf
);
3588 * Flush the channel list to remote servers
3591 joinbuf_flush(struct JoinBuf
*jbuf
)
3593 char chanlist
[BUFSIZE
];
3597 if (!jbuf
->jb_count
|| jbuf
->jb_type
== JOINBUF_TYPE_PARTALL
||
3598 jbuf
->jb_type
== JOINBUF_TYPE_JOIN
)
3599 return 0; /* no joins to process */
3601 for (i
= 0; i
< jbuf
->jb_count
; i
++) { /* build channel list */
3602 build_string(chanlist
, &chanlist_i
,
3603 jbuf
->jb_channels
[i
] ? jbuf
->jb_channels
[i
]->chname
: "0", 0,
3604 i
== 0 ? '\0' : ',');
3605 if (JOINBUF_TYPE_PART
== jbuf
->jb_type
)
3606 /* Remove user from channel */
3607 remove_user_from_channel(jbuf
->jb_source
, jbuf
->jb_channels
[i
]);
3609 jbuf
->jb_channels
[i
] = 0; /* mark slot empty */
3612 jbuf
->jb_count
= 0; /* reset base counters */
3613 jbuf
->jb_strlen
= ((jbuf
->jb_type
== JOINBUF_TYPE_PART
?
3614 STARTJOINLEN
: STARTCREATELEN
) +
3615 (jbuf
->jb_comment
? strlen(jbuf
->jb_comment
) + 2 : 0));
3617 /* and send the appropriate command */
3618 switch (jbuf
->jb_type
) {
3619 case JOINBUF_TYPE_CREATE
:
3620 sendcmdto_serv_butone(jbuf
->jb_source
, CMD_CREATE
, jbuf
->jb_connect
,
3621 "%s %Tu", chanlist
, jbuf
->jb_create
);
3622 if (MyUser(jbuf
->jb_source
) && (feature_bool(FEAT_AUTOCHANMODES
) &&
3623 feature_str(FEAT_AUTOCHANMODES_LIST
) && (strlen(feature_str(FEAT_AUTOCHANMODES_LIST
)) > 0))) {
3626 for (name
= ircd_strtok(&p
, chanlist
, ","); name
; name
= ircd_strtok(&p
, 0, ",")) {
3627 sendcmdto_serv_butone(jbuf
->jb_source
, CMD_MODE
, jbuf
->jb_connect
,
3628 "%s %s%s", name
, "+", feature_str(FEAT_AUTOCHANMODES_LIST
));
3633 case JOINBUF_TYPE_PART
:
3634 sendcmdto_serv_butone(jbuf
->jb_source
, CMD_PART
, jbuf
->jb_connect
,
3635 jbuf
->jb_comment
? "%s :%s" : "%s", chanlist
,
3643 /* Returns TRUE (1) if client is invited, FALSE (0) if not */
3644 int IsInvited(struct Client
* cptr
, const void* chptr
)
3648 for (lp
= (cli_user(cptr
))->invited
; lp
; lp
= lp
->next
)
3649 if (lp
->value
.chptr
== chptr
)
3654 /* RevealDelayedJoin: sends a join for a hidden user */
3656 void RevealDelayedJoin(struct Membership
*member
)
3658 ClearDelayedJoin(member
);
3659 sendcmdto_channel_butserv_butone(member
->user
, CMD_JOIN
, member
->channel
, member
->user
, 0, ":%H",
3661 CheckDelayedJoins(member
->channel
);
3664 /* CheckDelayedJoins: checks and clear +d if necessary */
3666 void CheckDelayedJoins(struct Channel
*chan
)
3668 struct Membership
*memb2
;
3670 if (chan
->mode
.mode
& MODE_WASDELJOINS
) {
3671 for (memb2
=chan
->members
;memb2
;memb2
=memb2
->next_member
)
3672 if (IsDelayedJoin(memb2
))
3677 chan
->mode
.mode
&= ~MODE_WASDELJOINS
;
3678 sendcmdto_channel_butserv_butone(&his
, CMD_MODE
, chan
, NULL
, 0,