From: Michael Poole Date: Tue, 11 May 2004 02:10:30 +0000 (+0000) Subject: Forward port of delayed-join.patch from Quakenet's "Asuka" patch set X-Git-Url: http://git.pk910.de/?p=ircu2.10.12-pk.git;a=commitdiff_plain;h=68344659492f852c34150dc9346006e05b66a90b Forward port of delayed-join.patch from Quakenet's "Asuka" patch set (which was a port of code I wrote for the other ircu). git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@1040 c9e4aea6-c8fd-4c43-8297-357d70d61c8c --- diff --git a/ChangeLog b/ChangeLog index 8ee50c8..bf0cc26 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,83 @@ +2004-05-10 Michael Poole + + Forward port of delayed-join.patch from Quakenet's "Asuka" patch + set (which was a port of code I wrote for the other ircu). + + * include/channel.h (CHFL_DELAYED): New membership flag. + (MODE_DELJOINS, MODE_WASDELJOINS): New channel modes. + (infochanmodes): Add +D to list of supported channel modes. + (IsDelayedJoin, SetDelayedJoin, ClearDelayedJoin): New macros. + (member_can_send_to_channel, client_can_send_to_channel): Add + "reveal" parameter to indicate whether a request should cause + a join-delayed user to become visible. + (RevealDelayedJoin, CheckDelayedJoins): New functions. + + * include/numeric.h (RPL_DELNAMREPLY): New numeric. + + * include/s_user.h (NAMES_DEL): New flag for do_names(). + + * include/supported.h (FEATURESVALUES2): Add +D to list of + supported channel modes. + + * ircd/channel.c (remove_member_from_channel, + member_can_send_to_channel, client_can_send_to_channel, + joinbuf_join): Handle join-delayed users. + (channel_modes, modebuf_flush_int, modebuf_mode, modebuf_flush, + modebuf_extract, mode_process_clients, mode_parse_mode, + mode_parse): Handle delayed-join channels. + (RevealDelayedJoin, CheckDelayedJoins): New functions. + + * ircd/ircd_relay.c (relay_channel_message, relay_channel_notice, + server_relay_channel_message, server_relay_channel_notice): Add + argument for "reveal" parameter to client_can_send_to_channel(). + + * ircd/m_burst.c (ms_burst): Support MODE_DELJOINS channels. + + * ircd/m_clearmode.c (do_clearmode): Support clearing mode +D. + + * ircd/m_join.c (join0): Pass the CHFL_DELAYED flag when parting a + channel with JOIN 0. + + * ircd/m_kick.c (m_kick): For join-delayed members, only send the + KICK to the kicker and kickee. Then check whether +d can be + removed. + + * ircd/m_names.c (do_names): Show join-delayed users if and only + if the NAMES_DEL flag is given. If NAMES_DEL is given, also use + RPL_DELNAMREPLY instead of RPL_NAMREPLY. + (m_names): If NAMES -D, pass NAMES_DEL to do_names(). + + * ircd/m_part.c (m_part, ms_part): Add "reveal" argument for + member_can_send_to_channel(). Set CHFL_DELAYED join in joinbuf if + the user is join-delayed. + + * ircd/m_quit.c (m_quit): Handle join-delayed users and new + argument for member_can_send_to_channel(). + + * ircd/m_topic.c (do_settopic): If a join-delayed channel member + changes the topic, reveal the member. + + * ircd/m_wallchops.c (m_wallchops, ms_wallchops): Add argument for + "reveal" parameter to client_can_send_to_channel(). + + * ircd/m_wallvoices.c (m_wallvoices, ms_wallvoices): Likewise. + + * ircd/m_who.c (m_who): Skip join-delayed members where we skip + zombies. + + * ircd/m_whois.c (do_whois): Use '<' as a prefix for join-delayed + users. Use slightly more efficient macros rather than function + calls to test for ops and voice. + + * ircd/s_err.c (RPL_DELNAMREPLY): New numeric response string. + + * ircd/s_user.c (hide_hostmask): For users with no modes in a + join-delayed channel, do not send JOIN to other members after the + QUIT :Registered. + + * ircd/send.c (sendcmdto_common_channels_butone): Skip + join-delayed users where we skip zombies. + 2004-05-10 Michael Poole * ircd/ircd_events.c: Actually reference and try to use the epoll diff --git a/include/channel.h b/include/channel.h index 0fc12e9..18d6d9f 100644 --- a/include/channel.h +++ b/include/channel.h @@ -70,7 +70,8 @@ struct Client; #define CHFL_BURST_ALREADY_OPPED 0x04000 /* In oob BURST, but was already joined and opped */ #define CHFL_BURST_ALREADY_VOICED 0x08000 /* In oob BURST, but was already joined and voiced */ #define CHFL_CHANNEL_MANAGER 0x10000 /* Set when creating channel or using Apass */ -#define CHFL_USER_PARTING 0x20000 /* User is already parting that channel */ +#define CHFL_USER_PARTING 0x20000 /* User is already parting that channel */ +#define CHFL_DELAYED 0x40000 /* User's join message is delayed */ #define CHFL_OVERLAP (CHFL_CHANOP | CHFL_VOICE) #define CHFL_BANVALIDMASK (CHFL_BANVALID | CHFL_BANNED) @@ -90,18 +91,20 @@ struct Client; #define MODE_BAN 0x0200 #define MODE_LIMIT 0x0400 #define MODE_REGONLY 0x0800 /* Only +r users may join */ +#define MODE_DELJOINS 0x1000 /* New join messages are delayed */ #define MODE_LISTED 0x10000 #define MODE_SAVE 0x20000 /* save this mode-with-arg 'til later */ #define MODE_FREE 0x40000 /* string needs to be passed to MyFree() */ #define MODE_BURSTADDED 0x80000 /* channel was created by a BURST */ #define MODE_UPASS 0x100000 #define MODE_APASS 0x200000 +#define MODE_WASDELJOINS 0x400000 /* Not DELJOINS, but some joins pending */ /* * mode flags which take another parameter (With PARAmeterS) */ #define MODE_WPARAS (MODE_CHANOP|MODE_VOICE|MODE_BAN|MODE_KEY|MODE_LIMIT|MODE_APASS|MODE_UPASS) -#define infochanmodes feature_bool(FEAT_OPLEVELS) ? "Abiklmnopstuvr" : "biklmnopstvr" +#define infochanmodes feature_bool(FEAT_OPLEVELS) ? "AbiklmnopstuvrD" : "biklmnopstvrD" #define infochanmodeswithparams feature_bool(FEAT_OPLEVELS) ? "Abklouv" : "bklov" #define HoldChannel(x) (!(x)) @@ -193,6 +196,7 @@ struct Membership { #define IsVoicedOrOpped(x) ((x)->status & CHFL_VOICED_OR_OPPED) #define IsChannelManager(x) ((x)->status & CHFL_CHANNEL_MANAGER) #define IsUserParting(x) ((x)->status & CHFL_USER_PARTING) +#define IsDelayedJoin(x) ((x)->status & CHFL_DELAYED) #define SetBanned(x) ((x)->status |= CHFL_BANNED) #define SetBanValid(x) ((x)->status |= CHFL_BANVALID) @@ -203,12 +207,14 @@ struct Membership { #define SetChannelManager(x) ((x)->status |= CHFL_CHANNEL_MANAGER) #define SetOpLevel(x, v) (void)((x)->oplevel = (v)) #define SetUserParting(x) ((x)->status |= CHFL_USER_PARTING) +#define SetDelayedJoin(x) ((x)->status |= CHFL_DELAYED) #define ClearBanned(x) ((x)->status &= ~CHFL_BANNED) #define ClearBanValid(x) ((x)->status &= ~CHFL_BANVALID) #define ClearDeopped(x) ((x)->status &= ~CHFL_DEOPPED) #define ClearServOpOk(x) ((x)->status &= ~CHFL_SERVOPOK) #define ClearBurstJoined(x) ((x)->status &= ~CHFL_BURST_JOINED) +#define ClearDelayedJoin(x) ((x)->status &= ~CHFL_DELAYED) struct Mode { @@ -345,8 +351,8 @@ int number_of_zombies(struct Channel *chptr); extern const char* find_no_nickchange_channel(struct Client* cptr); extern struct Membership* IsMember(struct Client *cptr, struct Channel *chptr); extern struct Membership* find_channel_member(struct Client* cptr, struct Channel* chptr); -extern int member_can_send_to_channel(struct Membership* member); -extern int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr); +extern int member_can_send_to_channel(struct Membership* member, int reveal); +extern int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr, int reveal); extern void remove_user_from_channel(struct Client *sptr, struct Channel *chptr); extern void remove_user_from_all_channels(struct Client* cptr); @@ -366,6 +372,9 @@ extern void del_invite(struct Client *cptr, struct Channel *chptr); extern void list_next_channels(struct Client *cptr, int nr); extern void list_set_default(void); /* this belongs elsewhere! */ +extern void RevealDelayedJoin(struct Membership *member); +extern void CheckDelayedJoins(struct Channel *chan); + extern void modebuf_init(struct ModeBuf *mbuf, struct Client *source, struct Client *connect, struct Channel *chan, unsigned int dest); diff --git a/include/numeric.h b/include/numeric.h index 05f62f5..334f7b0 100644 --- a/include/numeric.h +++ b/include/numeric.h @@ -267,6 +267,7 @@ extern const struct Numeric* get_error_numeric(int err); #define RPL_NAMREPLY 353 /* See also RPL_ENDOFNAMES */ #define RPL_WHOSPCRPL 354 /* Undernet extension, See also RPL_ENDOFWHO */ +#define RPL_DELNAMREPLY 355 /* QuakeNet extension */ #define RPL_KILLDONE 361 /* not used */ #define RPL_CLOSING 362 diff --git a/include/s_user.h b/include/s_user.h index 587b599..aebbbcd 100644 --- a/include/s_user.h +++ b/include/s_user.h @@ -108,6 +108,7 @@ extern int send_supported(struct Client *cptr); #define NAMES_ALL 1 /* List all users in channel */ #define NAMES_VIS 2 /* List only visible users in non-secret channels */ #define NAMES_EON 4 /* Add an 'End Of Names' reply to the end */ +#define NAMES_DEL 8 /* Show delayed joined users only */ void do_names(struct Client* sptr, struct Channel* chptr, int filter); diff --git a/include/supported.h b/include/supported.h index 650cfda..d875641 100644 --- a/include/supported.h +++ b/include/supported.h @@ -58,7 +58,7 @@ feature_int(FEAT_MAXBANS), NICKLEN, TOPICLEN, \ AWAYLEN, TOPICLEN -#define FEATURESVALUES2 feature_bool(FEAT_LOCAL_CHANNELS) ? "#&" : "#", "(ov)@+", "b,k,l,imnpstr", "rfc1459", \ +#define FEATURESVALUES2 feature_bool(FEAT_LOCAL_CHANNELS) ? "#&" : "#", "(ov)@+", "b,k,l,imnpstrD", "rfc1459", \ feature_str(FEAT_NETWORK) #endif /* INCLUDED_supported_h */ diff --git a/ircd/channel.c b/ircd/channel.c index 0aa8da8..00e4fd9 100644 --- a/ircd/channel.c +++ b/ircd/channel.c @@ -564,7 +564,13 @@ static int remove_member_from_channel(struct Membership* member) member->prev_member->next_member = member->next_member; else member->channel->members = member->next_member; - + + /* + * If this is the last delayed-join user, may have to clear WASDELJOINS. + */ + if (IsDelayedJoin(member)) + CheckDelayedJoins(chptr); + /* * unlink client channel list */ @@ -657,7 +663,7 @@ int has_voice(struct Client* cptr, struct Channel* chptr) return 0; } -int member_can_send_to_channel(struct Membership* member) +int member_can_send_to_channel(struct Membership* member, int reveal) { assert(0 != member); @@ -680,10 +686,14 @@ int member_can_send_to_channel(struct Membership* member) */ if (MyUser(member->user) && is_banned(member->user, member->channel, member)) return 0; + + if (IsDelayedJoin(member) && reveal) + RevealDelayedJoin(member); + return 1; } -int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr) +int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr, int reveal) { struct Membership *member; assert(0 != cptr); @@ -706,7 +716,7 @@ int client_can_send_to_channel(struct Client *cptr, struct Channel *chptr) else return !is_banned(cptr, chptr, NULL); } - return member_can_send_to_channel(member); + return member_can_send_to_channel(member, reveal); } /* @@ -758,6 +768,10 @@ void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen, *mbuf++ = 'n'; if (chptr->mode.mode & MODE_REGONLY) *mbuf++ = 'r'; + if (chptr->mode.mode & MODE_DELJOINS) + *mbuf++ = 'D'; + else if (MyUser(cptr) && (chptr->mode.mode & MODE_WASDELJOINS)) + *mbuf++ = 'd'; if (chptr->mode.limit) { *mbuf++ = 'l'; ircd_snprintf(0, pbuf, buflen, "%u", chptr->mode.limit); @@ -1501,6 +1515,8 @@ modebuf_flush_int(struct ModeBuf *mbuf, int all) MODE_INVITEONLY, 'i', MODE_NOPRIVMSGS, 'n', MODE_REGONLY, 'r', + MODE_DELJOINS, 'D', + MODE_WASDELJOINS, 'd', /* MODE_KEY, 'k', */ /* MODE_BAN, 'b', */ /* MODE_LIMIT, 'l', */ @@ -1886,7 +1902,8 @@ modebuf_mode(struct ModeBuf *mbuf, unsigned int mode) assert(0 != (mode & (MODE_ADD | MODE_DEL))); mode &= (MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET | MODE_MODERATED | - MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS | MODE_REGONLY); + MODE_TOPICLIMIT | MODE_INVITEONLY | MODE_NOPRIVMSGS | MODE_REGONLY | + MODE_DELJOINS | MODE_WASDELJOINS); if (!(mode & ~(MODE_ADD | MODE_DEL))) /* don't add empty modes... */ return; @@ -1968,6 +1985,21 @@ modebuf_mode_client(struct ModeBuf *mbuf, unsigned int mode, int modebuf_flush(struct ModeBuf *mbuf) { + struct Membership *memb; + + /* Check if MODE_WASDELJOINS should be set */ + if (!(mbuf->mb_channel->mode.mode & (MODE_DELJOINS | MODE_WASDELJOINS)) + && (mbuf->mb_rem & MODE_DELJOINS)) { + for (memb = mbuf->mb_channel->members; memb; memb = memb->next_member) { + if (IsDelayedJoin(memb)) { + mbuf->mb_channel->mode.mode |= MODE_WASDELJOINS; + mbuf->mb_add |= MODE_WASDELJOINS; + mbuf->mb_rem &= ~MODE_WASDELJOINS; + break; + } + } + } + return modebuf_flush_int(mbuf, 1); } @@ -1992,6 +2024,7 @@ modebuf_extract(struct ModeBuf *mbuf, char *buf) /* MODE_BAN, 'b', */ MODE_LIMIT, 'l', MODE_REGONLY, 'r', + MODE_DELJOINS, 'D', 0x0, 0x0 }; unsigned int add; @@ -2832,13 +2865,11 @@ mode_process_clients(struct ParseState *state) SetOpLevel(member, old_level == MAXOPLEVEL ? MAXOPLEVEL : (old_level + level_increment)); } - /* accumulate the change */ - modebuf_mode_client(state->mbuf, state->cli_change[i].flag, - state->cli_change[i].client); - /* actually effect the change */ if (state->flags & MODE_PARSE_SET) { if (state->cli_change[i].flag & MODE_ADD) { + if (IsDelayedJoin(member)) + RevealDelayedJoin(member); member->status |= (state->cli_change[i].flag & (MODE_CHANOP | MODE_VOICE)); if (state->cli_change[i].flag & MODE_CHANOP) @@ -2847,6 +2878,10 @@ mode_process_clients(struct ParseState *state) member->status &= ~(state->cli_change[i].flag & (MODE_CHANOP | MODE_VOICE)); } + + /* accumulate the change */ + modebuf_mode_client(state->mbuf, state->cli_change[i].flag, + state->cli_change[i].client); } /* for (i = 0; state->cli_change[i].flags; i++) */ } @@ -2876,6 +2911,10 @@ mode_parse_mode(struct ParseState *state, int *flag_p) state->add &= ~MODE_SECRET; state->del |= MODE_SECRET; } + if (flag_p[0] & MODE_DELJOINS) { + state->add &= ~MODE_WASDELJOINS; + state->del |= MODE_WASDELJOINS; + } } else { state->add &= ~flag_p[0]; state->del |= flag_p[0]; @@ -2911,6 +2950,7 @@ mode_parse(struct ModeBuf *mbuf, struct Client *cptr, struct Client *sptr, MODE_BAN, 'b', MODE_LIMIT, 'l', MODE_REGONLY, 'r', + MODE_DELJOINS, 'D', MODE_ADD, '+', MODE_DEL, '-', 0x0, 0x0 @@ -3147,7 +3187,7 @@ joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags) SetUserParting(member); /* Send notification to channel */ - if (!(flags & CHFL_ZOMBIE)) + if (!(flags & (CHFL_ZOMBIE | CHFL_DELAYED))) sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_PART, chan, NULL, (flags & CHFL_BANNED || !jbuf->jb_comment) ? ":%H" : "%H :%s", chan, jbuf->jb_comment); @@ -3167,20 +3207,26 @@ joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags) remove_user_from_channel(jbuf->jb_source, chan); } else { /* Add user to channel */ - add_user_to_channel(chan, jbuf->jb_source, flags, 0); + if ((chan->mode.mode & MODE_DELJOINS) && !(flags & CHFL_VOICED_OR_OPPED)) + add_user_to_channel(chan, jbuf->jb_source, flags | CHFL_DELAYED, 0); + else + add_user_to_channel(chan, jbuf->jb_source, flags, 0); /* send notification to all servers */ if (jbuf->jb_type != JOINBUF_TYPE_CREATE && !is_local) sendcmdto_serv_butone(jbuf->jb_source, CMD_JOIN, jbuf->jb_connect, "%H %Tu", chan, chan->creationtime); - /* Send the notification to the channel */ - sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_JOIN, chan, NULL, ":%H", chan); + if (!((chan->mode.mode & MODE_DELJOINS) && !(flags & CHFL_VOICED_OR_OPPED))) { + /* Send the notification to the channel */ + sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_JOIN, chan, NULL, "%H", chan); - /* send an op, too, if needed */ - if (!MyUser(jbuf->jb_source) && jbuf->jb_type == JOINBUF_TYPE_CREATE) - sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_MODE, chan, NULL, "%H +o %C", - chan, jbuf->jb_source); + /* send an op, too, if needed */ + if (!MyUser(jbuf->jb_source) && jbuf->jb_type == JOINBUF_TYPE_CREATE) + sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_MODE, chan, NULL, "%H +o %C", + chan, jbuf->jb_source); + } else if (MyUser(jbuf->jb_source)) + sendcmdto_one(jbuf->jb_source, CMD_JOIN, jbuf->jb_source, ":%H", chan); } if (jbuf->jb_type == JOINBUF_TYPE_PARTALL || @@ -3258,3 +3304,31 @@ int IsInvited(struct Client* cptr, const void* chptr) return 1; return 0; } + +/* RevealDelayedJoin: sends a join for a hidden user */ + +void RevealDelayedJoin(struct Membership *member) { + ClearDelayedJoin(member); + sendcmdto_channel_butserv_butone(member->user, CMD_JOIN, member->channel, member->user, ":%H", + member->channel); + CheckDelayedJoins(member->channel); +} + +/* CheckDelayedJoins: checks and clear +d if necessary */ + +void CheckDelayedJoins(struct Channel *chan) { + struct Membership *memb2; + + if (chan->mode.mode & MODE_WASDELJOINS) { + for (memb2=chan->members;memb2;memb2=memb2->next_member) + if (IsDelayedJoin(memb2)) + break; + + if (!memb2) { + /* clear +d */ + chan->mode.mode &= ~MODE_WASDELJOINS; + sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan, NULL, + "%H -d", chan); + } + } +} diff --git a/ircd/ircd_relay.c b/ircd/ircd_relay.c index f8cd9d4..3ac0850 100644 --- a/ircd/ircd_relay.c +++ b/ircd/ircd_relay.c @@ -68,7 +68,7 @@ void relay_channel_message(struct Client* sptr, const char* name, const char* te /* * This first: Almost never a server/service */ - if (!client_can_send_to_channel(sptr, chptr)) { + if (!client_can_send_to_channel(sptr, chptr, 1)) { send_reply(sptr, ERR_CANNOTSENDTOCHAN, chptr->chname); return; } @@ -92,7 +92,7 @@ void relay_channel_notice(struct Client* sptr, const char* name, const char* tex /* * This first: Almost never a server/service */ - if (!client_can_send_to_channel(sptr, chptr)) + if (!client_can_send_to_channel(sptr, chptr, 1)) return; if ((chptr->mode.mode & MODE_NOPRIVMSGS) && @@ -121,7 +121,7 @@ void server_relay_channel_message(struct Client* sptr, const char* name, const c * This first: Almost never a server/service * Servers may have channel services, need to check for it here */ - if (client_can_send_to_channel(sptr, chptr) || IsChannelService(sptr)) { + if (client_can_send_to_channel(sptr, chptr, 1) || IsChannelService(sptr)) { sendcmdto_channel_butone(sptr, CMD_PRIVATE, chptr, cli_from(sptr), SKIP_DEAF | SKIP_BURST, "%H :%s", chptr, text); } @@ -142,7 +142,7 @@ void server_relay_channel_notice(struct Client* sptr, const char* name, const ch * This first: Almost never a server/service * Servers may have channel services, need to check for it here */ - if (client_can_send_to_channel(sptr, chptr) || IsChannelService(sptr)) { + if (client_can_send_to_channel(sptr, chptr, 1) || IsChannelService(sptr)) { sendcmdto_channel_butone(sptr, CMD_NOTICE, chptr, cli_from(sptr), SKIP_DEAF | SKIP_BURST, "%H :%s", chptr, text); } diff --git a/ircd/m_burst.c b/ircd/m_burst.c index 289c93e..3354a04 100644 --- a/ircd/m_burst.c +++ b/ircd/m_burst.c @@ -238,7 +238,7 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) modebuf_mode(mbuf, MODE_DEL | chptr->mode.mode); /* wipeout modes */ chptr->mode.mode &= ~(MODE_ADD | MODE_DEL | MODE_PRIVATE | MODE_SECRET | MODE_MODERATED | MODE_TOPICLIMIT | MODE_INVITEONLY | - MODE_NOPRIVMSGS); + MODE_NOPRIVMSGS | MODE_DELJOINS); parse_flags |= (MODE_PARSE_SET | MODE_PARSE_WIPEOUT); /* wipeout keys */ @@ -340,12 +340,16 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) { struct Client *acptr; char *nicklist = parv[param], *p = 0, *nick, *ptr; - int current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; - int last_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; + int current_mode, last_mode, base_mode; int oplevel = -1; /* Mark first field with digits: means the same as 'o' (but with level). */ int last_oplevel = 0; struct Membership* member; + base_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; + if (chptr->mode.mode & MODE_DELJOINS) + base_mode |= CHFL_DELAYED; + current_mode = last_mode = base_mode; + for (nick = ircd_strtok(&p, nicklist, ","); nick; nick = ircd_strtok(&p, 0, ",")) { @@ -363,29 +367,29 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) */ oplevel = MAXOPLEVEL; if (current_mode_needs_reset) { - current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; + current_mode = base_mode; current_mode_needs_reset = 0; } - current_mode = (current_mode & ~CHFL_DEOPPED) | CHFL_CHANOP; + current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP; } else if (*ptr == 'v') { /* has voice status */ if (current_mode_needs_reset) { - current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; + current_mode = base_mode; current_mode_needs_reset = 0; } - current_mode |= CHFL_VOICE; + current_mode = (current_mode & ~CHFL_DELAYED) | CHFL_VOICE; oplevel = -1; /* subsequential digits are an absolute op-level value. */ } else if (isdigit(*ptr)) { int level_increment = 0; if (oplevel == -1) { /* op-level is absolute value? */ if (current_mode_needs_reset) { - current_mode = CHFL_DEOPPED | CHFL_BURST_JOINED; + current_mode = base_mode; current_mode_needs_reset = 0; } oplevel = 0; } - current_mode = (current_mode & ~CHFL_DEOPPED) | CHFL_CHANOP; + current_mode = (current_mode & ~(CHFL_DEOPPED | CHFL_DELAYED)) | CHFL_CHANOP; do { level_increment = 10 * level_increment + *ptr++ - '0'; } while(isdigit(*ptr)); @@ -425,7 +429,8 @@ int ms_burst(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) if (IsBurst(sptr) || !(member = find_member_link(chptr, acptr))) { add_user_to_channel(chptr, acptr, current_mode, oplevel); - sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, "%H", chptr); + if (!(current_mode & CHFL_DELAYED)) + sendcmdto_channel_butserv_butone(acptr, CMD_JOIN, chptr, NULL, "%H", chptr); } else { diff --git a/ircd/m_clearmode.c b/ircd/m_clearmode.c index 2ac7652..d2f99b1 100644 --- a/ircd/m_clearmode.c +++ b/ircd/m_clearmode.c @@ -124,6 +124,7 @@ do_clearmode(struct Client *cptr, struct Client *sptr, struct Channel *chptr, MODE_BAN, 'b', MODE_LIMIT, 'l', MODE_REGONLY, 'r', + MODE_DELJOINS, 'D', 0x0, 0x0 }; int *flag_p; diff --git a/ircd/m_join.c b/ircd/m_join.c index 143a090..76eb138 100644 --- a/ircd/m_join.c +++ b/ircd/m_join.c @@ -152,7 +152,9 @@ join0(struct JoinBuf *join, struct Client *cptr, struct Client *sptr, while ((member = cli_user(sptr)->channel)) joinbuf_join(&part, member->channel, - IsZombie(member) ? CHFL_ZOMBIE : 0); + IsZombie(member) ? CHFL_ZOMBIE : + IsDelayedJoin(member) ? CHFL_DELAYED : + 0); joinbuf_flush(&part); diff --git a/ircd/m_kick.c b/ircd/m_kick.c index 97b59e4..2096bf5 100644 --- a/ircd/m_kick.c +++ b/ircd/m_kick.c @@ -154,8 +154,16 @@ int m_kick(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) sendcmdto_serv_butone(sptr, CMD_KICK, cptr, "%H %C :%s", chptr, who, comment); - sendcmdto_channel_butserv_butone(sptr, CMD_KICK, chptr, NULL, "%H %C :%s", chptr, who, - comment); + if (IsDelayedJoin(member)) { + /* If it's a delayed join, only send the KICK to the person doing + * the kicking and the victim */ + if (MyUser(who)) + sendcmdto_one(sptr, CMD_KICK, who, "%H %C :%s", chptr, who, comment); + sendcmdto_one(sptr, CMD_KICK, sptr, "%H %C :%s", chptr, who, comment); + CheckDelayedJoins(chptr); + } else + sendcmdto_channel_butserv_butone(sptr, CMD_KICK, chptr, NULL, "%H %C :%s", chptr, who, + comment); make_zombie(member, who, cptr, sptr, chptr); diff --git a/ircd/m_names.c b/ircd/m_names.c index 02e80d2..2ee0c65 100644 --- a/ircd/m_names.c +++ b/ircd/m_names.c @@ -155,6 +155,12 @@ void do_names(struct Client* sptr, struct Channel* chptr, int filter) if (IsZombie(member) && member->user != sptr) continue; + if (IsDelayedJoin(member) && (member->user != sptr) && !(filter & NAMES_DEL)) + continue; + + if ((!IsDelayedJoin(member) || (member->user == sptr)) && (filter & NAMES_DEL)) + continue; + if (needs_space) { strcat(buf, " "); idx++; @@ -181,7 +187,7 @@ void do_names(struct Client* sptr, struct Channel* chptr, int filter) if (mlen + idx + NICKLEN + 5 > BUFSIZE) /* space, modifier, nick, \r \n \0 */ { - send_reply(sptr, RPL_NAMREPLY, buf); + send_reply(sptr, (filter & NAMES_DEL) ? RPL_DELNAMREPLY : RPL_NAMREPLY, buf); strcpy(buf, "* "); ircd_strncpy(buf + 2, chptr->chname, len + 1); buf[len + 2] = 0; @@ -196,7 +202,7 @@ void do_names(struct Client* sptr, struct Channel* chptr, int filter) } } if (flag) - send_reply(sptr, RPL_NAMREPLY, buf); + send_reply(sptr, (filter & NAMES_DEL) ? RPL_DELNAMREPLY : RPL_NAMREPLY, buf); if (filter&NAMES_EON) send_reply(sptr, RPL_ENDOFNAMES, chptr->chname); } @@ -216,8 +222,14 @@ int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) struct Membership* member; char* s; char* para = parc > 1 ? parv[1] : 0; + int showingdelayed = 0; - if (parc > 2 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %C", 2, parc, parv)) + if (parc > 1 && !ircd_strcmp(parv[1], "-D")) { + para = (parc > 2) ? parv[2] : 0; + showingdelayed = 1; + } + + if ((parc - showingdelayed) > 2 && hunt_server_cmd(sptr, CMD_NAMES, cptr, 1, "%s %C", 2+showingdelayed, parc, parv)) return 0; if (EmptyString(para)) { @@ -229,7 +241,7 @@ int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) s = strchr(para, ','); /* Recursively call m_names for each comma-seperated channel. Eww. */ if (s) { - parv[1] = ++s; + parv[1+showingdelayed] = ++s; m_names(cptr, sptr, parc, parv); } @@ -256,9 +268,9 @@ int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) if (find_channel_member(sptr, ch2ptr)) { - do_names(sptr, ch2ptr, NAMES_ALL); /* Full list if we're in this chan. */ + do_names(sptr, ch2ptr, (showingdelayed?NAMES_DEL:0)|NAMES_ALL); /* Full list if we're in this chan. */ } else { - do_names(sptr, ch2ptr, NAMES_VIS); + do_names(sptr, ch2ptr, (showingdelayed?NAMES_DEL:0)|NAMES_VIS); } } @@ -321,7 +333,7 @@ int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) member = find_member_link(chptr, sptr); if (member) { - do_names(sptr, chptr, NAMES_ALL); + do_names(sptr, chptr, (showingdelayed?NAMES_DEL:0)|NAMES_ALL); if (!EmptyString(para)) { send_reply(sptr, RPL_ENDOFNAMES, chptr ? chptr->chname : para); @@ -334,7 +346,7 @@ int m_names(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) * Special Case 3: User isn't on this channel, show all visible users, in * non secret channels. */ - do_names(sptr, chptr, NAMES_VIS); + do_names(sptr, chptr, (showingdelayed?NAMES_DEL:0)|NAMES_VIS); send_reply(sptr, RPL_ENDOFNAMES, para); } } else { /* Channel doesn't exist. */ diff --git a/ircd/m_part.c b/ircd/m_part.c index 4924692..00da1ef 100644 --- a/ircd/m_part.c +++ b/ircd/m_part.c @@ -139,13 +139,16 @@ int m_part(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) assert(!IsZombie(member)); /* Local users should never zombie */ - if (!member_can_send_to_channel(member)) + if (!member_can_send_to_channel(member, 0)) { flags |= CHFL_BANNED; /* Remote clients don't want to see a comment either. */ parts.jb_comment = 0; } + if (IsDelayedJoin(member)) + flags |= CHFL_DELAYED; + joinbuf_join(&parts, chptr, flags); /* part client from channel */ } @@ -194,12 +197,15 @@ int ms_part(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) if (IsZombie(member)) /* figure out special flags... */ flags |= CHFL_ZOMBIE; + if (IsDelayedJoin(member)) + flags |= CHFL_DELAYED; + /* * XXX BUG: If a client /part's with a part notice, on channels where * he's banned, local clients will not see the part notice, but remote * clients will. */ - if (!member_can_send_to_channel(member)) + if (!member_can_send_to_channel(member, 0)) flags |= CHFL_BANNED; /* part user from channel */ diff --git a/ircd/m_quit.c b/ircd/m_quit.c index 9ca6786..e09dc3c 100644 --- a/ircd/m_quit.c +++ b/ircd/m_quit.c @@ -107,7 +107,7 @@ int m_quit(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) if (cli_user(sptr)) { struct Membership* chan; for (chan = cli_user(sptr)->channel; chan; chan = chan->next_channel) { - if (!IsZombie(chan) && !member_can_send_to_channel(chan)) + if (!IsZombie(chan) && !IsDelayedJoin(chan) && !member_can_send_to_channel(chan, 0)) return exit_client(cptr, sptr, sptr, "Signed off"); } } diff --git a/ircd/m_topic.c b/ircd/m_topic.c index a41745c..83e89f2 100644 --- a/ircd/m_topic.c +++ b/ircd/m_topic.c @@ -97,13 +97,17 @@ static void do_settopic(struct Client *sptr, struct Client *cptr, struct Channel *chptr,char *topic) { + struct Membership *member; int newtopic; + member = find_channel_member(sptr, chptr); /* if +n and not @'d, return an error and ignore the topic */ - if ((chptr->mode.mode & MODE_TOPICLIMIT) != 0 && !is_chan_op(sptr, chptr)) + if ((chptr->mode.mode & MODE_TOPICLIMIT) != 0 && (!member || !IsChanOp(member))) { send_reply(sptr, ERR_CHANOPRIVSNEEDED, chptr->chname); return; } + if (member && IsDelayedJoin(member)) + RevealDelayedJoin(member); /* Note if this is just a refresh of an old topic, and don't * send it to all the clients to save bandwidth. We still send * it to other servers as they may have split and lost the topic. diff --git a/ircd/m_wallchops.c b/ircd/m_wallchops.c index f05769b..b569c83 100644 --- a/ircd/m_wallchops.c +++ b/ircd/m_wallchops.c @@ -114,7 +114,7 @@ int m_wallchops(struct Client* cptr, struct Client* sptr, int parc, char* parv[] return send_reply(sptr, ERR_NOTEXTTOSEND); if (IsChannelName(parv[1]) && (chptr = FindChannel(parv[1]))) { - if (client_can_send_to_channel(sptr, chptr)) { + if (client_can_send_to_channel(sptr, chptr, 0)) { if ((chptr->mode.mode & MODE_NOPRIVMSGS) && check_target_limit(sptr, chptr, chptr->chname, 0)) return 0; @@ -144,7 +144,7 @@ int ms_wallchops(struct Client* cptr, struct Client* sptr, int parc, char* parv[ return 0; if ((chptr = FindChannel(parv[1]))) { - if (client_can_send_to_channel(sptr, chptr)) { + if (client_can_send_to_channel(sptr, chptr, 0)) { sendcmdto_channel_butone(sptr, CMD_WALLCHOPS, chptr, cptr, SKIP_DEAF | SKIP_BURST | SKIP_NONOPS, "%H :%s", chptr, parv[parc - 1]); diff --git a/ircd/m_wallvoices.c b/ircd/m_wallvoices.c index b228f17..a922674 100644 --- a/ircd/m_wallvoices.c +++ b/ircd/m_wallvoices.c @@ -113,7 +113,7 @@ int m_wallvoices(struct Client* cptr, struct Client* sptr, int parc, char* parv[ return send_reply(sptr, ERR_NOTEXTTOSEND); if (IsChannelName(parv[1]) && (chptr = FindChannel(parv[1]))) { - if (client_can_send_to_channel(sptr, chptr)) { + if (client_can_send_to_channel(sptr, chptr, 0)) { if ((chptr->mode.mode & MODE_NOPRIVMSGS) && check_target_limit(sptr, chptr, chptr->chname, 0)) return 0; @@ -143,7 +143,7 @@ int ms_wallvoices(struct Client* cptr, struct Client* sptr, int parc, char* parv return 0; if ((chptr = FindChannel(parv[1]))) { - if (client_can_send_to_channel(sptr, chptr)) { + if (client_can_send_to_channel(sptr, chptr, 0)) { sendcmdto_channel_butone(sptr, CMD_WALLVOICES, chptr, cptr, SKIP_DEAF | SKIP_BURST | SKIP_NONVOICES, "%H :%s", chptr, parv[parc - 1]); diff --git a/ircd/m_who.c b/ircd/m_who.c index 473509e..797b5a8 100644 --- a/ircd/m_who.c +++ b/ircd/m_who.c @@ -323,7 +323,7 @@ int m_who(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) acptr = member->user; if ((bitsel & WHOSELECT_OPER) && !SeeOper(sptr,acptr)) continue; - if ((acptr != sptr) && (member->status & CHFL_ZOMBIE)) + if ((acptr != sptr) && (member->status & (CHFL_ZOMBIE | CHFL_DELAYED))) continue; if (!(isthere || (SEE_USER(sptr, acptr, bitsel)))) continue; diff --git a/ircd/m_whois.c b/ircd/m_whois.c index 319ea1d..ed0213a 100644 --- a/ircd/m_whois.c +++ b/ircd/m_whois.c @@ -166,9 +166,11 @@ static void do_whois(struct Client* sptr, struct Client *acptr, int parc) } if (IsDeaf(acptr)) *(buf + len++) = '-'; - if (is_chan_op(acptr, chptr)) + if (IsDelayedJoin(chan) && (sptr != acptr)) + *(buf + len++) = '<'; + else if (IsChanOp(chan)) *(buf + len++) = '@'; - else if (has_voice(acptr, chptr)) + else if (HasVoice(chan)) *(buf + len++) = '+'; else if (IsZombie(chan)) *(buf + len++) = '!'; diff --git a/ircd/s_err.c b/ircd/s_err.c index 0c88681..6145a1c 100644 --- a/ircd/s_err.c +++ b/ircd/s_err.c @@ -742,7 +742,7 @@ static Numeric replyTable[] = { /* 354 */ { RPL_WHOSPCRPL, "%s", "354" }, /* 355 */ - { 0 }, + { RPL_DELNAMREPLY, "%s", "355" }, /* 356 */ { 0 }, /* 357 */ diff --git a/ircd/s_user.c b/ircd/s_user.c index 9b561c9..8986e10 100644 --- a/ircd/s_user.c +++ b/ircd/s_user.c @@ -1082,8 +1082,14 @@ hide_hostmask(struct Client *cptr, unsigned int flag) */ for (chan = cli_user(cptr)->channel; chan; chan = chan->next_channel) { - sendcmdto_channel_butserv_butone(cptr, CMD_JOIN, chan->channel, cptr, - "%H", chan->channel); + /* For a user with no modes in a join-delayed channel, do not show + * the rejoin. */ + if (!IsChanOp(chan) && !HasVoice(chan) + && (chan->channel->mode.mode & MODE_DELJOINS)) + SetDelayedJoin(chan); + else + sendcmdto_channel_butserv_butone(cptr, CMD_JOIN, chan->channel, cptr, + "%H", chan->channel); if (IsChanOp(chan) && HasVoice(chan)) sendcmdto_channel_butserv_butone(&me, CMD_MODE, chan->channel, cptr, "%H +ov %C %C", chan->channel, cptr, diff --git a/ircd/send.c b/ircd/send.c index 4d562b7..33c8faa 100644 --- a/ircd/send.c +++ b/ircd/send.c @@ -403,7 +403,7 @@ void sendcmdto_common_channels_butone(struct Client *from, const char *cmd, * loop through from's channels, and the members on their channels */ for (chan = cli_user(from)->channel; chan; chan = chan->next_channel) { - if (IsZombie(chan)) + if (IsZombie(chan) || IsDelayedJoin(chan)) continue; for (member = chan->channel->members; member; member = member->next_member)