scar-chat7/shared/protocol/message.cpp

322 lines
12 KiB
C++
Raw Normal View History

2025-12-07 12:00:44 -07:00
#include "message.h"
#include <stdexcept>
#include <cstring>
#include <iostream>
#include <iomanip>
namespace scar {
constexpr uint8_t PROTOCOL_VERSION = 1;
Message::Message(MessageType type) {
header_.length = sizeof(MessageHeader);
header_.type = type;
header_.version = PROTOCOL_VERSION;
header_.reserved = 0;
}
std::vector<uint8_t> Message::serialize() const {
std::vector<uint8_t> buffer;
buffer.resize(header_.length);
// Copy header
std::memcpy(buffer.data(), &header_, sizeof(MessageHeader));
// Copy payload
if (!payload_.empty()) {
std::memcpy(buffer.data() + sizeof(MessageHeader), payload_.data(), payload_.size());
}
return buffer;
}
void Message::setPayload(const std::vector<uint8_t>& data) {
payload_ = data;
header_.length = sizeof(MessageHeader) + payload_.size();
}
std::unique_ptr<Message> Message::deserialize(const std::vector<uint8_t>& data) {
if (data.size() < sizeof(MessageHeader)) {
throw std::runtime_error("Invalid message: too short");
}
MessageHeader header;
std::memcpy(&header, data.data(), sizeof(MessageHeader));
if (header.length != data.size()) {
throw std::runtime_error("Invalid message: length mismatch");
}
std::vector<uint8_t> payload(data.begin() + sizeof(MessageHeader), data.end());
// Dispatch to specific message types
switch (header.type) {
case MessageType::LOGIN_REQUEST:
return LoginRequest::deserialize(payload);
case MessageType::LOGIN_RESPONSE:
return LoginResponse::deserialize(payload);
case MessageType::TEXT_MESSAGE:
return TextMessage::deserialize(payload);
default:
throw std::runtime_error("Unknown message type");
}
}
// LoginRequest implementation
LoginRequest::LoginRequest(const std::string& username, const std::string& password)
: Message(MessageType::LOGIN_REQUEST), username_(username), password_(password) {
std::cout << "LoginRequest constructor - Username: '" << username << "', Password length: " << password.length() << std::endl;
}
std::vector<uint8_t> LoginRequest::serialize() const {
std::cout << "LoginRequest::serialize - Username: '" << username_ << "', Password length: " << password_.length() << std::endl;
std::vector<uint8_t> payload;
// Username length + username
uint16_t username_len = username_.size();
std::cout << " Serializing username_len: " << username_len << std::endl;
payload.insert(payload.end(), reinterpret_cast<const uint8_t*>(&username_len),
reinterpret_cast<const uint8_t*>(&username_len) + sizeof(username_len));
payload.insert(payload.end(), username_.begin(), username_.end());
// Password length + password
uint16_t password_len = password_.size();
std::cout << " Serializing password_len: " << password_len << std::endl;
payload.insert(payload.end(), reinterpret_cast<const uint8_t*>(&password_len),
reinterpret_cast<const uint8_t*>(&password_len) + sizeof(password_len));
payload.insert(payload.end(), password_.begin(), password_.end());
std::cout << " Total payload size: " << payload.size() << std::endl;
const_cast<LoginRequest*>(this)->setPayload(payload);
return Message::serialize();
}
std::unique_ptr<LoginRequest> LoginRequest::deserialize(const std::vector<uint8_t>& payload) {
std::cout << "LoginRequest::deserialize - Payload size: " << payload.size() << std::endl;
// Debug: print raw bytes
std::cout << " Raw payload bytes: ";
for (size_t i = 0; i < std::min(payload.size(), size_t(20)); ++i) {
std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)payload[i] << " ";
}
std::cout << std::dec << std::endl;
size_t offset = 0;
// Read username
if (offset + sizeof(uint16_t) > payload.size()) {
throw std::runtime_error("Invalid LoginRequest: truncated username length");
}
uint16_t username_len;
std::memcpy(&username_len, payload.data() + offset, sizeof(username_len));
offset += sizeof(username_len);
std::cout << " Username length (raw bytes): " << (int)payload[0] << " " << (int)payload[1] << std::endl;
std::cout << " Username length (uint16_t): " << username_len << std::endl;
if (offset + username_len > payload.size()) {
throw std::runtime_error("Invalid LoginRequest: truncated username");
}
std::string username(payload.begin() + offset, payload.begin() + offset + username_len);
offset += username_len;
std::cout << " Username: '" << username << "'" << std::endl;
// Read password
if (offset + sizeof(uint16_t) > payload.size()) {
throw std::runtime_error("Invalid LoginRequest: truncated password length");
}
uint16_t password_len;
std::memcpy(&password_len, payload.data() + offset, sizeof(password_len));
offset += sizeof(password_len);
std::cout << " Password length: " << password_len << std::endl;
if (offset + password_len > payload.size()) {
throw std::runtime_error("Invalid LoginRequest: truncated password");
}
std::string password(payload.begin() + offset, payload.begin() + offset + password_len);
std::cout << " Password: (hidden, length=" << password.length() << ")" << std::endl;
return std::make_unique<LoginRequest>(username, password);
}
// LoginResponse implementation
LoginResponse::LoginResponse(bool success, const std::string& token, ErrorCode error)
: Message(MessageType::LOGIN_RESPONSE), success_(success), token_(token), error_(error) {}
std::vector<uint8_t> LoginResponse::serialize() const {
std::vector<uint8_t> payload;
// Success flag
payload.push_back(success_ ? 1 : 0);
// Error code
uint16_t error_code = static_cast<uint16_t>(error_);
payload.insert(payload.end(), reinterpret_cast<const uint8_t*>(&error_code),
reinterpret_cast<const uint8_t*>(&error_code) + sizeof(error_code));
// Token length + token
uint16_t token_len = token_.size();
payload.insert(payload.end(), reinterpret_cast<const uint8_t*>(&token_len),
reinterpret_cast<const uint8_t*>(&token_len) + sizeof(token_len));
payload.insert(payload.end(), token_.begin(), token_.end());
const_cast<LoginResponse*>(this)->setPayload(payload);
return Message::serialize();
}
std::unique_ptr<LoginResponse> LoginResponse::deserialize(const std::vector<uint8_t>& payload) {
size_t offset = 0;
// Read success flag
if (offset >= payload.size()) {
throw std::runtime_error("Invalid LoginResponse: missing success flag");
}
bool success = payload[offset++] != 0;
// Read error code
if (offset + sizeof(uint16_t) > payload.size()) {
throw std::runtime_error("Invalid LoginResponse: truncated error code");
}
uint16_t error_code;
std::memcpy(&error_code, payload.data() + offset, sizeof(error_code));
offset += sizeof(error_code);
ErrorCode error = static_cast<ErrorCode>(error_code);
// Read token
if (offset + sizeof(uint16_t) > payload.size()) {
throw std::runtime_error("Invalid LoginResponse: truncated token length");
}
uint16_t token_len;
std::memcpy(&token_len, payload.data() + offset, sizeof(token_len));
offset += sizeof(token_len);
if (offset + token_len > payload.size()) {
throw std::runtime_error("Invalid LoginResponse: truncated token");
}
std::string token(payload.begin() + offset, payload.begin() + offset + token_len);
return std::make_unique<LoginResponse>(success, token, error);
}
// TextMessage implementation
TextMessage::TextMessage(const std::string& sender, const std::string& content)
: Message(MessageType::TEXT_MESSAGE), sender_(sender), content_(content) {}
std::vector<uint8_t> TextMessage::serialize() const {
std::vector<uint8_t> payload;
// Sender length + sender
uint16_t sender_len = sender_.size();
payload.insert(payload.end(), reinterpret_cast<const uint8_t*>(&sender_len),
reinterpret_cast<const uint8_t*>(&sender_len) + sizeof(sender_len));
payload.insert(payload.end(), sender_.begin(), sender_.end());
// Content length + content
uint16_t content_len = content_.size();
payload.insert(payload.end(), reinterpret_cast<const uint8_t*>(&content_len),
reinterpret_cast<const uint8_t*>(&content_len) + sizeof(content_len));
payload.insert(payload.end(), content_.begin(), content_.end());
const_cast<TextMessage*>(this)->setPayload(payload);
return Message::serialize();
}
std::unique_ptr<TextMessage> TextMessage::deserialize(const std::vector<uint8_t>& payload) {
size_t offset = 0;
// Read sender
if (offset + sizeof(uint16_t) > payload.size()) {
throw std::runtime_error("Invalid TextMessage: truncated sender length");
}
uint16_t sender_len;
std::memcpy(&sender_len, payload.data() + offset, sizeof(sender_len));
offset += sizeof(sender_len);
if (offset + sender_len > payload.size()) {
throw std::runtime_error("Invalid TextMessage: truncated sender");
}
std::string sender(payload.begin() + offset, payload.begin() + offset + sender_len);
offset += sender_len;
// Read content
if (offset + sizeof(uint16_t) > payload.size()) {
throw std::runtime_error("Invalid TextMessage: truncated content length");
}
uint16_t content_len;
std::memcpy(&content_len, payload.data() + offset, sizeof(content_len));
offset += sizeof(content_len);
if (offset + content_len > payload.size()) {
throw std::runtime_error("Invalid TextMessage: truncated content");
}
std::string content(payload.begin() + offset, payload.begin() + offset + content_len);
return std::make_unique<TextMessage>(sender, content);
}
// ScreenShareStart implementation
ScreenShareStart::ScreenShareStart(uint32_t width, uint32_t height)
: Message(MessageType::SCREEN_SHARE_START), width_(width), height_(height) {
}
std::vector<uint8_t> ScreenShareStart::serialize() const {
std::vector<uint8_t> data;
data.resize(sizeof(width_) + sizeof(height_));
size_t offset = 0;
std::memcpy(data.data() + offset, &width_, sizeof(width_));
offset += sizeof(width_);
std::memcpy(data.data() + offset, &height_, sizeof(height_));
const_cast<ScreenShareStart*>(this)->setPayload(data);
return Message::serialize();
}
std::unique_ptr<ScreenShareStart> ScreenShareStart::deserialize(const std::vector<uint8_t>& payload) {
if (payload.size() < sizeof(uint32_t) * 2) {
throw std::runtime_error("Invalid ScreenShareStart: payload too small");
}
uint32_t width, height;
size_t offset = 0;
std::memcpy(&width, payload.data() + offset, sizeof(width));
offset += sizeof(width);
std::memcpy(&height, payload.data() + offset, sizeof(height));
return std::make_unique<ScreenShareStart>(width, height);
}
// ScreenShareData implementation
ScreenShareData::ScreenShareData(const std::vector<uint8_t>& frameData)
: Message(MessageType::SCREEN_SHARE_DATA), frameData_(frameData) {
}
std::vector<uint8_t> ScreenShareData::serialize() const {
const_cast<ScreenShareData*>(this)->setPayload(frameData_);
return Message::serialize();
}
std::unique_ptr<ScreenShareData> ScreenShareData::deserialize(const std::vector<uint8_t>& payload) {
return std::make_unique<ScreenShareData>(payload);
}
// ScreenShareStop implementation
ScreenShareStop::ScreenShareStop()
: Message(MessageType::SCREEN_SHARE_STOP) {
}
std::vector<uint8_t> ScreenShareStop::serialize() const {
return Message::serialize();
}
std::unique_ptr<ScreenShareStop> ScreenShareStop::deserialize(const std::vector<uint8_t>& payload) {
return std::make_unique<ScreenShareStop>();
}
2025-12-07 12:00:44 -07:00
} // namespace scar