catgirl/ignore.c
C. McEnroe 4b883177dc Split ignore fields to avoid over-eager * matching
Split ignore fields and match each separately to avoid an early *
eagerly matching across several fields. For example, "* JOIN * *" should
not match messages which happen to contain the word "JOIN" followed by
two other words.

Ignore capacity is reduced to 64 to keep the size of the array the same.
I don't think it's an issue.
2021-01-01 20:09:10 -05:00

105 lines
3.5 KiB
C

/* Copyright (C) 2020 C. McEnroe <june@causal.agency>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* Additional permission under GNU GPL version 3 section 7:
*
* If you modify this Program, or any covered work, by linking or
* combining it with OpenSSL (or a modified version of that library),
* containing parts covered by the terms of the OpenSSL License and the
* original SSLeay license, the licensors of this Program grant you
* additional permission to convey the resulting work. Corresponding
* Source for a non-source form of such a combination shall include the
* source code for the parts of OpenSSL used as well as that of the
* covered work.
*/
#include <err.h>
#include <fnmatch.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysexits.h>
#include "chat.h"
struct Ignore ignores[IgnoreCap];
static size_t len;
struct Ignore ignoreParse(char *pattern) {
struct Ignore ignore = {0};
ignore.mask = strsep(&pattern, " ");
ignore.cmd = strsep(&pattern, " ");
ignore.chan = strsep(&pattern, " ");
ignore.mesg = pattern;
return ignore;
}
struct Ignore ignoreAdd(const char *pattern) {
if (len == IgnoreCap) errx(EX_CONFIG, "ignore limit exceeded");
char *own;
if (!strchr(pattern, '!') && !strchr(pattern, ' ')) {
int n = asprintf(&own, "%s!*@*", pattern);
if (n < 0) err(EX_OSERR, "asprintf");
} else {
own = strdup(pattern);
if (!own) err(EX_OSERR, "strdup");
}
struct Ignore ignore = ignoreParse(own);
ignores[len++] = ignore;
return ignore;
}
bool ignoreRemove(struct Ignore ignore) {
bool found = false;
for (size_t i = len - 1; i < len; --i) {
if (!ignores[i].cmd != !ignore.cmd) continue;
if (!ignores[i].chan != !ignore.chan) continue;
if (!ignores[i].mesg != !ignore.mesg) continue;
if (strcasecmp(ignores[i].mask, ignore.mask)) continue;
if (ignore.cmd && strcasecmp(ignores[i].cmd, ignore.cmd)) continue;
if (ignore.chan && strcasecmp(ignores[i].chan, ignore.chan)) continue;
if (ignore.mesg && strcasecmp(ignores[i].mesg, ignore.mesg)) continue;
free(ignores[i].mask);
ignores[i] = ignores[--len];
ignores[len] = (struct Ignore) {0};
found = true;
}
return found;
}
static bool ignoreTest(
struct Ignore ignore, const char *mask, uint id, const struct Message *msg
) {
if (fnmatch(ignore.mask, mask, FNM_CASEFOLD)) return false;
if (!ignore.cmd) return true;
if (fnmatch(ignore.cmd, msg->cmd, FNM_CASEFOLD)) return false;
if (!ignore.chan) return true;
if (fnmatch(ignore.chan, idNames[id], FNM_CASEFOLD)) return false;
if (!ignore.mesg) return true;
if (!msg->params[1]) return false;
return !fnmatch(ignore.mesg, msg->params[1], FNM_CASEFOLD);
}
enum Heat ignoreCheck(enum Heat heat, uint id, const struct Message *msg) {
if (!len) return heat;
char mask[512];
snprintf(mask, sizeof(mask), "%s!%s@%s", msg->nick, msg->user, msg->host);
for (size_t i = 0; i < len; ++i) {
if (ignoreTest(ignores[i], mask, id, msg)) return Ice;
}
return heat;
}