+2004-05-10 Michael Poole <mdpoole@troilus.org>
+
+ 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 <mdpoole@troilus.org>
* ircd/ircd_events.c: Actually reference and try to use the epoll
#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)
#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))
#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)
#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 {
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);
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);
#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
#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);
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 */
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
*/
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);
*/
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);
else
return !is_banned(cptr, chptr, NULL);
}
- return member_can_send_to_channel(member);
+ return member_can_send_to_channel(member, reveal);
}
/*
*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);
MODE_INVITEONLY, 'i',
MODE_NOPRIVMSGS, 'n',
MODE_REGONLY, 'r',
+ MODE_DELJOINS, 'D',
+ MODE_WASDELJOINS, 'd',
/* MODE_KEY, 'k', */
/* MODE_BAN, 'b', */
/* MODE_LIMIT, 'l', */
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;
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);
}
/* MODE_BAN, 'b', */
MODE_LIMIT, 'l',
MODE_REGONLY, 'r',
+ MODE_DELJOINS, 'D',
0x0, 0x0
};
unsigned int add;
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)
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++) */
}
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];
MODE_BAN, 'b',
MODE_LIMIT, 'l',
MODE_REGONLY, 'r',
+ MODE_DELJOINS, 'D',
MODE_ADD, '+',
MODE_DEL, '-',
0x0, 0x0
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);
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 ||
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);
+ }
+ }
+}
/*
* 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;
}
/*
* 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) &&
* 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);
}
* 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);
}
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 */
{
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, ",")) {
*/
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));
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
{
MODE_BAN, 'b',
MODE_LIMIT, 'l',
MODE_REGONLY, 'r',
+ MODE_DELJOINS, 'D',
0x0, 0x0
};
int *flag_p;
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);
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);
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++;
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;
}
}
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);
}
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)) {
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);
}
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);
}
}
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);
* 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. */
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 */
}
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 */
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");
}
}
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.
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;
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]);
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;
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]);
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;
}
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++) = '!';
/* 354 */
{ RPL_WHOSPCRPL, "%s", "354" },
/* 355 */
- { 0 },
+ { RPL_DELNAMREPLY, "%s", "355" },
/* 356 */
{ 0 },
/* 357 */
*/
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,
* 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)