[IRC-Dev CVS] [CVS] Module ircd-ircdev: Change committed
Brought to you by:
zolty
From: Toni G. <zo...@us...> - 2007-07-21 23:51:52
|
Committer : zolty CVSROOT : /cvsroot/irc-dev Module : ircd-ircdev Commit time: 2007-07-21 23:51:49 UTC Modified files: ChangeLog ChangeLog.es include/channel.h include/client.h include/ircd_features.h include/msg.h include/patchlevel.h ircd/channel.c ircd/ircd_features.c ircd/m_burst.c ircd/m_join.c ircd/m_mode.c ircd/m_part.c Log message: Author: zoltan <zo...@ir...> Log message: 2007-07-22 Toni Garc�a <zo...@ir...> 1.0.beta7 * Chequeo de Spambot * Fix de DelayedJoin ---------------------- diff included ---------------------- Index: ircd-ircdev/ChangeLog diff -u ircd-ircdev/ChangeLog:1.55 ircd-ircdev/ChangeLog:1.56 --- ircd-ircdev/ChangeLog:1.55 Thu Apr 26 14:17:11 2007 +++ ircd-ircdev/ChangeLog Sat Jul 21 16:51:39 2007 @@ -1,11 +1,15 @@ # # ChangeLog for ircd-ircdev # -# $Id: ChangeLog,v 1.55 2007/04/26 21:17:11 zolty Exp $ +# $Id: ChangeLog,v 1.56 2007/07/21 23:51:39 zolty Exp $ # # Insert new changes at beginning of the change list. # -2007-04-26 Toni Garc�a <zo...@ir...> 1.0.beta6 +2007-07-22 Toni Garc�a <zo...@ir...> 1.0.beta7 + * Spambot checking + * DelayedJoin fix + +2007-04-26 Toni Garc�a <zo...@ir...> 1.0.beta6 * Aliases for /STATS * PING-PONG with high priority * Signal SIGCHLD support Index: ircd-ircdev/ChangeLog.es diff -u ircd-ircdev/ChangeLog.es:1.55 ircd-ircdev/ChangeLog.es:1.56 --- ircd-ircdev/ChangeLog.es:1.55 Thu Apr 26 14:17:11 2007 +++ ircd-ircdev/ChangeLog.es Sat Jul 21 16:51:39 2007 @@ -1,10 +1,14 @@ # # Log de Cambios para ircd-ircdev # -# $Id: ChangeLog.es,v 1.55 2007/04/26 21:17:11 zolty Exp $ +# $Id: ChangeLog.es,v 1.56 2007/07/21 23:51:39 zolty Exp $ # # Insertar los nuevos cambios al principio de esta lista de cambios. # +2007-07-22 Toni Garc�a <zo...@ir...> 1.0.beta7 + * Chequeo de Spambot + * Fix de DelayedJoin + 2007-04-26 Toni Garc�a <zo...@ir...> 1.0.beta6 * Aliases para /STATS * PING-PONG con alta prioridad Index: ircd-ircdev/include/channel.h diff -u ircd-ircdev/include/channel.h:1.17 ircd-ircdev/include/channel.h:1.18 --- ircd-ircdev/include/channel.h:1.17 Sat Apr 21 09:20:17 2007 +++ ircd-ircdev/include/channel.h Sat Jul 21 16:51:39 2007 @@ -22,7 +22,7 @@ */ /** @file * @brief Channel management and maintenance. - * @version $Id: channel.h,v 1.17 2007/04/21 16:20:17 zolty Exp $ + * @version $Id: channel.h,v 1.18 2007/07/21 23:51:39 zolty Exp $ */ #ifndef INCLUDED_channel_h #define INCLUDED_channel_h @@ -464,9 +464,7 @@ extern void add_invite(struct Client *cptr, struct Channel *chptr, struct Client *inviter); extern void del_invite(struct Client *cptr, struct Channel *chptr); extern void list_set_default(void); /* this belongs elsewhere! */ - -extern void RevealDelayedJoin(struct Membership *member); -extern void CheckDelayedJoins(struct Channel *chan); +extern void check_spambot_warning(struct Client *cptr); extern void modebuf_init(struct ModeBuf *mbuf, struct Client *source, struct Client *connect, struct Channel *chan, Index: ircd-ircdev/include/client.h diff -u ircd-ircdev/include/client.h:1.20 ircd-ircdev/include/client.h:1.21 --- ircd-ircdev/include/client.h:1.20 Thu Apr 26 12:17:31 2007 +++ ircd-ircdev/include/client.h Sat Jul 21 16:51:39 2007 @@ -21,7 +21,7 @@ */ /** @file * @brief Structures and functions for handling local clients. - * @version $Id: client.h,v 1.20 2007/04/26 19:17:31 zolty Exp $ + * @version $Id: client.h,v 1.21 2007/07/21 23:51:39 zolty Exp $ */ #ifndef INCLUDED_client_h #define INCLUDED_client_h @@ -190,6 +190,10 @@ time_t con_nexttarget;/**< Next time a target change is allowed */ time_t con_lasttime; /**< Last time data read from socket */ time_t con_since; /**< Last time we accepted a command */ + time_t con_last_join; /**< Last time this client joined a channel */ + time_t con_last_part; /**< Last time this client left a channel */ + int con_join_part_count; /**< Count of fast join/parts */ + int con_warn_countdown; /**< Counter for spambot warnings */ struct MsgQ con_sendQ; /**< Outgoing message queue */ struct DBuf con_recvQ; /**< Incoming data yet to be parsed */ unsigned int con_sendM; /**< Stats: protocol messages sent */ @@ -312,6 +316,15 @@ #define cli_username(cli) ((cli)->cli_username) /** Get client realname (information field). */ #define cli_info(cli) ((cli)->cli_info) +/** Get client's last channel join time. */ +#define cli_last_join(cli) (cli_connect(cli)->con_last_join) +/** Get client's last channel part time. */ +#define cli_last_part(cli) (cli_connect(cli)->con_last_part) +/** Get client's fast join/part count. */ +#define cli_join_part_count(cli) (cli_connect(cli)->con_join_part_count) +/** Get client's warning countdown value. */ +#define cli_warn_countdown(cli) (cli_connect(cli)->con_warn_countdown) + /** Get number of incoming bytes queued for client. */ #define cli_count(cli) con_count(cli_connect(cli)) Index: ircd-ircdev/include/ircd_features.h diff -u ircd-ircdev/include/ircd_features.h:1.19 ircd-ircdev/include/ircd_features.h:1.20 --- ircd-ircdev/include/ircd_features.h:1.19 Thu Apr 26 14:17:11 2007 +++ ircd-ircdev/include/ircd_features.h Sat Jul 21 16:51:39 2007 @@ -21,7 +21,7 @@ */ /** @file * @brief Public interfaces and declarations for dealing with configurable features. - * @version $Id: ircd_features.h,v 1.19 2007/04/26 21:17:11 zolty Exp $ + * @version $Id: ircd_features.h,v 1.20 2007/07/21 23:51:39 zolty Exp $ */ #ifndef INCLUDED_features_h #define INCLUDED_features_h @@ -60,6 +60,7 @@ FEAT_HIDDEN_IP, FEAT_CONNEXIT_NOTICES, FEAT_OPLEVELS, + FEAT_ZANNELS, FEAT_LOCAL_CHANNELS, FEAT_TOPIC_BURST, FEAT_USER_GLIST, @@ -193,6 +194,10 @@ FEAT_NETWORK, FEAT_URL_CLIENTS, FEAT_URLREG, + FEAT_SPAM_OPER_COUNTDOWN, + FEAT_SPAM_EXPIRE_TIME, + FEAT_SPAM_JOINED_TIME, + FEAT_SPAM_FJP_COUNT, FEAT_LAST_F }; Index: ircd-ircdev/include/msg.h diff -u ircd-ircdev/include/msg.h:1.15 ircd-ircdev/include/msg.h:1.16 --- ircd-ircdev/include/msg.h:1.15 Thu Apr 19 15:53:46 2007 +++ ircd-ircdev/include/msg.h Sat Jul 21 16:51:39 2007 @@ -21,7 +21,7 @@ */ /** @file * @brief Command and token declarations and structures. - * @version $Id: msg.h,v 1.15 2007/04/19 22:53:46 zolty Exp $ + * @version $Id: msg.h,v 1.16 2007/07/21 23:51:39 zolty Exp $ */ #ifndef INCLUDED_msg_h #define INCLUDED_msg_h @@ -201,11 +201,11 @@ #define MSG_WALLCHOPS "WALLCHOPS" /* WC */ #define TOK_WALLCHOPS "WC" -#define CMD_WALLCHOPS MSG_WALLCHOPS, TOK_WALLCHOPS +#define CMD_WALLCHOPS MSG_NOTICE, TOK_WALLCHOPS #define MSG_WALLVOICES "WALLVOICES" /* WV */ #define TOK_WALLVOICES "WV" -#define CMD_WALLVOICES MSG_WALLVOICES, TOK_WALLVOICES +#define CMD_WALLVOICES MSG_NOTICE, TOK_WALLVOICES #define MSG_CPRIVMSG "CPRIVMSG" /* CPRI */ #define TOK_CPRIVMSG "CP" Index: ircd-ircdev/include/patchlevel.h diff -u ircd-ircdev/include/patchlevel.h:1.54 ircd-ircdev/include/patchlevel.h:1.55 --- ircd-ircdev/include/patchlevel.h:1.54 Thu Apr 26 14:17:11 2007 +++ ircd-ircdev/include/patchlevel.h Sat Jul 21 16:51:39 2007 @@ -17,10 +17,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * - * $Id: patchlevel.h,v 1.54 2007/04/26 21:17:11 zolty Exp $ + * $Id: patchlevel.h,v 1.55 2007/07/21 23:51:39 zolty Exp $ * */ -#define PATCHLEVEL "6" +#define PATCHLEVEL "7" #define RELEASE "1.0.beta" Index: ircd-ircdev/ircd/channel.c diff -u ircd-ircdev/ircd/channel.c:1.31 ircd-ircdev/ircd/channel.c:1.32 --- ircd-ircdev/ircd/channel.c:1.31 Sat Apr 21 14:17:22 2007 +++ ircd-ircdev/ircd/channel.c Sat Jul 21 16:51:39 2007 @@ -21,7 +21,7 @@ */ /** @file * @brief Channel management and maintanance - * @version $Id: channel.c,v 1.31 2007/04/21 21:17:22 zolty Exp $ + * @version $Id: channel.c,v 1.32 2007/07/21 23:51:39 zolty Exp $ */ #include "config.h" @@ -74,21 +74,6 @@ /** Number of ban structures in use. */ static size_t bans_inuse; -#if !defined(NDEBUG) -/** return the length (>=0) of a chain of links. - * @param lp pointer to the start of the linked list - * @return the number of items in the list - */ -static int list_length(struct SLink *lp) -{ - int count = 0; - - for (; lp; lp = lp->next) - ++count; - return count; -} -#endif - /** Set the mask for a ban, checking for IP masks. * @param[in,out] ban Ban structure to modify. * @param[in] banstr Mask to ban. @@ -108,6 +93,41 @@ } } +/** Check a channel for join-delayed members. + * @param[in] chan Channel to search. + * @return Non-zero if any members are join-delayed; false if none are. + */ +static int +find_delayed_joins(const struct Channel *chan) +{ + const struct Membership *memb; + for (memb = chan->members; memb; memb = memb->next_member) + if (IsDelayedJoin(memb)) + return 1; + return 0; +} + +/* CheckDelayedJoins: checks and clear +d if necessary */ + +static void CheckDelayedJoins(struct Channel *chan) +{ + if ((chan->mode.mode & MODE_WASDELJOINS) && !find_delayed_joins(chan)) { + chan->mode.mode &= ~MODE_WASDELJOINS; + sendcmdto_channel(&his, CMD_MODE, chan, NULL, SKIP_SERVERS, + "%H -d", chan); + } +} + +/* RevealDelayedJoin: sends a join for a hidden user */ +static void RevealDelayedJoin(struct Membership *member) +{ + ClearDelayedJoin(member); + sendcmdto_channel(member->user, CMD_JOIN, member->channel, + member->user, SKIP_SERVERS, + ":%H", member->channel); + CheckDelayedJoins(member->channel); +} + /** Allocate a new Ban structure. * @param[in] banstr Ban mask to use. * @return Newly allocated ban. @@ -662,46 +682,6 @@ return 0; } -/** Check if a user is a Zombie on a specific channel. - * - * @param cptr The client to check. - * @param chptr The channel to check. - * - * @returns True if the client (cptr) is a zombie on the channel (chptr), - * False otherwise. - * - * @see \ref zombie - */ -int is_zombie(struct Client *cptr, struct Channel *chptr) -{ - struct Membership* member; - - assert(0 != chptr); - - if ((member = find_member_link(chptr, cptr))) - return IsZombie(member); - return 0; -} - -/** Returns if a user has voice on a channel. - * - * @param cptr The client - * @param chptr The channel - * - * @returns True if the client (cptr) is voiced on (chptr) and is not a zombie. - * @see \ref zombie - */ -int has_voice(struct Client* cptr, struct Channel* chptr) -{ - struct Membership* member; - - assert(0 != chptr); - if ((member = find_member_link(chptr, cptr))) - return (!IsZombie(member) && HasVoice(member)); - - return 0; -} - /** Can this member send to a channel * * A user can speak on a channel iff: @@ -726,12 +706,24 @@ { assert(0 != member); + /* Do not check for users on other servers: This should be a + * temporary desynch, or maybe they are on an older server, but + * we do not want to send ERR_CANNOTSENDTOCHAN more than once. + */ + if (!MyUser(member->user)) + { + if (IsDelayedJoin(member) && reveal) + RevealDelayedJoin(member); + return 1; + } + #if defined(UNDERNET) /* Discourage using the Apass to get op. They should use the upass. */ if (IsChannelManager(member) && member->channel->mode.apass[0]) return 0; #endif + /* If you have voice or ops, you can speak. */ if (IsVoicedOrOpped(member)) return 1; @@ -747,12 +739,9 @@ if (member->channel->mode.mode & MODE_REGONLY && !IsAccount(member->user)) return 0; #endif - /* - * If you're banned then you can't speak either. - * but because of the amount of CPU time that is_banned chews - * we only check it for our clients. - */ - if (MyUser(member->user) && is_banned(member)) + + /* If you're banned then you can't speak either. */ + if (is_banned(member)) return 0; if (IsDelayedJoin(member) && reveal) @@ -820,9 +809,13 @@ struct Membership* member; for (member = (cli_user(cptr))->channel; member; member = member->next_channel) { - if (!IsVoicedOrOpped(member) && - (is_banned(member) || - (member->channel->mode.mode & MODE_MODERATED))) + if (IsVoicedOrOpped(member)) + continue; + if ((member->channel->mode.mode & MODE_MODERATED) +#if defined(UNDERNET) + || (member->channel->mode.mode & MODE_REGONLY && !IsAccount(cptr)) +#endif + || is_banned(member)) return member->channel->chname; } } @@ -979,7 +972,7 @@ int opped_members_index = 0; struct Membership** opped_members = NULL; int last_oplevel = 0; - int feat_oplevels = (chptr->mode.apass[0]) != '\0'; + int send_oplevels = 0; #endif assert(0 != cptr); @@ -1045,6 +1038,9 @@ ++number_of_ops; else opped_members[opped_members_index++] = member; + /* We also send oplevels if anyone is below the weakest level. */ + if (OpLevel(member) < MAXOPLEVEL) + send_oplevels = 1; } /* Only handle the members with the flags that we are interested in. */ if ((member->status & CHFL_VOICED_OR_OPPED) == current_flags[flag_cnt]) @@ -1085,7 +1081,7 @@ if (IsChanOp(member)) /* flag_cnt == 2 or 3 */ { /* append the absolute value of the oplevel */ - if (feat_oplevels) + if (send_oplevels) loc += ircd_snprintf(0, tbuf + loc, sizeof(tbuf) - loc, "%u", last_oplevel = member->oplevel); else tbuf[loc++] = 'o'; @@ -1094,7 +1090,7 @@ msgq_append(&me, mb, tbuf); new_mode = 0; } - else if (feat_oplevels && flag_cnt > 1 && last_oplevel != member->oplevel) + else if (send_oplevels && flag_cnt > 1 && last_oplevel != member->oplevel) { /* * This can't be the first member of a (continued) BURST @@ -1696,7 +1692,6 @@ #endif MODE_REGONLY, 'R', MODE_DELJOINS, 'D', - MODE_WASDELJOINS, 'd', /* MODE_KEY, 'k', */ /* MODE_BAN, 'b', */ MODE_LIMIT, 'l', @@ -1706,15 +1701,19 @@ #endif 0x0, 0x0 }; - int i; + static int local_flags[] = { + MODE_WASDELJOINS, 'd', + 0x0, 0x0 + }; + unsigned int i; int *flag_p; struct Client *app_source; /* where the MODE appears to come from */ - char addbuf[20]; /* accumulates +psmtin, etc. */ - int addbuf_i = 0; - char rembuf[20]; /* accumulates -psmtin, etc. */ - int rembuf_i = 0; + char addbuf[20], addbuf_local[20]; /* accumulates +psmtin, etc. */ + int addbuf_i = 0, addbuf_local_i = 0; + char rembuf[20], rembuf_local[20]; /* accumulates -psmtin, etc. */ + int rembuf_i = 0, rembuf_local_i = 0; char *bufptr; /* we make use of indirection to simplify the code */ int *bufptr_i; @@ -1740,7 +1739,10 @@ /* 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) || IsMe(mbuf->mb_source)) + if (feature_bool(FEAT_HIS_MODEWHO) && + (mbuf->mb_dest & MODEBUF_DEST_OPMODE || + IsServer(mbuf->mb_source) || + IsMe(mbuf->mb_source))) app_source = &his; else app_source = mbuf->mb_source; @@ -1760,6 +1762,14 @@ rembuf[rembuf_i++] = flag_p[1]; } + /* Some flags may be for local display only. */ + for (flag_p = local_flags; flag_p[0]; flag_p += 2) { + if (*flag_p & mbuf->mb_add) + addbuf_local[addbuf_local_i++] = flag_p[1]; + else if (*flag_p & mbuf->mb_rem) + rembuf_local[rembuf_local_i++] = flag_p[1]; + } + /* Now go through the modes with arguments... */ for (i = 0; i < mbuf->mb_count; i++) { if (MB_TYPE(mbuf, i) & MODE_ADD) { /* adding or removing? */ @@ -1860,6 +1870,8 @@ /* terminate the mode strings */ addbuf[addbuf_i] = '\0'; rembuf[rembuf_i] = '\0'; + addbuf_local[addbuf_local_i] = '\0'; + rembuf_local[rembuf_local_i] = '\0'; /* If we're building a user visible MODE or HACK... */ if (mbuf->mb_dest & (MODEBUF_DEST_CHANNEL | MODEBUF_DEST_HACK2 | @@ -2279,19 +2291,22 @@ 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; - } - } + /* Check if MODE_WASDELJOINS should be set: */ + /* Must be set if going -D and some clients are hidden */ + if ((mbuf->mb_rem & MODE_DELJOINS) + && !(mbuf->mb_channel->mode.mode & (MODE_DELJOINS | MODE_WASDELJOINS)) + && find_delayed_joins(mbuf->mb_channel)) { + mbuf->mb_channel->mode.mode |= MODE_WASDELJOINS; + mbuf->mb_add |= MODE_WASDELJOINS; + mbuf->mb_rem &= ~MODE_WASDELJOINS; + } + /* Must be cleared if +D is set */ + if ((mbuf->mb_add & MODE_DELJOINS) + && ((mbuf->mb_channel->mode.mode & (MODE_WASDELJOINS | MODE_WASDELJOINS)) + == (MODE_WASDELJOINS | MODE_WASDELJOINS))) { + mbuf->mb_channel->mode.mode &= ~MODE_WASDELJOINS; + mbuf->mb_add &= ~MODE_WASDELJOINS; + mbuf->mb_rem |= MODE_WASDELJOINS; } return modebuf_flush_int(mbuf, 1); @@ -3167,8 +3182,10 @@ char *t_str; struct Client *acptr; #if defined(UNDERNET) + char *colon; struct Membership *member; int oplevel = MAXOPLEVEL + 1; + int req_oplevel; #endif int i; @@ -3188,9 +3205,29 @@ return; } - if (MyUser(state->sptr)) /* find client we're manipulating */ + if (MyUser(state->sptr)) { +#if defined(UNDERNET) + colon = strchr(t_str, ':'); + if (colon != NULL) { + *colon++ = '\0'; + req_oplevel = atoi(colon); + if (!(state->flags & MODE_PARSE_FORCE) + && state->member + && (req_oplevel < OpLevel(state->member) + || (req_oplevel == OpLevel(state->member) + && OpLevel(state->member) < MAXOPLEVEL) + || req_oplevel > MAXOPLEVEL)) + send_reply(state->sptr, ERR_NOTLOWEROPLEVEL, + t_str, state->chptr->chname, + OpLevel(state->member), req_oplevel, "op", + OpLevel(state->member) == req_oplevel ? "the same" : "a higher"); + else if (req_oplevel <= MAXOPLEVEL) + oplevel = req_oplevel; + } +#endif + /* find client we're manipulating */ acptr = find_chasing(state->sptr, t_str, NULL); - else { + } else { #if defined(UNDERNET) if (t_str[5] == ':') { t_str[5] = '\0'; @@ -3201,7 +3238,7 @@ * posible sitio para el ChanDb */ acptr = findNUser(t_str); -} + } if (!acptr) return; /* find_chasing() already reported an error to the user */ @@ -3290,11 +3327,14 @@ } #if defined(UNDERNET) - /* 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->chptr->mode.apass[0] + /* Forbid deopping other members with an oplevel less than + * one's own level, and other members with an oplevel the same + * as one's own unless both are at MAXOPLEVEL. */ + if (state->sptr != state->cli_change[i].client && state->member - && OpLevel(member) <= OpLevel(state->member)) { + && ((OpLevel(member) < OpLevel(state->member)) + || (OpLevel(member) == OpLevel(state->member) + && OpLevel(member) < MAXOPLEVEL))) { int equal = (OpLevel(member) == OpLevel(state->member)); send_reply(state->sptr, ERR_NOTLOWEROPLEVEL, cli_name(state->cli_change[i].client), @@ -3331,7 +3371,7 @@ /* actually effect the change */ if (state->flags & MODE_PARSE_SET) { if (state->cli_change[i].flag & MODE_ADD) { - if (IsDelayedJoin(member)) + if (IsDelayedJoin(member) && !IsZombie(member)) RevealDelayedJoin(member); member->status |= (state->cli_change[i].flag & #if defined(DDB) || defined(SERVICES) @@ -3385,10 +3425,6 @@ 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]; @@ -3551,7 +3587,7 @@ state.parc--; /* is it a TS? */ - if (IsServer(state.sptr) && !state.parc && IsDigit(*modestr)) { + if (IsServer(state.cptr) && !state.parc && IsDigit(*modestr)) { time_t recv_ts; if (!(state.flags & MODE_PARSE_SET)) /* don't set earlier TS if */ @@ -3561,6 +3597,35 @@ if (recv_ts && recv_ts < state.chptr->creationtime) state.chptr->creationtime = recv_ts; /* respect earlier TS */ + else if (recv_ts > state.chptr->creationtime) { + struct Client *sserv; + + /* Check whether the originating server has fully processed + * the burst to it. */ + sserv = state.cptr; + if (!IsServer(sserv)) + sserv = cli_user(sserv)->server; + if (IsBurstOrBurstAck(sserv)) { + /* This is a legal but unusual case; the source server + * probably just has not processed the BURST for this + * channel. It SHOULD wipe out all its modes soon, so + * silently ignore the mode change rather than send a + * bounce that could desync modes from our side (that + * have already been sent). + */ + state.mbuf->mb_add = 0; + state.mbuf->mb_rem = 0; + state.mbuf->mb_count = 0; + return state.args_used; + } else { + /* Server is desynced; bounce the mode and deop the source + * to fix it. */ + state.mbuf->mb_dest &= ~MODEBUF_DEST_CHANNEL; + state.mbuf->mb_dest |= MODEBUF_DEST_BOUNCE | MODEBUF_DEST_HACK2; + if (!IsServer(state.cptr)) + state.mbuf->mb_dest |= MODEBUF_DEST_DEOP; + } + } break; /* break out of while loop */ } else if (state.flags & MODE_PARSE_STRICT || @@ -3831,32 +3896,59 @@ return 0; } -/* RevealDelayedJoin: sends a join for a hidden user */ - -void RevealDelayedJoin(struct Membership *member) -{ - ClearDelayedJoin(member); - sendcmdto_channel(member->user, CMD_JOIN, member->channel, member->user, SKIP_SERVERS, ":%H", - member->channel); - CheckDelayedJoins(member->channel); -} - -/* CheckDelayedJoins: checks and clear +d if necessary */ - -void CheckDelayedJoins(struct Channel *chan) +/** Check whether \a sptr is acting like a spambot when it leaves a + * channel. + * Side effects: Updates \a sptr's join_part_count, last_part and + * warn_countdown fields; may warn operators. + * Based on code by Dianora. + * + * @param[in] sptr Client to check + * @param[in] name Channel being joined (or NULL if a part). + */ +void +check_spambot_warning(struct Client *sptr) { - struct Membership *memb2; + int decrement_count; + int spam_expire_time; - if (chan->mode.mode & MODE_WASDELJOINS) { - for (memb2=chan->members;memb2;memb2=memb2->next_member) - if (IsDelayedJoin(memb2)) - break; + spam_expire_time = feature_int(FEAT_SPAM_EXPIRE_TIME); + if (!spam_expire_time) + { + /* Admin apparently does not want this feature (we do not want to + * divide by zero below). */ + } + else if (!MyUser(sptr)) + { + /* This client's behavior is someone else's problem. */ + } + else if (cli_join_part_count(sptr) >= feature_int(FEAT_SPAM_FJP_COUNT)) + { + if (cli_warn_countdown(sptr) > 0) + cli_warn_countdown(sptr)--; - if (!memb2) { - /* clear +d */ - chan->mode.mode &= ~MODE_WASDELJOINS; - sendcmdto_channel(&his, CMD_MODE, chan, NULL, SKIP_SERVERS, - "%H -d", chan); + if (cli_warn_countdown(sptr) == 0) + { + /* Its already known as a possible spambot */ + sendto_opmask(0, SNO_OLDSNO, + "User %s (%s@%s) is a possible spambot", + cli_name(sptr), cli_username(sptr), + cli_sockhost(sptr)); + cli_warn_countdown(sptr) = feature_int(FEAT_SPAM_OPER_COUNTDOWN); } } + else if ((decrement_count = (CurrentTime - cli_last_part(sptr)) + / spam_expire_time) > 0) + { + if (decrement_count > cli_join_part_count(sptr)) + cli_join_part_count(sptr) = 0; + else + cli_join_part_count(sptr) -= decrement_count; + } + else if ((CurrentTime - (cli_last_join(sptr))) < feature_int(FEAT_SPAM_JOINED_TIME)) + { + /* oh, its a possible spambot */ + cli_join_part_count(sptr)++; + } + + cli_last_part(sptr) = CurrentTime; } Index: ircd-ircdev/ircd/ircd_features.c diff -u ircd-ircdev/ircd/ircd_features.c:1.19 ircd-ircdev/ircd/ircd_features.c:1.20 --- ircd-ircdev/ircd/ircd_features.c:1.19 Thu Apr 26 14:17:11 2007 +++ ircd-ircdev/ircd/ircd_features.c Sat Jul 21 16:51:39 2007 @@ -21,7 +21,7 @@ */ /** @file * @brief Implementation of configurable feature support. - * @version $Id: ircd_features.c,v 1.19 2007/04/26 21:17:11 zolty Exp $ + * @version $Id: ircd_features.c,v 1.20 2007/07/21 23:51:39 zolty Exp $ */ #include "config.h" @@ -384,6 +384,7 @@ F_S(HIDDEN_IP, 0, "127.0.0.1", 0), F_B(CONNEXIT_NOTICES, 0, 0, 0), F_B(OPLEVELS, 0, 1, set_isupport_chanmodes), + F_B(ZANNELS, 0, 1, 0), F_B(LOCAL_CHANNELS, 0, 1, set_isupport_chantypes), F_B(TOPIC_BURST, 0, 0, 0), F_B(USER_GLIST, 0, 1, 0), @@ -517,6 +518,10 @@ F_S(NETWORK, 0, "IRC-Dev", set_isupport_network), F_S(URL_CLIENTS, 0, "ftp://ftp.irc.org/pub/irc/clients", 0), F_S(URLREG, 0, "http://cservice.undernet.org/live/", 0), + F_I(SPAM_OPER_COUNTDOWN, 0, 5, 0), + F_I(SPAM_EXPIRE_TIME, 0, 120, 0), + F_I(SPAM_JOINED_TIME, 0, 60, 0), + F_I(SPAM_FJP_COUNT, 0, 5, 0), #undef F_S #undef F_B Index: ircd-ircdev/ircd/m_burst.c diff -u ircd-ircdev/ircd/m_burst.c:1.19 ircd-ircdev/ircd/m_burst.c:1.20 --- ircd-ircdev/ircd/m_burst.c:1.19 Sat Apr 21 14:17:23 2007 +++ ircd-ircdev/ircd/m_burst.c Sat Jul 21 16:51:39 2007 @@ -22,7 +22,7 @@ */ /** @file * @brief Handlers for BURST command. - * @version $Id: m_burst.c,v 1.19 2007/04/21 21:17:23 zolty Exp $ + * @version $Id: m_burst.c,v 1.20 2007/07/21 23:51:39 zolty Exp $ */ #include "config.h" @@ -197,6 +197,59 @@ timestamp = atoi(parv[2]); +#if defined(UNDERNET) + if (chptr->creationtime) /* 0 for new (empty) channels, + i.e. when this server just restarted. */ + { + if (parc == 3) /* Zannel BURST? */ + { + /* An empty channel without +A set, will cause a BURST message + with exactly 3 parameters (because all modes have been reset). + If the timestamp on such channels is only a few seconds older + from our own, then we ignore this burst: we do not deop our + own side. + Likewise, we expect the other (empty) side to copy our timestamp + from our own BURST message, even though it is slightly larger. + + The reason for this is to allow people to join an empty + non-A channel (a zannel) during a net.split, and not be + deopped when the net reconnects (with another zannel). When + someone joins a split zannel, their side increments the TS by one. + If they cycle a few times then we still don't have a reason to + deop them. Theoretically I see no reason not to accept ANY timestamp, + but to be sure, we only accept timestamps that are just a few + seconds off (one second for each time they cycled the channel). */ + + /* Don't even deop users who cycled four times during the net.break. */ + if (timestamp < chptr->creationtime && + chptr->creationtime <= timestamp + 4 && + chptr->users != 0) /* Only do this when WE have users, so that + if we do this the BURST that we sent has + parc > 3 and the other side will use the + test below: */ + timestamp = chptr->creationtime; /* Do not deop our side. */ + } + else if (chptr->creationtime < timestamp && + timestamp <= chptr->creationtime + 4 && + chptr->users == 0) + { + /* If one side of the net.junction does the above + timestamp = chptr->creationtime, then the other + side must do this: */ + chptr->creationtime = timestamp; /* Use the same TS on both sides. */ + } + /* In more complex cases, we might still end up with a + creationtime desync of a few seconds, but that should + be synced automatically rather quickly (every JOIN + caries a timestamp and will sync it; modes by users do + not carry timestamps and are accepted regardless). + Only when nobody joins the channel on the side with + the oldest timestamp before a new net.break occurs + precisely inbetween the desync, an unexpected bounce + might happen on reconnect. */ + } +#endif + if (!chptr->creationtime || chptr->creationtime > timestamp) { /* * Kick local members if channel is +i or +k and our TS was larger Index: ircd-ircdev/ircd/m_join.c diff -u ircd-ircdev/ircd/m_join.c:1.19 ircd-ircdev/ircd/m_join.c:1.20 --- ircd-ircdev/ircd/m_join.c:1.19 Sat Apr 21 14:17:23 2007 +++ ircd-ircdev/ircd/m_join.c Sat Jul 21 16:51:39 2007 @@ -21,7 +21,7 @@ */ /** @file * @brief Handlers for JOIN command. - * @version $Id: m_join.c,v 1.19 2007/04/21 21:17:23 zolty Exp $ + * @version $Id: m_join.c,v 1.20 2007/07/21 23:51:39 zolty Exp $ */ #include "config.h" @@ -418,10 +418,81 @@ remove_user_from_channel(sptr, chptr); chptr = FindChannel(name); } +#if defined(UNDERNET) + /* Always copy the timestamp when it is older, that is the only way to + 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); + 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); + } +#else + /* TODO-ZoltPoner el sistema de arriba para keys? */ + /* 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) chptr->creationtime = creation; +#endif } joinbuf_join(&join, chptr, 0); Index: ircd-ircdev/ircd/m_mode.c diff -u ircd-ircdev/ircd/m_mode.c:1.13 ircd-ircdev/ircd/m_mode.c:1.14 --- ircd-ircdev/ircd/m_mode.c:1.13 Sun Apr 22 06:56:21 2007 +++ ircd-ircdev/ircd/m_mode.c Sat Jul 21 16:51:39 2007 @@ -21,7 +21,7 @@ */ /** @file * @brief Handlers for MODE command. - * @version $Id: m_mode.c,v 1.13 2007/04/22 13:56:21 zolty Exp $ + * @version $Id: m_mode.c,v 1.14 2007/07/21 23:51:39 zolty Exp $ */ #include "config.h" @@ -30,6 +30,7 @@ #include "client.h" #include "hash.h" #include "ircd.h" +#include "ircd_features.h" #include "ircd_log.h" #include "ircd_reply.h" #include "ircd_string.h" @@ -148,16 +149,21 @@ (MODEBUF_DEST_CHANNEL | /* Send mode to clients */ MODEBUF_DEST_SERVER | /* Send mode to servers */ MODEBUF_DEST_HACK4)); /* Send a HACK(4) message */ - else #if defined(UNDERNET) + else if (!feature_bool(FEAT_OPLEVELS)) + modebuf_init(&mbuf, sptr, cptr, chptr, + (MODEBUF_DEST_CHANNEL | /* Send mode to clients */ + MODEBUF_DEST_SERVER | /* Send mode to servers */ + MODEBUF_DEST_HACK3)); /* Send a HACK(3) message */ /* Servers need to be able to op people who join using the Apass - * or upass, therefore we accept modes for channels with an Apass - * without generating a HACK3. */ + * or upass, as well as people joining a zannel, therefore we do + * not generate HACK3 when oplevels are on. */ + else modebuf_init(&mbuf, sptr, cptr, chptr, (MODEBUF_DEST_CHANNEL | /* Send mode to clients */ - MODEBUF_DEST_SERVER | /* Send mode to servers */ - (*chptr->mode.apass ? 0 : MODEBUF_DEST_HACK3))); + MODEBUF_DEST_SERVER)); /* Send mode to servers */ #else + else modebuf_init(&mbuf, sptr, cptr, chptr, (MODEBUF_DEST_CHANNEL | /* Send mode to clients */ MODEBUF_DEST_SERVER | /* Send mode to servers */ Index: ircd-ircdev/ircd/m_part.c diff -u ircd-ircdev/ircd/m_part.c:1.9 ircd-ircdev/ircd/m_part.c:1.10 --- ircd-ircdev/ircd/m_part.c:1.9 Sat Apr 21 09:20:18 2007 +++ ircd-ircdev/ircd/m_part.c Sat Jul 21 16:51:39 2007 @@ -21,7 +21,7 @@ */ /** @file * @brief Handlers for PART command. - * @version $Id: m_part.c,v 1.9 2007/04/21 16:20:18 zolty Exp $ + * @version $Id: m_part.c,v 1.10 2007/07/21 23:51:39 zolty Exp $ */ #include "config.h" @@ -98,6 +98,7 @@ flags |= CHFL_DELAYED; joinbuf_join(&parts, chptr, flags); /* part client from channel */ + check_spambot_warning(sptr); } return joinbuf_flush(&parts); /* flush channel parts */ ----------------------- End of diff ----------------------- |