From 689243e0990bb9541a81ab225c6937fba14ccd09 Mon Sep 17 00:00:00 2001 From: Bryan Biedenkapp Date: Mon, 6 Jul 2026 08:42:13 -0400 Subject: [PATCH] source code organization and cleanup; --- src/fne/restapi/RESTAPI.cpp | 674 +++++++++++++++++++----------------- src/fne/restapi/RESTAPI.h | 80 +++-- 2 files changed, 397 insertions(+), 357 deletions(-) diff --git a/src/fne/restapi/RESTAPI.cpp b/src/fne/restapi/RESTAPI.cpp index 49f01c8a..e3dc9c7b 100644 --- a/src/fne/restapi/RESTAPI.cpp +++ b/src/fne/restapi/RESTAPI.cpp @@ -651,6 +651,13 @@ void RESTAPI::initializeEndpoints() m_dispatcher.match(FNE_PUT_PEER_RESET).put(REST_API_BIND(RESTAPI::restAPI_PutPeerReset, this)); m_dispatcher.match(FNE_PUT_PEER_RESET_CONN).put(REST_API_BIND(RESTAPI::restAPI_PutPeerResetConn, this)); + m_dispatcher.match(FNE_GET_PEER_LIST).get(REST_API_BIND(RESTAPI::restAPI_GetPeerList, this)); + m_dispatcher.match(FNE_PUT_PEER_ADD).put(REST_API_BIND(RESTAPI::restAPI_PutPeerAdd, this)); + m_dispatcher.match(FNE_PUT_PEER_DELETE).put(REST_API_BIND(RESTAPI::restAPI_PutPeerDelete, this)); + m_dispatcher.match(FNE_GET_PEER_COMMIT).get(REST_API_BIND(RESTAPI::restAPI_GetPeerCommit, this)); + m_dispatcher.match(FNE_PUT_PEER_NAK_PEERID).put(REST_API_BIND(RESTAPI::restAPI_PutPeerNAKByPeerId, this)); + m_dispatcher.match(FNE_PUT_PEER_NAK_ADDRESS).put(REST_API_BIND(RESTAPI::restAPI_PutPeerNAKByAddress, this)); + m_dispatcher.match(FNE_GET_RID_QUERY).get(REST_API_BIND(RESTAPI::restAPI_GetRIDQuery, this)); m_dispatcher.match(FNE_PUT_RID_ADD).put(REST_API_BIND(RESTAPI::restAPI_PutRIDAdd, this)); m_dispatcher.match(FNE_PUT_RID_DELETE).put(REST_API_BIND(RESTAPI::restAPI_PutRIDDelete, this)); @@ -661,13 +668,6 @@ void RESTAPI::initializeEndpoints() m_dispatcher.match(FNE_PUT_TGID_DELETE).put(REST_API_BIND(RESTAPI::restAPI_PutTGDelete, this)); m_dispatcher.match(FNE_GET_TGID_COMMIT).get(REST_API_BIND(RESTAPI::restAPI_GetTGCommit, this)); - m_dispatcher.match(FNE_GET_PEER_LIST).get(REST_API_BIND(RESTAPI::restAPI_GetPeerList, this)); - m_dispatcher.match(FNE_PUT_PEER_ADD).put(REST_API_BIND(RESTAPI::restAPI_PutPeerAdd, this)); - m_dispatcher.match(FNE_PUT_PEER_DELETE).put(REST_API_BIND(RESTAPI::restAPI_PutPeerDelete, this)); - m_dispatcher.match(FNE_GET_PEER_COMMIT).get(REST_API_BIND(RESTAPI::restAPI_GetPeerCommit, this)); - m_dispatcher.match(FNE_PUT_PEER_NAK_PEERID).put(REST_API_BIND(RESTAPI::restAPI_PutPeerNAKByPeerId, this)); - m_dispatcher.match(FNE_PUT_PEER_NAK_ADDRESS).put(REST_API_BIND(RESTAPI::restAPI_PutPeerNAKByAddress, this)); - m_dispatcher.match(FNE_GET_ADJ_MAP_LIST).get(REST_API_BIND(RESTAPI::restAPI_GetAdjMapList, this)); m_dispatcher.match(FNE_PUT_ADJ_MAP_ADD).put(REST_API_BIND(RESTAPI::restAPI_PutAdjMapAdd, this)); m_dispatcher.match(FNE_PUT_ADJ_MAP_DELETE).put(REST_API_BIND(RESTAPI::restAPI_PutAdjMapDelete, this)); @@ -870,6 +870,10 @@ void RESTAPI::restAPI_GetStatus(const HTTPPayload& request, HTTPPayload& reply, reply.payload(response); } +/* +** Peer Operations +*/ + /* REST API endpoint; implements get peer query request. */ void RESTAPI::restAPI_GetPeerQuery(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) @@ -1012,9 +1016,9 @@ void RESTAPI::restAPI_PutPeerResetConn(const HTTPPayload& request, HTTPPayload& } } -/* REST API endpoint; implements get radio ID query request. */ +/* REST API endpoint; implements get peer list query request. */ -void RESTAPI::restAPI_GetRIDQuery(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +void RESTAPI::restAPI_GetPeerList(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) { if (!validateAuth(request, reply)) { return; @@ -1023,41 +1027,38 @@ void RESTAPI::restAPI_GetRIDQuery(const HTTPPayload& request, HTTPPayload& reply json::object response = json::object(); setResponseDefaultStatus(response); - json::array rids = json::array(); - if (m_ridLookup != nullptr) { - if (m_ridLookup->table().size() > 0) { - for (auto entry : m_ridLookup->table()) { - json::object ridObj = json::object(); + json::array peers = json::array(); + if (m_peerListLookup != nullptr) { + if (m_peerListLookup->table().size() > 0) { + for (auto entry : m_peerListLookup->table()) { + json::object peerObj = json::object(); - uint32_t rid = entry.first; - ridObj["id"].set(rid); - bool enabled = entry.second.radioEnabled(); - ridObj["enabled"].set(enabled); - std::string alias = entry.second.radioAlias(); - ridObj["alias"].set(alias); + uint32_t peerId = entry.first; + std::string peerAlias = entry.second.peerAlias(); + bool peerPassword = !entry.second.peerPassword().empty(); // True if password is not empty, otherwise false + bool peerReplica = entry.second.peerReplica(); bool canRequestKeys = entry.second.canRequestKeys(); - ridObj["canRequestKeys"].set(canRequestKeys); - bool canRekey = entry.second.canRekey(); - ridObj["canRekey"].set(canRekey); - json::array allowedKIds = json::array(); - std::vector kIds = entry.second.allowedKIds(); - for (uint16_t kId : kIds) { - allowedKIds.push_back(json::value((double)kId)); - } - ridObj["allowedKIds"].set(allowedKIds); - - rids.push_back(json::value(ridObj)); + bool canIssueInhibit = entry.second.canIssueInhibit(); + bool hasCallPriority = entry.second.hasCallPriority(); + peerObj["peerId"].set(peerId); + peerObj["peerAlias"].set(peerAlias); + peerObj["peerPassword"].set(peerPassword); + peerObj["peerReplica"].set(peerReplica); + peerObj["canRequestKeys"].set(canRequestKeys); + peerObj["canIssueInhibit"].set(canIssueInhibit); + peerObj["hasCallPriority"].set(hasCallPriority); + peers.push_back(json::value(peerObj)); } } } - response["rids"].set(rids); + response["peers"].set(peers); reply.payload(response); } -/* REST API endpoint; implements put radio ID add request. */ +/* REST API endpoint; implements put peer add request. */ -void RESTAPI::restAPI_PutRIDAdd(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +void RESTAPI::restAPI_PutPeerAdd(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) { if (!validateAuth(request, reply)) { return; @@ -1070,84 +1071,102 @@ void RESTAPI::restAPI_PutRIDAdd(const HTTPPayload& request, HTTPPayload& reply, errorPayload(reply, "OK", HTTPPayload::OK); - if (!req["rid"].is()) { - errorPayload(reply, "rid was not a valid integer"); + // Validate peer ID (required) + if (!req["peerId"].is()) { + errorPayload(reply, "peerId was not a valid integer"); return; } + // Get + uint32_t peerId = req["peerId"].get(); - uint32_t rid = req["rid"].get(); - - if (!req["enabled"].is()) { - errorPayload(reply, "enabled was not a valid boolean"); - return; + // Get peer alias (optional) + std::string peerAlias = ""; + if (req.find("peerAlias") != req.end()) { + // Validate + if (!req["peerAlias"].is()) { + errorPayload(reply, "peerAlias was not a valid string"); + return; + } + // Get + peerAlias = req["peerAlias"].get(); } - bool enabled = req["enabled"].get(); + // Get peer password (optional) + std::string peerPassword = ""; + if (req.find("peerPassword") != req.end()) { + // Validate + if (!req["peerPassword"].is()) { + errorPayload(reply, "peerPassword was not a valid string"); + return; + } + // Get + peerPassword = req["peerPassword"].get(); + } - std::string alias = ""; - // Check if we were provided an alias in the request - if (req.find("alias") != req.end()) { - alias = req["alias"].get(); + // Get peer link setting (optional) + bool peerReplica = false; + if (req.find("peerReplica") != req.end()) { + // Validate + if (!req["peerReplica"].is()) { + errorPayload(reply, "peerReplica was not a valid boolean"); + return; + } + // Get + peerReplica = req["peerReplica"].get(); } + // Get canRequestKeys bool canRequestKeys = false; if (req.find("canRequestKeys") != req.end()) { + // Validate if (!req["canRequestKeys"].is()) { errorPayload(reply, "canRequestKeys was not a valid boolean"); return; } - + // Get canRequestKeys = req["canRequestKeys"].get(); } - bool canRekey = false; - if (req.find("canRekey") != req.end()) { - if (!req["canRekey"].is()) { - errorPayload(reply, "canRekey was not a valid boolean"); + // Get canIssueInhibit + bool canIssueInhibit = false; + if (req.find("canIssueInhibit") != req.end()) { + // Validate + if (!req["canIssueInhibit"].is()) { + errorPayload(reply, "canIssueInhibit was not a valid boolean"); return; } - - canRekey = req["canRekey"].get(); + // Get + canIssueInhibit = req["canIssueInhibit"].get(); } - std::vector allowedKIds; - if (req.find("allowedKIds") != req.end()) { - if (!req["allowedKIds"].is()) { - errorPayload(reply, "allowedKIds was not a valid JSON array"); + // Get hasCallPriority + bool hasCallPriority = false; + if (req.find("hasCallPriority") != req.end()) { + // Validate + if (!req["hasCallPriority"].is()) { + errorPayload(reply, "hasCallPriority was not a valid boolean"); return; } + // Get + hasCallPriority = req["hasCallPriority"].get(); + } - json::array kIdArray = req["allowedKIds"].get(); - for (auto entry : kIdArray) { - if (!entry.is()) { - errorPayload(reply, "allowedKIds entry was not a valid number"); - return; - } - - uint32_t value = entry.get(); - if (value > 0xFFFFU) { - errorPayload(reply, "allowedKIds entry exceeded 16-bit key id range"); - return; - } + PeerId entry = PeerId(peerId, peerAlias, peerPassword, false); - allowedKIds.push_back((uint16_t)value); - } - } + // Set additional values + entry.peerReplica(peerReplica); + entry.canRequestKeys(canRequestKeys); + entry.canIssueInhibit(canIssueInhibit); + entry.hasCallPriority(hasCallPriority); - LogInfoEx(LOG_REST, "request to add RID ACL, rid = %u", rid); + LogInfoEx(LOG_REST, "request to add peer ACL, peerId = %u", peerId); - // The addEntry function will automatically update an existing entry, so no need to check for an exisitng one here - m_ridLookup->addEntry(rid, enabled, alias, "", canRequestKeys, canRekey, allowedKIds); -/* - if (m_network != nullptr) { - m_network->m_forceListUpdate = true; - } -*/ + m_peerListLookup->addEntry(peerId, entry); } -/* REST API endpoint; implements put radio ID delete request. */ +/* REST API endpoint; implements put peer delete request. */ -void RESTAPI::restAPI_PutRIDDelete(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +void RESTAPI::restAPI_PutPeerDelete(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) { if (!validateAuth(request, reply)) { return; @@ -1160,32 +1179,21 @@ void RESTAPI::restAPI_PutRIDDelete(const HTTPPayload& request, HTTPPayload& repl errorPayload(reply, "OK", HTTPPayload::OK); - if (!req["rid"].is()) { - errorPayload(reply, "rid was not a valid integer"); + if (!req["peerId"].is()) { + errorPayload(reply, "peerId was not a valid integer"); return; } - uint32_t rid = req["rid"].get(); - - RadioId radioId = m_ridLookup->find(rid); - if (radioId.radioDefault()) { - errorPayload(reply, "failed to find specified RID to delete"); - return; - } + uint32_t peerId = req["peerId"].get(); - LogInfoEx(LOG_REST, "request to delete RID ACL, rid = %u", rid); + LogInfoEx(LOG_REST, "request to delete peer ACL, peerId = %u", peerId); - m_ridLookup->eraseEntry(rid); -/* - if (m_network != nullptr) { - m_network->m_forceListUpdate = true; - } -*/ + m_peerListLookup->eraseEntry(peerId); } -/* REST API endpoint; implements put radio ID commit request. */ +/* REST API endpoint; implements put peer list commit request. */ -void RESTAPI::restAPI_GetRIDCommit(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +void RESTAPI::restAPI_GetPeerCommit(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) { if (!validateAuth(request, reply)) { return; @@ -1194,87 +1202,55 @@ void RESTAPI::restAPI_GetRIDCommit(const HTTPPayload& request, HTTPPayload& repl json::object response = json::object(); setResponseDefaultStatus(response); - LogInfoEx(LOG_REST, "request to commit and save RID ACLs"); - m_ridLookup->commit(); + LogInfoEx(LOG_REST, "request to commit and save peer ACLs"); + m_peerListLookup->commit(); reply.payload(response); } -/* REST API endpoint; implements get talkgroup ID query request. */ +/* REST API endpoint; implements put peer NAK request. */ -void RESTAPI::restAPI_GetTGQuery(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +void RESTAPI::restAPI_PutPeerNAKByPeerId(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) { if (!validateAuth(request, reply)) { return; } - json::object response = json::object(); - setResponseDefaultStatus(response); - - json::array tgs = json::array(); - if (m_tidLookup != nullptr) { - if (m_tidLookup->groupVoice().size() > 0) { - for (auto entry : m_tidLookup->groupVoice()) { - json::object tg = tgToJson(entry); - tgs.push_back(json::value(tg)); - } - } + json::object req = json::object(); + if (!parseRequestBody(request, reply, req)) { + return; } - response["tgs"].set(tgs); - reply.payload(response); -} - -/* REST API endpoint; implements put talkgroup ID add request. */ + errorPayload(reply, "OK", HTTPPayload::OK); -void RESTAPI::restAPI_PutTGAdd(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) -{ - if (!validateAuth(request, reply)) { + if (!req["peerId"].is()) { + errorPayload(reply, "peerId was not a valid integer"); return; } - json::object req = json::object(); - if (!parseRequestBody(request, reply, req)) { + uint32_t peerId = req["peerId"].get(); + + if (!req["tag"].is()) { + errorPayload(reply, "tag was not a valid string"); return; } - errorPayload(reply, "OK", HTTPPayload::OK); + std::string tag = req["tag"].get(); - TalkgroupRuleGroupVoice groupVoice = jsonToTG(req, reply); - if (groupVoice.isInvalid()) { - ::LogError(LOG_REST, "Unable to parse TG JSON from REST TgAdd"); + if (!req["reason"].is()) { + errorPayload(reply, "reason was not a valid integer"); return; } - std::string groupName = groupVoice.name(); - uint32_t tgId = groupVoice.source().tgId(); - uint8_t tgSlot = groupVoice.source().tgSlot(); - bool active = groupVoice.config().active(); - bool parrot = groupVoice.config().parrot(); - - uint32_t incCount = groupVoice.config().inclusion().size(); - uint32_t excCount = groupVoice.config().exclusion().size(); - uint32_t rewrCount = groupVoice.config().rewrite().size(); - uint32_t prefCount = groupVoice.config().preferred().size(); - - if (incCount > 0 && excCount > 0) { - ::LogWarning(LOG_REST, "Talkgroup (%s) defines both inclusions and exclusions! Inclusions take precedence and exclusions will be ignored.", groupName.c_str()); - } - - ::LogInfoEx(LOG_REST, "request to add TGID ACL"); - ::LogInfoEx(LOG_REST, "Talkgroup NAME: %s SRC_TGID: %u SRC_TS: %u ACTIVE: %u PARROT: %u INCLUSIONS: %u EXCLUSIONS: %u REWRITES: %u PREFERRED: %u", groupName.c_str(), tgId, tgSlot, active, parrot, incCount, excCount, rewrCount, prefCount); + uint32_t reasonCode = req["reason"].get(); - m_tidLookup->addEntry(groupVoice); -/* - if (m_network != nullptr) { - m_network->m_forceListUpdate = true; - } -*/ + LogInfoEx(LOG_REST, "sending NAK to %u, peerId = %u, tag = %s, reason = %u", peerId, peerId, tag.c_str(), reasonCode); + m_network->writePeerNAK(peerId, m_network->createStreamId(), tag.c_str(), (NET_CONN_NAK_REASON)reasonCode); } -/* REST API endpoint; implements put talkgroup ID delete request. */ +/* REST API endpoint; implements put peer NAK request. */ -void RESTAPI::restAPI_PutTGDelete(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +void RESTAPI::restAPI_PutPeerNAKByAddress(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) { if (!validateAuth(request, reply)) { return; @@ -1287,58 +1263,57 @@ void RESTAPI::restAPI_PutTGDelete(const HTTPPayload& request, HTTPPayload& reply errorPayload(reply, "OK", HTTPPayload::OK); - // validate state is a string within the JSON blob - if (!req["tgid"].is()) { - errorPayload(reply, "tgid was not a valid integer"); + if (!req["address"].is()) { + errorPayload(reply, "address was not a valid string"); return; } - // Validate slot - if (!req["slot"].is()) { - errorPayload(reply, "slot was not a valid char"); - } - - uint32_t tgid = req["tgid"].get(); - uint8_t slot = req["slot"].get(); + std::string address = req["address"].get(); - TalkgroupRuleGroupVoice groupVoice = m_tidLookup->find(tgid, slot); - if (groupVoice.isInvalid()) { - errorPayload(reply, "failed to find specified TGID to delete"); + if (!req["port"].is()) { + errorPayload(reply, "port was not a valid integer"); return; } - ::LogInfoEx(LOG_REST, "request to delete TGID ACL, tgid = %u, slot = %u", tgid, slot); - m_tidLookup->eraseEntry(groupVoice.source().tgId(), groupVoice.source().tgSlot()); -/* - if (m_network != nullptr) { - m_network->m_forceListUpdate = true; + uint16_t port = req["port"].get(); + + if (!req["peerId"].is()) { + errorPayload(reply, "peerId was not a valid integer"); + return; } -*/ -} -/* REST API endpoint; implements put talkgroup ID commit request. */ + uint32_t peerId = req["peerId"].get(); -void RESTAPI::restAPI_GetTGCommit(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) -{ - if (!validateAuth(request, reply)) { + if (!req["tag"].is()) { + errorPayload(reply, "tag was not a valid string"); return; } - json::object response = json::object(); - setResponseDefaultStatus(response); + std::string tag = req["tag"].get(); - LogInfoEx(LOG_REST, "request to commit and save TGID ACLs"); - if(!m_tidLookup->commit()) { - errorPayload(reply, "failed to write new TGID file"); + if (!req["reason"].is()) { + errorPayload(reply, "reason was not a valid integer"); return; } - reply.payload(response); + uint32_t reasonCode = req["reason"].get(); + + sockaddr_storage addr; + uint32_t addrLen; + + udp::Socket::lookup(address, port, addr, addrLen); + + LogInfoEx(LOG_REST, "sending NAK to %s:%u, peerId = %u, tag = %s, reason = %u", address.c_str(), port, peerId, tag.c_str(), reasonCode); + m_network->writePeerNAK(peerId, tag.c_str(), (NET_CONN_NAK_REASON)reasonCode, addr, addrLen); } -/* REST API endpoint; implements get peer list query request. */ +/* +** Radio ID Operations +*/ -void RESTAPI::restAPI_GetPeerList(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +/* REST API endpoint; implements get radio ID query request. */ + +void RESTAPI::restAPI_GetRIDQuery(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) { if (!validateAuth(request, reply)) { return; @@ -1347,38 +1322,41 @@ void RESTAPI::restAPI_GetPeerList(const HTTPPayload& request, HTTPPayload& reply json::object response = json::object(); setResponseDefaultStatus(response); - json::array peers = json::array(); - if (m_peerListLookup != nullptr) { - if (m_peerListLookup->table().size() > 0) { - for (auto entry : m_peerListLookup->table()) { - json::object peerObj = json::object(); + json::array rids = json::array(); + if (m_ridLookup != nullptr) { + if (m_ridLookup->table().size() > 0) { + for (auto entry : m_ridLookup->table()) { + json::object ridObj = json::object(); - uint32_t peerId = entry.first; - std::string peerAlias = entry.second.peerAlias(); - bool peerPassword = !entry.second.peerPassword().empty(); // True if password is not empty, otherwise false - bool peerReplica = entry.second.peerReplica(); + uint32_t rid = entry.first; + ridObj["id"].set(rid); + bool enabled = entry.second.radioEnabled(); + ridObj["enabled"].set(enabled); + std::string alias = entry.second.radioAlias(); + ridObj["alias"].set(alias); bool canRequestKeys = entry.second.canRequestKeys(); - bool canIssueInhibit = entry.second.canIssueInhibit(); - bool hasCallPriority = entry.second.hasCallPriority(); - peerObj["peerId"].set(peerId); - peerObj["peerAlias"].set(peerAlias); - peerObj["peerPassword"].set(peerPassword); - peerObj["peerReplica"].set(peerReplica); - peerObj["canRequestKeys"].set(canRequestKeys); - peerObj["canIssueInhibit"].set(canIssueInhibit); - peerObj["hasCallPriority"].set(hasCallPriority); - peers.push_back(json::value(peerObj)); + ridObj["canRequestKeys"].set(canRequestKeys); + bool canRekey = entry.second.canRekey(); + ridObj["canRekey"].set(canRekey); + json::array allowedKIds = json::array(); + std::vector kIds = entry.second.allowedKIds(); + for (uint16_t kId : kIds) { + allowedKIds.push_back(json::value((double)kId)); + } + ridObj["allowedKIds"].set(allowedKIds); + + rids.push_back(json::value(ridObj)); } } } - response["peers"].set(peers); + response["rids"].set(rids); reply.payload(response); } -/* REST API endpoint; implements put peer add request. */ +/* REST API endpoint; implements put radio ID add request. */ -void RESTAPI::restAPI_PutPeerAdd(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +void RESTAPI::restAPI_PutRIDAdd(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) { if (!validateAuth(request, reply)) { return; @@ -1391,102 +1369,84 @@ void RESTAPI::restAPI_PutPeerAdd(const HTTPPayload& request, HTTPPayload& reply, errorPayload(reply, "OK", HTTPPayload::OK); - // Validate peer ID (required) - if (!req["peerId"].is()) { - errorPayload(reply, "peerId was not a valid integer"); + if (!req["rid"].is()) { + errorPayload(reply, "rid was not a valid integer"); return; } - // Get - uint32_t peerId = req["peerId"].get(); - // Get peer alias (optional) - std::string peerAlias = ""; - if (req.find("peerAlias") != req.end()) { - // Validate - if (!req["peerAlias"].is()) { - errorPayload(reply, "peerAlias was not a valid string"); - return; - } - // Get - peerAlias = req["peerAlias"].get(); - } + uint32_t rid = req["rid"].get(); - // Get peer password (optional) - std::string peerPassword = ""; - if (req.find("peerPassword") != req.end()) { - // Validate - if (!req["peerPassword"].is()) { - errorPayload(reply, "peerPassword was not a valid string"); - return; - } - // Get - peerPassword = req["peerPassword"].get(); + if (!req["enabled"].is()) { + errorPayload(reply, "enabled was not a valid boolean"); + return; } - // Get peer link setting (optional) - bool peerReplica = false; - if (req.find("peerReplica") != req.end()) { - // Validate - if (!req["peerReplica"].is()) { - errorPayload(reply, "peerReplica was not a valid boolean"); - return; - } - // Get - peerReplica = req["peerReplica"].get(); + bool enabled = req["enabled"].get(); + + std::string alias = ""; + // Check if we were provided an alias in the request + if (req.find("alias") != req.end()) { + alias = req["alias"].get(); } - // Get canRequestKeys bool canRequestKeys = false; if (req.find("canRequestKeys") != req.end()) { - // Validate if (!req["canRequestKeys"].is()) { errorPayload(reply, "canRequestKeys was not a valid boolean"); return; } - // Get + canRequestKeys = req["canRequestKeys"].get(); } - // Get canIssueInhibit - bool canIssueInhibit = false; - if (req.find("canIssueInhibit") != req.end()) { - // Validate - if (!req["canIssueInhibit"].is()) { - errorPayload(reply, "canIssueInhibit was not a valid boolean"); + bool canRekey = false; + if (req.find("canRekey") != req.end()) { + if (!req["canRekey"].is()) { + errorPayload(reply, "canRekey was not a valid boolean"); return; } - // Get - canIssueInhibit = req["canIssueInhibit"].get(); + + canRekey = req["canRekey"].get(); } - // Get hasCallPriority - bool hasCallPriority = false; - if (req.find("hasCallPriority") != req.end()) { - // Validate - if (!req["hasCallPriority"].is()) { - errorPayload(reply, "hasCallPriority was not a valid boolean"); + std::vector allowedKIds; + if (req.find("allowedKIds") != req.end()) { + if (!req["allowedKIds"].is()) { + errorPayload(reply, "allowedKIds was not a valid JSON array"); return; } - // Get - hasCallPriority = req["hasCallPriority"].get(); - } - PeerId entry = PeerId(peerId, peerAlias, peerPassword, false); + json::array kIdArray = req["allowedKIds"].get(); + for (auto entry : kIdArray) { + if (!entry.is()) { + errorPayload(reply, "allowedKIds entry was not a valid number"); + return; + } - // Set additional values - entry.peerReplica(peerReplica); - entry.canRequestKeys(canRequestKeys); - entry.canIssueInhibit(canIssueInhibit); - entry.hasCallPriority(hasCallPriority); + uint32_t value = entry.get(); + if (value > 0xFFFFU) { + errorPayload(reply, "allowedKIds entry exceeded 16-bit key id range"); + return; + } - LogInfoEx(LOG_REST, "request to add peer ACL, peerId = %u", peerId); + allowedKIds.push_back((uint16_t)value); + } + } - m_peerListLookup->addEntry(peerId, entry); + LogInfoEx(LOG_REST, "request to add RID ACL, rid = %u", rid); + + // The addEntry function will automatically update an existing entry, so no need to check for an exisitng one here + m_ridLookup->addEntry(rid, enabled, alias, "", canRequestKeys, canRekey, allowedKIds); +/* + if (m_network != nullptr) { + m_network->m_forceListUpdate = true; + } +*/ } -/* REST API endpoint; implements put peer delete request. */ +/* REST API endpoint; implements put radio ID delete request. */ -void RESTAPI::restAPI_PutPeerDelete(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +void RESTAPI::restAPI_PutRIDDelete(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) { if (!validateAuth(request, reply)) { return; @@ -1499,21 +1459,32 @@ void RESTAPI::restAPI_PutPeerDelete(const HTTPPayload& request, HTTPPayload& rep errorPayload(reply, "OK", HTTPPayload::OK); - if (!req["peerId"].is()) { - errorPayload(reply, "peerId was not a valid integer"); + if (!req["rid"].is()) { + errorPayload(reply, "rid was not a valid integer"); return; } - uint32_t peerId = req["peerId"].get(); + uint32_t rid = req["rid"].get(); - LogInfoEx(LOG_REST, "request to delete peer ACL, peerId = %u", peerId); + RadioId radioId = m_ridLookup->find(rid); + if (radioId.radioDefault()) { + errorPayload(reply, "failed to find specified RID to delete"); + return; + } - m_peerListLookup->eraseEntry(peerId); + LogInfoEx(LOG_REST, "request to delete RID ACL, rid = %u", rid); + + m_ridLookup->eraseEntry(rid); +/* + if (m_network != nullptr) { + m_network->m_forceListUpdate = true; + } +*/ } -/* REST API endpoint; implements put peer list commit request. */ +/* REST API endpoint; implements put radio ID commit request. */ -void RESTAPI::restAPI_GetPeerCommit(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +void RESTAPI::restAPI_GetRIDCommit(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) { if (!validateAuth(request, reply)) { return; @@ -1522,15 +1493,44 @@ void RESTAPI::restAPI_GetPeerCommit(const HTTPPayload& request, HTTPPayload& rep json::object response = json::object(); setResponseDefaultStatus(response); - LogInfoEx(LOG_REST, "request to commit and save peer ACLs"); - m_peerListLookup->commit(); + LogInfoEx(LOG_REST, "request to commit and save RID ACLs"); + m_ridLookup->commit(); reply.payload(response); } -/* REST API endpoint; implements put peer NAK request. */ +/* +** Talkgroup Operations +*/ -void RESTAPI::restAPI_PutPeerNAKByPeerId(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +/* REST API endpoint; implements get talkgroup ID query request. */ + +void RESTAPI::restAPI_GetTGQuery(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +{ + if (!validateAuth(request, reply)) { + return; + } + + json::object response = json::object(); + setResponseDefaultStatus(response); + + json::array tgs = json::array(); + if (m_tidLookup != nullptr) { + if (m_tidLookup->groupVoice().size() > 0) { + for (auto entry : m_tidLookup->groupVoice()) { + json::object tg = tgToJson(entry); + tgs.push_back(json::value(tg)); + } + } + } + + response["tgs"].set(tgs); + reply.payload(response); +} + +/* REST API endpoint; implements put talkgroup ID add request. */ + +void RESTAPI::restAPI_PutTGAdd(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) { if (!validateAuth(request, reply)) { return; @@ -1543,34 +1543,41 @@ void RESTAPI::restAPI_PutPeerNAKByPeerId(const HTTPPayload& request, HTTPPayload errorPayload(reply, "OK", HTTPPayload::OK); - if (!req["peerId"].is()) { - errorPayload(reply, "peerId was not a valid integer"); + TalkgroupRuleGroupVoice groupVoice = jsonToTG(req, reply); + if (groupVoice.isInvalid()) { + ::LogError(LOG_REST, "Unable to parse TG JSON from REST TgAdd"); return; } - uint32_t peerId = req["peerId"].get(); - - if (!req["tag"].is()) { - errorPayload(reply, "tag was not a valid string"); - return; - } + std::string groupName = groupVoice.name(); + uint32_t tgId = groupVoice.source().tgId(); + uint8_t tgSlot = groupVoice.source().tgSlot(); + bool active = groupVoice.config().active(); + bool parrot = groupVoice.config().parrot(); - std::string tag = req["tag"].get(); + uint32_t incCount = groupVoice.config().inclusion().size(); + uint32_t excCount = groupVoice.config().exclusion().size(); + uint32_t rewrCount = groupVoice.config().rewrite().size(); + uint32_t prefCount = groupVoice.config().preferred().size(); - if (!req["reason"].is()) { - errorPayload(reply, "reason was not a valid integer"); - return; + if (incCount > 0 && excCount > 0) { + ::LogWarning(LOG_REST, "Talkgroup (%s) defines both inclusions and exclusions! Inclusions take precedence and exclusions will be ignored.", groupName.c_str()); } - uint32_t reasonCode = req["reason"].get(); + ::LogInfoEx(LOG_REST, "request to add TGID ACL"); + ::LogInfoEx(LOG_REST, "Talkgroup NAME: %s SRC_TGID: %u SRC_TS: %u ACTIVE: %u PARROT: %u INCLUSIONS: %u EXCLUSIONS: %u REWRITES: %u PREFERRED: %u", groupName.c_str(), tgId, tgSlot, active, parrot, incCount, excCount, rewrCount, prefCount); - LogInfoEx(LOG_REST, "sending NAK to %u, peerId = %u, tag = %s, reason = %u", peerId, peerId, tag.c_str(), reasonCode); - m_network->writePeerNAK(peerId, m_network->createStreamId(), tag.c_str(), (NET_CONN_NAK_REASON)reasonCode); + m_tidLookup->addEntry(groupVoice); +/* + if (m_network != nullptr) { + m_network->m_forceListUpdate = true; + } +*/ } -/* REST API endpoint; implements put peer NAK request. */ +/* REST API endpoint; implements put talkgroup ID delete request. */ -void RESTAPI::restAPI_PutPeerNAKByAddress(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +void RESTAPI::restAPI_PutTGDelete(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) { if (!validateAuth(request, reply)) { return; @@ -1583,50 +1590,59 @@ void RESTAPI::restAPI_PutPeerNAKByAddress(const HTTPPayload& request, HTTPPayloa errorPayload(reply, "OK", HTTPPayload::OK); - if (!req["address"].is()) { - errorPayload(reply, "address was not a valid string"); + // validate state is a string within the JSON blob + if (!req["tgid"].is()) { + errorPayload(reply, "tgid was not a valid integer"); return; } - std::string address = req["address"].get(); - - if (!req["port"].is()) { - errorPayload(reply, "port was not a valid integer"); - return; + // Validate slot + if (!req["slot"].is()) { + errorPayload(reply, "slot was not a valid char"); } - uint16_t port = req["port"].get(); + uint32_t tgid = req["tgid"].get(); + uint8_t slot = req["slot"].get(); - if (!req["peerId"].is()) { - errorPayload(reply, "peerId was not a valid integer"); + TalkgroupRuleGroupVoice groupVoice = m_tidLookup->find(tgid, slot); + if (groupVoice.isInvalid()) { + errorPayload(reply, "failed to find specified TGID to delete"); return; } - uint32_t peerId = req["peerId"].get(); - - if (!req["tag"].is()) { - errorPayload(reply, "tag was not a valid string"); - return; + ::LogInfoEx(LOG_REST, "request to delete TGID ACL, tgid = %u, slot = %u", tgid, slot); + m_tidLookup->eraseEntry(groupVoice.source().tgId(), groupVoice.source().tgSlot()); +/* + if (m_network != nullptr) { + m_network->m_forceListUpdate = true; } +*/ +} - std::string tag = req["tag"].get(); +/* REST API endpoint; implements put talkgroup ID commit request. */ - if (!req["reason"].is()) { - errorPayload(reply, "reason was not a valid integer"); +void RESTAPI::restAPI_GetTGCommit(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) +{ + if (!validateAuth(request, reply)) { return; } - uint32_t reasonCode = req["reason"].get(); - - sockaddr_storage addr; - uint32_t addrLen; + json::object response = json::object(); + setResponseDefaultStatus(response); - udp::Socket::lookup(address, port, addr, addrLen); + LogInfoEx(LOG_REST, "request to commit and save TGID ACLs"); + if(!m_tidLookup->commit()) { + errorPayload(reply, "failed to write new TGID file"); + return; + } - LogInfoEx(LOG_REST, "sending NAK to %s:%u, peerId = %u, tag = %s, reason = %u", address.c_str(), port, peerId, tag.c_str(), reasonCode); - m_network->writePeerNAK(peerId, tag.c_str(), (NET_CONN_NAK_REASON)reasonCode, addr, addrLen); + reply.payload(response); } +/* +** Adjacent Site Map Operations +*/ + /* REST API endpoint; implements get adjacent site map query request. */ void RESTAPI::restAPI_GetAdjMapList(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) @@ -1759,6 +1775,10 @@ void RESTAPI::restAPI_GetAdjMapCommit(const HTTPPayload& request, HTTPPayload& r reply.payload(response); } +/* +** Refresh and Statistics/Metadata Operations +*/ + /* */ void RESTAPI::restAPI_GetForceUpdate(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match) diff --git a/src/fne/restapi/RESTAPI.h b/src/fne/restapi/RESTAPI.h index 7bd7550f..acbaf7f8 100644 --- a/src/fne/restapi/RESTAPI.h +++ b/src/fne/restapi/RESTAPI.h @@ -169,6 +169,10 @@ private: * @param match HTTP request matcher. */ void restAPI_GetStatus(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + + /* + ** Peer Operations + */ /** * @brief REST API endpoint; implements get peer query request. @@ -200,105 +204,117 @@ private: void restAPI_PutPeerResetConn(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** - * @brief REST API endpoint; implements get radio ID query request. + * @brief REST API endpoint; implements get peer list query request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetRIDQuery(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + void restAPI_GetPeerList(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** - * @brief REST API endpoint; implements put radio ID add request. + * @brief REST API endpoint; implements put peer add request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutRIDAdd(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + void restAPI_PutPeerAdd(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** - * @brief REST API endpoint; implements put radio ID delete request. + * @brief REST API endpoint; implements put peer delete request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutRIDDelete(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + void restAPI_PutPeerDelete(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** - * @brief REST API endpoint; implements put radio ID commit request. + * @brief REST API endpoint; implements put peer list commit request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetRIDCommit(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); - + void restAPI_GetPeerCommit(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** - * @brief REST API endpoint; implements get talkgroup ID query request. + * @brief REST API endpoint; implements put peer NAK request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetTGQuery(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + void restAPI_PutPeerNAKByPeerId(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** - * @brief REST API endpoint; implements put talkgroup ID add request. + * @brief REST API endpoint; implements put peer NAK request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutTGAdd(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + void restAPI_PutPeerNAKByAddress(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + + /* + ** Radio ID Operations + */ + /** - * @brief REST API endpoint; implements put talkgroup ID delete request. + * @brief REST API endpoint; implements get radio ID query request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutTGDelete(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + void restAPI_GetRIDQuery(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** - * @brief REST API endpoint; implements put talkgroup ID commit request. + * @brief REST API endpoint; implements put radio ID add request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetTGCommit(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); - + void restAPI_PutRIDAdd(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** - * @brief REST API endpoint; implements get peer list query request. + * @brief REST API endpoint; implements put radio ID delete request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetPeerList(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + void restAPI_PutRIDDelete(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** - * @brief REST API endpoint; implements put peer add request. + * @brief REST API endpoint; implements put radio ID commit request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutPeerAdd(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + void restAPI_GetRIDCommit(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + + /* + ** Talkgroup ID Operations + */ + /** - * @brief REST API endpoint; implements put peer delete request. + * @brief REST API endpoint; implements get talkgroup ID query request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutPeerDelete(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + void restAPI_GetTGQuery(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** - * @brief REST API endpoint; implements put peer list commit request. + * @brief REST API endpoint; implements put talkgroup ID add request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_GetPeerCommit(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + void restAPI_PutTGAdd(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** - * @brief REST API endpoint; implements put peer NAK request. + * @brief REST API endpoint; implements put talkgroup ID delete request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutPeerNAKByPeerId(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + void restAPI_PutTGDelete(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); /** - * @brief REST API endpoint; implements put peer NAK request. + * @brief REST API endpoint; implements put talkgroup ID commit request. * @param request HTTP request. * @param reply HTTP reply. * @param match HTTP request matcher. */ - void restAPI_PutPeerNAKByAddress(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + void restAPI_GetTGCommit(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + + /* + ** Adjacent Site Map Operations + */ /** * @brief REST API endpoint; implements get adjacent site map list query request. @@ -329,6 +345,10 @@ private: */ void restAPI_GetAdjMapCommit(const HTTPPayload& request, HTTPPayload& reply, const restapi::RequestMatch& match); + /* + ** Refresh and Statistics/Metadata Operations + */ + /** * @brief * @param request HTTP request.