* Create a string of form "foo!bar@123.456.789.123" given foo, bar and the
* IP-number as the parameters. If NULL, they become "*".
*/
-#define NUI_BUFSIZE (NICKLEN + USERLEN + 16 + 3)
+#define NUI_BUFSIZE (NICKLEN + USERLEN + SOCKIPLEN + 4)
static char *make_nick_user_ip(char *ipbuf, char *nick, char *name,
- struct in_addr ip)
+ const struct irc_in_addr *ip)
{
- ircd_snprintf(0, ipbuf, NUI_BUFSIZE, "%s!%s@%s", nick, name,
- ircd_ntoa((const char*) &ip));
+ ircd_snprintf(0, ipbuf, NUI_BUFSIZE, "%s!%s@%s", nick, name, ircd_ntoa(ip));
return ipbuf;
}
if ((tmp->flags & CHFL_BAN_IPMASK)) {
if (!ip_s)
ip_s = make_nick_user_ip(nu_ip, cli_name(cptr),
- (cli_user(cptr))->username, cli_ip(cptr));
+ (cli_user(cptr))->username, &cli_ip(cptr));
if (match(tmp->value.ban.banstr, ip_s) == 0)
break;
}
- else if (match(tmp->value.ban.banstr, s) == 0)
+ if (match(tmp->value.ban.banstr, s) == 0)
break;
else if (sr && match(tmp->value.ban.banstr, sr) == 0)
break;
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);
previous_parameter = 1;
}
if (*chptr->mode.upass) {
- *mbuf++ = 'u';
+ *mbuf++ = 'U';
if (previous_parameter)
strcat(pbuf, " ");
if (IsServer(cptr) || (member && IsChanOp(member) && OpLevel(member) == 0)) {
int opped_members_index = 0;
struct Membership** opped_members = NULL;
int last_oplevel = 0;
+ int feat_oplevels = (chptr->mode.mode & MODE_APASS) != 0;
assert(0 != cptr);
assert(0 != chptr);
* Do we have a nick with a new mode ?
* Or are we starting a new BURST line?
*/
- if (new_mode)
+ if (new_mode || !feat_oplevels)
{
/*
* This means we are at the _first_ member that has only
tbuf[loc++] = 'v';
if (IsChanOp(member)) /* flag_cnt == 2 or 3 */
{
- /* append the absolute value of the oplevel */
- loc += ircd_snprintf(0, tbuf + loc, sizeof(tbuf) - loc, "%u", member->oplevel);
- last_oplevel = member->oplevel;
+ /* append the absolute value of the oplevel */
+ if (feat_oplevels)
+ loc += ircd_snprintf(0, tbuf + loc, sizeof(tbuf) - loc, "%u", last_oplevel = member->oplevel);
+ else
+ tbuf[loc++] = 'o';
}
tbuf[loc] = '\0';
msgq_append(&me, mb, tbuf);
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);
}
/*
int can_join(struct Client *sptr, struct Channel *chptr, char *key)
{
- struct SLink *lp;
int overrideJoin = 0;
/*
* Now a user CAN escape anything if invited -- Isomer
*/
- for (lp = (cli_user(sptr))->invited; lp; lp = lp->next)
- if (lp->value.chptr == chptr)
- return 0;
+ if (IsInvited(sptr, chptr))
+ return 0;
/* An oper can force a join on a local channel using "OVERRIDE" as the key.
a HACK(4) notice will be sent if he would not have been supposed
{
for (; chptr; chptr = chptr->next)
{
- if (!cli_user(cptr) || (!(HasPriv(cptr, PRIV_LIST_CHAN) && IsAnOper(cptr)) &&
- SecretChannel(chptr) && !find_channel_member(cptr, chptr)))
+ if (!cli_user(cptr))
+ continue;
+ if (!(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 &&
chptr->creationtime < args->max_time &&
- (!args->topic_limits || (*chptr->topic &&
+ (!(args->flags & LISTARG_TOPICLIMITS) || (*chptr->topic &&
chptr->topic_time > args->min_topic_time &&
chptr->topic_time < args->max_topic_time)))
{
- if (ShowChannel(cptr,chptr))
+ if ((args->flags & LISTARG_SHOWSECRET) || ShowChannel(cptr,chptr))
send_reply(cptr, RPL_LIST, chptr->chname, chptr->users,
chptr->topic);
chptr = chptr->next;
MODE_INVITEONLY, 'i',
MODE_NOPRIVMSGS, 'n',
MODE_REGONLY, 'r',
+ MODE_DELJOINS, 'D',
+ MODE_WASDELJOINS, 'd',
/* MODE_KEY, 'k', */
/* MODE_BAN, 'b', */
-/* MODE_LIMIT, 'l', */
+ MODE_LIMIT, 'l',
/* MODE_APASS, 'A', */
-/* MODE_UPASS, 'u', */
+/* MODE_UPASS, 'U', */
0x0, 0x0
};
int i;
mode_char = 'A';
break;
case MODE_UPASS:
- mode_char = 'u';
+ mode_char = 'U';
break;
default:
mode_char = 'b';
if (mbuf->mb_dest & MODEBUF_DEST_HACK4)
sendto_opmask_butone(0, SNO_HACK4, "HACK(4): %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,
addbuf_i ? "+" : "", addbuf, remstr, addstr);
if (mbuf->mb_dest & MODEBUF_DEST_CHANNEL)
- sendcmdto_channel_butserv_butone(app_source, CMD_MODE, mbuf->mb_channel, NULL,
+ sendcmdto_channel_butserv_butone(app_source, CMD_MODE, mbuf->mb_channel, NULL, 0,
"%H %s%s%s%s%s%s", mbuf->mb_channel,
rembuf_i ? "-" : "", rembuf,
addbuf_i ? "+" : "", addbuf, remstr, addstr);
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;
assert(0 != mbuf);
assert(0 != (mode & (MODE_ADD | MODE_DEL)));
+ if (mode == (MODE_LIMIT | MODE_DEL)) {
+ mbuf->mb_rem |= mode;
+ return;
+ }
MB_TYPE(mbuf, mbuf->mb_count) = mode;
MB_UINT(mbuf, mbuf->mb_count) = uint;
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_NOPRIVMSGS, 'n',
MODE_KEY, 'k',
MODE_APASS, 'A',
- MODE_UPASS, 'u',
+ MODE_UPASS, 'U',
/* MODE_BAN, 'b', */
MODE_LIMIT, 'l',
MODE_REGONLY, 'r',
+ MODE_DELJOINS, 'D',
0x0, 0x0
};
unsigned int add;
build_string(buf, &bufpos, key, 0, ' ');
else if (buf[i] == 'l')
build_string(buf, &bufpos, limitbuf, 0, ' ');
- else if (buf[i] == 'u')
+ else if (buf[i] == 'U')
build_string(buf, &bufpos, upass, 0, ' ');
else if (buf[i] == 'A')
build_string(buf, &bufpos, apass, 0, ' ');
state->parc--;
state->max_args--;
+ if ((int)t_limit<0) /* don't permit a negative limit */
+ return;
+
if (!(state->flags & MODE_PARSE_WIPEOUT) &&
(!t_limit || t_limit == state->chptr->mode.limit))
return;
return;
}
+ /* Can't remove a limit that's not there */
+ if (state->dir == MODE_DEL && !state->chptr->mode.limit)
+ return;
+
+ /* Skip if this is a burst and a lower limit than this is set already */
+ if ((state->flags & MODE_PARSE_BURST) &&
+ (state->chptr->mode.mode & flag_p[0]) &&
+ (state->chptr->mode.limit < t_limit))
+ return;
+
if (state->done & DONE_LIMIT) /* allow limit to be set only once */
return;
state->done |= DONE_LIMIT;
/* clean up the key string */
s = t_str;
- while (*++s > ' ' && *s != ':' && --t_len)
- ;
+ while (*s > ' ' && *s != ':' && *s != ',' && t_len--)
+ s++;
*s = '\0';
if (!*t_str) { /* warn if empty */
if (!state->mbuf)
return;
+ /* Skip if this is a burst, we have a key already and the new key is
+ * after the old one alphabetically */
+ if ((state->flags & MODE_PARSE_BURST) &&
+ *(state->chptr->mode.key) &&
+ ircd_strcmp(state->chptr->mode.key, t_str) <= 0)
+ return;
+
/* can't add a key if one is set, nor can one remove the wrong key */
if (!(state->flags & MODE_PARSE_FORCE))
if ((state->dir == MODE_ADD && *state->chptr->mode.key) ||
if (state->parc <= 0) { /* warn if not enough args */
if (MyUser(state->sptr))
- need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +u" :
- "MODE -u");
+ need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +U" :
+ "MODE -U");
return;
}
return;
}
+ /* If a non-service user is trying to force it, refuse. */
+ if (state->flags & MODE_PARSE_FORCE && !IsChannelService(state->sptr)) {
+ send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
+ "Use /JOIN", state->chptr->chname, " <AdminPass>.");
+ 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) {
if (!*t_str) { /* warn if empty */
if (MyUser(state->sptr))
- need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +u" :
- "MODE -u");
+ need_more_params(state->sptr, state->dir == MODE_ADD ? "MODE +U" :
+ "MODE -U");
return;
}
return;
}
+ /* If a non-service user is trying to force it, refuse. */
+ if (state->flags & MODE_PARSE_FORCE && !IsChannelService(state->sptr)) {
+ send_reply(state->sptr, ERR_NOTMANAGER, state->chptr->chname,
+ "Use /JOIN", state->chptr->chname, " <AdminPass>.");
+ 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);
if (MyUser(state->sptr)) {
/* don't allow local opers to be deopped on local channels */
- if (MyUser(state->sptr) &&
- state->cli_change[i].client != state->sptr &&
+ if (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,
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_NOPRIVMSGS, 'n',
MODE_KEY, 'k',
MODE_APASS, 'A',
- MODE_UPASS, 'u',
+ MODE_UPASS, 'U',
MODE_BAN, 'b',
MODE_LIMIT, 'l',
MODE_REGONLY, 'r',
+ MODE_DELJOINS, 'D',
MODE_ADD, '+',
MODE_DEL, '-',
0x0, 0x0
mode_parse_apass(&state, flag_p);
break;
- case 'u': /* deal with user passes */
+ case 'U': /* deal with user passes */
if (feature_bool(FEAT_OPLEVELS))
mode_parse_upass(&state, flag_p);
break;
SetUserParting(member);
/* Send notification to channel */
- if (!(flags & CHFL_ZOMBIE))
- sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_PART, chan, NULL,
+ if (!(flags & (CHFL_ZOMBIE | CHFL_DELAYED)))
+ sendcmdto_channel_butserv_butone(jbuf->jb_source, CMD_PART, chan, NULL, 0,
(flags & CHFL_BANNED || !jbuf->jb_comment) ?
":%H" : "%H :%s", chan, jbuf->jb_comment);
else if (MyUser(jbuf->jb_source))
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, 0, "%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, 0, "%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, 0, ":%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, 0,
+ "%H -d", chan);
+ }
+ }
+}