#ifdef ESP_PLATFORM #include #endif #if defined(CONFIG_SOC_WIFI_SUPPORTED) || defined(CONFIG_SLAVE_SOC_WIFI_SUPPORTED) #include #include #include #include namespace tt::app::chat { bool serializeTextMessage(uint32_t senderId, uint32_t targetId, const std::string& senderName, const std::string& target, const std::string& message, std::vector& out) { // Validate input lengths (min and max) if (senderName.size() < MIN_NICKNAME_LEN || senderName.size() > MAX_NICKNAME_LEN) { return false; } if (target.size() > MAX_TARGET_LEN) { return false; // MIN_TARGET_LEN is 0, so empty (broadcast) is allowed } if (message.size() < MIN_MESSAGE_LEN) { return false; } // Calculate payload size: nickname + null + target + null + message size_t payloadSize = senderName.size() + 1 + target.size() + 1 + message.size(); // Check against ESP-NOW limits (guard against underflow if getMaxDataLength < HEADER_SIZE) size_t maxData = service::espnow::getMaxDataLength(); if (maxData <= HEADER_SIZE) { return false; } size_t maxPayload = maxData - HEADER_SIZE; if (payloadSize > maxPayload || payloadSize > 255) { return false; // payload_size is uint8_t } // Build header MessageHeader header = { .magic = CHAT_MAGIC_V2, .protocol_version = PROTOCOL_VERSION, .from = senderId, .to = targetId, .payload_type = static_cast(PayloadType::TextMessage), .payload_size = static_cast(payloadSize) }; // Allocate output buffer out.resize(HEADER_SIZE + payloadSize); // Copy header to output memcpy(out.data(), &header, HEADER_SIZE); // Build payload: nickname\0 + target\0 + message uint8_t* payload = out.data() + HEADER_SIZE; size_t offset = 0; memcpy(payload + offset, senderName.c_str(), senderName.size() + 1); offset += senderName.size() + 1; memcpy(payload + offset, target.c_str(), target.size() + 1); offset += target.size() + 1; memcpy(payload + offset, message.c_str(), message.size()); // Note: message is NOT null-terminated in wire format (length is implicit) return true; } bool deserializeMessage(const uint8_t* data, size_t length, ParsedMessage& out) { // Minimum: header + min_nickname + null + min_target + null + min_message // = 16 + 2 + 1 + 0 + 1 + 1 = 21 bytes constexpr size_t MIN_PACKET_SIZE = HEADER_SIZE + MIN_NICKNAME_LEN + 1 + MIN_TARGET_LEN + 1 + MIN_MESSAGE_LEN; if (length < MIN_PACKET_SIZE) { return false; } // Copy header to aligned struct MessageHeader header; memcpy(&header, data, HEADER_SIZE); // Validate header if (header.magic != CHAT_MAGIC_V2) { return false; } if (header.protocol_version != PROTOCOL_VERSION) { return false; } // Validate payload size if (header.payload_size != length - HEADER_SIZE) { return false; } // Only handle text messages for now if (header.payload_type != static_cast(PayloadType::TextMessage)) { return false; } // Parse payload const uint8_t* payload = data + HEADER_SIZE; size_t payloadLen = header.payload_size; // Find nickname (null-terminated) const char* nicknameStart = reinterpret_cast(payload); size_t nicknameLen = strnlen(nicknameStart, payloadLen); if (nicknameLen >= payloadLen) { return false; // No null terminator found } size_t offset = nicknameLen + 1; size_t remaining = payloadLen - offset; // Find target (null-terminated) const char* targetStart = reinterpret_cast(payload + offset); size_t targetLen = strnlen(targetStart, remaining); if (targetLen >= remaining) { return false; // No null terminator found } offset += targetLen + 1; remaining = payloadLen - offset; // Rest is the message (not null-terminated) const char* messageStart = reinterpret_cast(payload + offset); // Validate field lengths (min and max) if (nicknameLen < MIN_NICKNAME_LEN || nicknameLen > MAX_NICKNAME_LEN) { return false; } if (targetLen > MAX_TARGET_LEN) { return false; } if (remaining < MIN_MESSAGE_LEN) { return false; } // Populate output out.senderId = header.from; out.targetId = header.to; out.senderName = std::string(nicknameStart, nicknameLen); out.target = std::string(targetStart, targetLen); out.message = std::string(messageStart, remaining); return true; } size_t getMaxMessageLength(size_t nicknameLen, size_t targetLen) { // Guard against underflow if getMaxDataLength < HEADER_SIZE size_t maxData = service::espnow::getMaxDataLength(); if (maxData <= HEADER_SIZE) { return 0; } size_t maxPayload = maxData - HEADER_SIZE; // Payload: nickname + null + target + null + message size_t overhead = nicknameLen + 1 + targetLen + 1; if (overhead >= maxPayload || overhead > 255) { return 0; } // Cap at 255 since payload_size is uint8_t size_t maxFromEspNow = maxPayload - overhead; size_t maxFromPayloadSize = 255 - overhead; return std::min(maxFromEspNow, maxFromPayloadSize); } } // namespace tt::app::chat #endif // CONFIG_SOC_WIFI_SUPPORTED || CONFIG_SLAVE_SOC_WIFI_SUPPORTED