diff --git a/dChatFilter/dChatFilter.cpp b/dChatFilter/dChatFilter.cpp index 844e3411..63ba472c 100644 --- a/dChatFilter/dChatFilter.cpp +++ b/dChatFilter/dChatFilter.cpp @@ -105,7 +105,7 @@ void dChatFilter::ExportWordlistToDCF(const std::string& filepath, bool allowLis } } -std::vector> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList) { +std::set> dChatFilter::IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList) { if (gmLevel > eGameMasterLevel::FORUM_MODERATOR) return { }; //If anything but a forum mod, return true. if (message.empty()) return { }; if (!allowList && m_DeniedWords.empty()) return { { 0, message.length() } }; @@ -114,7 +114,7 @@ std::vector> dChatFilter::IsSentenceOkay(const std:: std::string segment; std::regex reg("(!*|\\?*|\\;*|\\.*|\\,*)"); - std::vector> listOfBadSegments = std::vector>(); + std::set> listOfBadSegments = std::set>(); uint32_t position = 0; @@ -127,17 +127,17 @@ std::vector> dChatFilter::IsSentenceOkay(const std:: size_t hash = CalculateHash(segment); if (std::find(m_UserUnapprovedWordCache.begin(), m_UserUnapprovedWordCache.end(), hash) != m_UserUnapprovedWordCache.end() && allowList) { - listOfBadSegments.emplace_back(position, originalSegment.length()); + listOfBadSegments.emplace(position, originalSegment.length()); } if (std::find(m_ApprovedWords.begin(), m_ApprovedWords.end(), hash) == m_ApprovedWords.end() && allowList) { m_UserUnapprovedWordCache.push_back(hash); - listOfBadSegments.emplace_back(position, originalSegment.length()); + listOfBadSegments.emplace(position, originalSegment.length()); } if (std::find(m_DeniedWords.begin(), m_DeniedWords.end(), hash) != m_DeniedWords.end() && !allowList) { m_UserUnapprovedWordCache.push_back(hash); - listOfBadSegments.emplace_back(position, originalSegment.length()); + listOfBadSegments.emplace(position, originalSegment.length()); } position += originalSegment.length() + 1; diff --git a/dChatFilter/dChatFilter.h b/dChatFilter/dChatFilter.h index 0f1e49ba..ce2fc5f0 100644 --- a/dChatFilter/dChatFilter.h +++ b/dChatFilter/dChatFilter.h @@ -24,7 +24,7 @@ public: void ReadWordlistPlaintext(const std::string& filepath, bool allowList); bool ReadWordlistDCF(const std::string& filepath, bool allowList); void ExportWordlistToDCF(const std::string& filepath, bool allowList); - std::vector> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList = true); + std::set> IsSentenceOkay(const std::string& message, eGameMasterLevel gmLevel, bool allowList = true); private: bool m_DontGenerateDCF; diff --git a/dChatServer/CMakeLists.txt b/dChatServer/CMakeLists.txt index c281cbbe..ae574cf6 100644 --- a/dChatServer/CMakeLists.txt +++ b/dChatServer/CMakeLists.txt @@ -11,7 +11,7 @@ target_include_directories(ChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dChatFilter add_compile_definitions(ChatServer PRIVATE PROJECT_VERSION="\"${PROJECT_VERSION}\"") add_library(dChatServer ${DCHATSERVER_SOURCES}) -target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer") +target_include_directories(dChatServer PRIVATE "${PROJECT_SOURCE_DIR}/dServer" "${PROJECT_SOURCE_DIR}/dChatFilter") target_link_libraries(dChatServer ${COMMON_LIBRARIES} dChatFilter) target_link_libraries(ChatServer ${COMMON_LIBRARIES} dChatFilter dChatServer dServer dWeb) diff --git a/dChatServer/ChatPacketHandler.cpp b/dChatServer/ChatPacketHandler.cpp index ab19886b..90a66845 100644 --- a/dChatServer/ChatPacketHandler.cpp +++ b/dChatServer/ChatPacketHandler.cpp @@ -436,21 +436,20 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) { data.sender = Game::playerContainer.GetPlayerData(sender); if (!data.sender || data.sender.GetIsMuted()) return; - eChatChannel channel; uint32_t size; inStream.IgnoreBytes(4); inStream.Read(data.channel); inStream.Read(size); inStream.IgnoreBytes(77); - LOG("message size: %u", size); + data.message = LUWString(size); inStream.Read(data.message); LOG("Got message from (%s) via [%s]: %s", data.sender.playerName.c_str(), StringifiedEnum::ToString(data.channel).data(), data.message.GetAsString().c_str()); - switch (channel) { + switch (data.channel) { case eChatChannel::LOCAL: { break; } @@ -467,7 +466,7 @@ void ChatPacketHandler::HandleChatMessage(Packet* packet) { break; } default: - LOG("Unhandled Chat channel [%s]", StringifiedEnum::ToString(channel).data()); + LOG("Unhandled Chat channel [%s]", StringifiedEnum::ToString(data.channel).data()); break; } ChatWeb::SendWSChatMessage(data); @@ -526,7 +525,7 @@ void ChatPacketHandler::HandlePrivateChatMessage(Packet* packet) { SendPrivateChatMessage(data.sender, data.receiver, data.sender, data.message, data.channel, eChatMessageResponseCode::SENT); // To the receiver: SendPrivateChatMessage(data.sender, data.receiver, data.receiver, data.message, data.channel, eChatMessageResponseCode::RECEIVEDNEWWHISPER); - // To the WebSocket + // To the websocket: ChatWeb::SendWSChatMessage(data); return; } diff --git a/dChatServer/ChatPacketHandler.h b/dChatServer/ChatPacketHandler.h index e29a150f..03b50001 100644 --- a/dChatServer/ChatPacketHandler.h +++ b/dChatServer/ChatPacketHandler.h @@ -2,8 +2,7 @@ #include "dCommonVars.h" #include "dNetCommon.h" #include "BitStream.h" - -struct PlayerData; +#include "PlayerContainer.h" enum class eAddFriendResponseType : uint8_t; diff --git a/dChatServer/ChatWeb.cpp b/dChatServer/ChatWeb.cpp index 6216fdd6..bc24901e 100644 --- a/dChatServer/ChatWeb.cpp +++ b/dChatServer/ChatWeb.cpp @@ -16,6 +16,8 @@ #include "Database.h" #include "ChatJSONUtils.h" #include "JSONUtils.h" +#include "eGameMasterLevel.h" +#include "dChatFilter.h" using json = nlohmann::json; @@ -57,15 +59,53 @@ void HandleHTTPAnnounceRequest(HTTPReply& reply, std::string body) { } void HandleWSChat(mg_connection* connection, json data) { - auto check = JSONUtils::CheckRequiredData(data, { "user", "message" }); + auto check = JSONUtils::CheckRequiredData(data, { "user", "message", "gmlevel", "zone" }); if (!check.empty()) { LOG_DEBUG("Received invalid websocket message: %s", check.c_str()); } else { const auto user = data["user"].get(); const auto message = data["message"].get(); - LOG_DEBUG("EXTERNAL Chat message from %s: %s", user.c_str(), message.c_str()); - // TODO: use chat filter and respond if the message isn't allowed - // TODO: Send chat message to corret world server to broadcast to players + const auto gmlevel = GeneralUtils::TryParse(data["gmlevel"].get()).value_or(eGameMasterLevel::CIVILIAN); + const auto zone = data["zone"].get(); + + const auto filter_check = Game::chatFilter->IsSentenceOkay(message, gmlevel); + if (!filter_check.empty()) { + LOG_DEBUG("Chat message \"%s\" from %s was not allowed", message.c_str(), user.c_str()); + data["error"] = "Chat message blocked by filter"; + data["filtered"] = json::array(); + for (const auto& filtered : filter_check) { + data["filtered"].push_back(message.substr(filtered.first, filtered.second)); + } + mg_ws_send(connection, data.dump().c_str(), data.dump().size(), WEBSOCKET_OP_TEXT); + return; + } + LOG("%s: %s", user.c_str(), message.c_str()); + + // bodge to test + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GENERAL_CHAT_MESSAGE); + bitStream.Write(zone); + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GENERAL_CHAT_MESSAGE); + + bitStream.Write(0); + bitStream.Write(eChatChannel::LOCAL); + + bitStream.Write(message.size()); + bitStream.Write(LUWString(user)); + + bitStream.Write(0); + bitStream.Write(0); + bitStream.Write(0); + + for (uint32_t i = 0; i < message.size(); ++i) { + bitStream.Write(message[i]); + } + bitStream.Write(0); + Game::server->Send(bitStream, UNASSIGNED_SYSTEM_ADDRESS, true); + + // send to world servers with macthing zone + // Cry: this is the hard part since there is no instance manager + // Do we send it to master and let master sort it out via instance manager? } } @@ -110,7 +150,7 @@ namespace ChatWeb { json data; data["player_data"] = player; data["update_type"] = magic_enum::enum_name(activityType); - Game::web.SendWSMessage("player_update", data); + Game::web.SendWSMessage("player", data); } void SendWSChatMessage(const ChatMessage& chatMessage) { diff --git a/dNet/WorldPackets.cpp b/dNet/WorldPackets.cpp index ddd16c3e..512f2265 100644 --- a/dNet/WorldPackets.cpp +++ b/dNet/WorldPackets.cpp @@ -134,7 +134,7 @@ void WorldPackets::SendCreateCharacter(const SystemAddress& sysAddr, int64_t rep LOG("Sent CreateCharacter for ID: %llu", player); } -void WorldPackets::SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::vector> unacceptedItems) { +void WorldPackets::SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::set> unacceptedItems) { CBITSTREAM; BitStreamUtils::WriteHeader(bitStream, eConnectionType::CLIENT, MessageType::Client::CHAT_MODERATION_STRING); diff --git a/dNet/WorldPackets.h b/dNet/WorldPackets.h index 0081623e..0da98604 100644 --- a/dNet/WorldPackets.h +++ b/dNet/WorldPackets.h @@ -32,7 +32,7 @@ namespace WorldPackets { void SendTransferToWorld(const SystemAddress& sysAddr, const std::string& serverIP, uint32_t serverPort, bool mythranShift); void SendServerState(const SystemAddress& sysAddr); void SendCreateCharacter(const SystemAddress& sysAddr, int64_t reputation, LWOOBJID player, const std::string& xmlData, const std::u16string& username, eGameMasterLevel gm); - void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::vector> unacceptedItems); + void SendChatModerationResponse(const SystemAddress& sysAddr, bool requestAccepted, uint32_t requestID, const std::string& receiver, std::set> unacceptedItems); void SendGMLevelChange(const SystemAddress& sysAddr, bool success, eGameMasterLevel highestLevel, eGameMasterLevel prevLevel, eGameMasterLevel newLevel); void SendHTTPMonitorInfo(const SystemAddress& sysAddr, const HTTPMonitorInfo& info); void SendDebugOuput(const SystemAddress& sysAddr, const std::string& data); diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index 040241e3..98410a48 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -647,6 +647,21 @@ void HandlePacketChat(Packet* packet) { break; } + case MessageType::Chat::GENERAL_CHAT_MESSAGE: { + // First get the zone and check if we should forward it + CINSTREAM_SKIP_HEADER; + uint32_t zoneID; + inStream.Read(zoneID); + if (zoneID != Game::server->GetZoneID()) return; + //Write our stream outwards: + CBITSTREAM; + unsigned char data; + while (inStream.Read(data)) { + bitStream.Write(data); + } + SEND_PACKET_BROADCAST; + break; + } default: LOG("Received an unknown chat: %i", int(packet->data[3])); } @@ -1288,7 +1303,7 @@ void HandlePacket(Packet* packet) { } } - std::vector> segments = Game::chatFilter->IsSentenceOkay(request.message, entity->GetGMLevel(), !(isBestFriend && request.chatLevel == 1)); + auto segments = Game::chatFilter->IsSentenceOkay(request.message, entity->GetGMLevel(), !(isBestFriend && request.chatLevel == 1)); bool bAllClean = segments.empty(); @@ -1327,25 +1342,29 @@ void HandlePacket(Packet* packet) { std::string sMessage = GeneralUtils::UTF16ToWTF8(chatMessage.message); LOG("%s: %s", playerName.c_str(), sMessage.c_str()); ChatPackets::SendChatMessage(packet->systemAddress, chatMessage.chatChannel, playerName, user->GetLoggedInChar(), isMythran, chatMessage.message); + { + // TODO: make it so we don't write this manually, but instead use a proper read and writes + // aka: this is awful and should be fixed, but I can't be bothered to do it right now + // Forward to the chat server + CBITSTREAM; + BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GENERAL_CHAT_MESSAGE); - CBITSTREAM; - BitStreamUtils::WriteHeader(bitStream, eConnectionType::CHAT, MessageType::Chat::GENERAL_CHAT_MESSAGE); + bitStream.Write(user->GetLoggedInChar()); + bitStream.Write(chatMessage.message.size()); + bitStream.Write(chatMessage.chatChannel); + bitStream.Write(chatMessage.message.size()); - bitStream.Write(user->GetLoggedInChar()); - bitStream.Write(chatMessage.message.size()); - bitStream.Write(chatMessage.chatChannel); - bitStream.Write(chatMessage.message.size()); + for (uint32_t i = 0; i < 77; ++i) { + bitStream.Write(0); + } - for (uint32_t i = 0; i < 77; ++i) { - bitStream.Write(0); + for (uint32_t i = 0; i < chatMessage.message.size(); ++i) { + bitStream.Write(chatMessage.message[i]); + } + bitStream.Write(0); + Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, Game::chatSysAddr, false); } - for (uint32_t i = 0; i < chatMessage.message.size(); ++i) { - bitStream.Write(chatMessage.message[i]); - } - bitStream.Write(0); - Game::chatServer->Send(&bitStream, SYSTEM_PRIORITY, RELIABLE_ORDERED, 0, Game::chatSysAddr, false); - } break;