#include "ircd_defs.h"
#include "ircd_features.h"
#include "ircd_log.h"
-#include "ircd_policy.h"
#include "ircd_reply.h"
#include "ircd_snprintf.h"
#include "ircd_string.h"
* who then will educate them on the use of Apass/upass.
*/
+ if (feature_bool(FEAT_OPLEVELS)) {
if (TStime() - chptr->creationtime < 172800) /* Channel younger than 48 hours? */
schedule_destruct_event_1m(chptr); /* Get rid of it in approximately 4-5 minutes */
else
schedule_destruct_event_48h(chptr); /* Get rid of it in approximately 48 hours */
+ } else
+ destruct_channel(chptr);
return 0;
}
assert(0 != ban->value.ban.banstr);
strcpy(ban->value.ban.banstr, banid);
-#ifdef HEAD_IN_SAND_BANWHO
- if (IsServer(cptr))
+ if (IsServer(cptr) && feature_bool(FEAT_HIS_BANWHO))
DupString(ban->value.ban.who, cli_name(&me));
else
-#endif
DupString(ban->value.ban.who, cli_name(cptr));
assert(0 != ban->value.ban.who);
struct Membership* member)
{
struct SLink* tmp;
+ char tmphost[HOSTLEN + 1];
char nu_host[NUH_BUFSIZE];
char nu_realhost[NUH_BUFSIZE];
char nu_ip[NUI_BUFSIZE];
s = make_nick_user_host(nu_host, cli_name(cptr), (cli_user(cptr))->username,
(cli_user(cptr))->host);
- if (HasHiddenHost(cptr))
- sr = make_nick_user_host(nu_realhost, cli_name(cptr),
- (cli_user(cptr))->username,
- cli_user(cptr)->realhost);
+ if (IsAccount(cptr))
+ {
+ if (HasHiddenHost(cptr))
+ {
+ sr = make_nick_user_host(nu_realhost, cli_name(cptr),
+ (cli_user(cptr))->username,
+ cli_user(cptr)->realhost);
+ }
+ else
+ {
+ ircd_snprintf(0, tmphost, HOSTLEN, "%s.%s",
+ cli_user(cptr)->account, feature_str(FEAT_HIDDEN_HOST));
+ sr = make_nick_user_host(nu_realhost, cli_name(cptr),
+ cli_user(cptr)->username,
+ tmphost);
+ }
+ }
for (tmp = chptr->banlist; tmp; tmp = tmp->next) {
if ((tmp->flags & CHFL_BAN_IPMASK)) {
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 is_level0_op(struct Client *cptr, struct Channel *chptr)
-{
- return 0;
-}
-
int is_zombie(struct Client *cptr, struct Channel *chptr)
{
struct Membership* member;
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);
+ /* Discourage using the Apass to get op. They should use the upass. */
+ if (IsChannelManager(member) && *member->channel->mode.upass)
+ return 0;
+
if (IsVoicedOrOpped(member))
return 1;
/*
*/
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);
member = find_channel_member(cptr, chptr);
/*
- * You can't speak if your off channel, if the channel is modeless, or
- * +n (no external messages) or +m (moderated).
+ * You can't speak if you're off channel, and it is +n (no external messages)
+ * or +m (moderated).
*/
if (!member) {
if ((chptr->mode.mode & (MODE_NOPRIVMSGS|MODE_MODERATED)) ||
- IsModelessChannel(chptr->chname) ||
((chptr->mode.mode & MODE_REGONLY) && !IsAccount(cptr)))
return 0;
else
return !is_banned(cptr, chptr, NULL);
}
- return member_can_send_to_channel(member);
+ return member_can_send_to_channel(member, reveal);
}
/*
* find_no_nickchange_channel
- * if a member and not opped or voiced and banned
+ * if a member and not (opped or voiced) and (banned or moderated)
* return the name of the first channel banned on
*/
const char* find_no_nickchange_channel(struct Client* cptr)
struct Membership* member;
for (member = (cli_user(cptr))->channel; member;
member = member->next_channel) {
- if (!IsVoicedOrOpped(member) && is_banned(cptr, member->channel, member))
+ if (!IsVoicedOrOpped(member) &&
+ (is_banned(cptr, member->channel, member) ||
+ (member->channel->mode.mode & MODE_MODERATED)))
return member->channel->chname;
}
}
* with the parameters in pbuf.
*/
void channel_modes(struct Client *cptr, char *mbuf, char *pbuf, int buflen,
- struct Channel *chptr)
+ struct Channel *chptr, struct Membership *member)
{
int previous_parameter = 0;
*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);
*mbuf++ = 'u';
if (previous_parameter)
strcat(pbuf, " ");
- if (is_level0_op(cptr, chptr) || IsServer(cptr)) {
+ if (IsServer(cptr) || (member && IsChanOp(member) && OpLevel(member) == 0)) {
strcat(pbuf, chptr->mode.upass);
} else
strcat(pbuf, "*");
lp2 = chptr->banlist;
*modebuf = *parabuf = '\0';
- channel_modes(cptr, modebuf, parabuf, sizeof(parabuf), chptr);
+ channel_modes(cptr, modebuf, parabuf, sizeof(parabuf), chptr, 0);
for (first = 1; full; first = 0) /* Loop for multiple messages */
{
that didn't fit (full==1) */
if (opped_members)
MyFree(opped_members);
+ if (feature_bool(FEAT_TOPIC_BURST) && (chptr->topic[0] != '\0'))
+ sendcmdto_one(&me, CMD_TOPIC, cptr, "%H %Tu %Tu :%s", chptr,
+ chptr->creationtime, chptr->topic_time, chptr->topic);
}
/*
a HACK(4) notice will be sent if he would not have been supposed
to join normally. */
if (IsLocalChannel(chptr->chname) && HasPriv(sptr, PRIV_WALK_LCHAN) &&
- !BadPtr(key) && compall("OVERRIDE",key) == 0)
+ !BadPtr(key) && compall("OVERRIDE",chptr->mode.key) != 0 &&
+ compall("OVERRIDE",key) == 0)
overrideJoin = MAGIC_OPER_OVERRIDE;
if (chptr->mode.mode & MODE_INVITEONLY)
{
for (; chptr; chptr = chptr->next)
{
- if (!cli_user(cptr) || (SecretChannel(chptr) && !find_channel_member(cptr, chptr)))
+ if (!cli_user(cptr) || (!(HasPriv(cptr, PRIV_LIST_CHAN) && IsAnOper(cptr)) &&
+ SecretChannel(chptr) && !find_channel_member(cptr, chptr)))
continue;
if (chptr->users > args->min_users && chptr->users < args->max_users &&
chptr->creationtime > args->min_time &&
* via 'a', or on server 'B' via either 'b' or 'c', or on server D via 'd'.
*
* a) On server A : set CHFL_ZOMBIE for `who' (lp) and pass on the KICK.
- * Remove the user immedeately when no users are left on the channel.
+ * Remove the user immediately when no users are left on the channel.
* b) On server B : remove the user (who/lp) from the channel, send a
* PART upstream (to A) and pass on the KICK.
* c) KICKed by `client'; On server B : remove the user (who/lp) from the
* of course, str2 is not NULL)
*/
static void
-build_string(char *strptr, int *strptr_i, char *str1, char *str2, char c)
+build_string(char *strptr, int *strptr_i, const char *str1,
+ const char *str2, char c)
{
if (c)
strptr[(*strptr_i)++] = c;
MODE_INVITEONLY, 'i',
MODE_NOPRIVMSGS, 'n',
MODE_REGONLY, 'r',
+ MODE_DELJOINS, 'D',
+ MODE_WASDELJOINS, 'd',
/* MODE_KEY, 'k', */
/* MODE_BAN, 'b', */
/* MODE_LIMIT, 'l', */
if (mbuf->mb_add == 0 && mbuf->mb_rem == 0 && mbuf->mb_count == 0)
return 0;
- /* Ok, if we were given the OPMODE flag, hide the source if its a user */
- if (mbuf->mb_dest & MODEBUF_DEST_OPMODE && !IsServer(mbuf->mb_source))
+ /* Ok, if we were given the OPMODE flag, or its a server, hide the source.
+ */
+ if (mbuf->mb_dest & MODEBUF_DEST_OPMODE || IsServer(mbuf->mb_source))
app_source = &me;
else
app_source = mbuf->mb_source;
bufptr[(*bufptr_i)++] = MB_TYPE(mbuf, i) & MODE_CHANOP ? 'o' : 'v';
totalbuflen -= IRCD_MAX(5, tmp) + 1;
}
- } else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN | MODE_APASS | MODE_UPASS)) {
+ } else if (MB_TYPE(mbuf, i) & (MODE_BAN | MODE_APASS | MODE_UPASS)) {
tmp = strlen(MB_STRING(mbuf, i));
if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
else {
char mode_char;
- switch(MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN | MODE_APASS | MODE_UPASS))
+ switch(MB_TYPE(mbuf, i) & (MODE_BAN | MODE_APASS | MODE_UPASS))
{
case MODE_APASS:
mode_char = 'A';
case MODE_UPASS:
mode_char = 'u';
break;
- case MODE_KEY:
- mode_char = 'k';
- break;
default:
mode_char = 'b';
break;
bufptr[(*bufptr_i)++] = mode_char;
totalbuflen -= tmp + 1;
}
+ } else if (MB_TYPE(mbuf, i) & MODE_KEY) {
+ tmp = (mbuf->mb_dest & MODEBUF_DEST_NOKEY ? 1 :
+ strlen(MB_STRING(mbuf, i)));
+
+ if ((totalbuflen - tmp) <= 0) /* don't overflow buffer */
+ MB_TYPE(mbuf, i) |= MODE_SAVE; /* save for later */
+ else {
+ bufptr[(*bufptr_i)++] = 'k';
+ totalbuflen -= tmp + 1;
+ }
} else if (MB_TYPE(mbuf, i) & MODE_LIMIT) {
/* if it's a limit, we also format the number */
ircd_snprintf(0, limitbuf, sizeof(limitbuf), "%u", MB_UINT(mbuf, i));
if (MB_TYPE(mbuf, i) & (MODE_CHANOP | MODE_VOICE))
build_string(strptr, strptr_i, cli_name(MB_CLIENT(mbuf, i)), 0, ' ');
- /* deal with strings... */
- else if (MB_TYPE(mbuf, i) & (MODE_KEY | MODE_BAN))
+ /* deal with bans... */
+ else if (MB_TYPE(mbuf, i) & MODE_BAN)
build_string(strptr, strptr_i, MB_STRING(mbuf, i), 0, ' ');
+ /* deal with keys... */
+ else if (MB_TYPE(mbuf, i) & MODE_KEY)
+ build_string(strptr, strptr_i, mbuf->mb_dest & MODEBUF_DEST_NOKEY ?
+ "*" : MB_STRING(mbuf, i), 0, ' ');
+
/* deal with invisible passwords */
else if (MB_TYPE(mbuf, i) & (MODE_APASS | MODE_UPASS))
build_string(strptr, strptr_i, "*", 0, ' ');
if (mbuf->mb_dest & MODEBUF_DEST_HACK2)
sendto_opmask_butone(0, SNO_HACK2, "HACK(2): %s MODE %s %s%s%s%s%s%s "
"[%Tu]",
-#ifdef HEAD_IN_SAND_SNOTICES
- cli_name(mbuf->mb_source),
-#else
- cli_name(app_source),
-#endif
+ cli_name(feature_bool(FEAT_HIS_SNOTICES) ?
+ mbuf->mb_source : app_source),
mbuf->mb_channel->chname,
rembuf_i ? "-" : "", rembuf, addbuf_i ? "+" : "",
addbuf, remstr, addstr,
if (mbuf->mb_dest & MODEBUF_DEST_HACK3)
sendto_opmask_butone(0, SNO_HACK3, "BOUNCE or HACK(3): %s MODE %s "
"%s%s%s%s%s%s [%Tu]",
-#ifdef HEAD_IN_SAND_SNOTICES
- cli_name(mbuf->mb_source),
-#else
- cli_name(app_source),
-#endif
+ cli_name(feature_bool(FEAT_HIS_SNOTICES) ?
+ mbuf->mb_source : app_source),
mbuf->mb_channel->chname, rembuf_i ? "-" : "",
rembuf, addbuf_i ? "+" : "", addbuf, remstr, addstr,
mbuf->mb_channel->creationtime);
assert(0 != chan);
assert(0 != dest);
+ if (IsLocalChannel(chan->chname)) dest &= ~MODEBUF_DEST_SERVER;
+
mbuf->mb_add = 0;
mbuf->mb_rem = 0;
mbuf->mb_source = source;
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;
int i, bufpos = 0, len;
int *flag_p;
char *key = 0, limitbuf[20];
- char *apass, *upass;
+ char *apass = 0, *upass = 0;
assert(0 != mbuf);
assert(0 != buf);
return;
state->done |= DONE_KEY;
- t_len = KEYLEN + 1;
+ t_len = KEYLEN;
/* clean up the key string */
s = t_str;
return;
}
+ /* If they are not the channel manager, they are not allowed to change it */
+ if (MyUser(state->sptr) && !IsChannelManager(state->member)) {
+ if (*state->chptr->mode.apass) {
+ send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
+ "Use /JOIN", state->chptr->chname, "<AdminPass>.");
+ } else {
+ send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
+ "Re-create the channel. The channel must be *empty* for",
+ TStime() - state->chptr->creationtime >= 171000 ? "48 contiguous hours" : "a minute or two",
+ "before it can be recreated.");
+ }
+ return;
+ }
+
if (state->done & DONE_UPASS) /* allow upass to be set only once */
return;
state->done |= DONE_UPASS;
if (!state->mbuf)
return;
- /* can't add a upass if one is set, nor can one remove the wrong upass */
if (!(state->flags & MODE_PARSE_FORCE))
+ /* can't add the upass while apass is not set */
+ if (state->dir == MODE_ADD && !*state->chptr->mode.apass) {
+ send_reply(state->sptr, ERR_UPASSNOTSET, state->chptr->chname, state->chptr->chname);
+ return;
+ }
+ /* can't add a upass if one is set, nor can one remove the wrong upass */
if ((state->dir == MODE_ADD && *state->chptr->mode.upass) ||
(state->dir == MODE_DEL &&
ircd_strcmp(state->chptr->mode.upass, t_str))) {
return;
}
+ /* Don't allow to change the Apass if the channel is older than 48 hours. */
+ if (TStime() - state->chptr->creationtime >= 172800 && !IsAnOper(state->sptr)) {
+ send_reply(state->sptr, ERR_CHANSECURED, state->chptr->chname);
+ return;
+ }
+
+ /* If they are not the channel manager, they are not allowed to change it */
+ if (MyUser(state->sptr) && !IsChannelManager(state->member)) {
+ if (*state->chptr->mode.apass) {
+ send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
+ "Use /JOIN", state->chptr->chname, "<AdminPass>.");
+ } else {
+ send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
+ "Re-create the channel. The channel must be *empty* for",
+ "at least a whole minute", "before it can be recreated.");
+ }
+ return;
+ }
+
if (state->done & DONE_APASS) /* allow apass to be set only once */
return;
state->done |= DONE_APASS;
if (!state->mbuf)
return;
- /* can't add a apass if one is set, nor can one remove the wrong apass */
- if (!(state->flags & MODE_PARSE_FORCE))
+ if (!(state->flags & MODE_PARSE_FORCE)) {
+ /* can't remove the apass while upass is still set */
+ if (state->dir == MODE_DEL && *state->chptr->mode.upass) {
+ send_reply(state->sptr, ERR_UPASSSET, state->chptr->chname, state->chptr->chname);
+ return;
+ }
+ /* can't add an apass if one is set, nor can one remove the wrong apass */
if ((state->dir == MODE_ADD && *state->chptr->mode.apass) ||
- (state->dir == MODE_DEL &&
- ircd_strcmp(state->chptr->mode.apass, t_str))) {
+ (state->dir == MODE_DEL && ircd_strcmp(state->chptr->mode.apass, t_str))) {
send_reply(state->sptr, ERR_KEYSET, state->chptr->chname);
return;
}
+ }
if (!(state->flags & MODE_PARSE_WIPEOUT) && state->dir == MODE_ADD &&
!ircd_strcmp(state->chptr->mode.apass, t_str))
modebuf_mode_string(state->mbuf, state->dir | flag_p[0], t_str, 0);
if (state->flags & MODE_PARSE_SET) {
- if (state->dir == MODE_ADD) /* set the new apass */
+ if (state->dir == MODE_ADD) { /* set the new apass */
+ /* Make it VERY clear to the user that this is a one-time password */
ircd_strncpy(state->chptr->mode.apass, t_str, PASSLEN);
- else /* remove the old apass */
+ if (MyUser(state->sptr)) {
+ send_reply(state->sptr, RPL_APASSWARN,
+ "Channel Admin password (+A) set to '", state->chptr->mode.apass, "'. ",
+ "Are you SURE you want to use this as Admin password? ",
+ "You will NOT be able to change this password anymore once the channel is more than 48 hours old!");
+ send_reply(state->sptr, RPL_APASSWARN,
+ "Use \"/MODE ", state->chptr->chname, " -A ", state->chptr->mode.apass,
+ "\" to remove the password and then immediately set a new one. "
+ "IMPORTANT: YOU CANNOT RECOVER THIS PASSWORD, EVER; "
+ "WRITE THE PASSWORD DOWN (don't store this rescue password on disk)! "
+ "Now set the channel user password (+u).");
+ }
+ } else { /* remove the old apass */
*state->chptr->mode.apass = '\0';
+ if (MyUser(state->sptr))
+ send_reply(state->sptr, RPL_APASSWARN,
+ "WARNING: You removed the channel Admin password MODE (+A). ",
+ "If you would disconnect or leave the channel without setting a new password then you will ",
+ "not be able to set it again and lose ownership of this channel! ",
+ "SET A NEW PASSWORD NOW!", "");
+ }
}
}
} else if (!mmatch(t_str, ban->value.ban.banstr))
ban->flags |= MODE_DEL; /* mark ban for deletion: overlapping */
- if (!ban->next && (newban->flags & MODE_ADD)) {
+ if (!ban->next && (newban->flags & MODE_ADD))
+ {
ban->next = newban; /* add our ban with its flags */
break; /* get out of loop */
}
} else {
if (state->flags & MODE_PARSE_SET && MyUser(state->sptr) &&
(len > (feature_int(FEAT_AVBANLEN) * feature_int(FEAT_MAXBANS)) ||
- count >= feature_int(FEAT_MAXBANS))) {
+ count > feature_int(FEAT_MAXBANS))) {
send_reply(state->sptr, ERR_BANLISTFULL, state->chptr->chname,
ban->value.ban.banstr);
count--;
if (MyUser(state->sptr)) {
/* don't allow local opers to be deopped on local channels */
- if (state->cli_change[i].client != state->sptr &&
+ if (MyUser(state->sptr) &&
+ state->cli_change[i].client != state->sptr &&
IsLocalChannel(state->chptr->chname) &&
HasPriv(state->cli_change[i].client, PRIV_DEOP_LCHAN)) {
send_reply(state->sptr, ERR_ISOPERLCHAN,
continue;
}
+ if (feature_bool(FEAT_OPLEVELS)) {
/* don't allow to deop members with an op level that is <= our own level */
if (state->sptr != state->cli_change[i].client /* but allow to deop oneself */
&& state->member
}
}
}
+ }
/* set op-level of member being opped */
if ((state->cli_change[i].flag & (MODE_ADD | MODE_CHANOP)) ==
(MODE_ADD | MODE_CHANOP)) {
- int old_level = (state->member == NULL) ? -1 : OpLevel(state->member);
- SetOpLevel(member, old_level == MAXOPLEVEL ? MAXOPLEVEL : (old_level + 1));
+ /* If on a channel with upass set, someone with level x gives ops to someone else,
+ then that person gets level x-1. On other channels, where upass is not set,
+ the level stays the same. */
+ int level_increment = *state->chptr->mode.upass ? 1 : 0;
+ /* Someone being opped by a server gets op-level 0 */
+ int old_level = (state->member == NULL) ? -level_increment : OpLevel(state->member);
+ 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
break;
case 'A': /* deal with Admin passes */
+ if (feature_bool(FEAT_OPLEVELS))
mode_parse_apass(&state, flag_p);
break;
case 'u': /* deal with user passes */
+ if (feature_bool(FEAT_OPLEVELS))
mode_parse_upass(&state, flag_p);
break;
joinbuf_join(struct JoinBuf *jbuf, struct Channel *chan, unsigned int flags)
{
unsigned int len;
+ int is_local;
assert(0 != jbuf);
return;
}
+ is_local = IsLocalChannel(chan->chname);
+
if (jbuf->jb_type == JOINBUF_TYPE_PART ||
jbuf->jb_type == JOINBUF_TYPE_PARTALL) {
+ struct Membership *member = find_member_link(chan, jbuf->jb_source);
+ if (IsUserParting(member))
+ return;
+ 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);
* the original m_part.c */
if (jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
- IsLocalChannel(chan->chname)) /* got to remove user here */
+ is_local) /* got to remove user here */
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 && !IsLocalChannel(chan->chname))
+ 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 &&
- !IsModelessChannel(chan->chname))
- 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 || IsLocalChannel(chan->chname))
+ if (jbuf->jb_type == JOINBUF_TYPE_PARTALL ||
+ jbuf->jb_type == JOINBUF_TYPE_JOIN || is_local)
return; /* don't send to remote */
/* figure out if channel name will cause buffer to be overflowed */
return 0;
}
+
+/* Returns TRUE (1) if client is invited, FALSE (0) if not */
+int IsInvited(struct Client* cptr, const void* chptr)
+{
+ struct SLink *lp;
+
+ for (lp = (cli_user(cptr))->invited; lp; lp = lp->next)
+ if (lp->value.chptr == 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);
+ }
+ }
+}