From 1e1da25d40907e5976d3b47c42806b434a402413 Mon Sep 17 00:00:00 2001 From: Nils Brederlow <62596379+dingodoppelt@users.noreply.github.com> Date: Tue, 9 Jun 2026 22:49:47 +0200 Subject: [PATCH 1/5] add jamulusserver/privateChatMessage RPC method for sending messages to single channels --- docs/JSON-RPC.md | 18 ++++++++++++++++++ src/serverrpc.cpp | 18 ++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/docs/JSON-RPC.md b/docs/JSON-RPC.md index 276ffed470..7420715bdb 100644 --- a/docs/JSON-RPC.md +++ b/docs/JSON-RPC.md @@ -430,6 +430,24 @@ Results: | result.registrationStatus | string | The server registration status as string (see ESvrRegStatus and SerializeRegistrationStatus). | +### jamulusserver/privateChatMessage + +Sends a chat message to a single connected client. + +Parameters: + +| Name | Type | Description | +| --- | --- | --- | +| params.chatMessage | string | The chat message text. | +| params.id | number | The client's channel id. | + +Results: + +| Name | Type | Description | +| --- | --- | --- | +| result | string | Always "ok". | + + ### jamulusserver/restartRecording Restarts the recording into a new directory. diff --git a/src/serverrpc.cpp b/src/serverrpc.cpp index 2511a88549..5fdbb0dd59 100644 --- a/src/serverrpc.cpp +++ b/src/serverrpc.cpp @@ -349,6 +349,24 @@ CServerRpc::CServerRpc ( CServer* pServer, CRpcServer* pRpcServer, QObject* pare response["result"] = "acknowledged"; Q_UNUSED ( params ); } ); + + /// @rpc_method jamulusserver/privateChatMessage + /// @brief Sends a chat message to a single connected client. + /// @param {string} params.chatMessage - The chat message text. + /// @param {number} params.id - The client's channel id. + /// @result {string} result - Always "ok". + pRpcServer->HandleMethod ( "jamulusserver/privateChatMessage", [=] ( const QJsonObject& params, QJsonObject& response ) { + auto jsonChatMessage = params["chatMessage"]; + const int id = params["id"].toInt(); + if ( !jsonChatMessage.isString() ) + { + response["error"] = CRpcServer::CreateJsonRpcError ( CRpcServer::iErrInvalidParams, "Invalid params: chatMessage is not a string" ); + return; + } + + pServer->SendChatTextToConChannel ( id, jsonChatMessage.toString() ); + response["result"] = "ok"; + } ); } #if defined( Q_OS_MACOS ) && QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) From 0b30abf18ae4135f5001b449441b26342f8eff9b Mon Sep 17 00:00:00 2001 From: Nils Brederlow <62596379+dingodoppelt@users.noreply.github.com> Date: Sat, 27 Jun 2026 14:36:54 +0200 Subject: [PATCH 2/5] Check for invalid channel ID before sending private chat message and send corresponding response --- src/server.cpp | 7 ++++++- src/server.h | 2 +- src/serverrpc.cpp | 40 ++++++++++++++++++++++------------------ 3 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/server.cpp b/src/server.cpp index e44940c37d..e68cdd6466 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1379,13 +1379,18 @@ void CServer::SendChatTextToAllConChannels ( const int iSendingChanID, const QSt emit sentChatMessage ( iSendingChanID, strChatText ); } -void CServer::SendChatTextToConChannel ( const int iCurChanID, const QString& strChatText ) +bool CServer::SendChatTextToConChannel ( const int iCurChanID, const QString& strChatText ) { if ( MathUtils::InRange ( iCurChanID, 0, iMaxNumChannels - 1 ) && vecChannels[iCurChanID].IsConnected() ) { // send message vecChannels[iCurChanID].CreateChatTextMes ( strChatText ); } + else + { + return true; + } + return false; } void CServer::CreateAndSendRecorderStateForAllConChannels() diff --git a/src/server.h b/src/server.h index 1d828c86f5..b92f3f2656 100644 --- a/src/server.h +++ b/src/server.h @@ -192,7 +192,7 @@ class CServer : public QObject, public CServerSlots bool IsDelayPanningEnabled() { return bDelayPan; } void SendChatTextToAllConChannels ( const int iSendingChanID, const QString& strChatText ); - void SendChatTextToConChannel ( const int iCurChanID, const QString& strChatText ); + bool SendChatTextToConChannel ( const int iCurChanID, const QString& strChatText ); protected: // access functions for actual channels diff --git a/src/serverrpc.cpp b/src/serverrpc.cpp index 5fdbb0dd59..61a3b111d5 100644 --- a/src/serverrpc.cpp +++ b/src/serverrpc.cpp @@ -113,6 +113,28 @@ CServerRpc::CServerRpc ( CServer* pServer, CRpcServer* pRpcServer, QObject* pare response["result"] = "ok"; } ); + /// @rpc_method jamulusserver/privateChatMessage + /// @brief Sends a chat message to a single connected client. + /// @param {string} params.chatMessage - The chat message text. + /// @param {number} params.id - The client's channel id. + /// @result {string} result - Always "ok". + pRpcServer->HandleMethod ( "jamulusserver/privateChatMessage", [=] ( const QJsonObject& params, QJsonObject& response ) { + auto jsonChatMessage = params["chatMessage"]; + const int id = params["id"].toInt(); + if ( !jsonChatMessage.isString() ) + { + response["error"] = CRpcServer::CreateJsonRpcError ( CRpcServer::iErrInvalidParams, "Invalid params: chatMessage is not a string" ); + return; + } + + if ( pServer->SendChatTextToConChannel ( id, jsonChatMessage.toString() ) ) + { + response["error"] = "invalid channel ID"; + return; + } + response["result"] = "ok"; + } ); + /// @rpc_method jamulusserver/getRecorderStatus /// @brief Returns the recorder state. /// @param {object} params - No parameters (empty object). @@ -349,24 +371,6 @@ CServerRpc::CServerRpc ( CServer* pServer, CRpcServer* pRpcServer, QObject* pare response["result"] = "acknowledged"; Q_UNUSED ( params ); } ); - - /// @rpc_method jamulusserver/privateChatMessage - /// @brief Sends a chat message to a single connected client. - /// @param {string} params.chatMessage - The chat message text. - /// @param {number} params.id - The client's channel id. - /// @result {string} result - Always "ok". - pRpcServer->HandleMethod ( "jamulusserver/privateChatMessage", [=] ( const QJsonObject& params, QJsonObject& response ) { - auto jsonChatMessage = params["chatMessage"]; - const int id = params["id"].toInt(); - if ( !jsonChatMessage.isString() ) - { - response["error"] = CRpcServer::CreateJsonRpcError ( CRpcServer::iErrInvalidParams, "Invalid params: chatMessage is not a string" ); - return; - } - - pServer->SendChatTextToConChannel ( id, jsonChatMessage.toString() ); - response["result"] = "ok"; - } ); } #if defined( Q_OS_MACOS ) && QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) From b034576f93dc3b751422914f651da14fe0e931fc Mon Sep 17 00:00:00 2001 From: Nils Brederlow <62596379+dingodoppelt@users.noreply.github.com> Date: Sat, 27 Jun 2026 15:33:33 +0200 Subject: [PATCH 3/5] Added sanity checks for private chat message string --- src/serverrpc.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/serverrpc.cpp b/src/serverrpc.cpp index 61a3b111d5..5eb586835f 100644 --- a/src/serverrpc.cpp +++ b/src/serverrpc.cpp @@ -119,15 +119,17 @@ CServerRpc::CServerRpc ( CServer* pServer, CRpcServer* pRpcServer, QObject* pare /// @param {number} params.id - The client's channel id. /// @result {string} result - Always "ok". pRpcServer->HandleMethod ( "jamulusserver/privateChatMessage", [=] ( const QJsonObject& params, QJsonObject& response ) { - auto jsonChatMessage = params["chatMessage"]; - const int id = params["id"].toInt(); - if ( !jsonChatMessage.isString() ) + auto jsonChatMessage = params["chatMessage"]; + const int id = params["id"].toInt ( INVALID_CLIENT_ID ); + const QString chatMessage = jsonChatMessage.toString(); + if ( chatMessage.isEmpty() || chatMessage.size() > MAX_LEN_CHAT_TEXT ) { - response["error"] = CRpcServer::CreateJsonRpcError ( CRpcServer::iErrInvalidParams, "Invalid params: chatMessage is not a string" ); + response["error"] = + CRpcServer::CreateJsonRpcError ( CRpcServer::iErrInvalidParams, "Invalid params: chatMessage is not a string or malformed" ); return; } - if ( pServer->SendChatTextToConChannel ( id, jsonChatMessage.toString() ) ) + if ( pServer->SendChatTextToConChannel ( id, chatMessage ) ) { response["error"] = "invalid channel ID"; return; From f2ac3e85d4eef0875824cc4b65e065dacbccc5b3 Mon Sep 17 00:00:00 2001 From: Nils Brederlow <62596379+dingodoppelt@users.noreply.github.com> Date: Sun, 28 Jun 2026 16:45:47 +0200 Subject: [PATCH 4/5] update comment --- docs/JSON-RPC.md | 4 ++-- src/serverrpc.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/JSON-RPC.md b/docs/JSON-RPC.md index 7420715bdb..e9111187f4 100644 --- a/docs/JSON-RPC.md +++ b/docs/JSON-RPC.md @@ -344,7 +344,7 @@ Results: ### jamulusserver/broadcastChatMessage -Sends a message (as the server) to all connected clients. This can be used to broadcast messages from external sources (e.g. scripts or monitoring tools). +Sends a message (as the server) to all connected clients. This can be used to broadcast messages from external sources (e.g. scripts or monitoring tools). Parameters: @@ -445,7 +445,7 @@ Results: | Name | Type | Description | | --- | --- | --- | -| result | string | Always "ok". | +| result | string | "ok" or "error" if bad arguments. | ### jamulusserver/restartRecording diff --git a/src/serverrpc.cpp b/src/serverrpc.cpp index 5eb586835f..583d1d95f1 100644 --- a/src/serverrpc.cpp +++ b/src/serverrpc.cpp @@ -117,7 +117,7 @@ CServerRpc::CServerRpc ( CServer* pServer, CRpcServer* pRpcServer, QObject* pare /// @brief Sends a chat message to a single connected client. /// @param {string} params.chatMessage - The chat message text. /// @param {number} params.id - The client's channel id. - /// @result {string} result - Always "ok". + /// @result {string} result - "ok" or "error" if bad arguments. pRpcServer->HandleMethod ( "jamulusserver/privateChatMessage", [=] ( const QJsonObject& params, QJsonObject& response ) { auto jsonChatMessage = params["chatMessage"]; const int id = params["id"].toInt ( INVALID_CLIENT_ID ); From 7c8b09e1162e0e436f1e39b21f42b0fb519e140d Mon Sep 17 00:00:00 2001 From: Nils Brederlow <62596379+dingodoppelt@users.noreply.github.com> Date: Sun, 28 Jun 2026 20:27:52 +0200 Subject: [PATCH 5/5] Apply suggestions from code review Co-authored-by: Peter L Jones --- src/server.cpp | 13 +++++-------- src/serverrpc.cpp | 2 +- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/src/server.cpp b/src/server.cpp index e68cdd6466..ce2669cbf7 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -1381,16 +1381,13 @@ void CServer::SendChatTextToAllConChannels ( const int iSendingChanID, const QSt bool CServer::SendChatTextToConChannel ( const int iCurChanID, const QString& strChatText ) { - if ( MathUtils::InRange ( iCurChanID, 0, iMaxNumChannels - 1 ) && vecChannels[iCurChanID].IsConnected() ) + if ( !MathUtils::InRange ( iCurChanID, 0, iMaxNumChannels - 1 ) || !vecChannels[iCurChanID].IsConnected() ) { - // send message - vecChannels[iCurChanID].CreateChatTextMes ( strChatText ); + return false; } - else - { - return true; - } - return false; + // send message + vecChannels[iCurChanID].CreateChatTextMes ( strChatText ); + return true; } void CServer::CreateAndSendRecorderStateForAllConChannels() diff --git a/src/serverrpc.cpp b/src/serverrpc.cpp index 583d1d95f1..26db5257bb 100644 --- a/src/serverrpc.cpp +++ b/src/serverrpc.cpp @@ -129,7 +129,7 @@ CServerRpc::CServerRpc ( CServer* pServer, CRpcServer* pRpcServer, QObject* pare return; } - if ( pServer->SendChatTextToConChannel ( id, chatMessage ) ) + if ( !pServer->SendChatTextToConChannel ( id, chatMessage ) ) { response["error"] = "invalid channel ID"; return;