diff --git a/common/profanity_manager.cpp b/common/profanity_manager.cpp index 005fc4909..5b5cbea07 100644 --- a/common/profanity_manager.cpp +++ b/common/profanity_manager.cpp @@ -19,6 +19,7 @@ #include "profanity_manager.h" #include "dbcore.h" +#include "string_util.h" #include #include @@ -34,15 +35,17 @@ bool EQ::ProfanityManager::LoadProfanityList(DBcore *db) { return true; } - if (!load_database_entries(db)) + if (!load_database_entries(db)) { return false; + } return true; } bool EQ::ProfanityManager::UpdateProfanityList(DBcore *db) { - if (!load_database_entries(db)) + if (!load_database_entries(db)) { return false; + } update_originator_flag = true; @@ -58,53 +61,60 @@ bool EQ::ProfanityManager::DeleteProfanityList(DBcore *db) { return true; } -bool EQ::ProfanityManager::AddProfanity(DBcore *db, const char *profanity) { - if (!db || !profanity) +bool EQ::ProfanityManager::AddProfanity(DBcore *db, std::string profanity) { + if (!db || profanity.empty()) { return false; + } - std::string entry(profanity); + std::string entry = str_tolower(profanity); - std::transform(entry.begin(), entry.end(), entry.begin(), [](unsigned char c) -> unsigned char { return tolower(c); }); - - if (check_for_existing_entry(entry.c_str())) + if (check_for_existing_entry(entry)) { return true; + } - if (entry.length() < REDACTION_LENGTH_MIN) + if (entry.length() < REDACTION_LENGTH_MIN) { return false; + } profanity_list.push_back(entry); - std::string query = "REPLACE INTO `profanity_list` (`word`) VALUES ('"; - query.append(entry); - query.append("')"); + auto query = fmt::format( + "REPLACE INTO `profanity_list` (`word`) VALUES ('{}')", + profanity + ); auto results = db->QueryDatabase(query); - if (!results.Success()) + + if (!results.Success()) { return false; + } update_originator_flag = true; return true; } -bool EQ::ProfanityManager::RemoveProfanity(DBcore *db, const char *profanity) { - if (!db || !profanity) +bool EQ::ProfanityManager::RemoveProfanity(DBcore *db, std::string profanity) { + if (!db || profanity.empty()) { return false; + } - std::string entry(profanity); + std::string entry = str_tolower(profanity); - std::transform(entry.begin(), entry.end(), entry.begin(), [](unsigned char c) -> unsigned char { return tolower(c); }); - - if (!check_for_existing_entry(entry.c_str())) + if (!check_for_existing_entry(entry)) { return true; + } profanity_list.remove(entry); - std::string query = "DELETE FROM `profanity_list` WHERE `word` LIKE '"; - query.append(entry); - query.append("'"); + auto query = fmt::format( + "DELETE FROM `profanity_list` WHERE `word` = '{}'", + entry + ); auto results = db->QueryDatabase(query); - if (!results.Success()) + + if (!results.Success()) { return false; + } update_originator_flag = true; @@ -112,16 +122,16 @@ bool EQ::ProfanityManager::RemoveProfanity(DBcore *db, const char *profanity) { } void EQ::ProfanityManager::RedactMessage(char *message) { - if (!message) + if (!message) { return; + } - std::string test_message(message); + std::string test_message = str_tolower(message); // hard-coded max length based on channel message buffer size (4096 bytes).. // ..will need to change or remove if other sources are used for redaction - if (test_message.length() < REDACTION_LENGTH_MIN || test_message.length() >= 4096) + if (test_message.length() < REDACTION_LENGTH_MIN || test_message.length() >= 4096) { return; - - std::transform(test_message.begin(), test_message.end(), test_message.begin(), [](unsigned char c) -> unsigned char { return tolower(c); }); + } for (const auto &iter : profanity_list) { // consider adding textlink checks if it becomes an issue size_t pos = 0; @@ -129,12 +139,17 @@ void EQ::ProfanityManager::RedactMessage(char *message) { while (pos != std::string::npos) { pos = test_message.find(iter, start_pos); - if (pos == std::string::npos) + if (pos == std::string::npos) { continue; + } - if ((pos + iter.length()) == test_message.length() || !isalpha(test_message.at(pos + iter.length()))) { - if (pos == 0 || !isalpha(test_message.at(pos - 1))) + if ( + (pos + iter.length()) == test_message.length() || + !isalpha(test_message.at(pos + iter.length())) + ) { + if (pos == 0 || !isalpha(test_message.at(pos - 1))) { memset((message + pos), REDACTION_CHARACTER, iter.length()); + } } start_pos = (pos + iter.length()); @@ -143,25 +158,29 @@ void EQ::ProfanityManager::RedactMessage(char *message) { } void EQ::ProfanityManager::RedactMessage(std::string &message) { - if (message.length() < REDACTION_LENGTH_MIN || message.length() >= 4096) + if (message.length() < REDACTION_LENGTH_MIN || message.length() >= 4096) { return; + } - std::string test_message(const_cast(message)); + std::string test_message = str_tolower(message); - std::transform(test_message.begin(), test_message.end(), test_message.begin(), [](unsigned char c) -> unsigned char { return tolower(c); }); - - for (const auto &iter : profanity_list) { // consider adding textlink checks if it becomes an issue + for (const auto &iter : profanity_list) { size_t pos = 0; size_t start_pos = 0; while (pos != std::string::npos) { pos = test_message.find(iter, start_pos); - if (pos == std::string::npos) + if (pos == std::string::npos) { continue; + } - if ((pos + iter.length()) == test_message.length() || !isalpha(test_message.at(pos + iter.length()))) { - if (pos == 0 || !isalpha(test_message.at(pos - 1))) + if ( + (pos + iter.length()) == test_message.length() || + !isalpha(test_message.at(pos + iter.length())) + ) { + if (pos == 0 || !isalpha(test_message.at(pos - 1))) { message.replace(pos, iter.length(), iter.length(), REDACTION_CHARACTER); + } } start_pos = (pos + iter.length()); @@ -169,24 +188,18 @@ void EQ::ProfanityManager::RedactMessage(std::string &message) { } } -bool EQ::ProfanityManager::ContainsCensoredLanguage(const char *message) { - if (!message) - return false; - - return ContainsCensoredLanguage(std::string(message)); -} bool EQ::ProfanityManager::ContainsCensoredLanguage(const std::string &message) { - if (message.length() < REDACTION_LENGTH_MIN || message.length() >= 4096) + if (message.length() < REDACTION_LENGTH_MIN || message.length() >= 4096) { return false; + } - std::string test_message(message); - - std::transform(test_message.begin(), test_message.end(), test_message.begin(), [](unsigned char c) -> unsigned char { return tolower(c); }); + std::string test_message = str_tolower(message); for (const auto &iter : profanity_list) { - if (test_message.find(iter) != std::string::npos) + if (test_message.find(iter) != std::string::npos) { return true; + } } return false; @@ -197,26 +210,28 @@ const std::list &EQ::ProfanityManager::GetProfanityList() { } bool EQ::ProfanityManager::IsCensorshipActive() { - return (profanity_list.size() != 0); + return profanity_list.size() != 0; } bool EQ::ProfanityManager::load_database_entries(DBcore *db) { - if (!db) + if (!db) { return false; + } profanity_list.clear(); std::string query = "SELECT `word` FROM `profanity_list`"; auto results = db->QueryDatabase(query); - if (!results.Success()) + if (!results.Success()) { return false; + } - for (auto row = results.begin(); row != results.end(); ++row) { - if (std::strlen(row[0]) >= REDACTION_LENGTH_MIN) { - std::string entry(row[0]); - std::transform(entry.begin(), entry.end(), entry.begin(), [](unsigned char c) -> unsigned char { return tolower(c); }); - if (!check_for_existing_entry(entry.c_str())) - profanity_list.push_back((std::string)entry); + for (auto row : results) { + std::string entry = str_tolower(row[0]); + if (entry.length() >= REDACTION_LENGTH_MIN) { + if (!check_for_existing_entry(entry)) { + profanity_list.push_back(entry); + } } } @@ -224,26 +239,31 @@ bool EQ::ProfanityManager::load_database_entries(DBcore *db) { } bool EQ::ProfanityManager::clear_database_entries(DBcore *db) { - if (!db) + if (!db) { return false; + } profanity_list.clear(); std::string query = "DELETE FROM `profanity_list`"; auto results = db->QueryDatabase(query); - if (!results.Success()) + + if (!results.Success()) { return false; + } return true; } -bool EQ::ProfanityManager::check_for_existing_entry(const char *profanity) { - if (!profanity) +bool EQ::ProfanityManager::check_for_existing_entry(std::string profanity) { + if (profanity.empty()) { return false; + } for (const auto &iter : profanity_list) { - if (iter.compare(profanity) == 0) + if (!iter.compare(profanity)) { return true; + } } return false; diff --git a/common/profanity_manager.h b/common/profanity_manager.h index a1ad93a86..3f4291fd7 100644 --- a/common/profanity_manager.h +++ b/common/profanity_manager.h @@ -22,6 +22,7 @@ #include #include +#include class DBcore; @@ -34,13 +35,12 @@ namespace EQ static bool UpdateProfanityList(DBcore *db); static bool DeleteProfanityList(DBcore *db); - static bool AddProfanity(DBcore *db, const char *profanity); - static bool RemoveProfanity(DBcore *db, const char *profanity); + static bool AddProfanity(DBcore *db, std::string profanity); + static bool RemoveProfanity(DBcore *db, std::string profanity); static void RedactMessage(char *message); static void RedactMessage(std::string &message); - static bool ContainsCensoredLanguage(const char *message); static bool ContainsCensoredLanguage(const std::string &message); static const std::list &GetProfanityList(); @@ -53,7 +53,7 @@ namespace EQ private: static bool load_database_entries(DBcore *db); static bool clear_database_entries(DBcore *db); - static bool check_for_existing_entry(const char *profanity); + static bool check_for_existing_entry(std::string profanity); }; diff --git a/zone/gm_commands/profanity.cpp b/zone/gm_commands/profanity.cpp index 6774c43c6..bea088690 100755 --- a/zone/gm_commands/profanity.cpp +++ b/zone/gm_commands/profanity.cpp @@ -7,68 +7,149 @@ extern WorldServer worldserver; void command_profanity(Client *c, const Seperator *sep) { - std::string arg1(sep->arg[1]); - - while (true) { - if (arg1.compare("list") == 0) { - // do nothing - } - else if (arg1.compare("clear") == 0) { - EQ::ProfanityManager::DeleteProfanityList(&database); - auto pack = new ServerPacket(ServerOP_RefreshCensorship); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else if (arg1.compare("add") == 0) { - if (!EQ::ProfanityManager::AddProfanity(&database, sep->arg[2])) { - c->Message(Chat::Red, "Could not add '%s' to the profanity list.", sep->arg[2]); - } - auto pack = new ServerPacket(ServerOP_RefreshCensorship); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else if (arg1.compare("del") == 0) { - if (!EQ::ProfanityManager::RemoveProfanity(&database, sep->arg[2])) { - c->Message(Chat::Red, "Could not delete '%s' from the profanity list.", sep->arg[2]); - } - auto pack = new ServerPacket(ServerOP_RefreshCensorship); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else if (arg1.compare("reload") == 0) { - if (!EQ::ProfanityManager::UpdateProfanityList(&database)) { - c->Message(Chat::Red, "Could not reload the profanity list."); - } - auto pack = new ServerPacket(ServerOP_RefreshCensorship); - worldserver.SendPacket(pack); - safe_delete(pack); - } - else { - break; - } - - std::string popup; - const auto &list = EQ::ProfanityManager::GetProfanityList(); - for (const auto &iter : list) { - popup.append(iter); - popup.append("
"); - } - if (list.empty()) { - popup.append("** Censorship Inactive **
"); - } - else { - popup.append("** End of List **
"); - } - - c->SendPopupToClient("Profanity List", popup.c_str()); - + int arguments = sep->argnum; + if (!arguments) { + c->Message(Chat::White, "Usage: #profanity add [Word] - Adds a word to the profanity list"); + c->Message(Chat::White, "Usage: #profanity clear - Deletes all profanity list"); + c->Message(Chat::White, "Usage: #profanity delete [Word] - Deletes a word from the profanity list"); + c->Message(Chat::White, "Usage: #profanity list - Shows the profanity list"); + c->Message(Chat::White, "Usage: #profanity reload - Reloads the profanity list"); + return; + } + + bool is_add = !strcasecmp(sep->arg[1], "add"); + bool is_clear = !strcasecmp(sep->arg[1], "clear"); + bool is_delete = !strcasecmp(sep->arg[1], "delete"); + bool is_list = !strcasecmp(sep->arg[1], "list"); + bool is_reload = !strcasecmp(sep->arg[1], "reload"); + if ( + !is_add && + !is_clear && + !is_delete && + !is_list && + !is_reload + ) { + c->Message(Chat::White, "Usage: #profanity add [Word] - Adds a word to the profanity list"); + c->Message(Chat::White, "Usage: #profanity clear - Deletes all profanity list"); + c->Message(Chat::White, "Usage: #profanity delete [Word] - Deletes a word from the profanity list"); + c->Message(Chat::White, "Usage: #profanity list - Shows the profanity list"); + c->Message(Chat::White, "Usage: #profanity reload - Reloads the profanity list"); return; } - c->Message(Chat::White, "Usage: #profanity [list] - shows profanity list"); - c->Message(Chat::White, "Usage: #profanity [clear] - deletes all entries"); - c->Message(Chat::White, "Usage: #profanity [add] [] - adds entry"); - c->Message(Chat::White, "Usage: #profanity [del] [] - deletes entry"); - c->Message(Chat::White, "Usage: #profanity [reload] - reloads profanity list"); + if (is_add) { + if (arguments < 2) { + c->Message(Chat::White, "Usage: #profanity add [Word] - Adds a word to the profanity list"); + return; + } + + std::string profanity = sep->arg[2]; + if (!EQ::ProfanityManager::AddProfanity(&database, profanity)) { + c->Message( + Chat::White, + fmt::format( + "Could not add '{}' to the profanity list.", + profanity + ).c_str() + ); + return; + } else { + c->Message( + Chat::White, + fmt::format( + "Added '{}' to the profanity list.", + profanity + ).c_str() + ); + } + + auto pack = new ServerPacket(ServerOP_RefreshCensorship); + worldserver.SendPacket(pack); + safe_delete(pack); + } else if (is_clear) { + if (!EQ::ProfanityManager::DeleteProfanityList(&database)) { + c->Message(Chat::White, "Could not clear the profanity list."); + return; + } else { + c->Message(Chat::White, "Cleared the profanity list."); + } + + auto pack = new ServerPacket(ServerOP_RefreshCensorship); + worldserver.SendPacket(pack); + safe_delete(pack); + } else if (is_delete) { + if (arguments < 2) { + c->Message(Chat::White, "Usage: #profanity delete [Word] - Deletes a word from the profanity list"); + return; + } + + std::string profanity = sep->arg[2]; + if (!EQ::ProfanityManager::RemoveProfanity(&database, profanity)) { + c->Message( + Chat::White, + fmt::format( + "Could not delete '{}' from the profanity list.", + profanity + ).c_str() + ); + return; + } else { + c->Message( + Chat::White, + fmt::format( + "Deleted '{}' from the profanity list.", + profanity + ).c_str() + ); + } + + auto pack = new ServerPacket(ServerOP_RefreshCensorship); + worldserver.SendPacket(pack); + safe_delete(pack); + } else if (is_list) { + std::string popup_message; + std::string popup_title; + if (!EQ::ProfanityManager::IsCensorshipActive()) { + popup_title = "Profanity List [Empty]"; + popup_message = "The profanity list is empty."; + } else { + auto profanity_index = 1; + auto profanity_list = EQ::ProfanityManager::GetProfanityList(); + + popup_title = fmt::format( + "Profanity List [{} Entr{}]", + profanity_list.size(), + profanity_list.size() != 1 ? "ies" : "y" + ); + + for (const auto& profanity : profanity_list) { + popup_message.append( + fmt::format( + "{}. {}
", + profanity_index, + profanity + ) + ); + + profanity_index++; + } + } + + c->SendPopupToClient( + popup_title.c_str(), + popup_message.c_str() + ); + } else if (is_reload) { + if (!EQ::ProfanityManager::UpdateProfanityList(&database)) { + c->Message(Chat::White, "Could not reload the profanity list."); + return; + } else { + c->Message(Chat::White, "Reloaded the profanity list."); + } + + auto pack = new ServerPacket(ServerOP_RefreshCensorship); + worldserver.SendPacket(pack); + safe_delete(pack); + } }