X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fm_join.c;h=e0049667ca8a1eb691f603312e453ad0bb8688b6;hb=1570a04e15bec6b2945e4351b1e05211aecdcacc;hp=ad412956a64285c8e3aeb61e62511994fb336b09;hpb=51bd80f7a2a66fc3d2578ccedd68a94de84892ab;p=ircu2.10.12-pk.git diff --git a/ircd/m_join.c b/ircd/m_join.c index ad41295..e004966 100644 --- a/ircd/m_join.c +++ b/ircd/m_join.c @@ -41,6 +41,7 @@ #include "s_debug.h" #include "s_user.h" #include "send.h" +#include "sys.h" /* #include -- Now using assert in ircd_log.h */ #include @@ -60,10 +61,10 @@ last0(struct Client *cptr, struct Client *sptr, char *chanlist) int join0 = 0; for (p = chanlist; p[0]; p++) /* find last "JOIN 0" */ - if (p[0] == '0' && (p[1] == ',' || p[1] == '\0' || !IsChannelChar(p[1]))) { - if (*++p == ',') + if (p[0] == '0' && (p[1] == ',' || p[1] == '\0')) { + if (p[1] == ',') p++; - chanlist = p; + chanlist = p + 1; join0 = 1; } else { while (p[0] != ',' && p[0] != '\0') /* skip past channel name */ @@ -132,11 +133,10 @@ int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) *keys++ = '\0'; /* Empty keys are the same as no keys. */ - if (!key[0]) + if (key && !key[0]) key = 0; - clean_channelname(name); - if (!IsChannelName(name)) + if (!IsChannelName(name) || !strIsIrcCh(name)) { /* bad channel name */ send_reply(sptr, ERR_NOSUCHCHANNEL, name); @@ -157,7 +157,8 @@ int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) } if (!(chptr = FindChannel(name))) { - if ((name[0] == '&') && !feature_bool(FEAT_LOCAL_CHANNELS)) { + if (((name[0] == '&') && !feature_bool(FEAT_LOCAL_CHANNELS)) + || strlen(name) > IRCD_MIN(CHANNELLEN, feature_int(FEAT_CHANNELLEN))) { send_reply(sptr, ERR_NOSUCHCHANNEL, name); continue; } @@ -166,7 +167,7 @@ int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) continue; /* Try to add the new channel as a recent target for the user. */ - if (check_target_limit(sptr, chptr, chptr->chname, 1)) { + if (check_target_limit(sptr, chptr, chptr->chname, 0)) { chptr->members = 0; destruct_channel(chptr); continue; @@ -181,15 +182,17 @@ int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) int flags = CHFL_DEOPPED; int err = 0; - /* Check target change limits. */ - /* Check Apass/Upass -- since we only ever look at a single * "key" per channel now, this hampers brute force attacks. */ if (key && !strcmp(key, chptr->mode.apass)) flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER; else if (key && !strcmp(key, chptr->mode.upass)) flags = CHFL_CHANOP; - else if (IsInvited(sptr, chptr)) { + else if (chptr->users == 0 && !chptr->mode.apass[0]) { + /* Joining a zombie channel (zannel): give ops and increment TS. */ + flags = CHFL_CHANOP; + chptr->creationtime++; + } else if (IsInvited(sptr, chptr)) { /* Invites bypass these other checks. */ } else if (chptr->mode.mode & MODE_INVITEONLY) err = ERR_INVITEONLYCHAN; @@ -209,13 +212,17 @@ int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) if (IsLocalChannel(chptr->chname) && HasPriv(sptr, PRIV_WALK_LCHAN) && !(flags & CHFL_CHANOP) - && key && !strcmp(key, "OVERRIDE") - && strcmp(chptr->mode.key, "OVERRIDE")) + && key && !strcmp(key, "OVERRIDE")) { switch (err) { case 0: - send_reply(sptr, ERR_DONTCHEAT, chptr->chname); - continue; + if (strcmp(chptr->mode.key, "OVERRIDE") + && strcmp(chptr->mode.apass, "OVERRIDE") + && strcmp(chptr->mode.upass, "OVERRIDE")) { + send_reply(sptr, ERR_DONTCHEAT, chptr->chname); + continue; + } + break; case ERR_INVITEONLYCHAN: err = 'i'; break; case ERR_CHANNELISFULL: err = 'l'; break; case ERR_BANNEDFROMCHAN: err = 'b'; break; @@ -224,18 +231,42 @@ int m_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) default: err = '?'; break; } /* send accountability notice */ - sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H " - "(overriding +%c)", sptr, chptr, err); + if (err) + sendto_opmask_butone(0, SNO_HACK4, "OPER JOIN: %C JOIN %H " + "(overriding +%c)", sptr, chptr, err); err = 0; } /* Is there some reason the user may not join? */ if (err) { - send_reply(sptr, err, chptr->chname); + switch(err) { + case ERR_NEEDREGGEDNICK: + send_reply(sptr, + ERR_NEEDREGGEDNICK, + chptr->chname, + feature_str(FEAT_URLREG)); + break; + default: + send_reply(sptr, err, chptr->chname); + break; + } continue; } joinbuf_join(&join, chptr, flags); + if (flags & CHFL_CHANOP) { + struct ModeBuf mbuf; + /* Always let the server op him: this is needed on a net with older servers + because they 'destruct' channels immediately when they become empty without + sending out a DESTRUCT message. As a result, they would always bounce a mode + (as HACK(2)) when the user ops himself. + (There is also no particularly good reason to have the user op himself.) + */ + modebuf_init(&mbuf, &me, cptr, chptr, MODEBUF_DEST_SERVER); + modebuf_mode_client(&mbuf, MODE_ADD | MODE_CHANOP, sptr, + chptr->mode.apass[0] ? ((flags & CHFL_CHANNEL_MANAGER) ? 0 : 1) : MAXOPLEVEL); + modebuf_flush(&mbuf); + } } del_invite(sptr, chptr); @@ -296,18 +327,7 @@ int ms_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) for (name = ircd_strtok(&p, chanlist, ","); name; name = ircd_strtok(&p, 0, ",")) { - if (name[0] == '0' && name[1] == ':') - { - flags = CHFL_CHANOP | CHFL_CHANNEL_MANAGER; - name += 2; - } - else if (name[0] == '1' && name[1] == ':') - { - flags = CHFL_CHANOP; - name += 2; - } - else - flags = CHFL_DEOPPED; + flags = CHFL_DEOPPED; if (IsLocalChannel(name) || !IsChannelName(name)) { @@ -326,8 +346,7 @@ int ms_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) } flags |= HasFlag(sptr, FLAG_TS8) ? CHFL_SERVOPOK : 0; - /* when the network is 2.10.11+ then remove MAGIC_REMOTE_JOIN_TS */ - chptr->creationtime = creation ? creation : MAGIC_REMOTE_JOIN_TS; + chptr->creationtime = creation; } else { /* We have a valid channel? */ if ((member = find_member_link(chptr, sptr))) @@ -343,9 +362,70 @@ int ms_join(struct Client *cptr, struct Client *sptr, int parc, char *parv[]) else flags |= HasFlag(sptr, FLAG_TS8) ? CHFL_SERVOPOK : 0; /* Always copy the timestamp when it is older, that is the only way to - ensure network-wide synchronization of creation times. */ - if (creation && creation < chptr->creationtime) + ensure network-wide synchronization of creation times. + We now also copy a creation time that only 1 second younger... + this is needed because the timestamp must be incremented + by one when someone joins an existing, but empty, channel. + However, this is only necessary when the channel is still + empty (also here) and when this channel doesn't have +A set. + + To prevent this from allowing net-rides on the channel, we + clear all modes from the channel. + + (Scenario for a net ride: c1 - s1 - s2 - c2, with c1 the only + user in the channel; c1 parts and rejoins, gaining ops. + Before s2 sees c1's part, c2 joins the channel and parts + immediately. s1 sees c1 part, c1 create, c2 join, c2 part; + c2's join resets the timestamp. s2 sees c2 join, c2 part, c1 + part, c1 create; but since s2 sees the channel as a zannel or + non-existent, it does not bounce the create with the newer + timestamp.) + */ + if (creation && (creation < chptr->creationtime || + (!chptr->mode.apass[0] && chptr->users == 0))) { + struct Membership *member; + struct ModeBuf mbuf; + chptr->creationtime = creation; + /* Wipe out the current modes on the channel. */ + modebuf_init(&mbuf, sptr, cptr, chptr, MODEBUF_DEST_CHANNEL | MODEBUF_DEST_HACK3); + + modebuf_mode(&mbuf, MODE_DEL | chptr->mode.mode); + chptr->mode.mode &= MODE_BURSTADDED | MODE_WASDELJOINS; + + if (chptr->mode.limit) { + modebuf_mode_uint(&mbuf, MODE_DEL | MODE_LIMIT, chptr->mode.limit); + chptr->mode.limit = 0; + } + + if (chptr->mode.key[0]) { + modebuf_mode_string(&mbuf, MODE_DEL | MODE_KEY, chptr->mode.key, 0); + chptr->mode.key[0] = '\0'; + } + + if (chptr->mode.upass[0]) { + modebuf_mode_string(&mbuf, MODE_DEL | MODE_UPASS, chptr->mode.upass, 0); + chptr->mode.upass[0] = '\0'; + } + + if (chptr->mode.apass[0]) { + modebuf_mode_string(&mbuf, MODE_DEL | MODE_APASS, chptr->mode.apass, 0); + chptr->mode.apass[0] = '\0'; + } + + for (member = chptr->members; member; member = member->next_member) + { + if (IsChanOp(member)) { + modebuf_mode_client(&mbuf, MODE_DEL | MODE_CHANOP, member->user, OpLevel(member)); + member->status &= ~CHFL_CHANOP; + } + if (HasVoice(member)) { + modebuf_mode_client(&mbuf, MODE_DEL | MODE_VOICE, member->user, OpLevel(member)); + member->status &= ~CHFL_VOICE; + } + } + modebuf_flush(&mbuf); + } } joinbuf_join(&join, chptr, flags);