X-Git-Url: http://git.pk910.de/?a=blobdiff_plain;f=ircd%2Fm_silence.c;h=ce9d4dc5ed58d63738f446cc68a1f32020ee2019;hb=a25ce1d73a5f5a92057a6e7c62894e4683eb4280;hp=6451d981c72bfb3582ece667338d2db7338d79a5;hpb=80881d85337d93d70b67a505ae5178199b1b3155;p=ircu2.10.12-pk.git diff --git a/ircd/m_silence.c b/ircd/m_silence.c index 6451d98..ce9d4dc 100644 --- a/ircd/m_silence.c +++ b/ircd/m_silence.c @@ -19,79 +19,22 @@ * 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. */ -#if 0 -/* - * No need to include handlers.h here the signatures must match - * and we don't need to force a rebuild of all the handlers everytime - * we add a new one to the list. --Bleep +/** @file + * @brief Handlers for SILENCE command. + * @version $Id$ */ -#include "handlers.h" -#endif /* 0 */ + +#include "config.h" + #include "channel.h" #include "client.h" #include "hash.h" #include "ircd.h" +#include "ircd_features.h" +#include "ircd_log.h" #include "ircd_reply.h" +#include "ircd_snprintf.h" #include "ircd_string.h" #include "list.h" #include "msg.h" @@ -101,181 +44,289 @@ #include "send.h" #include "struct.h" -#include +/* #include -- Now using assert in ircd_log.h */ #include #include -/* - * m_silence - generic message handler +/** Attempt to apply a SILENCE update to a user. * - * 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 + * 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. * - * XXX - ugh + * @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. */ -int m_silence(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) +static struct Ban * +apply_silence(struct Client *sptr, char *mask) { - struct SLink* lp; - struct Client* acptr; - char c; - char* cp; + struct Ban *sile; + int flags; + int res; + char orig_mask[NICKLEN+USERLEN+HOSTLEN+3]; - assert(0 != cptr); - assert(cptr == sptr); + assert(mask && mask[0]); - acptr = sptr; + /* Check for add or remove. */ + if (mask[0] == '-') { + flags = BAN_DEL; + mask++; + } else if (mask[0] == '+') { + flags = BAN_ADD; + mask++; + } else + flags = BAN_ADD; - if (parc < 2 || EmptyString(parv[1]) || (acptr = FindUser(parv[1]))) { - if (!(acptr->user)) - return 0; - for (lp = acptr->user->silence; lp; lp = lp->next) - send_reply(sptr, RPL_SILELIST, acptr->name, lp->value.cp); - send_reply(sptr, RPL_ENDOFSILELIST, acptr->name); - 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]); + /* Check for being an exception. */ + if (mask[0] == '~') { + flags |= BAN_EXCEPTION; + mask++; } - 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); + + /* Make the silence and set additional flags. */ + ircd_strncpy(orig_mask, mask, sizeof(orig_mask) - 1); + sile = make_ban(pretty_mask(mask)); + sile->flags |= flags; + + /* If they're a local user trying to ban too broad a mask, forbid it. */ + if (MyUser(sptr) + && (sile->flags & BAN_IPMASK) + && sile->addrbits > 0 + && sile->addrbits < (irc_in_addr_is_ipv4(&sile->address) ? 112 : 32)) { + send_reply(sptr, ERR_MASKTOOWIDE, orig_mask); + free_ban(sile); + return NULL; } - return 0; + + /* Apply it to the silence list. */ + res = apply_ban(&cli_user(sptr)->silence, sile, 1); + return res ? NULL : sile; } -/* - * ms_silence - server message handler - * - * 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 +/** 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). */ -int ms_silence(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) +static void +forward_silences(struct Client *sptr, char *silences, struct Client *dest) { - struct Client* acptr; + struct Ban *accepted[MAXPARA], *sile, **plast; + char *cp, *p, buf[BUFSIZE]; + size_t ac_count, buf_used, slen, ii; - if (IsServer(sptr)) { - return protocol_violation(sptr,"Server trying to silence a user"); + /* 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 (parc < 3 || EmptyString(parv[2])) { - return need_more_params(sptr, "SILENCE"); + + 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. */ + plast = &cli_user(sptr)->silence; + 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); + plast = &sile->next; + } + 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)) { + *plast = NULL; + if (MyUser(sptr)) + send_reply(sptr, ERR_SILELISTFULL, accepted[ii]->banstr); + free_ban(accepted[ii]); + continue; + } + /* Update counts. */ + siles++; + totlength += slen; + plast = &sile->next; + } + /* 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; + } } - if (*parv[1]) /* can be a server */ - acptr = findNUser(parv[1]); - else - acptr = FindNServer(parv[1]); + /* 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; + } + } - if (*parv[2] == '-') { - if (!del_silence(sptr, parv[2] + 1)) - sendcmdto_serv_butone(sptr, CMD_SILENCE, cptr, "* %s", parv[2]); + /* 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; + } } - else { - add_silence(sptr, parv[2]); - if (acptr && IsServer(acptr->from)) { - sendcmdto_one(sptr, CMD_SILENCE, acptr, "%C %s", acptr, parv[2]); + + /* Free any silence-deleting updates. */ + for (ii = 0; ii < ac_count; ++ii) { + if ((accepted[ii]->flags & (BAN_ADD | BAN_DEL)) == BAN_DEL) { + free_ban(accepted[ii]); } } - return 0; } - -#if 0 -/* - * m_silence() - Added 19 May 1994 by Run. +/** Handle a SILENCE command from a local user. + * See @ref m_functions for general discussion of parameters. + * + * \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 * - * 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 + * @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[]) +int m_silence(struct Client* cptr, struct Client* sptr, int parc, char* parv[]) { - struct SLink *lp; struct Client *acptr; - char c, *cp; - - if (MyUser(sptr)) - { - acptr = sptr; - if (parc < 2 || *parv[1] == '\0' || (acptr = FindUser(parv[1]))) - { - if (!(acptr->user)) - return 0; - for (lp = acptr->user->silence; lp; lp = lp->next) - sendto_one(sptr, rpl_str(RPL_SILELIST), me.name, /* XXX DEAD */ - sptr->name, acptr->name, lp->value.cp); - sendto_one(sptr, rpl_str(RPL_ENDOFSILELIST), me.name, sptr->name, /* XXX DEAD */ - acptr->name); - return 0; - } - cp = parv[1]; - c = *cp; - if (c == '-' || c == '+') - cp++; - else if (!(strchr(cp, '@') || strchr(cp, '.') || - strchr(cp, '!') || strchr(cp, '*'))) - { - sendto_one(sptr, err_str(ERR_NOSUCHNICK), me.name, parv[0], parv[1]); /* XXX DEAD */ - return -1; - } - else - c = '+'; - cp = pretty_mask(cp); - if ((c == '-' && !del_silence(sptr, cp)) || - (c != '-' && !add_silence(sptr, cp))) - { - sendto_prefix_one(sptr, sptr, ":%s SILENCE %c%s", parv[0], c, cp); /* XXX DEAD */ - if (c == '-') - sendto_serv_butone(0, ":%s SILENCE * -%s", sptr->name, cp); /* XXX DEAD */ - } - } - else if (parc < 3 || *parv[2] == '\0') - return need_more_params(sptr, "SILENCE"); + struct Ban *sile; - else - { - if (*parv[1]) /* can be a server */ - acptr = findNUser(parv[1]); - else - acptr = FindNServer(parv[1]); + assert(0 != cptr); + assert(cptr == sptr); - if (*parv[2] == '-') - { - if (!del_silence(sptr, parv[2] + 1)) - sendto_serv_butone(cptr, ":%s SILENCE * %s", parv[0], parv[2]); /* XXX DEAD */ - } - else - { - add_silence(sptr, parv[2]); - if (acptr && IsServer(acptr->from)) - { - if (IsServer(acptr)) - sendto_one(acptr, ":%s SILENCE %s %s", /* XXX DEAD */ - parv[0], NumServ(acptr), parv[2]); - else - sendto_one(acptr, ":%s SILENCE %s%s %s", /* XXX DEAD */ - parv[0], NumNick(acptr), parv[2]); + /* 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) && ((acptr == sptr) || IsChannelService(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; } + + /* The user must be attempting to update their list. */ + forward_silences(sptr, parv[1], NULL); + return 0; +} + +/** 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. + * + * \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[]) +{ + 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"); + + /* Figure out which silences can be forwarded. */ + forward_silences(sptr, parv[2], findNUser(parv[1])); return 0; + (void)cptr; } -#endif /* 0 */