// Copyright (c) Hercules Dev Team & hemagx, licensed under GNU GPL. // See the LICENSE file #include #include #include #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(); }