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"
29 #include "ircd_reply.h"
30 #include "ircd_snprintf.h"
31 #include "ircd_string.h"
37 #include "querycmds.h"
44 #include "sprintf_irc.h"
55 struct Channel* GlobalChannelList = 0;
57 static unsigned int membershipAllocCount;
58 static struct Membership* membershipFreeList;
60 static struct SLink *next_overlapped_ban(void);
61 static int del_banid(struct Channel *, char *, int);
62 void del_invite(struct Client *, struct Channel *);
64 const char* const PartFmt1 = ":%s " MSG_PART " %s";
65 const char* const PartFmt2 = ":%s " MSG_PART " %s :%s";
66 const char* const PartFmt1serv = "%s%s " TOK_PART " %s";
67 const char* const PartFmt2serv = "%s%s " TOK_PART " %s :%s";
70 static struct SLink* next_ban;
71 static struct SLink* prev_ban;
72 static struct SLink* removed_bans_list;
75 * Use a global variable to remember if an oper set a mode on a local channel. Ugly,
76 * but the only way to do it without changing set_mode intensively.
78 int LocalChanOperMode = 0;
82 * return the length (>=0) of a chain of links.
84 static int list_length(struct SLink *lp)
88 for (; lp; lp = lp->next)
94 struct Membership* find_member_link(struct Channel* chptr, const struct Client* cptr)
100 /* Servers don't have member links */
101 if (IsServer(cptr)||IsMe(cptr))
104 /* +k users are typically on a LOT of channels. So we iterate over who
105 * is in the channel. X/W are +k and are in about 5800 channels each.
106 * however there are typically no more than 1000 people in a channel
109 if (IsChannelService(cptr)) {
112 assert(m->channel == chptr);
118 /* Users on the other hand aren't allowed on more than 15 channels. 50%
119 * of users that are on channels are on 2 or less, 95% are on 7 or less,
120 * and 99% are on 10 or less.
123 m = cptr->user->channel;
125 assert(m->user == cptr);
126 if (m->channel == chptr)
135 * find_chasing - Find the client structure for a nick name (user)
136 * using history mechanism if necessary. If the client is not found, an error
137 * message (NO SUCH NICK) is generated. If the client was found
138 * through the history, chasing will be 1 and otherwise 0.
140 struct Client* find_chasing(struct Client* sptr, const char* user, int* chasing)
142 struct Client* who = FindClient(user);
149 if (!(who = get_history(user, KILLCHASETIMELIMIT))) {
150 send_reply(sptr, ERR_NOSUCHNICK, user);
159 * Create a string of form "foo!bar@fubar" given foo, bar and fubar
160 * as the parameters. If NULL, they become "*".
162 static char *make_nick_user_host(const char *nick, const char *name,
165 static char namebuf[NICKLEN + USERLEN + HOSTLEN + 3];
166 sprintf_irc(namebuf, "%s!%s@%s", nick, name, host);
171 * Create a string of form "foo!bar@123.456.789.123" given foo, bar and the
172 * IP-number as the parameters. If NULL, they become "*".
174 static char *make_nick_user_ip(char *nick, char *name, struct in_addr ip)
176 static char ipbuf[NICKLEN + USERLEN + 16 + 3];
177 sprintf_irc(ipbuf, "%s!%s@%s", nick, name, ircd_ntoa((const char*) &ip));
182 static int DoesOp(const char* modebuf)
184 assert(0 != modebuf);
186 if (*modebuf == 'o' || *modebuf == 'v')
194 * This function should be removed when all servers are 2.10
196 static void sendmodeto_one(struct Client* cptr, const char* from,
197 const char* name, const char* mode,
198 const char* param, time_t creationtime)
200 if (IsServer(cptr) && DoesOp(mode) && creationtime)
201 sendto_one(cptr, ":%s MODE %s %s %s " TIME_T_FMT, /* XXX DEAD */
202 from, name, mode, param, creationtime);
204 sendto_one(cptr, ":%s MODE %s %s %s", from, name, mode, param); /* XXX DEAD */
209 * Subtract one user from channel i (and free channel
210 * block, if channel became empty).
211 * Returns: true (1) if channel still exists
212 * false (0) if the channel was destroyed
214 int sub1_from_channel(struct Channel* chptr)
219 if (chptr->users > 1) /* Can be 0, called for an empty channel too */
221 assert(0 != chptr->members);
226 assert(0 == chptr->members);
228 /* Channel became (or was) empty: Remove channel */
229 if (is_listed(chptr))
232 for (i = 0; i <= HighestFd; i++)
234 struct Client *acptr = 0;
235 if ((acptr = LocalClientArray[i]) && acptr->listing &&
236 acptr->listing->chptr == chptr)
238 list_next_channels(acptr, 1);
239 break; /* Only one client can list a channel */
244 * Now, find all invite links from channel structure
246 while ((tmp = chptr->invites))
247 del_invite(tmp->value.cptr, chptr);
249 tmp = chptr->banlist;
254 MyFree(obtmp->value.ban.banstr);
255 MyFree(obtmp->value.ban.who);
259 chptr->prev->next = chptr->next;
261 GlobalChannelList = chptr->next;
263 chptr->next->prev = chptr->prev;
265 --UserStats.channels;
267 * make sure that channel actually got removed from hash table
269 assert(chptr->hnext == chptr);
277 * `cptr' must be the client adding the ban.
279 * If `change' is true then add `banid' to channel `chptr'.
280 * Returns 0 if the ban was added.
281 * Returns -2 if the ban already existed and was marked CHFL_BURST_BAN_WIPEOUT.
282 * Return -1 otherwise.
284 * Those bans that overlapped with `banid' are flagged with CHFL_BAN_OVERLAPPED
285 * when `change' is false, otherwise they will be removed from the banlist.
286 * Subsequently calls to next_overlapped_ban() or next_removed_overlapped_ban()
287 * respectively will return these bans until NULL is returned.
289 * If `firsttime' is true, the ban list as returned by next_overlapped_ban()
290 * is reset (unless a non-zero value is returned, in which case the
291 * CHFL_BAN_OVERLAPPED flag might not have been reset!).
295 int add_banid(struct Client *cptr, struct Channel *chptr, char *banid,
296 int change, int firsttime)
301 int removed_bans = 0;
302 int len = strlen(banid);
307 assert(0 == prev_ban);
308 assert(0 == removed_bans_list);
312 for (banp = &chptr->banlist; *banp;)
314 len += strlen((*banp)->value.ban.banstr);
316 if (((*banp)->flags & CHFL_BURST_BAN_WIPEOUT))
318 if (!strcmp((*banp)->value.ban.banstr, banid))
320 (*banp)->flags &= ~CHFL_BURST_BAN_WIPEOUT;
324 else if (!mmatch((*banp)->value.ban.banstr, banid))
326 if (!mmatch(banid, (*banp)->value.ban.banstr))
328 struct SLink *tmp = *banp;
334 len -= strlen(tmp->value.ban.banstr);
338 /* Silently remove overlapping bans */
339 MyFree(tmp->value.ban.banstr);
340 MyFree(tmp->value.ban.who);
344 /* These will be sent to the user later as -b */
345 tmp->next = removed_bans_list;
346 removed_bans_list = tmp;
350 else if (!(tmp->flags & CHFL_BURST_BAN_WIPEOUT))
352 tmp->flags |= CHFL_BAN_OVERLAPPED;
363 (*banp)->flags &= ~CHFL_BAN_OVERLAPPED;
364 banp = &(*banp)->next;
367 if (MyUser(cptr) && !removed_bans && (len > MAXBANLENGTH || (cnt >= MAXBANS)))
369 send_reply(cptr, ERR_BANLISTFULL, chptr->chname, banid);
375 struct Membership* member;
377 ban->next = chptr->banlist;
379 ban->value.ban.banstr = (char*) MyMalloc(strlen(banid) + 1);
380 assert(0 != ban->value.ban.banstr);
381 strcpy(ban->value.ban.banstr, banid);
383 ban->value.ban.who = (char*) MyMalloc(strlen(cptr->name) + 1);
384 assert(0 != ban->value.ban.who);
385 strcpy(ban->value.ban.who, cptr->name);
387 ban->value.ban.when = TStime();
388 ban->flags = CHFL_BAN; /* This bit is never used I think... */
389 if ((ip_start = strrchr(banid, '@')) && check_if_ipmask(ip_start + 1))
390 ban->flags |= CHFL_BAN_IPMASK;
391 chptr->banlist = ban;
394 * Erase ban-valid-bit
396 for (member = chptr->members; member; member = member->next_member)
397 ClearBanValid(member); /* `ban' == channel member ! */
402 static struct SLink *next_overlapped_ban(void)
404 struct SLink *tmp = next_ban;
408 for (ban = tmp->next; ban; ban = ban->next)
409 if ((ban->flags & CHFL_BAN_OVERLAPPED))
416 struct SLink *next_removed_overlapped_ban(void)
418 struct SLink *tmp = removed_bans_list;
421 if (prev_ban->value.ban.banstr) /* Can be set to NULL in set_mode() */
422 MyFree(prev_ban->value.ban.banstr);
423 MyFree(prev_ban->value.ban.who);
428 removed_bans_list = removed_bans_list->next;
436 * If `change' is true, delete `banid' from channel `chptr'.
437 * Returns `false' if removal was (or would have been) successful.
439 static int del_banid(struct Channel *chptr, char *banid, int change)
446 for (ban = &(chptr->banlist); *ban; ban = &((*ban)->next)) {
447 if (0 == ircd_strcmp(banid, (*ban)->value.ban.banstr))
452 struct Membership* member;
454 MyFree(tmp->value.ban.banstr);
455 MyFree(tmp->value.ban.who);
458 * Erase ban-valid-bit, for channel members that are banned
460 for (member = chptr->members; member; member = member->next_member)
461 if (CHFL_BANVALIDMASK == (member->status & CHFL_BANVALIDMASK))
462 ClearBanValid(member); /* `tmp' == channel member */
471 * find_channel_member - returns Membership * if a person is joined and not a zombie
473 struct Membership* find_channel_member(struct Client* cptr, struct Channel* chptr)
475 struct Membership* member;
478 member = find_member_link(chptr, cptr);
479 return (member && !IsZombie(member)) ? member : 0;
483 * is_banned - a non-zero value if banned else 0.
485 static int is_banned(struct Client *cptr, struct Channel *chptr,
486 struct Membership* member)
495 if (member && IsBanValid(member))
496 return IsBanned(member);
498 s = make_nick_user_host(cptr->name, cptr->user->username, cptr->user->host);
500 for (tmp = chptr->banlist; tmp; tmp = tmp->next) {
501 if ((tmp->flags & CHFL_BAN_IPMASK)) {
503 ip_s = make_nick_user_ip(cptr->name, cptr->user->username, cptr->ip);
504 if (match(tmp->value.ban.banstr, ip_s) == 0)
507 else if (match(tmp->value.ban.banstr, s) == 0)
523 return (tmp != NULL);
527 * adds a user to a channel by adding another link to the channels member
530 void add_user_to_channel(struct Channel* chptr, struct Client* who,
538 struct Membership* member = membershipFreeList;
540 membershipFreeList = member->next_member;
542 member = (struct Membership*) MyMalloc(sizeof(struct Membership));
543 ++membershipAllocCount;
548 member->channel = chptr;
549 member->status = flags;
551 member->next_member = chptr->members;
552 if (member->next_member)
553 member->next_member->prev_member = member;
554 member->prev_member = 0;
555 chptr->members = member;
557 member->next_channel = who->user->channel;
558 if (member->next_channel)
559 member->next_channel->prev_channel = member;
560 member->prev_channel = 0;
561 who->user->channel = member;
568 static int remove_member_from_channel(struct Membership* member)
570 struct Channel* chptr;
572 chptr = member->channel;
574 * unlink channel member list
576 if (member->next_member)
577 member->next_member->prev_member = member->prev_member;
578 if (member->prev_member)
579 member->prev_member->next_member = member->next_member;
581 member->channel->members = member->next_member;
584 * unlink client channel list
586 if (member->next_channel)
587 member->next_channel->prev_channel = member->prev_channel;
588 if (member->prev_channel)
589 member->prev_channel->next_channel = member->next_channel;
591 member->user->user->channel = member->next_channel;
593 --member->user->user->joined;
595 member->next_member = membershipFreeList;
596 membershipFreeList = member;
598 return sub1_from_channel(chptr);
601 static int channel_all_zombies(struct Channel* chptr)
603 struct Membership* member;
605 for (member = chptr->members; member; member = member->next_member) {
606 if (!IsZombie(member))
613 void remove_user_from_channel(struct Client* cptr, struct Channel* chptr)
616 struct Membership* member;
619 if ((member = find_member_link(chptr, cptr))) {
620 if (remove_member_from_channel(member)) {
621 if (channel_all_zombies(chptr)) {
623 * XXX - this looks dangerous but isn't if we got the referential
624 * integrity right for channels
626 while (remove_member_from_channel(chptr->members))
633 void remove_user_from_all_channels(struct Client* cptr)
635 struct Membership* chan;
637 assert(0 != cptr->user);
639 while ((chan = cptr->user->channel))
640 remove_user_from_channel(cptr, chan->channel);
643 int is_chan_op(struct Client *cptr, struct Channel *chptr)
645 struct Membership* member;
647 if ((member = find_member_link(chptr, cptr)))
648 return (!IsZombie(member) && IsChanOp(member));
653 static int is_deopped(struct Client *cptr, struct Channel *chptr)
655 struct Membership* member;
658 if ((member = find_member_link(chptr, cptr)))
659 return IsDeopped(member);
661 return (IsUser(cptr) ? 1 : 0);
664 int is_zombie(struct Client *cptr, struct Channel *chptr)
666 struct Membership* member;
670 if ((member = find_member_link(chptr, cptr)))
671 return IsZombie(member);
675 int has_voice(struct Client* cptr, struct Channel* chptr)
677 struct Membership* member;
680 if ((member = find_member_link(chptr, cptr)))
681 return (!IsZombie(member) && HasVoice(member));
686 int member_can_send_to_channel(struct Membership* member)
690 if (IsVoicedOrOpped(member))
693 * If it's moderated, and you aren't a priviledged user, you can't
696 if (member->channel->mode.mode & MODE_MODERATED)
699 * If you're banned then you can't speak either.
700 * but because of the amount of CPU time that is_banned chews
701 * we only check it for our clients.
703 if (MyUser(member->user) && is_banned(member->user, member->channel, member))
708 int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr)
710 struct Membership *member;
713 * Servers can always speak on channels.
718 member = find_channel_member(cptr, chptr);
721 * You can't speak if your off channel, if the channel is modeless, or
722 * +n.(no external messages)
725 if ((chptr->mode.mode & MODE_NOPRIVMSGS) || IsModelessChannel(chptr->chname))
730 return member_can_send_to_channel(member);
734 * find_no_nickchange_channel
735 * if a member and not opped or voiced and banned
736 * return the name of the first channel banned on
738 const char* find_no_nickchange_channel(struct Client* cptr)
741 struct Membership* member;
742 for (member = cptr->user->channel; member; member = member->next_channel) {
743 if (!IsVoicedOrOpped(member) && is_banned(cptr, member->channel, member))
744 return member->channel->chname;
752 * write the "simple" list of channel modes for channel chptr onto buffer mbuf
753 * with the parameters in pbuf.
755 void channel_modes(struct Client *cptr, char *mbuf, char *pbuf,
756 struct Channel *chptr)
763 if (chptr->mode.mode & MODE_SECRET)
765 else if (chptr->mode.mode & MODE_PRIVATE)
767 if (chptr->mode.mode & MODE_MODERATED)
769 if (chptr->mode.mode & MODE_TOPICLIMIT)
771 if (chptr->mode.mode & MODE_INVITEONLY)
773 if (chptr->mode.mode & MODE_NOPRIVMSGS)
775 if (chptr->mode.limit) {
777 sprintf_irc(pbuf, "%d", chptr->mode.limit);
780 if (*chptr->mode.key) {
782 if (is_chan_op(cptr, chptr) || IsServer(cptr)) {
783 if (chptr->mode.limit)
785 strcat(pbuf, chptr->mode.key);
792 static int send_mode_list(struct Client *cptr, char *chname,
793 time_t creationtime, struct SLink *top,
803 cp = modebuf + strlen(modebuf);
804 if (*parabuf) /* mode +l or +k xx */
806 for (lp = top; lp; lp = lp->next)
808 if (!(lp->flags & mask))
810 if (mask == CHFL_BAN)
811 name = lp->value.ban.banstr;
813 name = lp->value.cptr->name;
814 if (strlen(parabuf) + strlen(name) + 11 < MODEBUFLEN)
816 strcat(parabuf, " ");
817 strcat(parabuf, name);
828 /* cptr is always a server! So we send creationtimes */
829 sendmodeto_one(cptr, me.name, chname, modebuf, parabuf, creationtime);
837 strcpy(parabuf, name);
850 * send "cptr" a full list of the modes for channel chptr.
852 void send_channel_modes(struct Client *cptr, struct Channel *chptr)
854 static unsigned int current_flags[4] =
855 { 0, CHFL_CHANOP | CHFL_VOICE, CHFL_VOICE, CHFL_CHANOP };
862 struct Membership* member;
864 char modebuf[MODEBUFLEN];
865 char parabuf[MODEBUFLEN];
866 char sndbuf[IRC_BUFSIZE];
871 if (IsLocalChannel(chptr->chname))
874 member = chptr->members;
875 lp2 = chptr->banlist;
877 *modebuf = *parabuf = '\0';
878 channel_modes(cptr, modebuf, parabuf, chptr);
880 for (first = 1; full; first = 0) /* Loop for multiple messages */
882 full = 0; /* Assume by default we get it
883 all in one message */
885 /* (Continued) prefix: "<Y> B <channel> <TS>" */
886 /* is there any better way we can do this? */
887 sblen = ircd_snprintf(&me, sndbuf, sizeof(sndbuf), "%C " TOK_BURST
888 " %H %Tu", &me, chptr, chptr->creationtime);
890 if (first && modebuf[1]) /* Add simple modes (iklmnpst)
893 /* prefix: "<Y> B <channel> <TS>[ <modes>[ <params>]]" */
894 sndbuf[sblen++] = ' ';
895 strcpy(sndbuf + sblen, modebuf);
896 sblen += strlen(modebuf);
899 sndbuf[sblen++] = ' ';
900 strcpy(sndbuf + sblen, parabuf);
901 sblen += strlen(parabuf);
906 * Attach nicks, comma seperated " nick[:modes],nick[:modes],..."
908 * Run 4 times over all members, to group the members with the
911 for (first = 1; flag_cnt < 4;
912 member = chptr->members, new_mode = 1, flag_cnt++)
914 for (; member; member = member->next_member)
916 if ((member->status & CHFL_VOICED_OR_OPPED) !=
917 current_flags[flag_cnt])
918 continue; /* Skip members with different flags */
919 if (sblen + NUMNICKLEN + 4 > BUFSIZE - 3)
920 /* The 4 is a possible ",:ov"
921 The -3 is for the "\r\n\0" that is added in send.c */
923 full = 1; /* Make sure we continue after
925 new_mode = 1; /* Ensure the new BURST line contains the current
927 break; /* Do not add this member to this message */
929 sndbuf[sblen++] = first ? ' ' : ',';
930 first = 0; /* From now on, us comma's to add new nicks */
932 sblen += ircd_snprintf(&me, sndbuf + sblen, sizeof(sndbuf) - sblen,
935 * Do we have a nick with a new mode ?
936 * Or are we starting a new BURST line?
941 if (IsVoicedOrOpped(member)) {
942 sndbuf[sblen++] = ':';
943 if (IsChanOp(member))
944 sndbuf[sblen++] = 'o';
945 if (HasVoice(member))
946 sndbuf[sblen++] = 'v';
956 /* Attach all bans, space seperated " :%ban ban ..." */
957 for (first = 2; lp2; lp2 = lp2->next)
959 len = strlen(lp2->value.ban.banstr);
960 if (sblen + len + 1 + first > BUFSIZE - 3)
961 /* The +1 stands for the added ' '.
962 * The +first stands for the added ":%".
963 * The -3 is for the "\r\n\0" that is added in send.c
972 sndbuf[sblen++] = ' ';
973 sndbuf[sblen++] = ':'; /* Will be last parameter */
974 sndbuf[sblen++] = '%'; /* To tell bans apart */
977 sndbuf[sblen++] = ' ';
978 strcpy(sndbuf + sblen, lp2->value.ban.banstr);
983 sndbuf[sblen] = '\0';
984 send_buffer(cptr, sndbuf); /* Send this message */
985 } /* Continue when there was something
986 that didn't fit (full==1) */
992 * by Carlo Wood (Run), 05 Oct 1998.
996 * When the nick is longer then NICKLEN, it is cut off (its an error of course).
997 * When the user name or host name are too long (USERLEN and HOSTLEN
998 * respectively) then they are cut off at the start with a '*'.
1000 * The following transformations are made:
1002 * 1) xxx -> nick!*@*
1003 * 2) xxx.xxx -> *!*@host
1004 * 3) xxx!yyy -> nick!user@*
1005 * 4) xxx@yyy -> *!user@host
1006 * 5) xxx!yyy@zzz -> nick!user@host
1008 char *pretty_mask(char *mask)
1010 static char star[2] = { '*', 0 };
1011 char *last_dot = NULL;
1014 /* Case 1: default */
1019 /* Do a _single_ pass through the characters of the mask: */
1020 for (ptr = mask; *ptr; ++ptr)
1024 /* Case 3 or 5: Found first '!' (without finding a '@' yet) */
1028 else if (*ptr == '@')
1030 /* Case 4: Found last '@' (without finding a '!' yet) */
1035 else if (*ptr == '.')
1037 /* Case 2: Found last '.' (without finding a '!' or '@' yet) */
1047 /* Case 4 or 5: Found last '@' */
1053 if (user == star && last_dot)
1063 char *nick_end = (user != star) ? user - 1 : ptr;
1064 if (nick_end - nick > NICKLEN)
1070 char *user_end = (host != star) ? host - 1 : ptr;
1071 if (user_end - user > USERLEN)
1073 user = user_end - USERLEN;
1078 if (host != star && ptr - host > HOSTLEN)
1080 host = ptr - HOSTLEN;
1083 return make_nick_user_host(nick, user, host);
1086 static void send_ban_list(struct Client* cptr, struct Channel* chptr)
1093 for (lp = chptr->banlist; lp; lp = lp->next)
1094 send_reply(cptr, RPL_BANLIST, chptr->chname, lp->value.ban.banstr,
1095 lp->value.ban.who, lp->value.ban.when);
1097 send_reply(cptr, RPL_ENDOFBANLIST, chptr->chname);
1101 * Check and try to apply the channel modes passed in the parv array for
1102 * the client ccptr to channel chptr. The resultant changes are printed
1103 * into mbuf and pbuf (if any) and applied to the channel.
1105 int set_mode(struct Client* cptr, struct Client* sptr,
1106 struct Channel* chptr, int parc, char* parv[],
1107 char* mbuf, char* pbuf, char* npbuf, int* badop)
1110 * This size is only needed when a broken
1111 * server sends more then MAXMODEPARAMS
1114 static struct SLink chops[MAXPARA - 2];
1115 static int flags[] = {
1118 MODE_MODERATED, 'm',
1119 MODE_NOPRIVMSGS, 'n',
1120 MODE_TOPICLIMIT, 't',
1121 MODE_INVITEONLY, 'i',
1127 char bmodebuf[MODEBUFLEN];
1128 char bparambuf[MODEBUFLEN];
1129 char nbparambuf[MODEBUFLEN]; /* "Numeric" Bounce Parameter Buffer */
1131 char* curr = parv[0];
1134 struct Membership* member_x;
1135 struct Membership* member_y;
1136 unsigned int whatt = MODE_ADD;
1137 unsigned int bwhatt = 0;
1140 int add_banid_called = 0;
1146 unsigned int nusers = 0;
1147 unsigned int newmode;
1158 static char numeric[16];
1159 char* bmbuf = bmodebuf;
1160 char* bpbuf = bparambuf;
1161 char* nbpbuf = nbparambuf;
1163 struct ConfItem* aconf;
1165 *mbuf = *pbuf = *npbuf = *bmbuf = *bpbuf = *nbpbuf = '\0';
1170 * Mode is accepted when sptr is a channel operator
1171 * but also when the mode is received from a server.
1172 * At this point, let any member pass, so they are allowed
1175 member_y = find_channel_member(sptr, chptr);
1176 if (!(IsServer(cptr) || member_y))
1179 #ifdef OPER_MODE_LCHAN
1180 if (IsOperOnLocalChannel(sptr, chptr->chname) && !IsChanOp(member_y))
1181 LocalChanOperMode = 1;
1184 mode = &(chptr->mode);
1185 memcpy(&oldm, mode, sizeof(struct Mode));
1187 newmode = mode->mode;
1189 while (curr && *curr) {
1202 if (MyUser(sptr) && opcnt >= MAXMODEPARAMS)
1205 * Check for nickname changes and try to follow these
1206 * to make sure the right client is affected by the
1208 * Even if we find a nick with find_chasing() there
1209 * is still a reason to ignore in a special case.
1210 * We need to ignore the mode when:
1211 * - It is part of a net.burst (from a server and
1212 * a MODE_ADD). Ofcourse we don't ignore mode
1213 * changes from Uworld.
1214 * - The found nick is not on the right side off
1216 * This fixes the bug that when someone (tries to)
1217 * ride a net.break and does so with the nick of
1218 * someone on the otherside, that he is nick collided
1219 * (killed) but his +o still ops the other person.
1223 if (!(who = find_chasing(sptr, parv[0], NULL)))
1228 if (!(who = findNUser(parv[0])))
1231 if (whatt == MODE_ADD && IsServer(sptr) && who->from != sptr->from &&
1232 !find_conf_byhost(cptr->confs, sptr->name, CONF_UWORLD))
1235 if (!(member_x = find_member_link(chptr, who)) ||
1236 (MyUser(sptr) && IsZombie(member_x)))
1238 send_reply(cptr, ERR_USERNOTINCHANNEL, who->name, chptr->chname);
1242 * if the user is +k, prevent a deop from local user
1244 if (whatt == MODE_DEL && IsChannelService(who) && *curr == 'o') {
1249 send_reply(cptr, ERR_ISCHANSERVICE, parv[0], chptr->chname);
1253 sprintf_irc(sendbuf,":%s NOTICE * :*** Notice -- Deop of +k user on %s by %s", /* XXX set_mode only called by old m_mode */
1254 me.name,chptr->chname,cptr->name);
1257 #ifdef NO_OPER_DEOP_LCHAN
1259 * if the user is an oper on a local channel, prevent him
1260 * from being deoped. that oper can deop himself though.
1262 if (whatt == MODE_DEL && IsOperOnLocalChannel(who, chptr->chname) &&
1263 (who != sptr) && MyUser(cptr) && *curr == 'o')
1265 send_reply(cptr, ERR_ISOPERLCHAN, parv[0], chptr->chname);
1269 if (whatt == MODE_ADD)
1271 lp = &chops[opcnt++];
1272 lp->value.cptr = who;
1273 if (IsServer(sptr) && (!(who->flags & FLAGS_TS8) || ((*curr == 'o') &&
1274 !(member_x->status & (CHFL_SERVOPOK | CHFL_CHANOP)))))
1275 *badop = ((member_x->status & CHFL_DEOPPED) && (*curr == 'o')) ? 2 : 3;
1276 lp->flags = (*curr == 'o') ? MODE_CHANOP : MODE_VOICE;
1277 lp->flags |= MODE_ADD;
1279 else if (whatt == MODE_DEL)
1281 lp = &chops[opcnt++];
1282 lp->value.cptr = who;
1283 doesdeop = 1; /* Also when -v */
1284 lp->flags = (*curr == 'o') ? MODE_CHANOP : MODE_VOICE;
1285 lp->flags |= MODE_DEL;
1294 /* check now so we eat the parameter if present */
1299 char *s = &(*parv)[-1];
1300 unsigned short count = KEYLEN + 1;
1302 while (*++s > ' ' && *s != ':' && --count);
1304 if (!**parv) /* nothing left in key */
1307 if (MyUser(sptr) && opcnt >= MAXMODEPARAMS)
1309 if (whatt == MODE_ADD)
1311 if (*mode->key && !IsServer(cptr))
1312 send_reply(cptr, ERR_KEYSET, chptr->chname);
1313 else if (!*mode->key || IsServer(cptr))
1315 lp = &chops[opcnt++];
1316 lp->value.cp = *parv;
1317 if (strlen(lp->value.cp) > KEYLEN)
1318 lp->value.cp[KEYLEN] = '\0';
1319 lp->flags = MODE_KEY | MODE_ADD;
1323 else if (whatt == MODE_DEL)
1325 /* Debug((DEBUG_INFO, "removing key: mode->key: >%s< *parv: >%s<", mode->key, *parv)); */
1326 if (0 == ircd_strcmp(mode->key, *parv) || IsServer(cptr))
1328 /* Debug((DEBUG_INFO, "key matched")); */
1329 lp = &chops[opcnt++];
1330 lp->value.cp = mode->key;
1331 lp->flags = MODE_KEY | MODE_DEL;
1338 if (0 == banlsent) {
1342 send_ban_list(cptr, chptr);
1348 if (EmptyString(*parv))
1352 if ((cp = strchr(*parv, ' ')))
1354 if (opcnt >= MAXMODEPARAMS || **parv == ':' || **parv == '\0')
1357 if (whatt == MODE_ADD)
1359 lp = &chops[opcnt++];
1360 lp->value.cp = *parv;
1361 lp->flags = MODE_ADD | MODE_BAN;
1363 else if (whatt == MODE_DEL)
1365 lp = &chops[opcnt++];
1366 lp->value.cp = *parv;
1367 lp->flags = MODE_DEL | MODE_BAN;
1372 * limit 'l' to only *1* change per mode command but
1377 if (whatt == MODE_ADD && --parc > 0)
1381 if (whatt == MODE_DEL)
1389 if (EmptyString(*parv))
1391 if (MyUser(sptr) && opcnt >= MAXMODEPARAMS)
1393 if (!(nusers = atoi(*++parv)))
1395 lp = &chops[opcnt++];
1396 lp->flags = MODE_ADD | MODE_LIMIT;
1400 need_more_params(cptr, "MODE +l");
1402 case 'i': /* falls through for default case */
1403 if (whatt == MODE_DEL)
1404 while ((lp = chptr->invites))
1405 del_invite(lp->value.cptr, chptr);
1407 for (ip = flags; *ip; ip += 2)
1408 if (*(ip + 1) == *curr)
1413 if (whatt == MODE_ADD)
1415 if (*ip == MODE_PRIVATE)
1416 newmode &= ~MODE_SECRET;
1417 else if (*ip == MODE_SECRET)
1418 newmode &= ~MODE_PRIVATE;
1424 else if (!IsServer(cptr))
1425 send_reply(cptr, ERR_UNKNOWNMODE, *curr);
1430 * Make sure mode strings such as "+m +t +p +i" are parsed
1433 if (!*curr && parc > 0)
1437 /* If this was from a server, and it is the last
1438 * parameter and it starts with a digit, it must
1439 * be the creationtime. --Run
1443 if (parc == 1 && IsDigit(*curr))
1445 newtime = atoi(curr);
1446 if (newtime && chptr->creationtime == MAGIC_REMOTE_JOIN_TS)
1448 chptr->creationtime = newtime;
1457 else if (newtime > chptr->creationtime)
1458 { /* It is a net-break ride if we have ops.
1459 bounce modes if we have ops. --Run */
1462 else if (chptr->creationtime == 0)
1464 if (chptr->creationtime == 0 || doesop)
1465 chptr->creationtime = newtime;
1473 * A legal *badop can occur when two
1474 * people join simultaneously a channel,
1475 * Allow for 10 min of lag (and thus hacking
1476 * on channels younger then 10 min) --Run
1478 else if (*badop == 0 ||
1479 chptr->creationtime > (TStime() - TS_LAG_TIME))
1481 if (newtime < chptr->creationtime)
1482 chptr->creationtime = newtime;
1491 } /* end of while loop for MODE processing */
1493 #ifdef OPER_MODE_LCHAN
1495 * Now reject non chan ops. Accept modes from opers on local channels
1496 * even if they are deopped
1498 if (!IsServer(cptr) &&
1499 (!member_y || !(IsChanOp(member_y) ||
1500 IsOperOnLocalChannel(sptr, chptr->chname))))
1502 if (!IsServer(cptr) && (!member_y || !IsChanOp(member_y)))
1506 return (opcnt || newmode != mode->mode || limitset || keychange) ? 0 : -1;
1509 if (doesop && newtime == 0 && IsServer(sptr))
1513 (aconf = find_conf_byhost(cptr->confs, sptr->name, CONF_UWORLD)))
1516 #ifdef OPER_MODE_LCHAN
1517 bounce = (*badop == 1 || *badop == 2 ||
1518 (is_deopped(sptr, chptr) &&
1519 !IsOperOnLocalChannel(sptr, chptr->chname))) ? 1 : 0;
1521 bounce = (*badop == 1 || *badop == 2 || is_deopped(sptr, chptr)) ? 1 : 0;
1525 for (ip = flags; *ip; ip += 2) {
1526 if ((*ip & newmode) && !(*ip & oldm.mode))
1530 if (bwhatt != MODE_DEL)
1535 *bmbuf++ = *(ip + 1);
1539 if (whatt != MODE_ADD)
1545 *mbuf++ = *(ip + 1);
1549 for (ip = flags; *ip; ip += 2) {
1550 if ((*ip & oldm.mode) && !(*ip & newmode))
1554 if (bwhatt != MODE_ADD)
1559 *bmbuf++ = *(ip + 1);
1563 if (whatt != MODE_DEL)
1569 *mbuf++ = *(ip + 1);
1574 if (limitset && !nusers && mode->limit)
1578 if (bwhatt != MODE_ADD)
1584 sprintf(numeric, "%-15d", mode->limit);
1585 if ((cp = strchr(numeric, ' ')))
1587 strcat(bpbuf, numeric);
1588 blen += strlen(numeric);
1590 strcat(nbpbuf, numeric);
1591 nblen += strlen(numeric);
1592 strcat(nbpbuf, " ");
1596 if (whatt != MODE_DEL)
1601 mode->mode &= ~MODE_LIMIT;
1607 * Reconstruct "+bkov" chain.
1613 unsigned int prev_whatt = 0;
1615 for (; i < opcnt; i++)
1619 * make sure we have correct mode change sign
1621 if (whatt != (lp->flags & (MODE_ADD | MODE_DEL)))
1623 if (lp->flags & MODE_ADD)
1637 nlen = strlen(npbuf);
1639 * get c as the mode char and tmp as a pointer to
1640 * the parameter for this mode change.
1642 switch (lp->flags & MODE_WPARAS)
1646 cp = lp->value.cptr->name;
1650 cp = lp->value.cptr->name;
1654 * I made this a bit more user-friendly (tm):
1656 * nick!user = nick!user@*
1657 * user@host = *!user@host
1658 * host.name = *!*@host.name --Run
1661 cp = pretty_mask(lp->value.cp);
1669 sprintf(numeric, "%-15d", nusers);
1670 if ((cp = strchr(numeric, ' ')))
1676 /* What could be added: cp+' '+' '+<TS>+'\0' */
1677 if (len + strlen(cp) + 13 > MODEBUFLEN ||
1678 nlen + strlen(cp) + NUMNICKLEN + 12 > MODEBUFLEN)
1681 switch (lp->flags & MODE_WPARAS)
1684 if (strlen(cp) > KEYLEN)
1685 *(cp + KEYLEN) = '\0';
1686 if ((whatt == MODE_ADD && (*mode->key == '\0' ||
1687 0 != ircd_strcmp(mode->key, cp))) ||
1688 (whatt == MODE_DEL && (*mode->key != '\0')))
1692 if (*mode->key == '\0')
1694 if (bwhatt != MODE_DEL)
1704 nblen += strlen(cp);
1705 strcat(nbpbuf, " ");
1710 if (bwhatt != MODE_ADD)
1715 strcat(bpbuf, mode->key);
1716 blen += strlen(mode->key);
1719 strcat(nbpbuf, mode->key);
1720 nblen += strlen(mode->key);
1721 strcat(nbpbuf, " ");
1726 if (*mbuf != '+' && *mbuf != '-')
1742 if (whatt == MODE_ADD)
1743 ircd_strncpy(mode->key, cp, KEYLEN);
1750 if (nusers && nusers != mode->limit)
1754 if (mode->limit == 0)
1756 if (bwhatt != MODE_DEL)
1764 if (bwhatt != MODE_ADD)
1769 sprintf(numeric, "%-15d", mode->limit);
1770 if ((cp = strchr(numeric, ' ')))
1772 strcat(bpbuf, numeric);
1773 blen += strlen(numeric);
1776 strcat(nbpbuf, numeric);
1777 nblen += strlen(numeric);
1778 strcat(nbpbuf, " ");
1783 if (*mbuf != '+' && *mbuf != '-')
1799 mode->limit = nusers;
1805 member_y = find_member_link(chptr, lp->value.cptr);
1806 if (lp->flags & MODE_ADD)
1808 change = (~member_y->status) & CHFL_VOICED_OR_OPPED & lp->flags;
1809 if (change && bounce)
1811 if (lp->flags & MODE_CHANOP)
1812 SetDeopped(member_y);
1814 if (bwhatt != MODE_DEL)
1820 strcat(bpbuf, lp->value.cptr->name);
1821 blen += strlen(lp->value.cptr->name);
1824 sprintf_irc(nbpbuf + nblen, "%s%s ", NumNick(lp->value.cptr));
1825 nblen += strlen(nbpbuf + nblen);
1830 member_y->status |= lp->flags & CHFL_VOICED_OR_OPPED;
1831 if (IsChanOp(member_y))
1833 ClearDeopped(member_y);
1835 ClearServOpOk(member_y);
1841 change = member_y->status & CHFL_VOICED_OR_OPPED & lp->flags;
1842 if (change && bounce)
1844 if (lp->flags & MODE_CHANOP)
1845 ClearDeopped(member_y);
1846 if (bwhatt != MODE_ADD)
1852 strcat(bpbuf, lp->value.cptr->name);
1853 blen += strlen(lp->value.cptr->name);
1856 sprintf_irc(nbpbuf + nblen, "%s%s ", NumNick(lp->value.cptr));
1857 blen += strlen(bpbuf + blen);
1862 member_y->status &= ~change;
1863 if ((change & MODE_CHANOP) && IsServer(sptr))
1864 SetDeopped(member_y);
1867 if (change || *badop == 2 || *badop == 4)
1874 sprintf_irc(npbuf + nlen, "%s%s ", NumNick(lp->value.cptr));
1875 nlen += strlen(npbuf + nlen);
1876 npbuf[nlen++] = ' ';
1882 if (*mbuf != '+' && *mbuf != '-')
1890 * Only bans aren't bounced, it makes no sense to bounce last second
1891 * bans while propagating bans done before the net.rejoin. The reason
1892 * why I don't bounce net.rejoin bans is because it is too much
1893 * work to take care of too long strings adding the necessary TS to
1894 * net.burst bans -- RunLazy
1895 * We do have to check for *badop==2 now, we don't want HACKs to take
1898 * Since BURST - I *did* implement net.rejoin ban bouncing. So now it
1899 * certainly makes sense to also bounce 'last second' bans (bans done
1900 * after the net.junction). -- RunHardWorker
1902 if ((change = (whatt & MODE_ADD) &&
1903 !add_banid(sptr, chptr, cp, !bounce, !add_banid_called)))
1904 add_banid_called = 1;
1906 change = (whatt & MODE_DEL) && !del_banid(chptr, cp, !bounce);
1908 if (bounce && change)
1911 if ((whatt & MODE_ADD))
1913 if (bwhatt != MODE_DEL)
1919 else if ((whatt & MODE_DEL))
1921 if (bwhatt != MODE_ADD)
1933 nblen += strlen(cp);
1934 strcat(nbpbuf, " ");
1952 if (*mbuf != '+' && *mbuf != '-')
1959 } /* for (; i < opcnt; i++) */
1966 if (!hacknotice && *bmodebuf && chptr->creationtime)
1968 sendcmdto_one(&me, CMD_MODE, cptr, "%H %s %s %Tu", chptr, bmodebuf,
1969 nbparambuf, *badop == 2 ? (time_t) 0 : chptr->creationtime);
1971 /* If there are possibly bans to re-add, bounce them now */
1972 if (add_banid_called && bounce)
1974 struct SLink *ban[6]; /* Max 6 bans at a time */
1975 size_t len[6], sblen, total_len;
1976 int cnt, delayed = 0;
1977 while (delayed || (ban[0] = next_overlapped_ban()))
1979 len[0] = strlen(ban[0]->value.ban.banstr);
1980 cnt = 1; /* We already got one ban :) */
1981 /* XXX sendbuf used to send ban bounces! */
1982 sblen = sprintf_irc(sendbuf, ":%s MODE %s +b", /* XXX set_mode only called by old m_mode */
1983 me.name, chptr->chname) - sendbuf; /* XXX set_mode only called by old m_mode */
1984 total_len = sblen + 1 + len[0]; /* 1 = ' ' */
1985 /* Find more bans: */
1987 while (cnt < 6 && (ban[cnt] = next_overlapped_ban()))
1989 len[cnt] = strlen(ban[cnt]->value.ban.banstr);
1990 if (total_len + 5 + len[cnt] > BUFSIZE) /* 5 = "b \r\n\0" */
1992 delayed = cnt + 1; /* != 0 */
1995 sendbuf[sblen++] = 'b'; /* XXX set_mode only called by old m_mode */
1996 total_len += 2 + len[cnt++]; /* 2 = "b " */
2000 sendbuf[sblen++] = ' '; /* XXX set_mode only called by old m_mode */
2001 strcpy(sendbuf + sblen, ban[cnt]->value.ban.banstr); /* XXX set_mode only called by old m_mode */
2004 sendbufto_one(cptr); /* Send bounce to uplink */ /* XXX set_mode only called by old m_mode */
2006 ban[0] = ban[delayed - 1];
2009 /* Send -b's of overlapped bans to clients to keep them synchronized */
2010 if (add_banid_called && !bounce)
2013 char *banstr[6]; /* Max 6 bans at a time */
2014 size_t len[6], sblen, psblen, total_len;
2015 int cnt, delayed = 0;
2016 struct Membership* member_z;
2017 struct Client *acptr;
2019 /* XXX sendbuf used to send ban bounces! */
2020 psblen = sprintf_irc(sendbuf, ":%s MODE %s -b", /* XXX set_mode only called by old m_mode */
2021 sptr->name, chptr->chname) - sendbuf; /* XXX set_mode only called by old m_mode */
2022 else /* We rely on IsRegistered(sptr) being true for MODE */
2023 psblen = sprintf_irc(sendbuf, ":%s!%s@%s MODE %s -b", sptr->name, /* XXX set_mode only called by old m_mode */
2024 sptr->user->username, sptr->user->host, chptr->chname) - sendbuf; /* XXX set_mode only called by old m_mode */
2025 while (delayed || (ban = next_removed_overlapped_ban()))
2029 len[0] = strlen((banstr[0] = ban->value.ban.banstr));
2030 ban->value.ban.banstr = NULL;
2032 cnt = 1; /* We already got one ban :) */
2034 total_len = sblen + 1 + len[0]; /* 1 = ' ' */
2035 /* Find more bans: */
2037 while (cnt < 6 && (ban = next_removed_overlapped_ban()))
2039 len[cnt] = strlen((banstr[cnt] = ban->value.ban.banstr));
2040 ban->value.ban.banstr = NULL;
2041 if (total_len + 5 + len[cnt] > BUFSIZE) /* 5 = "b \r\n\0" */
2043 delayed = cnt + 1; /* != 0 */
2046 sendbuf[sblen++] = 'b'; /* XXX set_mode only called by old m_mode */
2047 total_len += 2 + len[cnt++]; /* 2 = "b " */
2051 sendbuf[sblen++] = ' '; /* XXX set_mode only called by old m_mode */
2052 strcpy(sendbuf + sblen, banstr[cnt]); /* XXX set_mode only called by old m_mode */
2053 MyFree(banstr[cnt]);
2056 for (member_z = chptr->members; member_z; member_z = member_z->next_member) {
2057 acptr = member_z->user;
2058 if (MyConnect(acptr) && !IsZombie(member_z))
2059 sendbufto_one(acptr); /* XXX set_mode only called by old m_mode */
2063 banstr[0] = banstr[delayed - 1];
2064 len[0] = len[delayed - 1];
2069 return gotts ? 1 : -1;
2072 /* We are now treating the <key> part of /join <channel list> <key> as a key
2073 * ring; that is, we try one key against the actual channel key, and if that
2074 * doesn't work, we try the next one, and so on. -Kev -Texaco
2075 * Returns: 0 on match, 1 otherwise
2076 * This version contributed by SeKs <intru@info.polymtl.ca>
2078 static int compall(char *key, char *keyring)
2083 p1 = key; /* point to the key... */
2084 while (*p1 && *p1 == *keyring)
2085 { /* step through the key and ring until they
2091 if (!*p1 && (!*keyring || *keyring == ','))
2092 /* ok, if we're at the end of the and also at the end of one of the keys
2093 in the keyring, we have a match */
2096 if (!*keyring) /* if we're at the end of the key ring, there
2097 weren't any matches, so we return 1 */
2100 /* Not at the end of the key ring, so step
2101 through to the next key in the ring: */
2102 while (*keyring && *(keyring++) != ',');
2104 goto top; /* and check it against the key */
2107 int can_join(struct Client *sptr, struct Channel *chptr, char *key)
2110 int overrideJoin = 0;
2113 * Now a banned user CAN join if invited -- Nemesi
2114 * Now a user CAN escape channel limit if invited -- bfriendly
2115 * Now a user CAN escape anything if invited -- Isomer
2118 for (lp = sptr->user->invited; lp; lp = lp->next)
2119 if (lp->value.chptr == chptr)
2122 #ifdef OPER_WALK_THROUGH_LMODES
2123 /* An oper can force a join on a local channel using "OVERRIDE" as the key.
2124 a HACK(4) notice will be sent if he would not have been supposed
2125 to join normally. */
2126 if (IsOperOnLocalChannel(sptr,chptr->chname) && !BadPtr(key) && compall("OVERRIDE",key) == 0)
2128 overrideJoin = MAGIC_OPER_OVERRIDE;
2132 if (chptr->mode.mode & MODE_INVITEONLY)
2133 return overrideJoin + ERR_INVITEONLYCHAN;
2135 if (chptr->mode.limit && chptr->users >= chptr->mode.limit)
2136 return overrideJoin + ERR_CHANNELISFULL;
2138 if (is_banned(sptr, chptr, NULL))
2139 return overrideJoin + ERR_BANNEDFROMCHAN;
2142 * now using compall (above) to test against a whole key ring -Kev
2144 if (*chptr->mode.key && (EmptyString(key) || compall(chptr->mode.key, key)))
2145 return overrideJoin + ERR_BADCHANNELKEY;
2148 return ERR_DONTCHEAT;
2154 * Remove bells and commas from channel name
2156 void clean_channelname(char *cn)
2160 for (i = 0; cn[i]; i++) {
2161 if (i >= CHANNELLEN || !IsChannelChar(cn[i])) {
2165 if (IsChannelLower(cn[i])) {
2166 cn[i] = ToLower(cn[i]);
2172 if ((unsigned char)(cn[i]) == 0xd0)
2173 cn[i] = (char) 0xf0;
2180 * Get Channel block for i (and allocate a new channel
2181 * block, if it didn't exists before).
2183 struct Channel *get_channel(struct Client *cptr, char *chname, ChannelGetType flag)
2185 struct Channel *chptr;
2188 if (EmptyString(chname))
2191 len = strlen(chname);
2192 if (MyUser(cptr) && len > CHANNELLEN)
2195 *(chname + CHANNELLEN) = '\0';
2197 if ((chptr = FindChannel(chname)))
2199 if (flag == CGT_CREATE)
2201 chptr = (struct Channel*) MyMalloc(sizeof(struct Channel) + len);
2203 ++UserStats.channels;
2204 memset(chptr, 0, sizeof(struct Channel));
2205 strcpy(chptr->chname, chname);
2206 if (GlobalChannelList)
2207 GlobalChannelList->prev = chptr;
2209 chptr->next = GlobalChannelList;
2210 chptr->creationtime = MyUser(cptr) ? TStime() : (time_t) 0;
2211 GlobalChannelList = chptr;
2217 void add_invite(struct Client *cptr, struct Channel *chptr)
2219 struct SLink *inv, **tmp;
2221 del_invite(cptr, chptr);
2223 * Delete last link in chain if the list is max length
2225 assert(list_length(cptr->user->invited) == cptr->user->invites);
2226 if (cptr->user->invites>=MAXCHANNELSPERUSER)
2227 del_invite(cptr, cptr->user->invited->value.chptr);
2229 * Add client to channel invite list
2232 inv->value.cptr = cptr;
2233 inv->next = chptr->invites;
2234 chptr->invites = inv;
2236 * Add channel to the end of the client invite list
2238 for (tmp = &(cptr->user->invited); *tmp; tmp = &((*tmp)->next));
2240 inv->value.chptr = chptr;
2243 cptr->user->invites++;
2247 * Delete Invite block from channel invite list and client invite list
2249 void del_invite(struct Client *cptr, struct Channel *chptr)
2251 struct SLink **inv, *tmp;
2253 for (inv = &(chptr->invites); (tmp = *inv); inv = &tmp->next)
2254 if (tmp->value.cptr == cptr)
2259 cptr->user->invites--;
2263 for (inv = &(cptr->user->invited); (tmp = *inv); inv = &tmp->next)
2264 if (tmp->value.chptr == chptr)
2273 /* List and skip all channels that are listen */
2274 void list_next_channels(struct Client *cptr, int nr)
2276 struct ListingArgs *args = cptr->listing;
2277 struct Channel *chptr = args->chptr;
2278 chptr->mode.mode &= ~MODE_LISTED;
2279 while (is_listed(chptr) || --nr >= 0)
2281 for (; chptr; chptr = chptr->next)
2283 if (!cptr->user || (SecretChannel(chptr) && !find_channel_member(cptr, chptr)))
2285 if (chptr->users > args->min_users && chptr->users < args->max_users &&
2286 chptr->creationtime > args->min_time &&
2287 chptr->creationtime < args->max_time &&
2288 (!args->topic_limits || (*chptr->topic &&
2289 chptr->topic_time > args->min_topic_time &&
2290 chptr->topic_time < args->max_topic_time)))
2292 if (ShowChannel(cptr,chptr))
2293 send_reply(cptr, RPL_LIST, chptr->chname, chptr->users,
2295 chptr = chptr->next;
2301 MyFree(cptr->listing);
2302 cptr->listing = NULL;
2303 send_reply(cptr, RPL_LISTEND);
2309 cptr->listing->chptr = chptr;
2310 chptr->mode.mode |= MODE_LISTED;
2314 /* XXX AIEEEE! sendbuf is an institution here :( */
2315 void add_token_to_sendbuf(char *token, size_t *sblenp, int *firstp,
2316 int *send_itp, char is_a_ban, int mode)
2318 int first = *firstp;
2321 * Heh - we do not need to test if it still fits in the buffer, because
2322 * this BURST message is reconstructed from another BURST message, and
2323 * it only can become smaller. --Run
2326 if (*firstp) /* First token in this parameter ? */
2330 *send_itp = 1; /* Buffer contains data to be sent */
2331 sendbuf[(*sblenp)++] = ' '; /* XXX add_token_to_sendbuf only called by old m_burst */
2334 sendbuf[(*sblenp)++] = ':'; /* Bans are always the last "parv" */ /* XXX add_token_to_sendbuf only called by old m_burst */
2335 sendbuf[(*sblenp)++] = is_a_ban; /* XXX add_token_to_sendbuf only called by old m_burst */
2338 else /* Of course, 'send_it' is already set here */
2339 /* Seperate banmasks with a space because
2340 they can contain commas themselfs: */
2341 sendbuf[(*sblenp)++] = is_a_ban ? ' ' : ','; /* XXX add_token_to_sendbuf only called by old m_burst */
2342 strcpy(sendbuf + *sblenp, token); /* XXX add_token_to_sendbuf only called by old m_burst */
2343 *sblenp += strlen(token);
2344 if (!is_a_ban) /* nick list ? Need to take care
2345 of modes for nicks: */
2347 static int last_mode = 0;
2348 mode &= CHFL_CHANOP | CHFL_VOICE;
2351 if (last_mode != mode) /* Append mode like ':ov' if changed */
2354 sendbuf[(*sblenp)++] = ':'; /* XXX add_token_to_sendbuf only called by old m_burst */
2355 if (mode & CHFL_CHANOP)
2356 sendbuf[(*sblenp)++] = 'o'; /* XXX add_token_to_sendbuf only called by old m_burst */
2357 if (mode & CHFL_VOICE)
2358 sendbuf[(*sblenp)++] = 'v'; /* XXX add_token_to_sendbuf only called by old m_burst */
2360 sendbuf[*sblenp] = '\0'; /* XXX add_token_to_sendbuf only called by old m_burst */
2364 void cancel_mode(struct Client *sptr, struct Channel *chptr, char m,
2365 const char *param, int *count)
2370 int paramdoesntfit = 0;
2371 char parabuf[MODEBUFLEN];
2377 if (*count == -1) /* initialize ? */
2379 /* XXX sendbuf used! */
2381 sprintf_irc(sendbuf, ":%s MODE %s -", sptr->name, chptr->chname); /* XXX cancel_mode only called from old ms_burst */
2385 /* m == 0 means flush */
2390 size_t nplen = strlen(param);
2391 if (pb - parabuf + nplen + 23 > MODEBUFLEN)
2405 else if (*count == 0)
2407 if (*count == 6 || !m || paramdoesntfit)
2409 struct Membership* member;
2410 strcpy(sbp, parabuf);
2411 for (member = chptr->members; member; member = member->next_member)
2412 if (MyUser(member->user))
2413 sendbufto_one(member->user); /* XXX cancel_mode only called from old ms_burst */
2423 pb += strlen(param);
2436 * X --a--> A --b--> B --d--> D
2440 * Where `who' is being KICK-ed by a "KICK" message received by server 'A'
2441 * via 'a', or on server 'B' via either 'b' or 'c', or on server D via 'd'.
2443 * a) On server A : set CHFL_ZOMBIE for `who' (lp) and pass on the KICK.
2444 * Remove the user immedeately when no users are left on the channel.
2445 * b) On server B : remove the user (who/lp) from the channel, send a
2446 * PART upstream (to A) and pass on the KICK.
2447 * c) KICKed by `client'; On server B : remove the user (who/lp) from the
2448 * channel, and pass on the KICK.
2449 * d) On server D : remove the user (who/lp) from the channel, and pass on
2453 * - Setting the ZOMBIE flag never hurts, we either remove the
2454 * client after that or we don't.
2455 * - The KICK message was already passed on, as should be in all cases.
2456 * - `who' is removed in all cases except case a) when users are left.
2457 * - A PART is only sent upstream in case b).
2463 * 1 --- 2 --- 3 --- 4 --- 5
2467 * We also need to turn 'who' into a zombie on servers 1 and 6,
2468 * because a KICK from 'who' (kicking someone else in that direction)
2469 * can arrive there afterwards - which should not be bounced itself.
2470 * Therefore case a) also applies for servers 1 and 6.
2474 void make_zombie(struct Membership* member, struct Client* who, struct Client* cptr,
2475 struct Client* sptr, struct Channel* chptr)
2477 assert(0 != member);
2482 /* Default for case a): */
2485 /* Case b) or c) ?: */
2486 if (MyUser(who)) /* server 4 */
2488 if (IsServer(cptr)) /* Case b) ? */
2489 sendcmdto_one(who, CMD_PART, cptr, "%H", chptr);
2490 remove_user_from_channel(who, chptr);
2493 if (who->from == cptr) /* True on servers 1, 5 and 6 */
2495 struct Client *acptr = IsServer(sptr) ? sptr : sptr->user->server;
2496 for (; acptr != &me; acptr = acptr->serv->up)
2497 if (acptr == who->user->server) /* Case d) (server 5) */
2499 remove_user_from_channel(who, chptr);
2504 /* Case a) (servers 1, 2, 3 and 6) */
2505 if (channel_all_zombies(chptr))
2506 remove_user_from_channel(who, chptr);
2508 /* XXX Can't actually call Debug here; if the channel is all zombies,
2509 * chptr will no longer exist when we get here.
2510 Debug((DEBUG_INFO, "%s is now a zombie on %s", who->name, chptr->chname));
2514 int number_of_zombies(struct Channel *chptr)
2516 struct Membership* member;
2520 for (member = chptr->members; member; member = member->next_member) {
2521 if (IsZombie(member))
2528 * send_hack_notice()
2530 * parc & parv[] are the same as that of the calling function:
2531 * mtype == 1 is from m_mode, 2 is from m_create, 3 is from m_kick.
2533 * This function prepares sendbuf with the server notices and wallops
2534 * to be sent for all hacks. -Ghostwolf 18-May-97
2536 /* XXX let's get rid of this if we can */
2537 void send_hack_notice(struct Client *cptr, struct Client *sptr, int parc,
2538 char *parv[], int badop, int mtype)
2540 struct Channel *chptr;
2541 static char params[MODEBUFLEN];
2543 chptr = FindChannel(parv[1]);
2546 /* P10 servers require numeric nick conversion before sending. */
2549 case 1: /* Convert nicks for MODE HACKs here */
2551 char *mode = parv[2];
2554 while (*mode && *mode != 'o' && *mode != 'v')
2556 strcat(params, " ");
2557 if (*mode == 'o' || *mode == 'v')
2560 * blindly stumble through parameter list hoping one of them
2561 * might turn out to be a numeric nick
2562 * NOTE: this should not cause a problem but _may_ end up finding
2563 * something we aren't looking for. findNUser should be able to
2564 * handle any garbage that is thrown at it, but may return a client
2565 * if we happen to get lucky with a mode string or a timestamp
2567 struct Client *acptr;
2568 if ((acptr = findNUser(parv[i])) != NULL) /* Convert nicks here */
2569 strcat(params, acptr->name);
2572 strcat(params, "<");
2573 strcat(params, parv[i]);
2574 strcat(params, ">");
2577 else /* If it isn't a numnick, send it 'as is' */
2578 strcat(params, parv[i]);
2581 sprintf_irc(sendbuf, /* XXX send_hack_notice only called from old m_mode */
2582 ":%s NOTICE * :*** Notice -- %sHACK(%d): %s MODE %s %s%s ["
2583 TIME_T_FMT "]", me.name, (badop == 3) ? "BOUNCE or " : "", badop,
2584 parv[0], parv[1], parv[2], params, chptr->creationtime);
2585 sendbufto_op_mask((badop == 3) ? SNO_HACK3 : (badop == /* XXX DYING */ /* XXX send_hack_notice only called from old m_mode */
2586 4) ? SNO_HACK4 : SNO_HACK2);
2588 if ((IsServer(sptr)) && (badop == 2))
2590 sprintf_irc(sendbuf, ":%s DESYNCH :HACK: %s MODE %s %s%s", /* XXX send_hack_notice only called from old m_mode */
2591 me.name, parv[0], parv[1], parv[2], params);
2592 sendbufto_serv_butone(cptr); /* XXX DYING */ /* XXX send_hack_notice only called from old m_mode */
2596 case 2: /* No conversion is needed for CREATE; the only numnick is sptr */
2598 sendto_serv_butone(cptr, ":%s DESYNCH :HACK: %s CREATE %s %s", /* XXX DYING */
2599 me.name, sptr->name, chptr->chname, parv[2]);
2600 sendto_op_mask(SNO_HACK2, "HACK(2): %s CREATE %s %s", /* XXX DYING */
2601 sptr->name, chptr->chname, parv[2]);
2604 case 3: /* Convert nick in KICK message */
2606 struct Client *acptr;
2607 if ((acptr = findNUser(parv[2])) != NULL) /* attempt to convert nick */
2608 sprintf_irc(sendbuf, /* XXX send_hack_notice only called from old m_mode */
2609 ":%s NOTICE * :*** Notice -- HACK: %s KICK %s %s :%s",
2610 me.name, sptr->name, parv[1], acptr->name, parv[3]);
2611 else /* if conversion fails, send it 'as is' in <>'s */
2612 sprintf_irc(sendbuf, /* XXX send_hack_notice only called from old m_mode */
2613 ":%s NOTICE * :*** Notice -- HACK: %s KICK %s <%s> :%s",
2614 me.name, sptr->name, parv[1], parv[2], parv[3]);
2615 sendbufto_op_mask(SNO_HACK4); /* XXX DYING */ /* XXX send_hack_notice only called from old m_mode */
2622 * This helper function builds an argument string in strptr, consisting
2623 * of the original string, a space, and str1 and str2 concatenated (if,
2624 * of course, str2 is not NULL)
2627 build_string(char *strptr, int *strptr_i, char *str1, char *str2, char c)
2630 strptr[(*strptr_i)++] = c;
2633 strptr[(*strptr_i)++] = *(str1++);
2637 strptr[(*strptr_i)++] = *(str2++);
2639 strptr[(*strptr_i)] = '\0';
2643 * This is the workhorse of our ModeBuf suite; this actually generates the
2644 * output MODE commands, HACK notices, or whatever. It's pretty complicated.
2647 modebuf_flush_int(struct ModeBuf *mbuf, int all)
2649 /* we only need the flags that don't take args right now */
2650 static int flags[] = {
2651 /* MODE_CHANOP, 'o', */
2652 /* MODE_VOICE, 'v', */
2655 MODE_MODERATED, 'm',
2656 MODE_TOPICLIMIT, 't',
2657 MODE_INVITEONLY, 'i',
2658 MODE_NOPRIVMSGS, 'n',
2659 /* MODE_KEY, 'k', */
2660 /* MODE_BAN, 'b', */
2661 /* MODE_LIMIT, 'l', */
2667 struct Client *app_source; /* where the MODE appears to come from */
2669 char addbuf[20]; /* accumulates +psmtin, etc. */
2671 char rembuf[20]; /* accumulates -psmtin, etc. */
2673 char *bufptr; /* we make use of indirection to simplify the code */
2676 char addstr[BUFSIZE]; /* accumulates MODE parameters to add */
2678 char remstr[BUFSIZE]; /* accumulates MODE parameters to remove */
2680 char *strptr; /* more indirection to simplify the code */
2683 int totalbuflen = BUFSIZE - 200; /* fuzz factor -- don't overrun buffer! */
2686 char limitbuf[20]; /* convert limits to strings */
2688 unsigned int limitdel = MODE_LIMIT;
2692 /* If the ModeBuf is empty, we have nothing to do */
2693 if (mbuf->mb_add == 0 && mbuf->mb_rem == 0 && mbuf->mb_count == 0)
2696 /* Ok, if we were given the OPMODE flag, hide the source if its a user */
2697 if (mbuf->mb_dest & MODEBUF_DEST_OPMODE && !IsServer(mbuf->mb_source))
2698 app_source = mbuf->mb_source->user->server;
2700 app_source = mbuf->mb_source;
2703 * Account for user we're bouncing; we have to get it in on the first
2704 * bounced MODE, or we could have problems
2706 if (mbuf->mb_dest & MODEBUF_DEST_DEOP)
2707 totalbuflen -= 6; /* numeric nick == 5, plus one space */
2709 /* Calculate the simple flags */
2710 for (flag_p = flags; flag_p[0]; flag_p += 2) {
2711 if (*flag_p & mbuf->mb_add)
2712 addbuf[addbuf_i++] = flag_p[1];
2713 else if (*flag_p & mbuf->mb_rem)
2714 rembuf[rembuf_i++] = flag_p[1];
2717 /* Now go through the modes with arguments... */
2718 for (i = 0; i < mbuf->mb_count; i++) {
2719 if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
2721 bufptr_i = &addbuf_i;
2724 bufptr_i = &rembuf_i;
2727 if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE)) {
2728 tmp = strlen(MB_CLIENT(mbuf, i)->name);
2730 if ((totalbuflen - IRCD_MAX(5, tmp)) <= 0) /* don't overflow buffer */
2731 MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
2733 bufptr[(*bufptr_i)++] = MB_TYPE(mbuf, i) & MODE_CHANOP ? 'o' : 'v';
2734 totalbuflen -= IRCD_MAX(5, tmp) + 1;
2736 } else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN)) {
2737 tmp = strlen(MB_STRING(mbuf, i));
2739 if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
2740 MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
2742 bufptr[(*bufptr_i)++] = MB_TYPE(mbuf, i) & MODE_KEY ? 'k' : 'b';
2743 totalbuflen -= tmp + 1;
2745 } else if (MB_TYPE(mbuf, i) & MODE_LIMIT) {
2746 /* if it's a limit, we also format the number */
2747 sprintf_irc(limitbuf, "%d", MB_UINT(mbuf, i));
2749 tmp = strlen(limitbuf);
2751 if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
2752 MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
2754 bufptr[(*bufptr_i)++] = 'l';
2755 totalbuflen -= tmp + 1;
2760 /* terminate the mode strings */
2761 addbuf[addbuf_i] = '\0';
2762 rembuf[rembuf_i] = '\0';
2764 /* If we're building a user visible MODE or HACK... */
2765 if (mbuf->mb_dest & (MODEBUF_DEST_CHANNEL | MODEBUF_DEST_HACK2 |
2766 MODEBUF_DEST_HACK3 | MODEBUF_DEST_HACK4 |
2767 MODEBUF_DEST_LOG)) {
2768 /* Set up the parameter strings */
2774 for (i = 0; i < mbuf->mb_count; i++) {
2775 if (MB_TYPE(mbuf, i) & MODE_SAVE)
2778 if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
2780 strptr_i = &addstr_i;
2783 strptr_i = &remstr_i;
2786 /* deal with clients... */
2787 if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE))
2788 build_string(strptr, strptr_i, MB_CLIENT(mbuf, i)->name, 0, ' ');
2790 /* deal with strings... */
2791 else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN))
2792 build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
2795 * deal with limit; note we cannot include the limit parameter if we're
2798 else if ((MB_TYPE(mbuf, i) & (MODE_ADD | MODE_LIMIT)) ==
2799 (MODE_ADD | MODE_LIMIT))
2800 build_string(strptr, strptr_i, limitbuf, 0, ' ');
2803 /* send the messages off to their destination */
2804 if (mbuf->mb_dest & MODEBUF_DEST_HACK2) {
2805 sendto_opmask_butone(0, SNO_HACK2, "HACK(2): %s MODE %s %s%s%s%s%s%s "
2806 "[%Tu]", app_source->name, mbuf->mb_channel->chname,
2807 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
2808 addbuf, remstr, addstr,
2809 mbuf->mb_channel->creationtime);
2810 sendcmdto_serv_butone(&me, CMD_DESYNCH, mbuf->mb_connect,
2811 ":HACK: %s MODE %s %s%s%s%s%s%s [%Tu]",
2812 app_source->name, mbuf->mb_channel->chname,
2813 rembuf_i ? "-" : "", rembuf,
2814 addbuf_i ? "+" : "", addbuf, remstr, addstr,
2815 mbuf->mb_channel->creationtime);
2818 if (mbuf->mb_dest & MODEBUF_DEST_HACK3)
2819 sendto_opmask_butone(0, SNO_HACK3, "BOUNCE or HACK(3): %s MODE %s "
2820 "%s%s%s%s%s%s [%Tu]", app_source->name,
2821 mbuf->mb_channel->chname, rembuf_i ? "-" : "",
2822 rembuf, addbuf_i ? "+" : "", addbuf, remstr, addstr,
2823 mbuf->mb_channel->creationtime);
2825 if (mbuf->mb_dest & MODEBUF_DEST_HACK4)
2826 sendto_opmask_butone(0, SNO_HACK4, "HACK(4): %s MODE %s %s%s%s%s%s%s "
2827 "[%Tu]", app_source->name, mbuf->mb_channel->chname,
2828 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
2829 addbuf, remstr, addstr,
2830 mbuf->mb_channel->creationtime);
2832 if (mbuf->mb_dest & MODEBUF_DEST_LOG)
2833 log_write(LS_OPERMODE, L_INFO, LOG_NOSNOTICE,
2834 "%#C OPMODE %H %s%s%s%s%s%s", mbuf->mb_source,
2835 mbuf->mb_channel, rembuf_i ? "-" : "", rembuf,
2836 addbuf_i ? "+" : "", addbuf, remstr, addstr);
2838 if (mbuf->mb_dest & MODEBUF_DEST_CHANNEL)
2839 sendcmdto_channel_butserv(app_source, CMD_MODE, mbuf->mb_channel,
2840 "%H %s%s%s%s%s%s", mbuf->mb_channel,
2841 rembuf_i ? "-" : "", rembuf,
2842 addbuf_i ? "+" : "", addbuf, remstr, addstr);
2845 /* Now are we supposed to propagate to other servers? */
2846 if (mbuf->mb_dest & MODEBUF_DEST_SERVER) {
2847 /* set up parameter string */
2854 * limit is supressed if we're removing it; we have to figure out which
2855 * direction is the direction for it to be removed, though...
2857 limitdel |= (mbuf->mb_dest & MODEBUF_DEST_HACK2) ? MODE_DEL : MODE_ADD;
2859 for (i = 0; i < mbuf->mb_count; i++) {
2860 if (MB_TYPE(mbuf, i) & MODE_SAVE)
2863 if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */
2865 strptr_i = &addstr_i;
2868 strptr_i = &remstr_i;
2871 /* deal with modes that take clients */
2872 if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE))
2873 build_string(strptr, strptr_i, NumNick(MB_CLIENT(mbuf, i)), ' ');
2875 /* deal with modes that take strings */
2876 else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN))
2877 build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
2880 * deal with the limit. Logic here is complicated; if HACK2 is set,
2881 * we're bouncing the mode, so sense is reversed, and we have to
2882 * include the original limit if it looks like it's being removed
2884 else if ((MB_TYPE(mbuf, i) & limitdel) == limitdel)
2885 build_string(strptr, strptr_i, limitbuf, 0, ' ');
2888 /* we were told to deop the source */
2889 if (mbuf->mb_dest & MODEBUF_DEST_DEOP) {
2890 addbuf[addbuf_i++] = 'o'; /* remember, sense is reversed */
2891 addbuf[addbuf_i] = '\0'; /* terminate the string... */
2892 build_string(addstr, &addstr_i, NumNick(mbuf->mb_source), ' ');
2894 /* mark that we've done this, so we don't do it again */
2895 mbuf->mb_dest &= ~MODEBUF_DEST_DEOP;
2898 if (mbuf->mb_dest & MODEBUF_DEST_OPMODE) {
2899 /* If OPMODE was set, we're propagating the mode as an OPMODE message */
2900 sendcmdto_serv_butone(mbuf->mb_source, CMD_OPMODE, mbuf->mb_connect,
2901 "%H %s%s%s%s%s%s", mbuf->mb_channel,
2902 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
2903 addbuf, remstr, addstr);
2904 } else if (mbuf->mb_dest & MODEBUF_DEST_BOUNCE) {
2906 * If HACK2 was set, we're bouncing; we send the MODE back to the
2907 * connection we got it from with the senses reversed and a TS of 0;
2910 sendcmdto_one(&me, CMD_MODE, mbuf->mb_connect, "%H %s%s%s%s%s%s %Tu",
2911 mbuf->mb_channel, addbuf_i ? "-" : "", addbuf,
2912 rembuf_i ? "+" : "", rembuf, addstr, remstr,
2913 mbuf->mb_channel->creationtime);
2916 * We're propagating a normal MODE command to the rest of the network;
2917 * we send the actual channel TS unless this is a HACK3 or a HACK4
2919 if (IsServer(mbuf->mb_source))
2920 sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
2921 "%H %s%s%s%s%s%s %Tu", mbuf->mb_channel,
2922 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
2923 addbuf, remstr, addstr,
2924 (mbuf->mb_dest & MODEBUF_DEST_HACK4) ? 0 :
2925 mbuf->mb_channel->creationtime);
2927 sendcmdto_serv_butone(mbuf->mb_source, CMD_MODE, mbuf->mb_connect,
2928 "%H %s%s%s%s%s%s", mbuf->mb_channel,
2929 rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
2930 addbuf, remstr, addstr);
2934 /* We've drained the ModeBuf... */
2939 /* reinitialize the mode-with-arg slots */
2940 for (i = 0; i < MAXMODEPARAMS; i++) {
2941 /* If we saved any, pack them down */
2942 if (MB_TYPE(mbuf, i) & MODE_SAVE) {
2943 mbuf->mb_modeargs[mbuf->mb_count] = mbuf->mb_modeargs[i];
2944 MB_TYPE(mbuf, mbuf->mb_count) &= ~MODE_SAVE; /* don't save anymore */
2946 if (mbuf->mb_count++ == i) /* don't overwrite our hard work */
2948 } else if (MB_TYPE(mbuf, i) & MODE_FREE)
2949 MyFree(MB_STRING(mbuf, i)); /* free string if needed */
2951 MB_TYPE(mbuf, i) = 0;
2952 MB_UINT(mbuf, i) = 0;
2955 /* If we're supposed to flush it all, do so--all hail tail recursion */
2956 if (all && mbuf->mb_count)
2957 return modebuf_flush_int(mbuf, 1);
2963 * This routine just initializes a ModeBuf structure with the information
2964 * needed and the options given.
2967 modebuf_init(struct ModeBuf *mbuf, struct Client *source,
2968 struct Client *connect, struct Channel *chan, unsigned int dest)
2973 assert(0 != source);
2979 mbuf->mb_source = source;
2980 mbuf->mb_connect = connect;
2981 mbuf->mb_channel = chan;
2982 mbuf->mb_dest = dest;
2985 /* clear each mode-with-parameter slot */
2986 for (i = 0; i < MAXMODEPARAMS; i++) {
2987 MB_TYPE(mbuf, i) = 0;
2988 MB_UINT(mbuf, i) = 0;
2993 * This routine simply adds modes to be added or deleted; do a binary OR
2994 * with either MODE_ADD or MODE_DEL
2997 modebuf_mode(struct ModeBuf *mbuf, unsigned int mode)
3000 assert(0 != (mode & (MODE_ADD | MODE_DEL)));
3002 mode &= (MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET | MODE_MODERATED |
3003 MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS);
3005 if (mode & MODE_ADD) {
3006 mbuf->mb_rem &= ~mode;
3007 mbuf->mb_add |= mode;
3009 mbuf->mb_add &= ~mode;
3010 mbuf->mb_rem |= mode;
3015 * This routine adds a mode to be added or deleted that takes a unsigned
3016 * int parameter; mode may *only* be the relevant mode flag ORed with one
3017 * of MODE_ADD or MODE_DEL
3020 modebuf_mode_uint(struct ModeBuf *mbuf, unsigned int mode, unsigned int uint)
3023 assert(0 != (mode & (MODE_ADD | MODE_DEL)));
3025 MB_TYPE(mbuf, mbuf->mb_count) = mode;
3026 MB_UINT(mbuf, mbuf->mb_count) = uint;
3028 /* when we've reached the maximal count, flush the buffer */
3029 if (++mbuf->mb_count >=
3030 (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
3031 modebuf_flush_int(mbuf, 0);
3035 * This routine adds a mode to be added or deleted that takes a string
3036 * parameter; mode may *only* be the relevant mode flag ORed with one of
3037 * MODE_ADD or MODE_DEL
3040 modebuf_mode_string(struct ModeBuf *mbuf, unsigned int mode, char *string,
3044 assert(0 != (mode & (MODE_ADD | MODE_DEL)));
3046 MB_TYPE(mbuf, mbuf->mb_count) = mode | (free ? MODE_FREE : 0);
3047 MB_STRING(mbuf, mbuf->mb_count) = string;
3049 /* when we've reached the maximal count, flush the buffer */
3050 if (++mbuf->mb_count >=
3051 (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
3052 modebuf_flush_int(mbuf, 0);
3056 * This routine adds a mode to be added or deleted that takes a client
3057 * parameter; mode may *only* be the relevant mode flag ORed with one of
3058 * MODE_ADD or MODE_DEL
3061 modebuf_mode_client(struct ModeBuf *mbuf, unsigned int mode,
3062 struct Client *client)
3065 assert(0 != (mode & (MODE_ADD | MODE_DEL)));
3067 MB_TYPE(mbuf, mbuf->mb_count) = mode;
3068 MB_CLIENT(mbuf, mbuf->mb_count) = client;
3070 /* when we've reached the maximal count, flush the buffer */
3071 if (++mbuf->mb_count >=
3072 (MAXMODEPARAMS - (mbuf->mb_dest & MODEBUF_DEST_DEOP ? 1 : 0)))
3073 modebuf_flush_int(mbuf, 0);
3077 * This is the exported binding for modebuf_flush()
3080 modebuf_flush(struct ModeBuf *mbuf)
3082 return modebuf_flush_int(mbuf, 1);
3086 * This extracts the simple modes contained in mbuf
3089 modebuf_extract(struct ModeBuf *mbuf, char *buf)
3091 static int flags[] = {
3092 /* MODE_CHANOP, 'o', */
3093 /* MODE_VOICE, 'v', */
3096 MODE_MODERATED, 'm',
3097 MODE_TOPICLIMIT, 't',
3098 MODE_INVITEONLY, 'i',
3099 MODE_NOPRIVMSGS, 'n',
3101 /* MODE_BAN, 'b', */
3106 int i, bufpos = 0, len;
3108 char *key = 0, limitbuf[20];
3117 for (i = 0; i < mbuf->mb_count; i++) { /* find keys and limits */
3118 if (MB_TYPE(mbuf, i) & MODE_ADD) {
3119 add |= MB_TYPE(mbuf, i) & (MODE_KEY | MODE_LIMIT);
3121 if (MB_TYPE(mbuf, i) & MODE_KEY) /* keep strings */
3122 key = MB_STRING(mbuf, i);
3123 else if (MB_TYPE(mbuf, i) & MODE_LIMIT)
3124 ircd_snprintf(0, limitbuf, sizeof(limitbuf), "%d", MB_UINT(mbuf, i));
3131 buf[bufpos++] = '+'; /* start building buffer */
3133 for (flag_p = flags; flag_p[0]; flag_p += 2)
3135 buf[bufpos++] = flag_p[1];
3137 for (i = 0, len = bufpos; i < len; i++) {
3139 build_string(buf, &bufpos, key, 0, ' ');
3140 else if (buf[i] == 'l')
3141 build_string(buf, &bufpos, limitbuf, 0, ' ');
3150 * Simple function to invalidate bans
3153 mode_ban_invalidate(struct Channel *chan)
3155 struct Membership *member;
3157 for (member = chan->members; member; member = member->next_member)
3158 ClearBanValid(member);
3162 * Simple function to drop invite structures
3165 mode_invite_clear(struct Channel *chan)
3167 while (chan->invites)
3168 del_invite(chan->invites->value.cptr, chan);
3171 /* What we've done for mode_parse so far... */
3172 #define DONE_LIMIT 0x01 /* We've set the limit */
3173 #define DONE_KEY 0x02 /* We've set the key */
3174 #define DONE_BANLIST 0x04 /* We've sent the ban list */
3175 #define DONE_NOTOPER 0x08 /* We've sent a "Not oper" error */
3176 #define DONE_BANCLEAN 0x10 /* We've cleaned bans... */
3179 struct ModeBuf *mbuf;
3180 struct Client *cptr;
3181 struct Client *sptr;
3182 struct Channel *chptr;
3193 struct SLink banlist[MAXPARA];
3196 struct Client *client;
3197 } cli_change[MAXPARA];
3201 * Here's a helper function to deal with sending along "Not oper" or
3202 * "Not member" messages
3205 send_notoper(struct ParseState *state)
3207 if (state->done & DONE_NOTOPER)
3210 send_reply(state->sptr, (state->flags & MODE_PARSE_NOTOPER) ?
3211 ERR_CHANOPRIVSNEEDED : ERR_NOTONCHANNEL, state->chptr->chname);
3213 state->done |= DONE_NOTOPER;
3217 * Helper function to convert limits
3220 mode_parse_limit(struct ParseState *state, int *flag_p)
3222 unsigned int t_limit;
3224 if (state->dir == MODE_ADD) { /* convert arg only if adding limit */
3225 if (MyUser(state->sptr) && state->max_args <= 0) /* too many args? */
3228 if (state->parc <= 0) { /* warn if not enough args */
3229 if (MyUser(state->sptr))
3230 need_more_params(state->sptr, "MODE +l");
3234 t_limit = atoi(state->parv[state->args_used++]); /* grab arg */
3238 if (!(state->flags & MODE_PARSE_WIPEOUT) &&
3239 (!t_limit || t_limit == state->chptr->mode.limit))
3242 t_limit = state->chptr->mode.limit;
3244 /* If they're not an oper, they can't change modes */
3245 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
3246 send_notoper(state);
3250 if (state->done & DONE_LIMIT) /* allow limit to be set only once */
3252 state->done |= DONE_LIMIT;
3257 modebuf_mode_uint(state->mbuf, state->dir | flag_p[0], t_limit);
3259 if (state->flags & MODE_PARSE_SET) { /* set the limit */
3260 if (state->dir & MODE_ADD) {
3261 state->chptr->mode.mode |= flag_p[0];
3262 state->chptr->mode.limit = t_limit;
3264 state->chptr->mode.mode &= ~flag_p[0];
3265 state->chptr->mode.limit = 0;
3271 * Helper function to convert keys
3274 mode_parse_key(struct ParseState *state, int *flag_p)
3279 if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
3282 if (state->parc <= 0) { /* warn if not enough args */
3283 if (MyUser(state->sptr))
3284 need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +k" :
3289 t_str = state->parv[state->args_used++]; /* grab arg */
3293 /* If they're not an oper, they can't change modes */
3294 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
3295 send_notoper(state);
3299 if (state->done & DONE_KEY) /* allow key to be set only once */
3301 state->done |= DONE_KEY;
3305 /* clean up the key string */
3307 while (*++s > ' ' && *s != ':' && --t_len)
3311 if (!*t_str) { /* warn if empty */
3312 if (MyUser(state->sptr))
3313 need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +k" :
3321 /* can't add a key if one is set, nor can one remove the wrong key */
3322 if (!(state->flags & MODE_PARSE_FORCE))
3323 if ((state->dir == MODE_ADD && *state->chptr->mode.key) ||
3324 (state->dir == MODE_DEL &&
3325 ircd_strcmp(state->chptr->mode.key, t_str))) {
3326 send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
3330 if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
3331 !ircd_strcmp(state->chptr->mode.key, t_str))
3332 return; /* no key change */
3334 if (state->flags & MODE_PARSE_BOUNCE) {
3335 if (*state->chptr->mode.key) /* reset old key */
3336 modebuf_mode_string(state->mbuf, MODE_DEL | flag_p[0],
3337 state->chptr->mode.key, 0);
3338 else /* remove new bogus key */
3339 modebuf_mode_string(state->mbuf, MODE_ADD | flag_p[0], t_str, 0);
3340 } else /* send new key */
3341 modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
3343 if (state->flags & MODE_PARSE_SET) {
3344 if (state->dir == MODE_ADD) /* set the new key */
3345 ircd_strncpy(state->chptr->mode.key, t_str, KEYLEN);
3346 else /* remove the old key */
3347 *state->chptr->mode.key = '\0';
3352 * Helper function to convert bans
3355 mode_parse_ban(struct ParseState *state, int *flag_p)
3358 struct SLink *ban, *newban = 0;
3360 if (state->parc <= 0) { /* Not enough args, send ban list */
3361 if (MyUser(state->sptr) && !(state->done & DONE_BANLIST)) {
3362 send_ban_list(state->sptr, state->chptr);
3363 state->done |= DONE_BANLIST;
3369 if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
3372 t_str = state->parv[state->args_used++]; /* grab arg */
3376 /* If they're not an oper, they can't change modes */
3377 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
3378 send_notoper(state);
3382 if ((s = strchr(t_str, ' ')))
3385 if (!*t_str || *t_str == ':') { /* warn if empty */
3386 if (MyUser(state->sptr))
3387 need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +b" :
3392 t_str = collapse(pretty_mask(t_str));
3394 /* remember the ban for the moment... */
3395 if (state->dir == MODE_ADD) {
3396 newban = state->banlist + (state->numbans++);
3399 DupString(newban->value.ban.banstr, t_str);
3400 newban->value.ban.who = state->sptr->name;
3401 newban->value.ban.when = TStime();
3403 newban->flags = CHFL_BAN | MODE_ADD;
3405 if ((s = strrchr(t_str, '@')) && check_if_ipmask(s + 1))
3406 newban->flags |= CHFL_BAN_IPMASK;
3409 if (!state->chptr->banlist) {
3410 state->chptr->banlist = newban; /* add our ban with its flags */
3411 state->done |= DONE_BANCLEAN;
3415 /* Go through all bans */
3416 for (ban = state->chptr->banlist; ban; ban = ban->next) {
3417 /* first, clean the ban flags up a bit */
3418 if (!(state->done & DONE_BANCLEAN))
3419 /* Note: We're overloading *lots* of bits here; be careful! */
3420 ban->flags &= ~(MODE_ADD | MODE_DEL | CHFL_BAN_OVERLAPPED);
3424 * MODE_ADD - Ban was added; if we're bouncing modes,
3425 * then we'll remove it below; otherwise,
3426 * we'll have to allocate a real ban
3428 * MODE_DEL - Ban was marked for deletion; if we're
3429 * bouncing modes, we'll have to re-add it,
3430 * otherwise, we'll have to remove it
3432 * CHFL_BAN_OVERLAPPED - The ban we added turns out to overlap
3433 * with a ban already set; if we're
3434 * bouncing modes, we'll have to bounce
3435 * this one; otherwise, we'll just ignore
3436 * it when we process added bans
3439 if (state->dir == MODE_DEL && !ircd_strcmp(ban->value.ban.banstr, t_str)) {
3440 ban->flags |= MODE_DEL; /* delete one ban */
3442 if (state->done & DONE_BANCLEAN) /* If we're cleaning, finish */
3444 } else if (state->dir == MODE_ADD) {
3445 /* if the ban already exists, don't worry about it */
3446 if (!ircd_strcmp(ban->value.ban.banstr, t_str)) {
3447 if (state->done & DONE_BANCLEAN) /* If we're cleaning, finish */
3450 } else if (!mmatch(ban->value.ban.banstr, t_str)) {
3451 if (!(ban->flags & MODE_DEL))
3452 newban->flags |= CHFL_BAN_OVERLAPPED; /* our ban overlaps */
3453 } else if (!mmatch(t_str, ban->value.ban.banstr))
3454 ban->flags |= MODE_DEL; /* mark ban for deletion: overlapping */
3457 ban->next = newban; /* add our ban with its flags */
3458 break; /* get out of loop */
3462 state->done |= DONE_BANCLEAN;
3466 * This is the bottom half of the ban processor
3469 mode_process_bans(struct ParseState *state)
3471 struct SLink *ban, *newban, *prevban, *nextban;
3477 for (prevban = 0, ban = state->chptr->banlist; ban; ban = nextban) {
3479 banlen = strlen(ban->value.ban.banstr);
3481 nextban = ban->next;
3483 if ((ban->flags & (MODE_DEL | MODE_ADD)) == (MODE_DEL | MODE_ADD)) {
3485 prevban->next = 0; /* Break the list; ban isn't a real ban */
3487 state->chptr->banlist = 0;
3492 MyFree(ban->value.ban.banstr);
3495 } else if (ban->flags & MODE_DEL) { /* Deleted a ban? */
3496 modebuf_mode_string(state->mbuf, MODE_DEL | MODE_BAN,
3497 ban->value.ban.banstr,
3498 state->flags & MODE_PARSE_SET);
3500 if (state->flags & MODE_PARSE_SET) { /* Ok, make it take effect */
3501 if (prevban) /* clip it out of the list... */
3502 prevban->next = ban->next;
3504 state->chptr->banlist = ban->next;
3509 MyFree(ban->value.ban.who);
3513 continue; /* next ban; keep prevban like it is */
3515 ban->flags &= (CHFL_BAN | CHFL_BAN_IPMASK); /* unset other flags */
3516 } else if (ban->flags & MODE_ADD) { /* adding a ban? */
3518 prevban->next = 0; /* Break the list; ban isn't a real ban */
3520 state->chptr->banlist = 0;
3522 /* If we're supposed to ignore it, do so. */
3523 if (ban->flags & CHFL_BAN_OVERLAPPED &&
3524 !(state->flags & MODE_PARSE_BOUNCE)) {
3528 MyFree(ban->value.ban.banstr);
3530 if (state->flags & MODE_PARSE_SET && MyUser(state->sptr) &&
3531 (len > MAXBANLENGTH || count >= MAXBANS)) {
3532 send_reply(state->sptr, ERR_BANLISTFULL, state->chptr->chname,
3533 ban->value.ban.banstr);
3537 MyFree(ban->value.ban.banstr);
3539 /* add the ban to the buffer */
3540 modebuf_mode_string(state->mbuf, MODE_ADD | MODE_BAN,
3541 ban->value.ban.banstr,
3542 !(state->flags & MODE_PARSE_SET));
3544 if (state->flags & MODE_PARSE_SET) { /* create a new ban */
3545 newban = make_link();
3546 newban->value.ban.banstr = ban->value.ban.banstr;
3547 DupString(newban->value.ban.who, ban->value.ban.who);
3548 newban->value.ban.when = ban->value.ban.when;
3549 newban->flags = ban->flags & (CHFL_BAN | CHFL_BAN_IPMASK);
3551 newban->next = state->chptr->banlist; /* and link it in */
3552 state->chptr->banlist = newban;
3561 } /* for (prevban = 0, ban = state->chptr->banlist; ban; ban = nextban) { */
3563 if (changed) /* if we changed the ban list, we must invalidate the bans */
3564 mode_ban_invalidate(state->chptr);
3568 * Helper function to process client changes
3571 mode_parse_client(struct ParseState *state, int *flag_p)
3574 struct Client *acptr;
3577 if (MyUser(state->sptr) && state->max_args <= 0) /* drop if too many args */
3580 if (state->parc <= 0) /* return if not enough args */
3583 t_str = state->parv[state->args_used++]; /* grab arg */
3587 /* If they're not an oper, they can't change modes */
3588 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
3589 send_notoper(state);
3593 if (MyUser(state->sptr)) /* find client we're manipulating */
3594 acptr = find_chasing(state->sptr, t_str, NULL);
3596 acptr = findNUser(t_str);
3599 return; /* find_chasing() already reported an error to the user */
3601 for (i = 0; i < MAXPARA; i++) /* find an element to stick them in */
3602 if (!state->cli_change[i].flag || (state->cli_change[i].client == acptr &&
3603 state->cli_change[i].flag & flag_p[0]))
3604 break; /* found a slot */
3606 /* Store what we're doing to them */
3607 state->cli_change[i].flag = state->dir | flag_p[0];
3608 state->cli_change[i].client = acptr;
3612 * Helper function to process the changed client list
3615 mode_process_clients(struct ParseState *state)
3618 struct Membership *member;
3620 for (i = 0; state->cli_change[i].flag; i++) {
3621 assert(0 != state->cli_change[i].client);
3623 /* look up member link */
3624 if (!(member = find_member_link(state->chptr,
3625 state->cli_change[i].client)) ||
3626 (MyUser(state->sptr) && IsZombie(member))) {
3627 if (MyUser(state->sptr))
3628 send_reply(state->sptr, ERR_USERNOTINCHANNEL,
3629 state->cli_change[i].client->name, state->chptr->chname);
3633 if ((state->cli_change[i].flag & MODE_ADD &&
3634 (state->cli_change[i].flag & member->status)) ||
3635 (state->cli_change[i].flag & MODE_DEL &&
3636 !(state->cli_change[i].flag & member->status)))
3637 continue; /* no change made, don't do anything */
3639 /* see if the deop is allowed */
3640 if ((state->cli_change[i].flag & (MODE_DEL | MODE_CHANOP)) ==
3641 (MODE_DEL | MODE_CHANOP)) {
3642 /* prevent +k users from being deopped */
3643 if (IsChannelService(state->cli_change[i].client)) {
3644 if (state->flags & MODE_PARSE_FORCE) /* it was forced */
3645 sendto_opmask_butone(0, SNO_HACK4, "Deop of +k user on %H by %s",
3647 (IsServer(state->sptr) ? state->sptr->name :
3648 state->sptr->user->server->name));
3650 else if (MyUser(state->sptr) && state->flags & MODE_PARSE_SET) {
3651 send_reply(state->sptr, ERR_ISCHANSERVICE,
3652 state->cli_change[i].client->name, state->chptr->chname);
3657 #ifdef NO_OPER_DEOP_LCHAN
3658 /* don't allow local opers to be deopped on local channels */
3659 if (MyUser(state->sptr) && state->cli_change[i].client != state->sptr &&
3660 IsOperOnLocalChannel(state->cli_change[i].client,
3661 state->chptr->chname)) {
3662 send_reply(state->sptr, ERR_ISOPERLCHAN,
3663 state->cli_change[i].client->name, state->chptr->chname);
3669 /* accumulate the change */
3670 modebuf_mode_client(state->mbuf, state->cli_change[i].flag,
3671 state->cli_change[i].client);
3673 /* actually effect the change */
3674 if (state->flags & MODE_PARSE_SET) {
3675 if (state->cli_change[i].flag & MODE_ADD) {
3676 member->status |= (state->cli_change[i].flag &
3677 (MODE_CHANOP | MODE_VOICE));
3678 if (state->cli_change[i].flag & MODE_CHANOP)
3679 ClearDeopped(member);
3681 member->status &= ~(state->cli_change[i].flag &
3682 (MODE_CHANOP | MODE_VOICE));
3684 } /* for (i = 0; state->cli_change[i].flags; i++) { */
3688 * Helper function to process the simple modes
3691 mode_parse_mode(struct ParseState *state, int *flag_p)
3693 /* If they're not an oper, they can't change modes */
3694 if (state->flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER)) {
3695 send_notoper(state);
3702 if (state->dir == MODE_ADD) {
3703 state->add |= flag_p[0];
3704 state->del &= ~flag_p[0];
3706 if (flag_p[0] & MODE_SECRET) {
3707 state->add &= ~MODE_PRIVATE;
3708 state->del |= MODE_PRIVATE;
3709 } else if (flag_p[0] & MODE_PRIVATE) {
3710 state->add &= ~MODE_SECRET;
3711 state->del |= MODE_SECRET;
3714 state->add &= ~flag_p[0];
3715 state->del |= flag_p[0];
3718 assert(0 == (state->add & state->del));
3719 assert((MODE_SECRET | MODE_PRIVATE) !=
3720 (state->add & (MODE_SECRET | MODE_PRIVATE)));
3724 * This routine is intended to parse MODE or OPMODE commands and effect the
3725 * changes (or just build the bounce buffer). We pass the starting offset
3729 mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr,
3730 struct Channel *chptr, int parc, char *parv[], unsigned int flags)
3732 static int chan_flags[] = {
3737 MODE_MODERATED, 'm',
3738 MODE_TOPICLIMIT, 't',
3739 MODE_INVITEONLY, 'i',
3740 MODE_NOPRIVMSGS, 'n',
3750 unsigned int t_mode;
3752 struct ParseState state;
3763 state.chptr = chptr;
3766 state.flags = flags;
3767 state.dir = MODE_ADD;
3771 state.args_used = 0;
3772 state.max_args = MAXMODEPARAMS;
3775 for (i = 0; i < MAXPARA; i++) { /* initialize ops/voices arrays */
3776 state.banlist[i].next = 0;
3777 state.banlist[i].value.ban.banstr = 0;
3778 state.banlist[i].value.ban.who = 0;
3779 state.banlist[i].value.ban.when = 0;
3780 state.banlist[i].flags = 0;
3781 state.cli_change[i].flag = 0;
3782 state.cli_change[i].client = 0;
3785 modestr = state.parv[state.args_used++];
3789 for (; *modestr; modestr++) {
3790 for (flag_p = chan_flags; flag_p[0]; flag_p += 2) /* look up flag */
3791 if (flag_p[1] == *modestr)
3794 if (!flag_p[0]) { /* didn't find it? complain and continue */
3795 if (MyUser(state.sptr))
3796 send_reply(state.sptr, ERR_UNKNOWNMODE, *modestr);
3801 case '+': /* switch direction to MODE_ADD */
3802 case '-': /* switch direction to MODE_DEL */
3803 state.dir = flag_p[0];
3806 case 'l': /* deal with limits */
3807 mode_parse_limit(&state, flag_p);
3810 case 'k': /* deal with keys */
3811 mode_parse_key(&state, flag_p);
3814 case 'b': /* deal with bans */
3815 mode_parse_ban(&state, flag_p);
3818 case 'o': /* deal with ops/voice */
3820 mode_parse_client(&state, flag_p);
3823 default: /* deal with other modes */
3824 mode_parse_mode(&state, flag_p);
3826 } /* switch (*modestr) { */
3827 } /* for (; *modestr; modestr++) { */
3829 if (state.flags & MODE_PARSE_BURST)
3830 break; /* don't interpret any more arguments */
3832 if (state.parc > 0) { /* process next argument in string */
3833 modestr = state.parv[state.args_used++];
3837 if (IsServer(state.sptr) && !state.parc && IsDigit(*modestr)) {
3840 if (!(state.flags & MODE_PARSE_SET)) /* don't set earlier TS if */
3841 break; /* we're then going to bounce the mode! */
3843 recv_ts = atoi(modestr);
3845 if (recv_ts && recv_ts < state.chptr->creationtime)
3846 state.chptr->creationtime = recv_ts; /* respect earlier TS */
3848 break; /* break out of while loop */
3849 } else if (state.flags & MODE_PARSE_STRICT ||
3850 (MyUser(state.sptr) && state.max_args <= 0)) {
3851 state.parc++; /* we didn't actually gobble the argument */
3853 break; /* break out of while loop */
3856 } /* while (*modestr) { */
3859 * the rest of the function finishes building resultant MODEs; if the
3860 * origin isn't a member or an oper, skip it.
3862 if (!state.mbuf || state.flags & (MODE_PARSE_NOTOPER | MODE_PARSE_NOTMEMBER))
3863 return state.args_used; /* tell our parent how many args we gobbled */
3865 t_mode = state.chptr->mode.mode;
3867 if (state.del & t_mode) { /* delete any modes to be deleted... */
3868 modebuf_mode(state.mbuf, MODE_DEL | (state.del & t_mode));
3870 t_mode &= ~state.del;
3872 if (state.add & ~t_mode) { /* add any modes to be added... */
3873 modebuf_mode(state.mbuf, MODE_ADD | (state.add & ~t_mode));
3875 t_mode |= state.add;
3878 if (state.flags & MODE_PARSE_SET) { /* set the channel modes */
3879 if ((state.chptr->mode.mode & MODE_INVITEONLY) &&
3880 !(t_mode & MODE_INVITEONLY))
3881 mode_invite_clear(state.chptr);
3883 state.chptr->mode.mode = t_mode;
3886 if (state.flags & MODE_PARSE_WIPEOUT) {
3887 if (state.chptr->mode.limit && !(state.done & DONE_LIMIT))
3888 modebuf_mode_uint(state.mbuf, MODE_DEL | MODE_LIMIT,
3889 state.chptr->mode.limit);
3890 if (*state.chptr->mode.key && !(state.done & DONE_KEY))
3891 modebuf_mode_string(state.mbuf, MODE_DEL | MODE_KEY,
3892 state.chptr->mode.key, 0);
3895 if (state.done & DONE_BANCLEAN) /* process bans */
3896 mode_process_bans(&state);
3898 /* process client changes */
3899 if (state.cli_change[0].flag)
3900 mode_process_clients(&state);
3902 return state.args_used; /* tell our parent how many args we gobbled */
3906 * Initialize a join buffer
3909 joinbuf_init(struct JoinBuf *jbuf, struct Client *source,
3910 struct Client *connect, unsigned int type, char *comment,
3916 assert(0 != source);
3917 assert(0 != connect);
3919 jbuf->jb_source = source; /* just initialize struct JoinBuf */
3920 jbuf->jb_connect = connect;
3921 jbuf->jb_type = type;
3922 jbuf->jb_comment = comment;
3923 jbuf->jb_create = create;
3925 jbuf->jb_strlen = (((type == JOINBUF_TYPE_JOIN ||
3926 type == JOINBUF_TYPE_PART ||
3927 type == JOINBUF_TYPE_PARTALL) ?
3928 STARTJOINLEN : STARTCREATELEN) +
3929 (comment ? strlen(comment) + 2 : 0));
3931 for (i = 0; i < MAXJOINARGS; i++)
3932 jbuf->jb_channels[i] = 0;
3936 * Add a channel to the join buffer
3939 joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
3946 if (jbuf->jb_type == JOINBUF_TYPE_JOIN)
3947 sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect, "0");
3952 if (jbuf->jb_type == JOINBUF_TYPE_PART ||
3953 jbuf->jb_type == JOINBUF_TYPE_PARTALL) {
3954 /* Send notification to channel */
3955 if (!(flags & CHFL_ZOMBIE))
3956 sendcmdto_channel_butserv(jbuf->jb_source, CMD_PART, chan,
3957 (flags & CHFL_BANNED || !jbuf->jb_comment) ?
3958 ":%H" : "%H :%s", chan, jbuf->jb_comment);
3959 else if (MyUser(jbuf->jb_source))
3960 sendcmdto_one(jbuf->jb_source, CMD_PART, jbuf->jb_source,
3961 (flags & CHFL_BANNED || !jbuf->jb_comment) ?
3962 ":%H" : "%H :%s", chan, jbuf->jb_comment);
3963 /* XXX: Shouldn't we send a PART here anyway? */
3964 /* to users on the channel? Why? From their POV, the user isn't on
3965 * the channel anymore anyway. We don't send to servers until below,
3966 * when we gang all the channel parts together. Note that this is
3967 * exactly the same logic, albeit somewhat more concise, as was in
3968 * the original m_part.c */
3970 if (jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
3971 IsLocalChannel(chan->chname)) /* got to remove user here */
3972 remove_user_from_channel(jbuf->jb_source, chan);
3974 /* Add user to channel */
3975 add_user_to_channel(chan, jbuf->jb_source, flags);
3977 /* send notification to all servers */
3978 if (jbuf->jb_type != JOINBUF_TYPE_CREATE && !IsLocalChannel(chan->chname))
3979 sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect,
3980 "%H %Tu", chan, chan->creationtime);
3982 /* Send the notification to the channel */
3983 sendcmdto_channel_butserv(jbuf->jb_source, CMD_JOIN, chan, ":%H", chan);
3985 /* send an op, too, if needed */
3986 if (!MyUser(jbuf->jb_source) && jbuf->jb_type == JOINBUF_TYPE_CREATE &&
3987 !IsModelessChannel(chan->chname))
3988 sendcmdto_channel_butserv(jbuf->jb_source, CMD_MODE, chan, "%H +o %C",
3989 chan, jbuf->jb_source);
3992 if (jbuf->jb_type == JOINBUF_TYPE_PARTALL || IsLocalChannel(chan->chname))
3993 return; /* don't send to remote */
3995 /* figure out if channel name will cause buffer to be overflowed */
3996 len = chan ? strlen(chan->chname) + 1 : 2;
3997 if (jbuf->jb_strlen + len > IRC_BUFSIZE)
3998 joinbuf_flush(jbuf);
4000 /* add channel to list of channels to send and update counts */
4001 jbuf->jb_channels[jbuf->jb_count++] = chan;
4002 jbuf->jb_strlen += len;
4004 /* if we've used up all slots, flush */
4005 if (jbuf->jb_count >= MAXJOINARGS)
4006 joinbuf_flush(jbuf);
4010 * Flush the channel list to remote servers
4013 joinbuf_flush(struct JoinBuf *jbuf)
4015 char chanlist[IRC_BUFSIZE];
4019 if (!jbuf->jb_count || jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
4020 jbuf->jb_type == JOINBUF_TYPE_JOIN)
4021 return 0; /* no joins to process */
4023 for (i = 0; i < jbuf->jb_count; i++) { /* build channel list */
4024 build_string(chanlist, &chanlist_i,
4025 jbuf->jb_channels[i] ? jbuf->jb_channels[i]->chname : "0", 0,
4026 i == 0 ? '\0' : ',');
4027 if (JOINBUF_TYPE_PART == jbuf->jb_type)
4028 /* Remove user from channel */
4029 remove_user_from_channel(jbuf->jb_source, jbuf->jb_channels[i]);
4031 jbuf->jb_channels[i] = 0; /* mark slot empty */
4034 jbuf->jb_count = 0; /* reset base counters */
4035 jbuf->jb_strlen = ((jbuf->jb_type == JOINBUF_TYPE_PART ?
4036 STARTJOINLEN : STARTCREATELEN) +
4037 (jbuf->jb_comment ? strlen(jbuf->jb_comment) + 2 : 0));
4039 /* and send the appropriate command */
4040 switch (jbuf->jb_type) {
4041 case JOINBUF_TYPE_CREATE:
4042 sendcmdto_serv_butone(jbuf->jb_source, CMD_CREATE, jbuf->jb_connect,
4043 "%s %Tu", chanlist, jbuf->jb_create);
4046 case JOINBUF_TYPE_PART:
4047 sendcmdto_serv_butone(jbuf->jb_source, CMD_PART, jbuf->jb_connect,
4048 jbuf->jb_comment ? "%s :%s" : "%s", chanlist,