pissircd/src/modules/usermodes/censor.c

273 lines
6.0 KiB
C

/*
* User Mode +G
* (C) Copyright 2005-current Bram Matthys and The UnrealIRCd team.
*/
#include "unrealircd.h"
ModuleHeader MOD_HEADER
= {
"usermodes/censor",
"4.2",
"User Mode +G",
"UnrealIRCd Team",
"unrealircd-6",
};
long UMODE_CENSOR = 0L;
#define IsCensored(x) (x->umodes & UMODE_CENSOR)
int censor_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype);
int censor_config_test(ConfigFile *, ConfigEntry *, int, int *);
int censor_config_run(ConfigFile *, ConfigEntry *, int);
ModuleInfo *ModInfo = NULL;
ConfigItem_badword *conf_badword_message = NULL;
static ConfigItem_badword *copy_badword_struct(ConfigItem_badword *ca, int regex, int regflags);
int censor_stats_badwords_user(Client *client, const char *para);
MOD_TEST()
{
HookAdd(modinfo->handle, HOOKTYPE_CONFIGTEST, 0, censor_config_test);
return MOD_SUCCESS;
}
MOD_INIT()
{
ModInfo = modinfo;
MARK_AS_OFFICIAL_MODULE(modinfo);
UmodeAdd(modinfo->handle, 'G', UMODE_GLOBAL, 0, NULL, &UMODE_CENSOR);
HookAdd(modinfo->handle, HOOKTYPE_CAN_SEND_TO_USER, 0, censor_can_send_to_user);
HookAdd(modinfo->handle, HOOKTYPE_STATS, 0, censor_stats_badwords_user);
HookAdd(modinfo->handle, HOOKTYPE_CONFIGRUN, 0, censor_config_run);
return MOD_SUCCESS;
}
MOD_LOAD()
{
return MOD_SUCCESS;
}
MOD_UNLOAD()
{
ConfigItem_badword *badword, *next;
for (badword = conf_badword_message; badword; badword = next)
{
next = badword->next;
DelListItem(badword, conf_badword_message);
badword_config_free(badword);
}
return MOD_SUCCESS;
}
int censor_config_test(ConfigFile *cf, ConfigEntry *ce, int type, int *errs)
{
int errors = 0;
ConfigEntry *cep;
char has_word = 0, has_replace = 0, has_action = 0, action = 'r';
if (type != CONFIG_MAIN)
return 0;
if (!ce || !ce->name || strcmp(ce->name, "badword"))
return 0; /* not interested */
if (!ce->value)
{
config_error("%s:%i: badword without type",
ce->file->filename, ce->line_number);
*errs = 1;
return -1;
}
else if (strcmp(ce->value, "message") && strcmp(ce->value, "all")) {
/* config_error("%s:%i: badword with unknown type",
ce->file->filename, ce->line_number); -- can't do that.. */
return 0; /* unhandled */
}
for (cep = ce->items; cep; cep = cep->next)
{
if (config_is_blankorempty(cep, "badword"))
{
errors++;
continue;
}
if (!strcmp(cep->name, "word"))
{
const char *errbuf;
if (has_word)
{
config_warn_duplicate(cep->file->filename,
cep->line_number, "badword::word");
continue;
}
has_word = 1;
if ((errbuf = badword_config_check_regex(cep->value,1,1)))
{
config_error("%s:%i: badword::%s contains an invalid regex: %s",
cep->file->filename,
cep->line_number,
cep->name, errbuf);
errors++;
}
}
else if (!strcmp(cep->name, "replace"))
{
if (has_replace)
{
config_warn_duplicate(cep->file->filename,
cep->line_number, "badword::replace");
continue;
}
has_replace = 1;
}
else if (!strcmp(cep->name, "action"))
{
if (has_action)
{
config_warn_duplicate(cep->file->filename,
cep->line_number, "badword::action");
continue;
}
has_action = 1;
if (!strcmp(cep->value, "replace"))
action = 'r';
else if (!strcmp(cep->value, "block"))
action = 'b';
else
{
config_error("%s:%d: Unknown badword::action '%s'",
cep->file->filename, cep->line_number,
cep->value);
errors++;
}
}
else
{
config_error_unknown(cep->file->filename, cep->line_number,
"badword", cep->name);
errors++;
}
}
if (!has_word)
{
config_error_missing(ce->file->filename, ce->line_number,
"badword::word");
errors++;
}
if (has_action)
{
if (has_replace && action == 'b')
{
config_error("%s:%i: badword::action is block but badword::replace exists",
ce->file->filename, ce->line_number);
errors++;
}
}
*errs = errors;
return errors ? -1 : 1;
}
int censor_config_run(ConfigFile *cf, ConfigEntry *ce, int type)
{
ConfigEntry *cep, *word = NULL;
ConfigItem_badword *ca;
if (type != CONFIG_MAIN)
return 0;
if (!ce || !ce->name || strcmp(ce->name, "badword"))
return 0; /* not interested */
if (strcmp(ce->value, "message") && strcmp(ce->value, "all"))
return 0; /* not for us */
ca = safe_alloc(sizeof(ConfigItem_badword));
ca->action = BADWORD_REPLACE;
for (cep = ce->items; cep; cep = cep->next)
{
if (!strcmp(cep->name, "action"))
{
if (!strcmp(cep->value, "block"))
{
ca->action = BADWORD_BLOCK;
}
}
else if (!strcmp(cep->name, "replace"))
{
safe_strdup(ca->replace, cep->value);
}
else if (!strcmp(cep->name, "word"))
{
word = cep;
}
}
badword_config_process(ca, word->value);
if (!strcmp(ce->value, "message"))
{
AddListItem(ca, conf_badword_message);
} else
if (!strcmp(ce->value, "all"))
{
AddListItem(ca, conf_badword_message);
return 0; /* pretend we didn't see it, so other modules can handle 'all' as well */
}
return 1;
}
const char *stripbadwords_message(const char *str, int *blocked)
{
return stripbadwords(str, conf_badword_message, blocked);
}
int censor_can_send_to_user(Client *client, Client *target, const char **text, const char **errmsg, SendType sendtype)
{
int blocked = 0;
if (MyUser(client) && IsCensored(target))
{
*text = stripbadwords_message(*text, &blocked);
if (blocked)
{
*errmsg = "User does not accept private messages containing swearing";
return HOOK_DENY;
}
}
return HOOK_CONTINUE;
}
int censor_stats_badwords_user(Client *client, const char *para)
{
ConfigItem_badword *words;
if (!para || !(!strcmp(para, "b") || !strcasecmp(para, "badword")))
return 0;
for (words = conf_badword_message; words; words = words->next)
{
sendtxtnumeric(client, "m %c %s%s%s %s", words->type & BADW_TYPE_REGEX ? 'R' : 'F',
(words->type & BADW_TYPE_FAST_L) ? "*" : "", words->word,
(words->type & BADW_TYPE_FAST_R) ? "*" : "",
words->action == BADWORD_REPLACE ? (words->replace ? words->replace : "<censored>") : "");
}
return 1;
}