// Copyright (c) Hercules Dev Team & hemagx, licensed under GNU GPL.
// See the LICENSE file
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../common/HPMi.h"
#include "../common/mmo.h"
#include "../common/malloc.h"
#include "../common/strlib.h"
#include "../map/atcommand.h"
#include "../map/pc.h"
#include "../map/clif.h"
#include "../../3rdparty/pcre/include/pcre.h"
#include "../common/HPMDataCheck.h" // should always be the last file included! (if you don't make it last, it'll intentionally break compile time)
HPExport struct hplugin_info pinfo = {
"PCRE Chat Filter",
SERVER_TYPE_MAP,
"0.1",
HPM_VERSION,
};
struct manner_pcre {
pcre *regex;
pcre_extra *extra;
};
struct manner_config {
int min_gm_level;
int mute_after;
int mute_time;
char bypass_var[64];
};
struct manner_pcre *mannerlist = NULL;
struct manner_config config = {60, 0, 0, "chat_filter"};
unsigned int mannersize = 0;
unsigned int bypass_chat_filter = UINT_MAX - 1;
bool filter_chat(bool retVal, struct map_session_data *sd, int *format, char **name_, size_t *namelen_, char **message_, size_t *messagelen_) {
char *message = *message_;
unsigned int i;
int mute_times;
if( !retVal || !message )
return false;
if( !mannersize || pc_has_permission(sd, bypass_chat_filter) )
return true;
if ((pc_get_group_level(sd) >= config.min_gm_level) || (pc_readglobalreg(sd, script->add_str(config.bypass_var))))
return true;
for(i = 0; i < mannersize; i++) {
if(pcre_exec(mannerlist[i].regex, mannerlist[i].extra, message, (int)strlen(message), 0, 0, NULL, 0) != PCRE_ERROR_NOMATCH) {
char output[254];
safesnprintf(output, 254, "Chat Filter: Yeah, uh, I don't think so, buddy...");
clif->colormes(sd->fd, COLOR_RED, output);
if ( config.mute_after > 0 ) {
mute_times = pc_readglobalreg(sd, script->add_str("mute_filter"));
if ( mute_times == config.mute_after ) {
status->change_start( NULL, &sd->bl, SC_NOCHAT, 100, 0, 0,0,0,config.mute_time * 60 * 1000, 0);
// status->change_start( NULL, &sd->bl, SC_TRICKDEAD, 10000, 1, 0, 0, 0, 1000, 0);
clif->GM_silence(sd, sd, 1);
pc_setglobalreg(sd, script->add_str("mute_filter"), 0);
}else{
pc_setglobalreg(sd, script->add_str("mute_filter"), mute_times + 1);
}
}
return false;
}
}
return true;
}
bool read_manner(const char* confpath) {
FILE* fp;
char line[1024], param[1024];
const char *error = NULL;
int offset;
pcre *regex = NULL;
pcre_extra *extra = NULL;
fp = fopen(confpath, "r");
if (fp == NULL) {
ShowError("File not found: '%s'.\n", confpath);
return false;
}
while(fgets(line, sizeof(line), fp)) {
if((line[0] == '/' && line[1] == '/') || ( line[0] == '\0' || line[0] == '\n' || line[0] == '\r'))
continue;
if (sscanf(line, "%1023s", param) != 1)
continue;
regex = pcre_compile(param, 0, &error, &offset, NULL);
if ( regex == NULL ) {
ShowError("Could not compile regex '%s'. Error: '%s'.\n", param, error);
continue;
}
extra = pcre_study(regex, 0, &error);
if (error != NULL) {
ShowError("Could not optimize '%s' Error: '%s'.\n", param, error);
continue;
}
//ShowDebug("Loaded '%s'\n", param);
RECREATE(mannerlist, struct manner_pcre, mannersize + 1);
mannerlist[mannersize].regex = regex;
mannerlist[mannersize].extra = extra;
mannersize++;
}
ShowStatus("Done reading '"CL_WHITE"%d"CL_RESET"' entries in '"CL_WHITE"%s"CL_RESET"'.\n", mannersize, confpath);
return true;
}
bool read_manner_config(const char* confpath) {
char line[1024], w1[1024], w2[1024];
FILE* fp;
fp = fopen(confpath, "r");
if (fp == NULL) {
ShowError("File not found: '%s' \n", confpath);
return false;
}
while(fgets(line, sizeof(line), fp))
{
if(line[0] == '/' && line[1] == '/')
continue;
if (sscanf(line, "%[^:]: %[^\r\n]", w1, w2) != 2)
continue;
if(!strcmpi(w1, "gm_min_level"))
config.min_gm_level = atoi(w2);
else if (!strcmpi(w1, "mute_after"))
config.mute_after = atoi(w2);
else if (!strcmpi(w1, "mute_time"))
config.mute_time = atoi(w2);
else if (!strcmpi(w1, "bypass_var"))
strncpy(config.bypass_var, w2, sizeof(config.bypass_var));
}
ShowStatus("Chat filter configuration has been loaded.\n");
return true;
}
void clean_manner(void) {
unsigned int i;
for (i = 0; i < mannersize; i++) {
pcre_free(mannerlist[i].regex);
if (mannerlist[i].extra)
pcre_free(mannerlist[i].extra);
}
aFree(mannerlist);
mannerlist = NULL;
mannersize = 0;
}
ACMD(reloadmanner) {
clean_manner();
read_manner("conf/plugin/manner.txt");
read_manner_config("conf/plugin/manner.conf");
return true;
}
HPExport void plugin_init(void) {
clif = GET_SYMBOL("clif");
pc = GET_SYMBOL("pc");
iMalloc = GET_SYMBOL("iMalloc");
strlib = GET_SYMBOL("strlib");
script = GET_SYMBOL("script");
status = GET_SYMBOL("status");
addGroupPermission("bypass_chat_filter", bypass_chat_filter);
addHookPost("clif->process_message", filter_chat);
addAtcommand("reloadmanner", reloadmanner);
read_manner("conf/plugin/manner.txt");
read_manner_config("conf/plugin/manner.conf");
}
HPExport void plugin_final(void) {
clean_manner();
}