From: Michael Poole Date: Sun, 7 Nov 2004 19:34:15 +0000 (+0000) Subject: Implement silence exceptions. X-Git-Url: http://git.pk910.de/?a=commitdiff_plain;h=4ce006b23328ec3570adeac8d6525c4c7bb3525f;p=ircu2.10.12-pk.git Implement silence exceptions. git-svn-id: file:///home/klmitch/undernet-ircu/undernet-ircu-svn/ircu2/trunk@1262 c9e4aea6-c8fd-4c43-8297-357d70d61c8c --- diff --git a/ChangeLog b/ChangeLog index d3dd7a9..5df016b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,23 @@ +2004-11-07 Michael Poole + + * include/s_user.h (add_silence): Delete. + (del_silence): Delete. + + * include/struct.h (struct User): Convert silence list to struct Ban. + + * ircd/m_silence.c (apply_silence, forward_silences): New functions. + (m_silence): Use forward_silences() instead of add_silence(). + (ms_silence): Likewise. + + * ircd/s_err.c (replyTable): Update RPL_SILELIST. + + * ircd/s_misc.c (exit_one_client): Update to new silence list type. + + * ircd/s_user.c (is_silenced): Use find_ban() to search for + silences. If one is found, send it plus any silence exceptions. + (del_silence): Delete. + (add_silence): Delete. + 2004-11-07 Michael Poole * include/channel.h: Remove declarations for undefined functions diff --git a/include/s_user.h b/include/s_user.h index 80cae35..a395f35 100644 --- a/include/s_user.h +++ b/include/s_user.h @@ -77,7 +77,6 @@ extern int whisper(struct Client* source, const char* nick, const char* channel, const char* text, int is_notice); extern void send_user_info(struct Client* to, char* names, int rpl, InfoFormatter fmt); -extern int add_silence(struct Client* sptr, const char* mask); extern int hide_hostmask(struct Client *cptr, unsigned int flags); extern int set_user_mode(struct Client *cptr, struct Client *sptr, @@ -95,7 +94,6 @@ extern struct Client* next_client(struct Client* next, const char* ch); extern char *umode_str(struct Client *cptr); extern void send_umode(struct Client *cptr, struct Client *sptr, struct Flags *old, int sendset); -extern int del_silence(struct Client *sptr, char *mask); extern void set_snomask(struct Client *, unsigned int, int); extern int is_snomask(char *); extern int check_target_limit(struct Client *sptr, void *target, const char *name, diff --git a/include/struct.h b/include/struct.h index b78edd3..fcbf74b 100644 --- a/include/struct.h +++ b/include/struct.h @@ -67,7 +67,7 @@ struct User { struct Client* server; /**< client structure of server */ struct Membership* channel; /**< chain of channel pointer blocks */ struct SLink* invited; /**< chain of invite pointer blocks */ - struct SLink* silence; /**< chain of silence pointer blocks */ + struct Ban* silence; /**< chain of silence pointer blocks */ char* away; /**< pointer to away message */ time_t last; /**< last time user sent a message */ unsigned int refcnt; /**< Number of times this block is referenced */ diff --git a/ircd/m_silence.c b/ircd/m_silence.c index 21df5b6..a3baa51 100644 --- a/ircd/m_silence.c +++ b/ircd/m_silence.c @@ -19,73 +19,21 @@ * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * $Id$ */ - -/* - * m_functions execute protocol messages on this server: - * - * cptr is always NON-NULL, pointing to a *LOCAL* client - * structure (with an open socket connected!). This - * identifies the physical socket where the message - * originated (or which caused the m_function to be - * executed--some m_functions may call others...). - * - * sptr is the source of the message, defined by the - * prefix part of the message if present. If not - * or prefix not found, then sptr==cptr. - * - * (!IsServer(cptr)) => (cptr == sptr), because - * prefixes are taken *only* from servers... - * - * (IsServer(cptr)) - * (sptr == cptr) => the message didn't - * have the prefix. - * - * (sptr != cptr && IsServer(sptr) means - * the prefix specified servername. (?) - * - * (sptr != cptr && !IsServer(sptr) means - * that message originated from a remote - * user (not local). - * - * combining - * - * (!IsServer(sptr)) means that, sptr can safely - * taken as defining the target structure of the - * message in this server. - * - * *Always* true (if 'parse' and others are working correct): - * - * 1) sptr->from == cptr (note: cptr->from == cptr) - * - * 2) MyConnect(sptr) <=> sptr == cptr (e.g. sptr - * *cannot* be a local connection, unless it's - * actually cptr!). [MyConnect(x) should probably - * be defined as (x == x->from) --msa ] - * - * parc number of variable parameter strings (if zero, - * parv is allowed to be NULL) - * - * parv a NULL terminated list of parameter pointers, - * - * parv[0], sender (prefix string), if not present - * this points to an empty string. - * parv[1]...parv[parc-1] - * pointers to additional parameters - * parv[parc] == NULL, *always* - * - * note: it is guaranteed that parv[0]..parv[parc-1] are all - * non-NULL pointers. +/** @file + * @brief Handlers for SILENCE command. + * @version $Id$ */ + #include "config.h" #include "channel.h" #include "client.h" #include "hash.h" #include "ircd.h" +#include "ircd_features.h" #include "ircd_reply.h" +#include "ircd_snprintf.h" #include "ircd_string.h" #include "list.h" #include "msg.h" @@ -99,91 +47,263 @@ #include #include -/* - * m_silence - generic message handler +/** Attempt to apply a SILENCE update to a user. + * + * Silences are propagated lazily between servers to save on bandwidth + * and remote memory. Any removal and any silence exception must be + * propagated until a server has not seen the mask being removed or + * has no positive silences for the user. + * + * @param[in] sptr Client to update. + * @param[in] mask Single silence mask to apply, optionally preceded by '+' or '-' and maybe '~'. + * @return The new ban entry on success, NULL on failure. + */ +static struct Ban * +apply_silence(struct Client *sptr, const char *mask) +{ + struct Ban *sile; + int flags; + + assert(mask && mask[0]); + + /* Check for add or remove. */ + if (mask[0] == '-') { + flags = BAN_DEL; + mask++; + } else if (mask[0] == '+') { + flags = BAN_ADD; + mask++; + } else + flags = BAN_ADD; + + /* Check for being an exception. */ + if (mask[0] == '~') { + flags |= BAN_EXCEPTION; + mask++; + } + + /* Make the silence, set flags, and apply it. */ + sile = make_ban(mask); + sile->flags |= flags; + return apply_ban(&cli_user(sptr)->silence, sile) ? NULL : sile; +} + +/** Apply and send silence updates for a user. + * @param[in] sptr Client whose silence list has been updated. + * @param[in] silences Comma-separated list of silence updates. + * @param[in] dest Direction to send updates in (NULL for broadcast). + */ +static void +forward_silences(struct Client *sptr, char *silences, struct Client *dest) +{ + struct Ban *accepted[MAXPARA], *sile, **plast; + char *cp, *p, buf[BUFSIZE]; + size_t ac_count, buf_used, slen, ii; + + /* Split the list of silences and try to apply each one in turn. */ + for (cp = ircd_strtok(&p, silences, ","), ac_count = 0; + cp && (ac_count < MAXPARA); + cp = ircd_strtok(&p, 0, ",")) { + if ((sile = apply_silence(sptr, cp))) + accepted[ac_count++] = sile; + } + + + if (MyUser(sptr)) { + size_t siles, maxsiles, totlength, maxlength, jj; + + /* Check that silence count and total length are permitted. */ + maxsiles = feature_int(FEAT_MAXSILES); + maxlength = maxsiles * feature_int(FEAT_AVBANLEN); + siles = totlength = 0; + /* Count number of current silences and their total length. */ + for (sile = cli_user(sptr)->silence; sile; sile = sile->next) { + if (sile->flags & (BAN_OVERLAPPED | BAN_ADD | BAN_DEL)) + continue; + siles++; + totlength += strlen(sile->banstr); + } + for (ii = jj = 0; ii < ac_count; ++ii) { + sile = accepted[ii]; + /* If the update is being added, and we would exceed the maximum + * count or length, drop the update. + */ + if (!(sile->flags & (BAN_OVERLAPPED | BAN_DEL))) { + slen = strlen(sile->banstr); + if ((siles >= maxsiles) || (totlength + slen >= maxlength)) { + free_ban(accepted[ii]); + continue; + } + /* Update counts. */ + siles++; + totlength += slen; + } + /* Store the update. */ + accepted[jj++] = sile; + } + /* Write back the number of accepted updates. */ + ac_count = jj; + + /* Send the silence update list, including overlapped silences (to + * make it easier on clients). + */ + buf_used = 0; + for (sile = cli_user(sptr)->silence; sile; sile = sile->next) { + char ch; + if (sile->flags & (BAN_OVERLAPPED | BAN_DEL)) + ch = '-'; + else if (sile->flags & BAN_ADD) + ch = '+'; + else + continue; + slen = strlen(sile->banstr); + if (buf_used + slen + 4 > 400) { + buf[buf_used] = '\0'; + sendcmdto_one(sptr, CMD_SILENCE, sptr, "%s", buf); + buf_used = 0; + } + if (buf_used) + buf[buf_used++] = ','; + buf[buf_used++] = ch; + if (sile->flags & BAN_EXCEPTION) + buf[buf_used++] = '~'; + memcpy(buf + buf_used, sile->banstr, slen); + buf_used += slen; + } + if (buf_used > 0) { + buf[buf_used] = '\0'; + sendcmdto_one(sptr, CMD_SILENCE, sptr, "%s", buf); + buf_used = 0; + } + } + + /* Forward any silence removals or exceptions updates to other + * servers if the user has positive silences. + */ + if (!dest || !MyUser(dest)) { + for (ii = buf_used = 0; ii < ac_count; ++ii) { + char ch; + sile = accepted[ii]; + if (sile->flags & BAN_OVERLAPPED) + continue; + else if (sile->flags & BAN_DEL) + ch = '-'; + else if (sile->flags & BAN_ADD) { + if (!(sile->flags & BAN_EXCEPTION)) + continue; + ch = '+'; + } else + continue; + slen = strlen(sile->banstr); + if (buf_used + slen + 4 > 400) { + buf[buf_used] = '\0'; + if (dest) + sendcmdto_one(sptr, CMD_SILENCE, dest, "%C %s", dest, buf); + else + sendcmdto_serv_butone(sptr, CMD_SILENCE, sptr, "* %s", buf); + buf_used = 0; + } + if (buf_used) + buf[buf_used++] = ','; + buf[buf_used++] = ch; + if (sile->flags & BAN_EXCEPTION) + buf[buf_used++] = '~'; + memcpy(buf + buf_used, sile->banstr, slen); + buf_used += slen; + } + if (buf_used > 0) { + buf[buf_used] = '\0'; + if (dest) + sendcmdto_one(sptr, CMD_SILENCE, dest, "%C %s", dest, buf); + else + sendcmdto_serv_butone(sptr, CMD_SILENCE, sptr, "* %s", buf); + buf_used = 0; + } + } + + /* Remove overlapped and deleted silences from the user's silence + * list. Clear BAN_ADD since we're walking the list anyway. + */ + for (plast = &cli_user(sptr)->silence; (sile = *plast) != NULL; ) { + if (sile->flags & (BAN_OVERLAPPED | BAN_DEL)) { + *plast = sile->next; + free_ban(sile); + } else { + sile->flags &= ~BAN_ADD; + *plast = sile; + plast = &sile->next; + } + } + + /* Free any silence-deleting updates. */ + for (ii = 0; ii < ac_count; ++ii) { + if (accepted[ii]->flags & BAN_DEL) + free_ban(accepted[ii]); + } +} + +/** Handle a SILENCE command from a local user. + * See @ref m_functions for general discussion of parameters. * - * parv[0] = sender prefix - * From local client: - * parv[1] = mask (NULL sends the list) - * From remote client: - * parv[1] = Numeric nick that must be silenced - * parv[2] = mask + * \a parv[1] may be any of the following: + * \li Omitted or empty, to view your own silence list. + * \li Nickname of a user, to view that user's silence list. + * \li A comma-separated list of silence updates * - * XXX - ugh + * @param[in] cptr Client that sent us the message. + * @param[in] sptr Original source of message. + * @param[in] parc Number of arguments. + * @param[in] parv Argument vector. */ int m_silence(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { - struct SLink* lp; - struct Client* acptr; - char c; - char* cp; + struct Client *acptr; + struct Ban *sile; assert(0 != cptr); assert(cptr == sptr); + /* See if the user is requesting a silence list. */ acptr = sptr; - if (parc < 2 || EmptyString(parv[1]) || (acptr = FindUser(parv[1]))) { - if (!(cli_user(acptr))) - return 0; - for (lp = cli_user(acptr)->silence; lp; lp = lp->next) - send_reply(sptr, RPL_SILELIST, cli_name(acptr), lp->value.cp); + if (cli_user(acptr)) { + for (sile = cli_user(acptr)->silence; sile; sile = sile->next) { + send_reply(sptr, RPL_SILELIST, cli_name(acptr), + (sile->flags & BAN_EXCEPTION ? "~" : ""), sile->banstr); + } + } send_reply(sptr, RPL_ENDOFSILELIST, cli_name(acptr)); return 0; } - cp = parv[1]; - c = *cp; - if (c == '-' || c == '+') - cp++; - else if (!(strchr(cp, '@') || strchr(cp, '.') || strchr(cp, '!') || strchr(cp, '*'))) { - return send_reply(sptr, ERR_NOSUCHNICK, parv[1]); - } - else - c = '+'; - cp = pretty_mask(cp); - if ((c == '-' && !del_silence(sptr, cp)) || (c != '-' && !add_silence(sptr, cp))) { - sendcmdto_one(sptr, CMD_SILENCE, sptr, "%c%s", c, cp); - if (c == '-') - sendcmdto_serv_butone(sptr, CMD_SILENCE, 0, " * -%s", cp); - } + + /* The user must be attempting to update their list. */ + forward_silences(sptr, parv[1], NULL); return 0; } -/* - * ms_silence - server message handler +/** Handle a SILENCE command from a server. + * See @ref m_functions for general discussion of parameters. + * + * \a parv[1] may be one of the following: + * \li "*" to indicate a broadcast update (removing a SILENCE) + * \li A client numnick that should be specifically SILENCEd. * - * parv[0] = sender prefix - * From local client: - * parv[1] = mask (NULL sends the list) - * From remote client: - * parv[1] = Numeric nick that must be silenced - * parv[2] = mask + * \a parv[2] is a comma-separated list of silence updates. + * + * @param[in] cptr Client that sent us the message. + * @param[in] sptr Original source of message. + * @param[in] parc Number of arguments. + * @param[in] parv Argument vector. */ int ms_silence(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { - struct Client* acptr; - - if (IsServer(sptr)) { - return protocol_violation(sptr,"Server trying to silence a user"); - } - if (parc < 3 || EmptyString(parv[2])) { + if (IsServer(sptr)) + return protocol_violation(sptr, "Server trying to silence a user"); + if (parc < 3 || EmptyString(parv[2])) return need_more_params(sptr, "SILENCE"); - } - if (*parv[1]) /* can be a server */ - acptr = findNUser(parv[1]); - else - acptr = FindNServer(parv[1]); - - if (*parv[2] == '-') { - if (!del_silence(sptr, parv[2] + 1)) - sendcmdto_serv_butone(sptr, CMD_SILENCE, cptr, "* %s", parv[2]); - } - else { - add_silence(sptr, parv[2]); - if (acptr && IsServer(cli_from(acptr))) { - sendcmdto_one(sptr, CMD_SILENCE, acptr, "%C %s", acptr, parv[2]); - } - } + /* Figure out which silences can be forwarded. */ + forward_silences(sptr, parv[2], findNUser(parv[1])); return 0; + (void)cptr; } diff --git a/ircd/s_err.c b/ircd/s_err.c index 843e2f0..85f1883 100644 --- a/ircd/s_err.c +++ b/ircd/s_err.c @@ -573,7 +573,7 @@ static Numeric replyTable[] = { /* 270 */ { RPL_PRIVS, "%s :", "270" }, /* 271 */ - { RPL_SILELIST, "%s %s", "271" }, + { RPL_SILELIST, "%s %s%s", "271" }, /* 272 */ { RPL_ENDOFSILELIST, "%s :End of Silence List", "272" }, /* 273 */ diff --git a/ircd/s_misc.c b/ircd/s_misc.c index 631cf6b..cdf6250 100644 --- a/ircd/s_misc.c +++ b/ircd/s_misc.c @@ -202,6 +202,7 @@ void get_sockhost(struct Client *cptr, char *host) static void exit_one_client(struct Client* bcptr, const char* comment) { struct SLink *lp; + struct Ban *bp; if (cli_serv(bcptr) && cli_serv(bcptr)->client_list) /* Was SetServerYXX called ? */ ClearServerYXX(bcptr); /* Removes server from server_list[] */ @@ -233,8 +234,10 @@ static void exit_one_client(struct Client* bcptr, const char* comment) del_invite(bcptr, lp->value.chptr); /* Clean up silencefield */ - while ((lp = cli_user(bcptr)->silence)) - del_silence(bcptr, lp->value.cp); + while ((bp = cli_user(bcptr)->silence)) { + cli_user(bcptr)->silence = bp->next; + free_ban(bp); + } /* Clean up snotice lists */ if (MyUser(bcptr)) diff --git a/ircd/s_user.c b/ircd/s_user.c index ff178cf..5e6ebe3 100644 --- a/ircd/s_user.c +++ b/ircd/s_user.c @@ -1659,121 +1659,52 @@ void set_snomask(struct Client *cptr, unsigned int newmask, int what) /** Check whether \a sptr is allowed to send a message to \a acptr. * If \a sptr is a remote user, it means some server has an outdated - * SILENCE list for \a acptr, so send the missing SILENCE back in the - * direction of \a sptr. + * SILENCE list for \a acptr, so send the missing SILENCE mask(s) back + * in the direction of \a sptr. * @param[in] sptr Client trying to send a message. * @param[in] acptr Destination of message. * @return Non-zero if \a sptr is SILENCEd by \a acptr, zero if not. */ int is_silenced(struct Client *sptr, struct Client *acptr) { - struct SLink *lp; + struct Ban *found; struct User *user; - static char sender[HOSTLEN + NICKLEN + USERLEN + 5]; - static char senderip[16 + NICKLEN + USERLEN + 5]; - static char senderh[HOSTLEN + ACCOUNTLEN + USERLEN + 6]; + size_t buf_used, slen; + char buf[BUFSIZE]; - if (!cli_user(acptr) || !(lp = cli_user(acptr)->silence) || !(user = cli_user(sptr))) + if (!(user = cli_user(acptr)) + || !(found = find_ban(sptr, user->silence))) return 0; - ircd_snprintf(0, sender, sizeof(sender), "%s!%s@%s", cli_name(sptr), - user->username, user->host); - ircd_snprintf(0, senderip, sizeof(senderip), "%s!%s@%s", cli_name(sptr), - user->username, ircd_ntoa(&cli_ip(sptr))); - if (HasHiddenHost(sptr)) - ircd_snprintf(0, senderh, sizeof(senderh), "%s!%s@%s", cli_name(sptr), - user->username, user->realhost); - for (; lp; lp = lp->next) - { - if ((!(lp->flags & CHFL_SILENCE_IPMASK) && (!match(lp->value.cp, sender) || - (HasHiddenHost(sptr) && !match(lp->value.cp, senderh)))) || - ((lp->flags & CHFL_SILENCE_IPMASK) && !match(lp->value.cp, senderip))) - { - if (!MyConnect(sptr)) - { - sendcmdto_one(acptr, CMD_SILENCE, cli_from(sptr), "%C %s", sptr, - lp->value.cp); + assert(!(found->flags & BAN_EXCEPTION)); + if (!MyConnect(sptr)) { + /* Buffer positive silence to send back. */ + buf_used = strlen(found->banstr); + memcpy(buf, found->banstr, buf_used); + /* Add exceptions to buffer. */ + for (found = user->silence; found; found = found->next) { + if (!(found->flags & BAN_EXCEPTION)) + continue; + slen = strlen(found->banstr); + if (buf_used + slen + 4 > 400) { + buf[buf_used] = '\0'; + sendcmdto_one(acptr, CMD_SILENCE, cli_from(sptr), "%C %s", sptr, buf); + buf_used = 0; } - return 1; + if (buf_used) + buf[buf_used++] = ','; + buf[buf_used++] = '+'; + buf[buf_used++] = '~'; + memcpy(buf + buf_used, found->banstr, slen); + buf_used += slen; } - } - return 0; -} - -/** Remove all silence masks from \a sptr that match \a mask. - * @param[in,out] sptr Client to update. - * @param[in] mask Silence mask to remove. - * @return Zero if any silence masks were removed; non-zero if all were kept. - */ -int del_silence(struct Client *sptr, char *mask) -{ - struct SLink **lp; - struct SLink *tmp; - int ret = -1; - - for (lp = &(cli_user(sptr))->silence; *lp;) { - if (!mmatch(mask, (*lp)->value.cp)) - { - tmp = *lp; - *lp = tmp->next; - MyFree(tmp->value.cp); - free_link(tmp); - ret = 0; + /* Flush silence buffer. */ + if (buf_used) { + buf[buf_used] = '\0'; + sendcmdto_one(acptr, CMD_SILENCE, cli_from(sptr), "%C %s", sptr, buf); + buf_used = 0; } - else - lp = &(*lp)->next; } - return ret; -} - -/** Add \a mask to the silence masks for \a sptr. - * Removes any silence masks that are subsets of \a mask. - * @param[in,out] sptr Client adding silence mask. - * @param[in] mask Silence mask to add. - * @return Zero on success; non-zero on any failure. - */ -int add_silence(struct Client* sptr, const char* mask) -{ - struct SLink *lp, **lpp; - int cnt = 0, len = strlen(mask); - char *ip_start; - - for (lpp = &(cli_user(sptr))->silence, lp = *lpp; lp;) - { - if (0 == ircd_strcmp(mask, lp->value.cp)) - return -1; - if (!mmatch(mask, lp->value.cp)) - { - struct SLink *tmp = lp; - *lpp = lp = lp->next; - MyFree(tmp->value.cp); - free_link(tmp); - continue; - } - if (MyUser(sptr)) - { - len += strlen(lp->value.cp); - if ((len > (feature_int(FEAT_AVBANLEN) * feature_int(FEAT_MAXSILES))) || - (++cnt >= feature_int(FEAT_MAXSILES))) - { - send_reply(sptr, ERR_SILELISTFULL, mask); - return -1; - } - else if (!mmatch(lp->value.cp, mask)) - return -1; - } - lpp = &lp->next; - lp = *lpp; - } - lp = make_link(); - memset(lp, 0, sizeof(struct SLink)); - lp->next = cli_user(sptr)->silence; - lp->value.cp = (char*) MyMalloc(strlen(mask) + 1); - assert(0 != lp->value.cp); - strcpy(lp->value.cp, mask); - if ((ip_start = strrchr(mask, '@')) && check_if_ipmask(ip_start + 1)) - lp->flags = CHFL_SILENCE_IPMASK; - cli_user(sptr)->silence = lp; - return 0; + return 1; } /** Send RPL_ISUPPORT lines to \a cptr.