/*! * * Copyright (c) 2024-2026 Diality Inc. - All Rights Reserved. * \copyright * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * \file CloudConnectServer.cpp * \author (original) Stephen Quong * \date (original) 15-Jul-2026 * */ #include "CloudConnectServer.h" #include /*! * \brief CloudConnectServer::CloudConnectServer * \details Constructor. Wires the server newConnection signal. * \param parent Optional QObject parent. */ CloudConnectServer::CloudConnectServer(QObject *parent) : QObject(parent) { connect(&_server, &QLocalServer::newConnection, this, &CloudConnectServer::onNewConnection); } /*! * \brief CloudConnectServer::listen * \details Removes any stale socket file, then starts the server. * \param socketPath Path to the Unix domain socket. * \return true on success, false if the server could not bind. */ bool CloudConnectServer::listen(const QString &socketPath) { QLocalServer::removeServer(socketPath); if (!_server.listen(socketPath)) { qCritical().noquote() << metaObject()->className() << ": failed to listen on" << socketPath << "—" << _server.errorString(); return false; } qInfo().noquote() << metaObject()->className() << ": listening on" << socketPath; return true; } /*! * \brief CloudConnectServer::send * \details Builds a CloudConnectFrame and writes it to the connected client. * \param msgId Message identifier for the frame header. * \param sequence Caller-managed sequence number. * \param payload Message payload; pass empty for zero-length frames. * \return true if written to the socket, false if no client is connected. */ bool CloudConnectServer::send(CloudConnectFrame::MsgId msgId, quint16 sequence, const QByteArray &payload) { if (_client == nullptr || _client->state() != QLocalSocket::ConnectedState) { return false; } const QByteArray frame = CloudConnectFrame::build(msgId, sequence, payload); _client->write(frame); _client->flush(); return true; } /*! * \brief CloudConnectServer::isConnected * \details Reports whether a client is currently attached to the server. * \return true if a client is currently connected. */ bool CloudConnectServer::isConnected() const { return _client != nullptr; } /*! * \brief CloudConnectServer::onNewConnection * \details Accepts the pending connection. If a client is already connected the * new socket is discarded with a warning. */ void CloudConnectServer::onNewConnection() { if (_client) { qWarning().noquote() << metaObject()->className() << ": second connection attempt rejected — already connected"; _server.nextPendingConnection()->deleteLater(); return; } _client = _server.nextPendingConnection(); qInfo().noquote() << metaObject()->className() << ": client connected"; connect(_client, &QLocalSocket::readyRead, this, &CloudConnectServer::onReadyRead); connect(_client, &QLocalSocket::disconnected, this, &CloudConnectServer::onDisconnected); emit didConnect(); } /*! * \brief CloudConnectServer::onDisconnected * \details Releases the client socket and resets inbound parser state. */ void CloudConnectServer::onDisconnected() { qInfo().noquote() << metaObject()->className() << ": client disconnected"; _client->deleteLater(); _client = nullptr; _rxBuf.clear(); _rxMsg.reset(); emit didDisconnect(); } /*! * \brief CloudConnectServer::onReadyRead * \details Appends incoming bytes to the receive buffer and drains it through * the CloudConnectFrame parser, emitting didMessageReceive() for each * complete frame. */ void CloudConnectServer::onReadyRead() { _rxBuf.append(_client->readAll()); CloudConnectFrame::FeedResult result; do { result = _rxMsg.feed(_rxBuf); switch (result) { case CloudConnectFrame::FeedResult::Complete: emit didMessageReceive(_rxMsg.msgId(), _rxMsg.sequence(), _rxMsg.payload()); _rxMsg.reset(); break; case CloudConnectFrame::FeedResult::HeaderError: qWarning().noquote() << metaObject()->className() << ": header CRC error — frame dropped"; break; case CloudConnectFrame::FeedResult::PayloadError: qWarning().noquote() << metaObject()->className() << ": payload CRC error — frame dropped"; break; case CloudConnectFrame::FeedResult::Incomplete: break; } } while (result == CloudConnectFrame::FeedResult::Complete && !_rxBuf.isEmpty()); }