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.
26 #include "ircd_alloc.h"
27 #include "ircd_chattr.h"
28 #include "ircd_defs.h"
30 #include "ircd_reply.h"
31 #include "ircd_snprintf.h"
32 #include "ircd_string.h"
39 #include "querycmds.h"
46 #include "sprintf_irc.h"
57 struct Channel* GlobalChannelList = 0;
59 static unsigned int membershipAllocCount;
60 static struct Membership* membershipFreeList;
62 static struct SLink *next_overlapped_ban(void);
63 static int del_banid(struct Channel *, char *, int);
64 void del_invite(struct Client *, struct Channel *);
66 const char* const PartFmt1 = ":%s " MSG_PART " %s";
67 const char* const PartFmt2 = ":%s " MSG_PART " %s :%s";
68 const char* const PartFmt1serv = "%s%s " TOK_PART " %s";
69 const char* const PartFmt2serv = "%s%s " TOK_PART " %s :%s";
72 static struct SLink* next_ban;
73 static struct SLink* prev_ban;
74 static struct SLink* removed_bans_list;
77 * Use a global variable to remember if an oper set a mode on a local channel. Ugly,
78 * but the only way to do it without changing set_mode intensively.
80 int LocalChanOperMode = 0;
84 * return the length (>=0) of a chain of links.
86 static int list_length(struct SLink *lp)
90 for (; lp; lp = lp->next)
96 struct Membership* find_member_link(struct Channel* chptr, const struct Client* cptr)
102 /* Servers don't have member links */
103 if (IsServer(cptr)||IsMe(cptr))
106 /* +k users are typically on a LOT of channels. So we iterate over who
107 * is in the channel. X/W are +k and are in about 5800 channels each.
108 * however there are typically no more than 1000 people in a channel
111 if (IsChannelService(cptr)) {
114 assert(m->channel == chptr);
120 /* Users on the other hand aren't allowed on more than 15 channels. 50%
121 * of users that are on channels are on 2 or less, 95% are on 7 or less,
122 * and 99% are on 10 or less.
125 m = cptr->user->channel;
127 assert(m->user == cptr);
128 if (m->channel == chptr)
137 * find_chasing - Find the client structure for a nick name (user)
138 * using history mechanism if necessary. If the client is not found, an error
139 * message (NO SUCH NICK) is generated. If the client was found
140 * through the history, chasing will be 1 and otherwise 0.
142 struct Client* find_chasing(struct Client* sptr, const char* user, int* chasing)
144 struct Client* who = FindClient(user);
151 if (!(who = get_history(user, KILLCHASETIMELIMIT))) {
152 send_reply(sptr, ERR_NOSUCHNICK, user);
161 * Create a string of form "foo!bar@fubar" given foo, bar and fubar
162 * as the parameters. If NULL, they become "*".
164 static char *make_nick_user_host(const char *nick, const char *name,
167 static char namebuf[NICKLEN + USERLEN + HOSTLEN + 3];
168 sprintf_irc(namebuf, "%s!%s@%s", nick, name, host);
173 * Create a string of form "foo!bar@123.456.789.123" given foo, bar and the
174 * IP-number as the parameters. If NULL, they become "*".
176 static char *make_nick_user_ip(char *nick, char *name, struct in_addr ip)
178 static char ipbuf[NICKLEN + USERLEN + 16 + 3];
179 sprintf_irc(ipbuf, "%s!%s@%s", nick, name, ircd_ntoa((const char*) &ip));
184 static int DoesOp(const char* modebuf)
186 assert(0 != modebuf);
188 if (*modebuf == 'o' || *modebuf == 'v')
196 * This function should be removed when all servers are 2.10
198 static void sendmodeto_one(struct Client* cptr, const char* from,
199 const char* name, const char* mode,
200 const char* param, time_t creationtime)
202 if (IsServer(cptr) && DoesOp(mode) && creationtime)
203 sendto_one(cptr, ":%s MODE %s %s %s " TIME_T_FMT, /* XXX DEAD */
204 from, name, mode, param, creationtime);
206 sendto_one(cptr, ":%s MODE %s %s %s", from, name, mode, param); /* XXX DEAD */
211 * Subtract one user from channel i (and free channel
212 * block, if channel became empty).
213 * Returns: true (1) if channel still exists
214 * false (0) if the channel was destroyed
216 int sub1_from_channel(struct Channel* chptr)
221 if (chptr->users > 1) /* Can be 0, called for an empty channel too */
223 assert(0 != chptr->members);
228 assert(0 == chptr->members);
230 /* Channel became (or was) empty: Remove channel */
231 if (is_listed(chptr))
234 for (i = 0; i <= HighestFd; i++)
236 struct Client *acptr = 0;
237 if ((acptr = LocalClientArray[i]) && acptr->listing &&
238 acptr->listing->chptr == chptr)
240 list_next_channels(acptr, 1);
241 break; /* Only one client can list a channel */
246 * Now, find all invite links from channel structure
248 while ((tmp = chptr->invites))
249 del_invite(tmp->value.cptr, chptr);
251 tmp = chptr->banlist;
256 MyFree(obtmp->value.ban.banstr);
257 MyFree(obtmp->value.ban.who);
261 chptr->prev->next = chptr->next;
263 GlobalChannelList = chptr->next;
265 chptr->next->prev = chptr->prev;
267 --UserStats.channels;
269 * make sure that channel actually got removed from hash table
271 assert(chptr->hnext == chptr);
279 * `cptr' must be the client adding the ban.
281 * If `change' is true then add `banid' to channel `chptr'.
282 * Returns 0 if the ban was added.
283 * Returns -2 if the ban already existed and was marked CHFL_BURST_BAN_WIPEOUT.
284 * Return -1 otherwise.
286 * Those bans that overlapped with `banid' are flagged with CHFL_BAN_OVERLAPPED
287 * when `change' is false, otherwise they will be removed from the banlist.
288 * Subsequently calls to next_overlapped_ban() or next_removed_overlapped_ban()
289 * respectively will return these bans until NULL is returned.
291 * If `firsttime' is true, the ban list as returned by next_overlapped_ban()
292 * is reset (unless a non-zero value is returned, in which case the
293 * CHFL_BAN_OVERLAPPED flag might not have been reset!).
297 int add_banid(struct Client *cptr, struct Channel *chptr, char *banid,
298 int change, int firsttime)
303 int removed_bans = 0;
304 int len = strlen(banid);
309 assert(0 == prev_ban);
310 assert(0 == removed_bans_list);
314 for (banp = &chptr->banlist; *banp;)
316 len += strlen((*banp)->value.ban.banstr);
318 if (((*banp)->flags & CHFL_BURST_BAN_WIPEOUT))
320 if (!strcmp((*banp)->value.ban.banstr, banid))
322 (*banp)->flags &= ~CHFL_BURST_BAN_WIPEOUT;
326 else if (!mmatch((*banp)->value.ban.banstr, banid))
328 if (!mmatch(banid, (*banp)->value.ban.banstr))
330 struct SLink *tmp = *banp;
336 len -= strlen(tmp->value.ban.banstr);
340 /* Silently remove overlapping bans */
341 MyFree(tmp->value.ban.banstr);
342 MyFree(tmp->value.ban.who);
346 /* These will be sent to the user later as -b */
347 tmp->next = removed_bans_list;
348 removed_bans_list = tmp;
352 else if (!(tmp->flags & CHFL_BURST_BAN_WIPEOUT))
354 tmp->flags |= CHFL_BAN_OVERLAPPED;
365 (*banp)->flags &= ~CHFL_BAN_OVERLAPPED;
366 banp = &(*banp)->next;
369 if (MyUser(cptr) && !removed_bans && (len > MAXBANLENGTH || (cnt >= MAXBANS)))
371 send_reply(cptr, ERR_BANLISTFULL, chptr->chname, banid);
377 struct Membership* member;
379 ban->next = chptr->banlist;
381 ban->value.ban.banstr = (char*) MyMalloc(strlen(banid) + 1);
382 assert(0 != ban->value.ban.banstr);
383 strcpy(ban->value.ban.banstr, banid);
385 ban->value.ban.who = (char*) MyMalloc(strlen(cptr->name) + 1);
386 assert(0 != ban->value.ban.who);
387 strcpy(ban->value.ban.who, cptr->name);
389 ban->value.ban.when = TStime();
390 ban->flags = CHFL_BAN; /* This bit is never used I think... */
391 if ((ip_start = strrchr(banid, '@')) && check_if_ipmask(ip_start + 1))
392 ban->flags |= CHFL_BAN_IPMASK;
393 chptr->banlist = ban;
396 * Erase ban-valid-bit
398 for (member = chptr->members; member; member = member->next_member)
399 ClearBanValid(member); /* `ban' == channel member ! */
404 static struct SLink *next_overlapped_ban(void)
406 struct SLink *tmp = next_ban;
410 for (ban = tmp->next; ban; ban = ban->next)
411 if ((ban->flags & CHFL_BAN_OVERLAPPED))
418 struct SLink *next_removed_overlapped_ban(void)
420 struct SLink *tmp = removed_bans_list;
423 if (prev_ban->value.ban.banstr) /* Can be set to NULL in set_mode() */
424 MyFree(prev_ban->value.ban.banstr);
425 MyFree(prev_ban->value.ban.who);
430 removed_bans_list = removed_bans_list->next;
438 * If `change' is true, delete `banid' from channel `chptr'.
439 * Returns `false' if removal was (or would have been) successful.
441 static int del_banid(struct Channel *chptr, char *banid, int change)
448 for (ban = &(chptr->banlist); *ban; ban = &((*ban)->next)) {
449 if (0 == ircd_strcmp(banid, (*ban)->value.ban.banstr))
454 struct Membership* member;
456 MyFree(tmp->value.ban.banstr);
457 MyFree(tmp->value.ban.who);
460 * Erase ban-valid-bit, for channel members that are banned
462 for (member = chptr->members; member; member = member->next_member)
463 if (CHFL_BANVALIDMASK == (member->status & CHFL_BANVALIDMASK))
464 ClearBanValid(member); /* `tmp' == channel member */
473 * find_channel_member - returns Membership * if a person is joined and not a zombie
475 struct Membership* find_channel_member(struct Client* cptr, struct Channel* chptr)
477 struct Membership* member;
480 member = find_member_link(chptr, cptr);
481 return (member && !IsZombie(member)) ? member : 0;
485 * is_banned - a non-zero value if banned else 0.
487 static int is_banned(struct Client *cptr, struct Channel *chptr,
488 struct Membership* member)
497 if (member && IsBanValid(member))
498 return IsBanned(member);
500 s = make_nick_user_host(cptr->name, cptr->user->username, cptr->user->host);
502 for (tmp = chptr->banlist; tmp; tmp = tmp->next) {
503 if ((tmp->flags & CHFL_BAN_IPMASK)) {
505 ip_s = make_nick_user_ip(cptr->name, cptr->user->username, cptr->ip);
506 if (match(tmp->value.ban.banstr, ip_s) == 0)
509 else if (match(tmp->value.ban.banstr, s) == 0)
525 return (tmp != NULL);
529 * adds a user to a channel by adding another link to the channels member
532 void add_user_to_channel(struct Channel* chptr, struct Client* who,
540 struct Membership* member = membershipFreeList;
542 membershipFreeList = member->next_member;
544 member = (struct Membership*) MyMalloc(sizeof(struct Membership));
545 ++membershipAllocCount;
550 member->channel = chptr;
551 member->status = flags;
553 member->next_member = chptr->members;
554 if (member->next_member)
555 member->next_member->prev_member = member;
556 member->prev_member = 0;
557 chptr->members = member;
559 member->next_channel = who->user->channel;
560 if (member->next_channel)
561 member->next_channel->prev_channel = member;
562 member->prev_channel = 0;
563 who->user->channel = member;
570 static int remove_member_from_channel(struct Membership* member)
572 struct Channel* chptr;
574 chptr = member->channel;
576 * unlink channel member list
578 if (member->next_member)
579 member->next_member->prev_member = member->prev_member;
580 if (member->prev_member)
581 member->prev_member->next_member = member->next_member;
583 member->channel->members = member->next_member;
586 * unlink client channel list
588 if (member->next_channel)
589 member->next_channel->prev_channel = member->prev_channel;
590 if (member->prev_channel)
591 member->prev_channel->next_channel = member->next_channel;
593 member->user->user->channel = member->next_channel;
595 --member->user->user->joined;
597 member->next_member = membershipFreeList;
598 membershipFreeList = member;
600 return sub1_from_channel(chptr);
603 static int channel_all_zombies(struct Channel* chptr)
605 struct Membership* member;
607 for (member = chptr->members; member; member = member->next_member) {
608 if (!IsZombie(member))
615 void remove_user_from_channel(struct Client* cptr, struct Channel* chptr)
618 struct Membership* member;
621 if ((member = find_member_link(chptr, cptr))) {
622 if (remove_member_from_channel(member)) {
623 if (channel_all_zombies(chptr)) {
625 * XXX - this looks dangerous but isn't if we got the referential
626 * integrity right for channels
628 while (remove_member_from_channel(chptr->members))
635 void remove_user_from_all_channels(struct Client* cptr)
637 struct Membership* chan;
639 assert(0 != cptr->user);
641 while ((chan = cptr->user->channel))
642 remove_user_from_channel(cptr, chan->channel);
645 int is_chan_op(struct Client *cptr, struct Channel *chptr)
647 struct Membership* member;
649 if ((member = find_member_link(chptr, cptr)))
650 return (!IsZombie(member) && IsChanOp(member));
655 static int is_deopped(struct Client *cptr, struct Channel *chptr)
657 struct Membership* member;
660 if ((member = find_member_link(chptr, cptr)))
661 return IsDeopped(member);
663 return (IsUser(cptr) ? 1 : 0);
666 int is_zombie(struct Client *cptr, struct Channel *chptr)
668 struct Membership* member;
672 if ((member = find_member_link(chptr, cptr)))
673 return IsZombie(member);
677 int has_voice(struct Client* cptr, struct Channel* chptr)
679 struct Membership* member;
682 if ((member = find_member_link(chptr, cptr)))
683 return (!IsZombie(member) && HasVoice(member));
688 int member_can_send_to_channel(struct Membership* member)
692 if (IsVoicedOrOpped(member))
695 * If it's moderated, and you aren't a priviledged user, you can't
698 if (member->channel->mode.mode & MODE_MODERATED)
701 * If you're banned then you can't speak either.
702 * but because of the amount of CPU time that is_banned chews
703 * we only check it for our clients.
705 if (MyUser(member->user) && is_banned(member->user, member->channel, member))
710 int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr)
712 struct Membership *member;
715 * Servers can always speak on channels.
720 member = find_channel_member(cptr, chptr);
723 * You can't speak if your off channel, if the channel is modeless, or
724 * +n.(no external messages)
727 if ((chptr->mode.mode & MODE_NOPRIVMSGS) || IsModelessChannel(chptr->chname))
732 return member_can_send_to_channel(member);
736 * find_no_nickchange_channel
737 * if a member and not opped or voiced and banned
738 * return the name of the first channel banned on
740 const char* find_no_nickchange_channel(struct Client* cptr)
743 struct Membership* member;
744 for (member = cptr->user->channel; member; member = member->next_channel) {
745 if (!IsVoicedOrOpped(member) && is_banned(cptr, member->channel, member))
746 return member->channel->chname;
754 * write the "simple" list of channel modes for channel chptr onto buffer mbuf
755 * with the parameters in pbuf.
757 void channel_modes(struct Client *cptr, char *mbuf, char *pbuf,
758 struct Channel *chptr)
765 if (chptr->mode.mode & MODE_SECRET)
767 else if (chptr->mode.mode & MODE_PRIVATE)
769 if (chptr->mode.mode & MODE_MODERATED)
771 if (chptr->mode.mode & MODE_TOPICLIMIT)
773 if (chptr->mode.mode & MODE_INVITEONLY)
775 if (chptr->mode.mode & MODE_NOPRIVMSGS)
777 if (chptr->mode.limit) {
779 sprintf_irc(pbuf, "%d", chptr->mode.limit);
782 if (*chptr->mode.key) {
784 if (is_chan_op(cptr, chptr) || IsServer(cptr)) {
785 if (chptr->mode.limit)
787 strcat(pbuf, chptr->mode.key);
794 static int send_mode_list(struct Client *cptr, char *chname,
795 time_t creationtime, struct SLink *top,
805 cp = modebuf + strlen(modebuf);
806 if (*parabuf) /* mode +l or +k xx */
808 for (lp = top; lp; lp = lp->next)
810 if (!(lp->flags & mask))
812 if (mask == CHFL_BAN)
813 name = lp->value.ban.banstr;
815 name = lp->value.cptr->name;
816 if (strlen(parabuf) + strlen(name) + 11 < MODEBUFLEN)
818 strcat(parabuf, " ");
819 strcat(parabuf, name);
830 /* cptr is always a server! So we send creationtimes */
831 sendmodeto_one(cptr, me.name, chname, modebuf, parabuf, creationtime);
839 strcpy(parabuf, name);
852 * send "cptr" a full list of the modes for channel chptr.
854 void send_channel_modes(struct Client *cptr, struct Channel *chptr)
856 static unsigned int current_flags[4] =
857 { 0, CHFL_CHANOP | CHFL_VOICE, CHFL_VOICE, CHFL_CHANOP };
863 struct Membership* member;
865 char modebuf[MODEBUFLEN];
866 char parabuf[MODEBUFLEN];
872 if (IsLocalChannel(chptr->chname))
875 member = chptr->members;
876 lp2 = chptr->banlist;
878 *modebuf = *parabuf = '\0';
879 channel_modes(cptr, modebuf, parabuf, chptr);
881 for (first = 1; full; first = 0) /* Loop for multiple messages */
883 full = 0; /* Assume by default we get it
884 all in one message */
886 /* (Continued) prefix: "<Y> B <channel> <TS>" */
887 /* is there any better way we can do this? */
888 mb = msgq_make(&me, "%C " TOK_BURST " %H %Tu", &me, chptr,
889 chptr->creationtime);
891 if (first && modebuf[1]) /* Add simple modes (iklmnpst)
894 /* prefix: "<Y> B <channel> <TS>[ <modes>[ <params>]]" */
895 msgq_append(&me, mb, " %s", modebuf);
898 msgq_append(&me, mb, " %s", parabuf);
902 * Attach nicks, comma seperated " nick[:modes],nick[:modes],..."
904 * Run 4 times over all members, to group the members with the
907 for (first = 1; flag_cnt < 4;
908 member = chptr->members, new_mode = 1, flag_cnt++)
910 for (; member; member = member->next_member)
912 if ((member->status & CHFL_VOICED_OR_OPPED) !=
913 current_flags[flag_cnt])
914 continue; /* Skip members with different flags */
915 if (msgq_bufleft(mb) < NUMNICKLEN + 4)
916 /* The 4 is a possible ",:ov" */
918 full = 1; /* Make sure we continue after
920 new_mode = 1; /* Ensure the new BURST line contains the current
922 break; /* Do not add this member to this message */
924 msgq_append(&me, mb, "%c%C", first ? ' ' : ',', member->user);
925 first = 0; /* From now on, us comma's to add new nicks */
928 * Do we have a nick with a new mode ?
929 * Or are we starting a new BURST line?
934 if (IsVoicedOrOpped(member)) {
938 if (IsChanOp(member))
940 if (HasVoice(member))
943 msgq_append(&me, mb, tbuf);
953 /* Attach all bans, space seperated " :%ban ban ..." */
954 for (first = 2; lp2; lp2 = lp2->next)
956 len = strlen(lp2->value.ban.banstr);
957 if (msgq_bufleft(mb) < len + 1 + first)
958 /* The +1 stands for the added ' '.
959 * The +first stands for the added ":%".
965 msgq_append(&me, mb, " %s%s", first ? ":%" : "",
966 lp2->value.ban.banstr);
971 send_buffer(cptr, mb, 0); /* Send this message */
973 } /* Continue when there was something
974 that didn't fit (full==1) */
980 * by Carlo Wood (Run), 05 Oct 1998.
984 * When the nick is longer then NICKLEN, it is cut off (its an error of course).
985 * When the user name or host name are too long (USERLEN and HOSTLEN
986 * respectively) then they are cut off at the start with a '*'.
988 * The following transformations are made:
991 * 2) xxx.xxx -> *!*@host
992 * 3) xxx!yyy -> nick!user@*
993 * 4) xxx@yyy -> *!user@host
994 * 5) xxx!yyy@zzz -> nick!user@host
996 char *pretty_mask(char *mask)
998 static char star[2] = { '*', 0 };
999 char *last_dot = NULL;
1002 /* Case 1: default */
1007 /* Do a _single_ pass through the characters of the mask: */
1008 for (ptr = mask; *ptr; ++ptr)
1012 /* Case 3 or 5: Found first '!' (without finding a '@' yet) */
1016 else if (*ptr == '@')
1018 /* Case 4: Found last '@' (without finding a '!' yet) */
1023 else if (*ptr == '.')
1025 /* Case 2: Found last '.' (without finding a '!' or '@' yet) */
1035 /* Case 4 or 5: Found last '@' */
1041 if (user == star && last_dot)
1051 char *nick_end = (user != star) ? user - 1 : ptr;
1052 if (nick_end - nick > NICKLEN)
1058 char *user_end = (host != star) ? host - 1 : ptr;
1059 if (user_end - user > USERLEN)
1061 user = user_end - USERLEN;
1066 if (host != star && ptr - host > HOSTLEN)
1068 host = ptr - HOSTLEN;
1071 return make_nick_user_host(nick, user, host);
1074 static void send_ban_list(struct Client* cptr, struct Channel* chptr)
1081 for (lp = chptr->banlist; lp; lp = lp->next)
1082 send_reply(cptr, RPL_BANLIST, chptr->chname, lp->value.ban.banstr,
1083 lp->value.ban.who, lp->value.ban.when);
1085 send_reply(cptr, RPL_ENDOFBANLIST, chptr->chname);
1088 /* We are now treating the <key> part of /join <channel list> <key> as a key
1089 * ring; that is, we try one key against the actual channel key, and if that
1090 * doesn't work, we try the next one, and so on. -Kev -Texaco
1091 * Returns: 0 on match, 1 otherwise
1092 * This version contributed by SeKs <intru@info.polymtl.ca>
1094 static int compall(char *key, char *keyring)
1099 p1 = key; /* point to the key... */
1100 while (*p1 && *p1 == *keyring)
1101 { /* step through the key and ring until they
1107 if (!*p1 && (!*keyring || *keyring == ','))
1108 /* ok, if we're at the end of the and also at the end of one of the keys
1109 in the keyring, we have a match */
1112 if (!*keyring) /* if we're at the end of the key ring, there
1113 weren't any matches, so we return 1 */
1116 /* Not at the end of the key ring, so step
1117 through to the next key in the ring: */
1118 while (*keyring && *(keyring++) != ',');
1120 goto top; /* and check it against the key */
1123 int can_join(struct Client *sptr, struct Channel *chptr, char *key)
1126 int overrideJoin = 0;
1129 * Now a banned user CAN join if invited -- Nemesi
1130 * Now a user CAN escape channel limit if invited -- bfriendly
1131 * Now a user CAN escape anything if invited -- Isomer
1134 for (lp = sptr->user->invited; lp; lp = lp->next)
1135 if (lp->value.chptr == chptr)
1138 #ifdef OPER_WALK_THROUGH_LMODES
1139 /* An oper can force a join on a local channel using "OVERRIDE" as the key.
1140 a HACK(4) notice will be sent if he would not have been supposed
1141 to join normally. */
1142 if (IsOperOnLocalChannel(sptr,chptr->chname) && !BadPtr(key) && compall("OVERRIDE",key) == 0)
1144 overrideJoin = MAGIC_OPER_OVERRIDE;
1148 if (chptr->mode.mode & MODE_INVITEONLY)
1149 return overrideJoin + ERR_INVITEONLYCHAN;
1151 if (chptr->mode.limit && chptr->users >= chptr->mode.limit)
1152 return overrideJoin + ERR_CHANNELISFULL;
1154 if (is_banned(sptr, chptr, NULL))
1155 return overrideJoin + ERR_BANNEDFROMCHAN;
1158 * now using compall (above) to test against a whole key ring -Kev
1160 if (*chptr->mode.key && (EmptyString(key) || compall(chptr->mode.key, key)))
1161 return overrideJoin + ERR_BADCHANNELKEY;
1164 return ERR_DONTCHEAT;
1170 * Remove bells and commas from channel name
1172 void clean_channelname(char *cn)
1176 for (i = 0; cn[i]; i++) {
1177 if (i >= CHANNELLEN || !IsChannelChar(cn[i])) {
1181 if (IsChannelLower(cn[i])) {
1182 cn[i] = ToLower(cn[i]);
1188 if ((unsigned char)(cn[i]) == 0xd0)
1189 cn[i] = (char) 0xf0;
1196 * Get Channel block for i (and allocate a new channel
1197 * block, if it didn't exists before).
1199 struct Channel *get_channel(struct Client *cptr, char *chname, ChannelGetType flag)
1201 struct Channel *chptr;
1204 if (EmptyString(chname))
1207 len = strlen(chname);
1208 if (MyUser(cptr) && len > CHANNELLEN)
1211 *(chname + CHANNELLEN) = '\0';
1213 if ((chptr = FindChannel(chname)))
1215 if (flag == CGT_CREATE)
1217 chptr = (struct Channel*) MyMalloc(sizeof(struct Channel) + len);
1219 ++UserStats.channels;
1220 memset(chptr, 0, sizeof(struct Channel));
1221 strcpy(chptr->chname, chname);
1222 if (GlobalChannelList)
1223 GlobalChannelList->prev = chptr;
1225 chptr->next = GlobalChannelList;
1226 chptr->creationtime = MyUser(cptr) ? TStime() : (time_t) 0;
1227 GlobalChannelList = chptr;
1233 void add_invite(struct Client *cptr, struct Channel *chptr)
1235 struct SLink *inv, **tmp;
1237 del_invite(cptr, chptr);
1239 * Delete last link in chain if the list is max length
1241 assert(list_length(cptr->user->invited) == cptr->user->invites);
1242 if (cptr->user->invites>=MAXCHANNELSPERUSER)
1243 del_invite(cptr, cptr->user->invited->value.chptr);
1245 * Add client to channel invite list
1248 inv->value.cptr = cptr;
1249 inv->next = chptr->invites;
1250 chptr->invites = inv;
1252 * Add channel to the end of the client invite list
1254 for (tmp = &(cptr->user->invited); *tmp; tmp = &((*tmp)->next));
1256 inv->value.chptr = chptr;
1259 cptr->user->invites++;
1263 * Delete Invite block from channel invite list and client invite list
1265 void del_invite(struct Client *cptr, struct Channel *chptr)
1267 struct SLink **inv, *tmp;
1269 for (inv = &(chptr->invites); (tmp = *inv); inv = &tmp->next)
1270 if (tmp->value.cptr == cptr)
1275 cptr->user->invites--;
1279 for (inv = &(cptr->user->invited); (tmp = *inv); inv = &tmp->next)
1280 if (tmp->value.chptr == chptr)
1289 /* List and skip all channels that are listen */
1290 void list_next_channels(struct Client *cptr, int nr)
1292 struct ListingArgs *args = cptr->listing;
1293 struct Channel *chptr = args->chptr;
1294 chptr->mode.mode &= ~MODE_LISTED;
1295 while (is_listed(chptr) || --nr >= 0)
1297 for (; chptr; chptr = chptr->next)
1299 if (!cptr->user || (SecretChannel(chptr) && !find_channel_member(cptr, chptr)))
1301 if (chptr->users > args->min_users && chptr->users < args->max_users &&
1302 chptr->creationtime > args->min_time &&
1303 chptr->creationtime < args->max_time &&
1304 (!args->topic_limits || (*chptr->topic &&
1305 chptr->topic_time > args->min_topic_time &&
1306 chptr->topic_time < args->max_topic_time)))
1308 if (ShowChannel(cptr,chptr))
1309 send_reply(cptr, RPL_LIST, chptr->chname, chptr->users,
1311 chptr = chptr->next;
1317 MyFree(cptr->listing);
1318 cptr->listing = NULL;
1319 send_reply(cptr, RPL_LISTEND);
1325 cptr->listing->chptr = chptr;
1326 chptr->mode.mode |= MODE_LISTED;
1337 * X --a--> A --b--> B --d--> D
1341 * Where `who' is being KICK-ed by a "KICK" message received by server 'A'
1342 * via 'a', or on server 'B' via either 'b' or 'c', or on server D via 'd'.
1344 * a) On server A : set CHFL_ZOMBIE for `who' (lp) and pass on the KICK.
1345 * Remove the user immedeately when no users are left on the channel.
1346 * b) On server B : remove the user (who/lp) from the channel, send a
1347 * PART upstream (to A) and pass on the KICK.
1348 * c) KICKed by `client'; On server B : remove the user (who/lp) from the
1349 * channel, and pass on the KICK.
1350 * d) On server D : remove the user (who/lp) from the channel, and pass on
1354 * - Setting the ZOMBIE flag never hurts, we either remove the
1355 * client after that or we don't.
1356 * - The KICK message was already passed on, as should be in all cases.
1357 * - `who' is removed in all cases except case a) when users are left.
1358 * - A PART is only sent upstream in case b).
1364 * 1 --- 2 --- 3 --- 4 --- 5
1368 * We also need to turn 'who' into a zombie on servers 1 and 6,
1369 * because a KICK from 'who' (kicking someone else in that direction)
1370 * can arrive there afterwards - which should not be bounced itself.
1371 * Therefore case a) also applies for servers 1 and 6.
1375 void make_zombie(struct Membership* member, struct Client* who, struct Client* cptr,
1376 struct Client* sptr, struct Channel* chptr)
1378 assert(0 != member);
1383 /* Default for case a): */
1386 /* Case b) or c) ?: */
1387 if (MyUser(who)) /* server 4 */
1389 if (IsServer(cptr)) /* Case b) ? */
1390 sendcmdto_one(who, CMD_PART, cptr, "%H", chptr);
1391 remove_user_from_channel(who, chptr);
1394 if (who->from == cptr) /* True on servers 1, 5 and 6 */
1396 struct Client *acptr = IsServer(sptr) ? sptr : sptr->user->server;
1397 for (; acptr != &me; acptr = acptr->serv->up)
1398 if (acptr == who->user->server) /* Case d) (server 5) */
1400 remove_user_from_channel(who, chptr);
1405 /* Case a) (servers 1, 2, 3 and 6) */
1406 if (channel_all_zombies(chptr))
1407 remove_user_from_channel(who, chptr);
1409 /* XXX Can't actually call Debug here; if the channel is all zombies,
1410 * chptr will no longer exist when we get here.
1411 Debug((DEBUG_INFO, "%s is now a zombie on %s", who->name, chptr->chname));
1415 int number_of_zombies(struct Channel *chptr)
1417 struct Membership* member;
1421 for (member = chptr->members; member; member = member->next_member) {
1422 if (IsZombie(member))
1429 * This helper function builds an argument string in strptr, consisting
1430 * of the original string, a space, and str1 and str2 concatenated (if,
1431 * of course, str2 is not NULL)
1434 build_string(char *strptr, int *strptr_i, char *str1, char *str2, char c)
1437 strptr[(*strptr_i)++] = c;
1440 strptr[(*strptr_i)++] = *(str1++);
1444 strptr[(*strptr_i)++] = *(str2++);
1446 strptr[(*strptr_i)] = '\0';
1450 * This is the workhorse of our ModeBuf suite; this actually generates the
1451 * output MODE commands, HACK notices, or whatever. It's pretty complicated.
1454 modebuf_flush_int(struct ModeBuf *mbuf, int all)
1456 /* we only need the flags that don't take args right now */
1457 static int flags[] = {
1458 /* MODE_CHANOP, 'o', */
1459 /* MODE_VOICE, 'v', */
1462 MODE_MODERATED, 'm',
1463 MODE_TOPICLIMIT, 't',
1464 MODE_INVITEONLY, 'i',
1465 MODE_NOPRIVMSGS, 'n',
1466 /* MODE_KEY, 'k', */
1467 /* MODE_BAN, 'b', */
1468 /* MODE_LIMIT, 'l', */
1474 struct Client *app_source; /* where the MODE appears to come from */
1476 char addbuf[20]; /* accumulates +psmtin, etc. */
1478 char rembuf[20]; /* accumulates -psmtin, etc. */
1480 char *bufptr; /* we make use of indirection to simplify the code */
1483 char addstr[BUFSIZE]; /* accumulates MODE parameters to add */
1485 char remstr[BUFSIZE]; /* accumulates MODE parameters to remove */
1487 char *strptr; /* more indirection to simplify the code */
1490 int totalbuflen = BUFSIZE - 200; /* fuzz factor -- don't overrun buffer! */
1493 char limitbuf[20]; /* convert limits to strings */
1495 unsigned int limitdel = MODE_LIMIT;
1499 /* If the ModeBuf is empty, we have nothing to do */
1500 if (mbuf->mb_add == 0 && mbuf->mb_rem == 0 && mbuf->mb_count == 0)
1503 /* Ok, if we were given the OPMODE flag, hide the source if its a user */
1504 if (mbuf->mb_dest & MODEBUF_DEST_OPMODE && !IsServer(mbuf->mb_source))
1505 app_source = mbuf->mb_source->user->server;
1507 app_source = mbuf->mb_source;
1510 * Account for user we're bouncing; we have to get it in on the first
1511 * bounced MODE, or we could have problems
1513 if (mbuf->mb_dest & MODEBUF_DEST_DEOP)
1514 totalbuflen -= 6; /* numeric nick == 5, plus one space */
1516 /* Calculate the simple flags */
1517 for (flag_p = flags; flag_p[0]; flag_p += 2) {
1518 if (*flag_p & mbuf->mb_add)
1519 addbuf[addbuf_i++] = flag_p[1];
1520 else if (*flag_p & mbuf->mb_rem)
1521 rembuf[rembuf_i++] = flag_p[1];
1524 /* Now go through the modes with arguments... */
1525 for (i = 0; i < mbuf->mb_count; i++) {
1526 if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
1528 bufptr_i = &addbuf_i;
1531 bufptr_i = &rembuf_i;
1534 if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE)) {
1535 tmp = strlen(MB_CLIENT(mbuf, i)->name);
1537 if ((totalbuflen - IRCD_MAX(5, tmp)) <= 0) /* don't overflow buffer */
1538 MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1540 bufptr[(*bufptr_i)++] = MB_TYPE(mbuf, i) & MODE_CHANOP ? 'o' : 'v';
1541 totalbuflen -= IRCD_MAX(5, tmp) + 1;
1543 } else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN)) {
1544 tmp = strlen(MB_STRING(mbuf, i));
1546 if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
1547 MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1549 bufptr[(*bufptr_i)++] = MB_TYPE(mbuf, i) & MODE_KEY ? 'k' : 'b';
1550 totalbuflen -= tmp + 1;
1552 } else if (MB_TYPE(mbuf, i) & MODE_LIMIT) {
1553 /* if it's a limit, we also format the number */
1554 sprintf_irc(limitbuf, "%d", MB_UINT(mbuf, i));
1556 tmp = strlen(limitbuf);
1558 if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
1559 MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
1561 bufptr[(*bufptr_i)++] = 'l';
1562 totalbuflen -= tmp + 1;
1567 /* terminate the mode strings */
1568 addbuf[addbuf_i] = '\0';
1569 rembuf[rembuf_i] = '\0';
1571 /* If we're building a user visible MODE or HACK... */
1572 if (mbuf->mb_dest & (MODEBUF_DEST_CHANNEL | MODEBUF_DEST_HACK2 |
1573 MODEBUF_DEST_HACK3 | MODEBUF_DEST_HACK4 |
1574 MODEBUF_DEST_LOG)) {
1575 /* Set up the parameter strings */
1581 for (i = 0; i < mbuf->mb_count; i++) {
1582 if (MB_TYPE(mbuf, i) & MODE_SAVE)
1585 if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
1587 strptr_i = &addstr_i;
1590 strptr_i = &remstr_i;
1593 /* deal with clients... */
1594 if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE))
1595 build_string(strptr, strptr_i, MB_CLIENT(mbuf, i)->name, 0, ' ');
1597 /* deal with strings... */
1598 else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN))
1599 build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
1602 * deal with limit; note we cannot include the limit parameter if we're
1605 else if ((MB_TYPE(mbuf, i) & (MODE_ADD | MODE_LIMIT)) ==
1606 (MODE_ADD | MODE_LIMIT))
1607 build_string(strptr, strptr_i, limitbuf, 0, ' ');
1610 /* send the messages off to their destination */
1611 if (mbuf->mb_dest & MODEBUF_DEST_HACK2) {
1612 sendto_opmask_butone(0, SNO_HACK2, "HACK(2): %s MODE %s %s%s%s%s%s%s "
1613 "[%Tu]", app_source->name, mbuf->mb_channel->chname,
1614 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1615 addbuf, remstr, addstr,
1616 mbuf->mb_channel->creationtime);
1617 sendcmdto_serv_butone(&me, CMD_DESYNCH, mbuf->mb_connect,
1618 ":HACK: %s MODE %s %s%s%s%s%s%s [%Tu]",
1619 app_source->name, mbuf->mb_channel->chname,
1620 rembuf_i ? "-" : "", rembuf,
1621 addbuf_i ? "+" : "", addbuf, remstr, addstr,
1622 mbuf->mb_channel->creationtime);
1625 if (mbuf->mb_dest & MODEBUF_DEST_HACK3)
1626 sendto_opmask_butone(0, SNO_HACK3, "BOUNCE or HACK(3): %s MODE %s "
1627 "%s%s%s%s%s%s [%Tu]", app_source->name,
1628 mbuf->mb_channel->chname, rembuf_i ? "-" : "",
1629 rembuf, addbuf_i ? "+" : "", addbuf, remstr, addstr,
1630 mbuf->mb_channel->creationtime);
1632 if (mbuf->mb_dest & MODEBUF_DEST_HACK4)
1633 sendto_opmask_butone(0, SNO_HACK4, "HACK(4): %s MODE %s %s%s%s%s%s%s "
1634 "[%Tu]", app_source->name, mbuf->mb_channel->chname,
1635 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1636 addbuf, remstr, addstr,
1637 mbuf->mb_channel->creationtime);
1639 if (mbuf->mb_dest & MODEBUF_DEST_LOG)
1640 log_write(LS_OPERMODE, L_INFO, LOG_NOSNOTICE,
1641 "%#C OPMODE %H %s%s%s%s%s%s", mbuf->mb_source,
1642 mbuf->mb_channel, rembuf_i ? "-" : "", rembuf,
1643 addbuf_i ? "+" : "", addbuf, remstr, addstr);
1645 if (mbuf->mb_dest & MODEBUF_DEST_CHANNEL)
1646 sendcmdto_channel_butserv(app_source, CMD_MODE, mbuf->mb_channel,
1647 "%H %s%s%s%s%s%s", mbuf->mb_channel,
1648 rembuf_i ? "-" : "", rembuf,
1649 addbuf_i ? "+" : "", addbuf, remstr, addstr);
1652 /* Now are we supposed to propagate to other servers? */
1653 if (mbuf->mb_dest & MODEBUF_DEST_SERVER) {
1654 /* set up parameter string */
1661 * limit is supressed if we're removing it; we have to figure out which
1662 * direction is the direction for it to be removed, though...
1664 limitdel |= (mbuf->mb_dest & MODEBUF_DEST_HACK2) ? MODE_DEL : MODE_ADD;
1666 for (i = 0; i < mbuf->mb_count; i++) {
1667 if (MB_TYPE(mbuf, i) & MODE_SAVE)
1670 if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
1672 strptr_i = &addstr_i;
1675 strptr_i = &remstr_i;
1678 /* deal with modes that take clients */
1679 if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE))
1680 build_string(strptr, strptr_i, NumNick(MB_CLIENT(mbuf, i)), ' ');
1682 /* deal with modes that take strings */
1683 else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN))
1684 build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
1687 * deal with the limit. Logic here is complicated; if HACK2 is set,
1688 * we're bouncing the mode, so sense is reversed, and we have to
1689 * include the original limit if it looks like it's being removed
1691 else if ((MB_TYPE(mbuf, i) & limitdel) == limitdel)
1692 build_string(strptr, strptr_i, limitbuf, 0, ' ');
1695 /* we were told to deop the source */
1696 if (mbuf->mb_dest & MODEBUF_DEST_DEOP) {
1697 addbuf[addbuf_i++] = 'o'; /* remember, sense is reversed */
1698 addbuf[addbuf_i] = '\0'; /* terminate the string... */
1699 build_string(addstr, &addstr_i, NumNick(mbuf->mb_source), ' ');
1701 /* mark that we've done this, so we don't do it again */
1702 mbuf->mb_dest &= ~MODEBUF_DEST_DEOP;
1705 if (mbuf->mb_dest & MODEBUF_DEST_OPMODE) {
1706 /* If OPMODE was set, we're propagating the mode as an OPMODE message */
1707 sendcmdto_serv_butone(mbuf->mb_source, CMD_OPMODE, mbuf->mb_connect,
1708 "%H %s%s%s%s%s%s", mbuf->mb_channel,
1709 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1710 addbuf, remstr, addstr);
1711 } else if (mbuf->mb_dest & MODEBUF_DEST_BOUNCE) {
1713 * If HACK2 was set, we're bouncing; we send the MODE back to the
1714 * connection we got it from with the senses reversed and a TS of 0;
1717 sendcmdto_one(&me, CMD_MODE, mbuf->mb_connect, "%H %s%s%s%s%s%s %Tu",
1718 mbuf->mb_channel, addbuf_i ? "-" : "", addbuf,
1719 rembuf_i ? "+" : "", rembuf, addstr, remstr,
1720 mbuf->mb_channel->creationtime);
1723 * We're propagating a normal MODE command to the rest of the network;
1724 * we send the actual channel TS unless this is a HACK3 or a HACK4
1726 if (IsServer(mbuf->mb_source))
1727 sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
1728 "%H %s%s%s%s%s%s %Tu", mbuf->mb_channel,
1729 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1730 addbuf, remstr, addstr,
1731 (mbuf->mb_dest & MODEBUF_DEST_HACK4) ? 0 :
1732 mbuf->mb_channel->creationtime);
1734 sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
1735 "%H %s%s%s%s%s%s", mbuf->mb_channel,
1736 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
1737 addbuf, remstr, addstr);
1741 /* We've drained the ModeBuf... */
1746 /* reinitialize the mode-with-arg slots */
1747 for (i = 0; i < MAXMODEPARAMS; i++) {
1748 /* If we saved any, pack them down */
1749 if (MB_TYPE(mbuf, i) & MODE_SAVE) {
1750 mbuf->mb_modeargs[mbuf->mb_count] = mbuf->mb_modeargs[i];
1751 MB_TYPE(mbuf, mbuf->mb_count) &= ~MODE_SAVE; /* don't save anymore */
1753 if (mbuf->mb_count++ == i) /* don't overwrite our hard work */
1755 } else if (MB_TYPE(mbuf, i) & MODE_FREE)
1756 MyFree(MB_STRING(mbuf, i)); /* free string if needed */
1758 MB_TYPE(mbuf, i) = 0;
1759 MB_UINT(mbuf, i) = 0;
1762 /* If we're supposed to flush it all, do so--all hail tail recursion */
1763 if (all && mbuf->mb_count)
1764 return modebuf_flush_int(mbuf, 1);
1770 * This routine just initializes a ModeBuf structure with the information
1771 * needed and the options given.
1774 modebuf_init(struct ModeBuf *mbuf, struct Client *source,
1775 struct Client *connect, struct Channel *chan, unsigned int dest)
1780 assert(0 != source);
1786 mbuf->mb_source = source;
1787 mbuf->mb_connect = connect;
1788 mbuf->mb_channel = chan;
1789 mbuf->mb_dest = dest;
1792 /* clear each mode-with-parameter slot */
1793 for (i = 0; i < MAXMODEPARAMS; i++) {
1794 MB_TYPE(mbuf, i) = 0;
1795 MB_UINT(mbuf, i) = 0;
1800 * This routine simply adds modes to be added or deleted; do a binary OR
1801 * with either MODE_ADD or MODE_DEL
1804 modebuf_mode(struct ModeBuf *mbuf, unsigned int mode)
1807 assert(0 != (mode & (MODE_ADD | MODE_DEL)));
1809 mode &= (MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET | MODE_MODERATED |
1810 MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS);
1812 if (!(mode & ~(MODE_ADD | MODE_DEL))) /* don't add empty modes... */
1815 if (mode & MODE_ADD) {
1816 mbuf->mb_rem &= ~mode;
1817 mbuf->mb_add |= mode;
1819 mbuf->mb_add &= ~mode;
1820 mbuf->mb_rem |= mode;
1825 * This routine adds a mode to be added or deleted that takes a unsigned
1826 * int parameter; mode may *only* be the relevant mode flag ORed with one
1827 * of MODE_ADD or MODE_DEL
1830 modebuf_mode_uint(struct ModeBuf *mbuf, unsigned int mode, unsigned int uint)
1833 assert(0 != (mode & (MODE_ADD | MODE_DEL)));
1835 MB_TYPE(mbuf, mbuf->mb_count) = mode;
1836 MB_UINT(mbuf, mbuf->mb_count) = uint;
1838 /* when we've reached the maximal count, flush the buffer */
1839 if (++mbuf->mb_count >=
1840 (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
1841 modebuf_flush_int(mbuf, 0);
1845 * This routine adds a mode to be added or deleted that takes a string
1846 * parameter; mode may *only* be the relevant mode flag ORed with one of
1847 * MODE_ADD or MODE_DEL
1850 modebuf_mode_string(struct ModeBuf *mbuf, unsigned int mode, char *string,
1854 assert(0 != (mode & (MODE_ADD | MODE_DEL)));
1856 MB_TYPE(mbuf, mbuf->mb_count) = mode | (free ? MODE_FREE : 0);
1857 MB_STRING(mbuf, mbuf->mb_count) = string;
1859 /* when we've reached the maximal count, flush the buffer */
1860 if (++mbuf->mb_count >=
1861 (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
1862 modebuf_flush_int(mbuf, 0);
1866 * This routine adds a mode to be added or deleted that takes a client
1867 * parameter; mode may *only* be the relevant mode flag ORed with one of
1868 * MODE_ADD or MODE_DEL
1871 modebuf_mode_client(struct ModeBuf *mbuf, unsigned int mode,
1872 struct Client *client)
1875 assert(0 != (mode & (MODE_ADD | MODE_DEL)));
1877 MB_TYPE(mbuf, mbuf->mb_count) = mode;
1878 MB_CLIENT(mbuf, mbuf->mb_count) = client;
1880 /* when we've reached the maximal count, flush the buffer */
1881 if (++mbuf->mb_count >=
1882 (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
1883 modebuf_flush_int(mbuf, 0);
1887 * This is the exported binding for modebuf_flush()
1890 modebuf_flush(struct ModeBuf *mbuf)
1892 return modebuf_flush_int(mbuf, 1);
1896 * This extracts the simple modes contained in mbuf
1899 modebuf_extract(struct ModeBuf *mbuf, char *buf)
1901 static int flags[] = {
1902 /* MODE_CHANOP, 'o', */
1903 /* MODE_VOICE, 'v', */
1906 MODE_MODERATED, 'm',
1907 MODE_TOPICLIMIT, 't',
1908 MODE_INVITEONLY, 'i',
1909 MODE_NOPRIVMSGS, 'n',
1911 /* MODE_BAN, 'b', */
1916 int i, bufpos = 0, len;
1918 char *key = 0, limitbuf[20];
1927 for (i = 0; i < mbuf->mb_count; i++) { /* find keys and limits */
1928 if (MB_TYPE(mbuf, i) & MODE_ADD) {
1929 add |= MB_TYPE(mbuf, i) & (MODE_KEY | MODE_LIMIT);
1931 if (MB_TYPE(mbuf, i) & MODE_KEY) /* keep strings */
1932 key = MB_STRING(mbuf, i);
1933 else if (MB_TYPE(mbuf, i) & MODE_LIMIT)
1934 ircd_snprintf(0, limitbuf, sizeof(limitbuf), "%d", MB_UINT(mbuf, i));
1941 buf[bufpos++] = '+'; /* start building buffer */
1943 for (flag_p = flags; flag_p[0]; flag_p += 2)
1945 buf[bufpos++] = flag_p[1];
1947 for (i = 0, len = bufpos; i < len; i++) {
1949 build_string(buf, &bufpos, key, 0, ' ');
1950 else if (buf[i] == 'l')
1951 build_string(buf, &bufpos, limitbuf, 0, ' ');
1960 * Simple function to invalidate bans
1963 mode_ban_invalidate(struct Channel *chan)
1965 struct Membership *member;
1967 for (member = chan->members; member; member = member->next_member)
1968 ClearBanValid(member);
1972 * Simple function to drop invite structures
1975 mode_invite_clear(struct Channel *chan)
1977 while (chan->invites)
1978 del_invite(chan->invites->value.cptr, chan);
1981 /* What we've done for mode_parse so far... */
1982 #define DONE_LIMIT 0x01 /* We've set the limit */
1983 #define DONE_KEY 0x02 /* We've set the key */
1984 #define DONE_BANLIST 0x04 /* We've sent the ban list */
1985 #define DONE_NOTOPER 0x08 /* We've sent a "Not oper" error */
1986 #define DONE_BANCLEAN 0x10 /* We've cleaned bans... */
1989 struct ModeBuf *mbuf;
1990 struct Client *cptr;
1991 struct Client *sptr;
1992 struct Channel *chptr;
2003 struct SLink banlist[MAXPARA];
2006 struct Client *client;
2007 } cli_change[MAXPARA];
2011 * Here's a helper function to deal with sending along "Not oper" or
2012 * "Not member" messages
2015 send_notoper(struct ParseState *state)
2017 if (state->done & DONE_NOTOPER)
2020 send_reply(state->sptr, (state->flags & MODE_PARSE_NOTOPER) ?
2021 ERR_CHANOPRIVSNEEDED : ERR_NOTONCHANNEL, state->chptr->chname);
2023 state->done |= DONE_NOTOPER;
2027 * Helper function to convert limits
2030 mode_parse_limit(struct ParseState *state, int *flag_p)
2032 unsigned int t_limit;
2034 if (state->dir == MODE_ADD) { /* convert arg only if adding limit */
2035 if (MyUser(state->sptr) && state->max_args <= 0) /* too many args? */
2038 if (state->parc <= 0) { /* warn if not enough args */
2039 if (MyUser(state->sptr))
2040 need_more_params(state->sptr, "MODE +l");
2044 t_limit = atoi(state->parv[state->args_used++]); /* grab arg */
2048 if (!(state->flags & MODE_PARSE_WIPEOUT) &&
2049 (!t_limit || t_limit == state->chptr->mode.limit))
2052 t_limit = state->chptr->mode.limit;
2054 /* If they're not an oper, they can't change modes */
2055 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2056 send_notoper(state);
2060 if (state->done & DONE_LIMIT) /* allow limit to be set only once */
2062 state->done |= DONE_LIMIT;
2067 modebuf_mode_uint(state->mbuf, state->dir | flag_p[0], t_limit);
2069 if (state->flags & MODE_PARSE_SET) { /* set the limit */
2070 if (state->dir & MODE_ADD) {
2071 state->chptr->mode.mode |= flag_p[0];
2072 state->chptr->mode.limit = t_limit;
2074 state->chptr->mode.mode &= ~flag_p[0];
2075 state->chptr->mode.limit = 0;
2081 * Helper function to convert keys
2084 mode_parse_key(struct ParseState *state, int *flag_p)
2089 if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2092 if (state->parc <= 0) { /* warn if not enough args */
2093 if (MyUser(state->sptr))
2094 need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +k" :
2099 t_str = state->parv[state->args_used++]; /* grab arg */
2103 /* If they're not an oper, they can't change modes */
2104 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2105 send_notoper(state);
2109 if (state->done & DONE_KEY) /* allow key to be set only once */
2111 state->done |= DONE_KEY;
2115 /* clean up the key string */
2117 while (*++s > ' ' && *s != ':' && --t_len)
2121 if (!*t_str) { /* warn if empty */
2122 if (MyUser(state->sptr))
2123 need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +k" :
2131 /* can't add a key if one is set, nor can one remove the wrong key */
2132 if (!(state->flags & MODE_PARSE_FORCE))
2133 if ((state->dir == MODE_ADD && *state->chptr->mode.key) ||
2134 (state->dir == MODE_DEL &&
2135 ircd_strcmp(state->chptr->mode.key, t_str))) {
2136 send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
2140 if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
2141 !ircd_strcmp(state->chptr->mode.key, t_str))
2142 return; /* no key change */
2144 if (state->flags & MODE_PARSE_BOUNCE) {
2145 if (*state->chptr->mode.key) /* reset old key */
2146 modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
2147 state->chptr->mode.key, 0);
2148 else /* remove new bogus key */
2149 modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
2150 } else /* send new key */
2151 modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
2153 if (state->flags & MODE_PARSE_SET) {
2154 if (state->dir == MODE_ADD) /* set the new key */
2155 ircd_strncpy(state->chptr->mode.key, t_str, KEYLEN);
2156 else /* remove the old key */
2157 *state->chptr->mode.key = '\0';
2162 * Helper function to convert bans
2165 mode_parse_ban(struct ParseState *state, int *flag_p)
2168 struct SLink *ban, *newban = 0;
2170 if (state->parc <= 0) { /* Not enough args, send ban list */
2171 if (MyUser(state->sptr) && !(state->done & DONE_BANLIST)) {
2172 send_ban_list(state->sptr, state->chptr);
2173 state->done |= DONE_BANLIST;
2179 if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2182 t_str = state->parv[state->args_used++]; /* grab arg */
2186 /* If they're not an oper, they can't change modes */
2187 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2188 send_notoper(state);
2192 if ((s = strchr(t_str, ' ')))
2195 if (!*t_str || *t_str == ':') { /* warn if empty */
2196 if (MyUser(state->sptr))
2197 need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +b" :
2202 t_str = collapse(pretty_mask(t_str));
2204 /* remember the ban for the moment... */
2205 if (state->dir == MODE_ADD) {
2206 newban = state->banlist + (state->numbans++);
2209 DupString(newban->value.ban.banstr, t_str);
2210 newban->value.ban.who = state->sptr->name;
2211 newban->value.ban.when = TStime();
2213 newban->flags = CHFL_BAN | MODE_ADD;
2215 if ((s = strrchr(t_str, '@')) && check_if_ipmask(s + 1))
2216 newban->flags |= CHFL_BAN_IPMASK;
2219 if (!state->chptr->banlist) {
2220 state->chptr->banlist = newban; /* add our ban with its flags */
2221 state->done |= DONE_BANCLEAN;
2225 /* Go through all bans */
2226 for (ban = state->chptr->banlist; ban; ban = ban->next) {
2227 /* first, clean the ban flags up a bit */
2228 if (!(state->done & DONE_BANCLEAN))
2229 /* Note: We're overloading *lots* of bits here; be careful! */
2230 ban->flags &= ~(MODE_ADD | MODE_DEL | CHFL_BAN_OVERLAPPED);
2234 * MODE_ADD - Ban was added; if we're bouncing modes,
2235 * then we'll remove it below; otherwise,
2236 * we'll have to allocate a real ban
2238 * MODE_DEL - Ban was marked for deletion; if we're
2239 * bouncing modes, we'll have to re-add it,
2240 * otherwise, we'll have to remove it
2242 * CHFL_BAN_OVERLAPPED - The ban we added turns out to overlap
2243 * with a ban already set; if we're
2244 * bouncing modes, we'll have to bounce
2245 * this one; otherwise, we'll just ignore
2246 * it when we process added bans
2249 if (state->dir == MODE_DEL && !ircd_strcmp(ban->value.ban.banstr, t_str)) {
2250 ban->flags |= MODE_DEL; /* delete one ban */
2252 if (state->done & DONE_BANCLEAN) /* If we're cleaning, finish */
2254 } else if (state->dir == MODE_ADD) {
2255 /* if the ban already exists, don't worry about it */
2256 if (!ircd_strcmp(ban->value.ban.banstr, t_str)) {
2257 if (state->done & DONE_BANCLEAN) /* If we're cleaning, finish */
2260 } else if (!mmatch(ban->value.ban.banstr, t_str)) {
2261 if (!(ban->flags & MODE_DEL))
2262 newban->flags |= CHFL_BAN_OVERLAPPED; /* our ban overlaps */
2263 } else if (!mmatch(t_str, ban->value.ban.banstr))
2264 ban->flags |= MODE_DEL; /* mark ban for deletion: overlapping */
2267 ban->next = newban; /* add our ban with its flags */
2268 break; /* get out of loop */
2272 state->done |= DONE_BANCLEAN;
2276 * This is the bottom half of the ban processor
2279 mode_process_bans(struct ParseState *state)
2281 struct SLink *ban, *newban, *prevban, *nextban;
2287 for (prevban = 0, ban = state->chptr->banlist; ban; ban = nextban) {
2289 banlen = strlen(ban->value.ban.banstr);
2291 nextban = ban->next;
2293 if ((ban->flags & (MODE_DEL | MODE_ADD)) == (MODE_DEL | MODE_ADD)) {
2295 prevban->next = 0; /* Break the list; ban isn't a real ban */
2297 state->chptr->banlist = 0;
2302 MyFree(ban->value.ban.banstr);
2305 } else if (ban->flags & MODE_DEL) { /* Deleted a ban? */
2306 modebuf_mode_string(state->mbuf, MODE_DEL | MODE_BAN,
2307 ban->value.ban.banstr,
2308 state->flags & MODE_PARSE_SET);
2310 if (state->flags & MODE_PARSE_SET) { /* Ok, make it take effect */
2311 if (prevban) /* clip it out of the list... */
2312 prevban->next = ban->next;
2314 state->chptr->banlist = ban->next;
2319 MyFree(ban->value.ban.who);
2323 continue; /* next ban; keep prevban like it is */
2325 ban->flags &= (CHFL_BAN | CHFL_BAN_IPMASK); /* unset other flags */
2326 } else if (ban->flags & MODE_ADD) { /* adding a ban? */
2328 prevban->next = 0; /* Break the list; ban isn't a real ban */
2330 state->chptr->banlist = 0;
2332 /* If we're supposed to ignore it, do so. */
2333 if (ban->flags & CHFL_BAN_OVERLAPPED &&
2334 !(state->flags & MODE_PARSE_BOUNCE)) {
2338 MyFree(ban->value.ban.banstr);
2340 if (state->flags & MODE_PARSE_SET && MyUser(state->sptr) &&
2341 (len > MAXBANLENGTH || count >= MAXBANS)) {
2342 send_reply(state->sptr, ERR_BANLISTFULL, state->chptr->chname,
2343 ban->value.ban.banstr);
2347 MyFree(ban->value.ban.banstr);
2349 /* add the ban to the buffer */
2350 modebuf_mode_string(state->mbuf, MODE_ADD | MODE_BAN,
2351 ban->value.ban.banstr,
2352 !(state->flags & MODE_PARSE_SET));
2354 if (state->flags & MODE_PARSE_SET) { /* create a new ban */
2355 newban = make_link();
2356 newban->value.ban.banstr = ban->value.ban.banstr;
2357 DupString(newban->value.ban.who, ban->value.ban.who);
2358 newban->value.ban.when = ban->value.ban.when;
2359 newban->flags = ban->flags & (CHFL_BAN | CHFL_BAN_IPMASK);
2361 newban->next = state->chptr->banlist; /* and link it in */
2362 state->chptr->banlist = newban;
2371 } /* for (prevban = 0, ban = state->chptr->banlist; ban; ban = nextban) { */
2373 if (changed) /* if we changed the ban list, we must invalidate the bans */
2374 mode_ban_invalidate(state->chptr);
2378 * Helper function to process client changes
2381 mode_parse_client(struct ParseState *state, int *flag_p)
2384 struct Client *acptr;
2387 if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
2390 if (state->parc <= 0) /* return if not enough args */
2393 t_str = state->parv[state->args_used++]; /* grab arg */
2397 /* If they're not an oper, they can't change modes */
2398 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2399 send_notoper(state);
2403 if (MyUser(state->sptr)) /* find client we're manipulating */
2404 acptr = find_chasing(state->sptr, t_str, NULL);
2406 acptr = findNUser(t_str);
2409 return; /* find_chasing() already reported an error to the user */
2411 for (i = 0; i < MAXPARA; i++) /* find an element to stick them in */
2412 if (!state->cli_change[i].flag || (state->cli_change[i].client == acptr &&
2413 state->cli_change[i].flag & flag_p[0]))
2414 break; /* found a slot */
2416 /* Store what we're doing to them */
2417 state->cli_change[i].flag = state->dir | flag_p[0];
2418 state->cli_change[i].client = acptr;
2422 * Helper function to process the changed client list
2425 mode_process_clients(struct ParseState *state)
2428 struct Membership *member;
2430 for (i = 0; state->cli_change[i].flag; i++) {
2431 assert(0 != state->cli_change[i].client);
2433 /* look up member link */
2434 if (!(member = find_member_link(state->chptr,
2435 state->cli_change[i].client)) ||
2436 (MyUser(state->sptr) && IsZombie(member))) {
2437 if (MyUser(state->sptr))
2438 send_reply(state->sptr, ERR_USERNOTINCHANNEL,
2439 state->cli_change[i].client->name, state->chptr->chname);
2443 if ((state->cli_change[i].flag & MODE_ADD &&
2444 (state->cli_change[i].flag & member->status)) ||
2445 (state->cli_change[i].flag & MODE_DEL &&
2446 !(state->cli_change[i].flag & member->status)))
2447 continue; /* no change made, don't do anything */
2449 /* see if the deop is allowed */
2450 if ((state->cli_change[i].flag & (MODE_DEL | MODE_CHANOP)) ==
2451 (MODE_DEL | MODE_CHANOP)) {
2452 /* prevent +k users from being deopped */
2453 if (IsChannelService(state->cli_change[i].client)) {
2454 if (state->flags & MODE_PARSE_FORCE) /* it was forced */
2455 sendto_opmask_butone(0, SNO_HACK4, "Deop of +k user on %H by %s",
2457 (IsServer(state->sptr) ? state->sptr->name :
2458 state->sptr->user->server->name));
2460 else if (MyUser(state->sptr) && state->flags & MODE_PARSE_SET) {
2461 send_reply(state->sptr, ERR_ISCHANSERVICE,
2462 state->cli_change[i].client->name, state->chptr->chname);
2467 #ifdef NO_OPER_DEOP_LCHAN
2468 /* don't allow local opers to be deopped on local channels */
2469 if (MyUser(state->sptr) && state->cli_change[i].client != state->sptr &&
2470 IsOperOnLocalChannel(state->cli_change[i].client,
2471 state->chptr->chname)) {
2472 send_reply(state->sptr, ERR_ISOPERLCHAN,
2473 state->cli_change[i].client->name, state->chptr->chname);
2479 /* accumulate the change */
2480 modebuf_mode_client(state->mbuf, state->cli_change[i].flag,
2481 state->cli_change[i].client);
2483 /* actually effect the change */
2484 if (state->flags & MODE_PARSE_SET) {
2485 if (state->cli_change[i].flag & MODE_ADD) {
2486 member->status |= (state->cli_change[i].flag &
2487 (MODE_CHANOP | MODE_VOICE));
2488 if (state->cli_change[i].flag & MODE_CHANOP)
2489 ClearDeopped(member);
2491 member->status &= ~(state->cli_change[i].flag &
2492 (MODE_CHANOP | MODE_VOICE));
2494 } /* for (i = 0; state->cli_change[i].flags; i++) { */
2498 * Helper function to process the simple modes
2501 mode_parse_mode(struct ParseState *state, int *flag_p)
2503 /* If they're not an oper, they can't change modes */
2504 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
2505 send_notoper(state);
2512 if (state->dir == MODE_ADD) {
2513 state->add |= flag_p[0];
2514 state->del &= ~flag_p[0];
2516 if (flag_p[0] & MODE_SECRET) {
2517 state->add &= ~MODE_PRIVATE;
2518 state->del |= MODE_PRIVATE;
2519 } else if (flag_p[0] & MODE_PRIVATE) {
2520 state->add &= ~MODE_SECRET;
2521 state->del |= MODE_SECRET;
2524 state->add &= ~flag_p[0];
2525 state->del |= flag_p[0];
2528 assert(0 == (state->add & state->del));
2529 assert((MODE_SECRET | MODE_PRIVATE) !=
2530 (state->add & (MODE_SECRET | MODE_PRIVATE)));
2534 * This routine is intended to parse MODE or OPMODE commands and effect the
2535 * changes (or just build the bounce buffer). We pass the starting offset
2539 mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
2540 struct Channel *chptr, int parc, char *parv[], unsigned int flags)
2542 static int chan_flags[] = {
2547 MODE_MODERATED, 'm',
2548 MODE_TOPICLIMIT, 't',
2549 MODE_INVITEONLY, 'i',
2550 MODE_NOPRIVMSGS, 'n',
2560 unsigned int t_mode;
2562 struct ParseState state;
2573 state.chptr = chptr;
2576 state.flags = flags;
2577 state.dir = MODE_ADD;
2581 state.args_used = 0;
2582 state.max_args = MAXMODEPARAMS;
2585 for (i = 0; i < MAXPARA; i++) { /* initialize ops/voices arrays */
2586 state.banlist[i].next = 0;
2587 state.banlist[i].value.ban.banstr = 0;
2588 state.banlist[i].value.ban.who = 0;
2589 state.banlist[i].value.ban.when = 0;
2590 state.banlist[i].flags = 0;
2591 state.cli_change[i].flag = 0;
2592 state.cli_change[i].client = 0;
2595 modestr = state.parv[state.args_used++];
2599 for (; *modestr; modestr++) {
2600 for (flag_p = chan_flags; flag_p[0]; flag_p += 2) /* look up flag */
2601 if (flag_p[1] == *modestr)
2604 if (!flag_p[0]) { /* didn't find it? complain and continue */
2605 if (MyUser(state.sptr))
2606 send_reply(state.sptr, ERR_UNKNOWNMODE, *modestr);
2611 case '+': /* switch direction to MODE_ADD */
2612 case '-': /* switch direction to MODE_DEL */
2613 state.dir = flag_p[0];
2616 case 'l': /* deal with limits */
2617 mode_parse_limit(&state, flag_p);
2620 case 'k': /* deal with keys */
2621 mode_parse_key(&state, flag_p);
2624 case 'b': /* deal with bans */
2625 mode_parse_ban(&state, flag_p);
2628 case 'o': /* deal with ops/voice */
2630 mode_parse_client(&state, flag_p);
2633 default: /* deal with other modes */
2634 mode_parse_mode(&state, flag_p);
2636 } /* switch (*modestr) { */
2637 } /* for (; *modestr; modestr++) { */
2639 if (state.flags & MODE_PARSE_BURST)
2640 break; /* don't interpret any more arguments */
2642 if (state.parc > 0) { /* process next argument in string */
2643 modestr = state.parv[state.args_used++];
2647 if (IsServer(state.sptr) && !state.parc && IsDigit(*modestr)) {
2650 if (!(state.flags & MODE_PARSE_SET)) /* don't set earlier TS if */
2651 break; /* we're then going to bounce the mode! */
2653 recv_ts = atoi(modestr);
2655 if (recv_ts && recv_ts < state.chptr->creationtime)
2656 state.chptr->creationtime = recv_ts; /* respect earlier TS */
2658 break; /* break out of while loop */
2659 } else if (state.flags & MODE_PARSE_STRICT ||
2660 (MyUser(state.sptr) && state.max_args <= 0)) {
2661 state.parc++; /* we didn't actually gobble the argument */
2663 break; /* break out of while loop */
2666 } /* while (*modestr) { */
2669 * the rest of the function finishes building resultant MODEs; if the
2670 * origin isn't a member or an oper, skip it.
2672 if (!state.mbuf || state.flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER))
2673 return state.args_used; /* tell our parent how many args we gobbled */
2675 t_mode = state.chptr->mode.mode;
2677 if (state.del & t_mode) { /* delete any modes to be deleted... */
2678 modebuf_mode(state.mbuf, MODE_DEL | (state.del & t_mode));
2680 t_mode &= ~state.del;
2682 if (state.add & ~t_mode) { /* add any modes to be added... */
2683 modebuf_mode(state.mbuf, MODE_ADD | (state.add & ~t_mode));
2685 t_mode |= state.add;
2688 if (state.flags & MODE_PARSE_SET) { /* set the channel modes */
2689 if ((state.chptr->mode.mode & MODE_INVITEONLY) &&
2690 !(t_mode & MODE_INVITEONLY))
2691 mode_invite_clear(state.chptr);
2693 state.chptr->mode.mode = t_mode;
2696 if (state.flags & MODE_PARSE_WIPEOUT) {
2697 if (state.chptr->mode.limit && !(state.done & DONE_LIMIT))
2698 modebuf_mode_uint(state.mbuf, MODE_DEL | MODE_LIMIT,
2699 state.chptr->mode.limit);
2700 if (*state.chptr->mode.key && !(state.done & DONE_KEY))
2701 modebuf_mode_string(state.mbuf, MODE_DEL | MODE_KEY,
2702 state.chptr->mode.key, 0);
2705 if (state.done & DONE_BANCLEAN) /* process bans */
2706 mode_process_bans(&state);
2708 /* process client changes */
2709 if (state.cli_change[0].flag)
2710 mode_process_clients(&state);
2712 return state.args_used; /* tell our parent how many args we gobbled */
2716 * Initialize a join buffer
2719 joinbuf_init(struct JoinBuf *jbuf, struct Client *source,
2720 struct Client *connect, unsigned int type, char *comment,
2726 assert(0 != source);
2727 assert(0 != connect);
2729 jbuf->jb_source = source; /* just initialize struct JoinBuf */
2730 jbuf->jb_connect = connect;
2731 jbuf->jb_type = type;
2732 jbuf->jb_comment = comment;
2733 jbuf->jb_create = create;
2735 jbuf->jb_strlen = (((type == JOINBUF_TYPE_JOIN ||
2736 type == JOINBUF_TYPE_PART ||
2737 type == JOINBUF_TYPE_PARTALL) ?
2738 STARTJOINLEN : STARTCREATELEN) +
2739 (comment ? strlen(comment) + 2 : 0));
2741 for (i = 0; i < MAXJOINARGS; i++)
2742 jbuf->jb_channels[i] = 0;
2746 * Add a channel to the join buffer
2749 joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
2756 if (jbuf->jb_type == JOINBUF_TYPE_JOIN)
2757 sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect, "0");
2762 if (jbuf->jb_type == JOINBUF_TYPE_PART ||
2763 jbuf->jb_type == JOINBUF_TYPE_PARTALL) {
2764 /* Send notification to channel */
2765 if (!(flags & CHFL_ZOMBIE))
2766 sendcmdto_channel_butserv(jbuf->jb_source, CMD_PART, chan,
2767 (flags & CHFL_BANNED || !jbuf->jb_comment) ?
2768 ":%H" : "%H :%s", chan, jbuf->jb_comment);
2769 else if (MyUser(jbuf->jb_source))
2770 sendcmdto_one(jbuf->jb_source, CMD_PART, jbuf->jb_source,
2771 (flags & CHFL_BANNED || !jbuf->jb_comment) ?
2772 ":%H" : "%H :%s", chan, jbuf->jb_comment);
2773 /* XXX: Shouldn't we send a PART here anyway? */
2774 /* to users on the channel? Why? From their POV, the user isn't on
2775 * the channel anymore anyway. We don't send to servers until below,
2776 * when we gang all the channel parts together. Note that this is
2777 * exactly the same logic, albeit somewhat more concise, as was in
2778 * the original m_part.c */
2780 if (jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
2781 IsLocalChannel(chan->chname)) /* got to remove user here */
2782 remove_user_from_channel(jbuf->jb_source, chan);
2784 /* Add user to channel */
2785 add_user_to_channel(chan, jbuf->jb_source, flags);
2787 /* send notification to all servers */
2788 if (jbuf->jb_type != JOINBUF_TYPE_CREATE && !IsLocalChannel(chan->chname))
2789 sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect,
2790 "%H %Tu", chan, chan->creationtime);
2792 /* Send the notification to the channel */
2793 sendcmdto_channel_butserv(jbuf->jb_source, CMD_JOIN, chan, ":%H", chan);
2795 /* send an op, too, if needed */
2796 if (!MyUser(jbuf->jb_source) && jbuf->jb_type == JOINBUF_TYPE_CREATE &&
2797 !IsModelessChannel(chan->chname))
2798 sendcmdto_channel_butserv(jbuf->jb_source, CMD_MODE, chan, "%H +o %C",
2799 chan, jbuf->jb_source);
2802 if (jbuf->jb_type == JOINBUF_TYPE_PARTALL || IsLocalChannel(chan->chname))
2803 return; /* don't send to remote */
2805 /* figure out if channel name will cause buffer to be overflowed */
2806 len = chan ? strlen(chan->chname) + 1 : 2;
2807 if (jbuf->jb_strlen + len > BUFSIZE)
2808 joinbuf_flush(jbuf);
2810 /* add channel to list of channels to send and update counts */
2811 jbuf->jb_channels[jbuf->jb_count++] = chan;
2812 jbuf->jb_strlen += len;
2814 /* if we've used up all slots, flush */
2815 if (jbuf->jb_count >= MAXJOINARGS)
2816 joinbuf_flush(jbuf);
2820 * Flush the channel list to remote servers
2823 joinbuf_flush(struct JoinBuf *jbuf)
2825 char chanlist[BUFSIZE];
2829 if (!jbuf->jb_count || jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
2830 jbuf->jb_type == JOINBUF_TYPE_JOIN)
2831 return 0; /* no joins to process */
2833 for (i = 0; i < jbuf->jb_count; i++) { /* build channel list */
2834 build_string(chanlist, &chanlist_i,
2835 jbuf->jb_channels[i] ? jbuf->jb_channels[i]->chname : "0", 0,
2836 i == 0 ? '\0' : ',');
2837 if (JOINBUF_TYPE_PART == jbuf->jb_type)
2838 /* Remove user from channel */
2839 remove_user_from_channel(jbuf->jb_source, jbuf->jb_channels[i]);
2841 jbuf->jb_channels[i] = 0; /* mark slot empty */
2844 jbuf->jb_count = 0; /* reset base counters */
2845 jbuf->jb_strlen = ((jbuf->jb_type == JOINBUF_TYPE_PART ?
2846 STARTJOINLEN : STARTCREATELEN) +
2847 (jbuf->jb_comment ? strlen(jbuf->jb_comment) + 2 : 0));
2849 /* and send the appropriate command */
2850 switch (jbuf->jb_type) {
2851 case JOINBUF_TYPE_CREATE:
2852 sendcmdto_serv_butone(jbuf->jb_source, CMD_CREATE, jbuf->jb_connect,
2853 "%s %Tu", chanlist, jbuf->jb_create);
2856 case JOINBUF_TYPE_PART:
2857 sendcmdto_serv_butone(jbuf->jb_source, CMD_PART, jbuf->jb_connect,
2858 jbuf->jb_comment ? "%s :%s" : "%s", chanlist,