implement the majority of REST API support (this commit breaks RCON and any RCON-based tools [like dvmcmd]) (NOTE: not *all* RCON commands are implemented as REST API yet);

pull/19/head
Bryan Biedenkapp 3 years ago
parent 069adffd7c
commit 529d5c1e83

@ -36,6 +36,7 @@
#include <memory>
#include <sstream>
#include <ios>
#include <algorithm>
#include <string>
#if !defined(ENABLE_DMR) && !defined(ENABLE_P25) && !defined(ENABLE_NXDN)
@ -134,7 +135,7 @@ typedef unsigned long long ulong64_t;
const uint32_t REMOTE_MODEM_PORT = 3334;
const uint32_t TRAFFIC_DEFAULT_PORT = 62031;
const uint32_t RCON_DEFAULT_PORT = 9990;
const uint32_t REST_API_DEFAULT_PORT = 9990;
const uint8_t BIT_MASK_TABLE[] = { 0x80U, 0x40U, 0x20U, 0x10U, 0x08U, 0x04U, 0x02U, 0x01U };
@ -224,6 +225,17 @@ inline std::string __IP_FROM_ULONG(const ulong64_t& value) {
return ss.str();
}
/// <summary>
/// Helper to lower-case an input string.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
inline std::string strtolower(const std::string value) {
std::string v = value;
std::transform(v.begin(), v.end(), v.begin(), ::tolower);
return v;
}
// ---------------------------------------------------------------------------
// Macros
// ---------------------------------------------------------------------------

@ -40,7 +40,7 @@
// ---------------------------------------------------------------------------
#define LOG_HOST "HOST"
#define LOG_RCON "RCON"
#define LOG_REST "RESTAPI"
#define LOG_MODEM "MODEM"
#define LOG_RF "RF"
#define LOG_NET "NET"

@ -206,7 +206,7 @@ void Control::setCCRunning(bool ccRunning)
m_slot2->setCCRunning(ccRunning);
break;
default:
LogError(LOG_NET, "DMR, invalid slot, TSCC disabled, slotNo = %u", m_tsccSlotNo);
LogError(LOG_DMR, "DMR, invalid slot, TSCC disabled, slotNo = %u", m_tsccSlotNo);
break;
}
}
@ -226,7 +226,7 @@ void Control::setCCHalted(bool ccHalted)
m_slot2->setCCHalted(ccHalted);
break;
default:
LogError(LOG_NET, "DMR, invalid slot, TSCC disabled, slotNo = %u", m_tsccSlotNo);
LogError(LOG_DMR, "DMR, invalid slot, TSCC disabled, slotNo = %u", m_tsccSlotNo);
break;
}
}
@ -285,7 +285,7 @@ bool Control::processFrame(uint32_t slotNo, uint8_t *data, uint32_t len)
case 2U:
return m_slot2->processFrame(data, len);
default:
LogError(LOG_NET, "DMR, invalid slot, slotNo = %u", slotNo);
LogError(LOG_DMR, "DMR, invalid slot, slotNo = %u", slotNo);
return false;
}
}
@ -305,7 +305,7 @@ uint32_t Control::getFrame(uint32_t slotNo, uint8_t* data)
case 2U:
return m_slot2->getFrame(data);
default:
LogError(LOG_NET, "DMR, invalid slot, slotNo = %u", slotNo);
LogError(LOG_DMR, "DMR, invalid slot, slotNo = %u", slotNo);
return 0U;
}
}
@ -328,7 +328,7 @@ void Control::clock()
m_slot2->processNetwork(data);
break;
default:
LogError(LOG_NET, "DMR, invalid slot, slotNo = %u", slotNo);
LogError(LOG_DMR, "DMR, invalid slot, slotNo = %u", slotNo);
break;
}
}
@ -353,9 +353,28 @@ void Control::permittedTG(uint32_t dstId, uint8_t slot)
m_slot2->permittedTG(dstId);
break;
default:
LogError(LOG_NET, "DMR, invalid slot, slotNo = %u", slot);
LogError(LOG_DMR, "DMR, invalid slot, slotNo = %u", slot);
break;
}
}
/// <summary>
/// Gets instance of the AffiliationLookup class.
/// </summary>
dmr::lookups::DMRAffiliationLookup Control::affiliations()
{
switch (m_tsccSlotNo) {
case 1U:
return m_slot1->m_affiliations;
case 2U:
return m_slot2->m_affiliations;
break;
default:
LogError(LOG_DMR, "DMR, invalid slot, slotNo = %u", m_tsccSlotNo);
break;
}
return 0; // ??
}
/// <summary>
@ -372,7 +391,7 @@ Slot* Control::getTSCCSlot() const
return m_slot2;
break;
default:
LogError(LOG_NET, "DMR, invalid slot, TSCC disabled, slotNo = %u", m_tsccSlotNo);
LogError(LOG_DMR, "DMR, invalid slot, TSCC disabled, slotNo = %u", m_tsccSlotNo);
return nullptr;
}
}
@ -394,7 +413,7 @@ void Control::writeRF_Ext_Func(uint32_t slotNo, uint32_t func, uint32_t arg, uin
m_slot2->control()->writeRF_Ext_Func(func, arg, dstId);
break;
default:
LogError(LOG_NET, "DMR, invalid slot, slotNo = %u", slotNo);
LogError(LOG_DMR, "DMR, invalid slot, slotNo = %u", slotNo);
break;
}
}
@ -415,7 +434,7 @@ void Control::writeRF_Call_Alrt(uint32_t slotNo, uint32_t srcId, uint32_t dstId)
m_slot2->control()->writeRF_Call_Alrt(srcId, dstId);
break;
default:
LogError(LOG_NET, "DMR, invalid slot, slotNo = %u", slotNo);
LogError(LOG_DMR, "DMR, invalid slot, slotNo = %u", slotNo);
break;
}
}

@ -33,15 +33,14 @@
#include "Defines.h"
#include "dmr/data/Data.h"
#include "dmr/lookups/DMRAffiliationLookup.h"
#include "dmr/Slot.h"
#include "modem/Modem.h"
#include "network/BaseNetwork.h"
#include "network/RemoteControl.h"
#include "lookups/RSSIInterpolator.h"
#include "lookups/IdenTableLookup.h"
#include "lookups/RadioIdLookup.h"
#include "lookups/TalkgroupIdLookup.h"
#include "lookups/AffiliationLookup.h"
#include "yaml/Yaml.h"
namespace dmr
@ -95,6 +94,9 @@ namespace dmr
/// <summary>Permits a TGID on a non-authoritative host.</summary>
void permittedTG(uint32_t dstId, uint8_t slot);
/// <summary>Gets instance of the DMRAffiliationLookup class.</summary>
lookups::DMRAffiliationLookup affiliations();
/// <summary>Helper to return the slot carrying the TSCC.</summary>
Slot* getTSCCSlot() const;

@ -143,7 +143,7 @@ Host::Host(const std::string& confFile) :
m_controlPermitTG(false),
m_activeTickDelay(5U),
m_idleTickDelay(5U),
m_remoteControl(nullptr)
m_RESTAPI(nullptr)
{
UDPSocket::startup();
}
@ -722,8 +722,8 @@ int Host::run()
setState(STATE_IDLE);
}
if (m_remoteControl != nullptr) {
m_remoteControl->setProtocols(dmr.get(), p25.get(), nxdn.get());
if (m_RESTAPI != nullptr) {
m_RESTAPI->setProtocols(dmr.get(), p25.get(), nxdn.get());
}
::LogInfoEx(LOG_HOST, "Host is performing late initialization and warmup");
@ -1335,14 +1335,6 @@ int Host::run()
nxdn->clock(ms);
#endif // defined(ENABLE_NXDN)
// ------------------------------------------------------
// -- Remote Control Processing --
// ------------------------------------------------------
if (m_remoteControl != nullptr) {
m_remoteControl->process(this, dmr.get(), p25.get(), nxdn.get());
}
// ------------------------------------------------------
// -- Timer Clocking --
// ------------------------------------------------------
@ -1848,13 +1840,13 @@ bool Host::readParams()
chNo = 4095U;
}
std::string rconAddress = channel["rconAddress"].as<std::string>("127.0.0.1");
uint16_t rconPort = (uint16_t)channel["rconPort"].as<uint32_t>(RCON_DEFAULT_PORT);
std::string rconPassword = channel["rconPassword"].as<std::string>();
std::string restApiAddress = channel["rconAddress"].as<std::string>("127.0.0.1");
uint16_t restApiPort = (uint16_t)channel["rconPort"].as<uint32_t>(REST_API_DEFAULT_PORT);
std::string restApiPassword = channel["rconPassword"].as<std::string>();
::LogInfoEx(LOG_HOST, "Voice Channel Id %u Channel No $%04X RCON Adddress %s:%u", m_channelId, chNo, rconAddress.c_str(), rconPort);
::LogInfoEx(LOG_HOST, "Voice Channel Id %u Channel No $%04X REST API Adddress %s:%u", m_channelId, chNo, restApiAddress.c_str(), restApiPort);
VoiceChData data = VoiceChData(chNo, rconAddress, rconPort, rconPassword);
VoiceChData data = VoiceChData(chNo, restApiAddress, restApiPort, restApiPassword);
m_voiceChData[chNo] = data;
m_voiceChNo.push_back(chNo);
}
@ -2260,20 +2252,20 @@ bool Host::createNetwork()
{
yaml::Node networkConf = m_conf["network"];
bool netEnable = networkConf["enable"].as<bool>(false);
bool rconEnable = networkConf["rconEnable"].as<bool>(false);
bool restApiEnable = networkConf["rconEnable"].as<bool>(false);
// dump out if both networking and RCON are disabled
if (!netEnable && !rconEnable) {
// dump out if both networking and REST API are disabled
if (!netEnable && !restApiEnable) {
return true;
}
std::string address = networkConf["address"].as<std::string>();
uint16_t port = (uint16_t)networkConf["port"].as<uint32_t>(TRAFFIC_DEFAULT_PORT);
uint16_t local = (uint16_t)networkConf["local"].as<uint32_t>(0U);
std::string rconAddress = networkConf["rconAddress"].as<std::string>("127.0.0.1");
uint16_t rconPort = (uint16_t)networkConf["rconPort"].as<uint32_t>(RCON_DEFAULT_PORT);
std::string rconPassword = networkConf["rconPassword"].as<std::string>();
bool rconDebug = networkConf["rconDebug"].as<bool>(false);
std::string restApiAddress = networkConf["rconAddress"].as<std::string>("127.0.0.1");
uint16_t restApiPort = (uint16_t)networkConf["rconPort"].as<uint32_t>(REST_API_DEFAULT_PORT);
std::string restApiPassword = networkConf["rconPassword"].as<std::string>();
bool restApiDebug = networkConf["rconDebug"].as<bool>(false);
uint32_t id = networkConf["id"].as<uint32_t>(0U);
uint32_t jitter = networkConf["talkgroupHang"].as<uint32_t>(360U);
std::string password = networkConf["password"].as<std::string>();
@ -2284,11 +2276,16 @@ bool Host::createNetwork()
bool updateLookup = networkConf["updateLookups"].as<bool>(false);
bool debug = networkConf["debug"].as<bool>(false);
if (rconPassword.length() > 64) {
std::string password = rconPassword;
rconPassword = password.substr(0, 64);
if (restApiPassword.length() > 64) {
std::string password = restApiPassword;
restApiPassword = password.substr(0, 64);
::LogWarning(LOG_HOST, "REST API password is too long; truncating to the first 64 characters.");
}
::LogWarning(LOG_HOST, "RCON password is too long; truncating to the first 64 characters.");
if (restApiPassword.empty() && restApiEnable) {
::LogWarning(LOG_HOST, "REST API password not provided; REST API disabled.");
restApiEnable = false;
}
IdenTable entry = m_idenTable->find(m_channelId);
@ -2314,13 +2311,13 @@ bool Host::createNetwork()
LogInfo(" Debug: yes");
}
}
LogInfo(" RCON Enabled: %s", rconEnable ? "yes" : "no");
if (rconEnable) {
LogInfo(" RCON Address: %s", rconAddress.c_str());
LogInfo(" RCON Port: %u", rconPort);
LogInfo(" REST API Enabled: %s", restApiEnable ? "yes" : "no");
if (restApiEnable) {
LogInfo(" REST API Address: %s", restApiAddress.c_str());
LogInfo(" REST API Port: %u", restApiPort);
if (rconDebug) {
LogInfo(" RCON Debug: yes");
if (restApiDebug) {
LogInfo(" REST API Debug: yes");
}
}
@ -2331,8 +2328,8 @@ bool Host::createNetwork()
m_network->setLookups(m_ridLookup, m_tidLookup);
m_network->setMetadata(m_identity, m_rxFrequency, m_txFrequency, entry.txOffsetMhz(), entry.chBandwidthKhz(), m_channelId, m_channelNo,
m_power, m_latitude, m_longitude, m_height, m_location);
if (rconEnable) {
m_network->setRconData(rconPassword, rconPort);
if (restApiEnable) {
m_network->setRESTAPIData(restApiPassword, restApiPort);
}
bool ret = m_network->open();
@ -2348,19 +2345,19 @@ bool Host::createNetwork()
}
// initialize network remote command
if (rconEnable) {
m_remoteControl = new RemoteControl(rconAddress, rconPort, rconPassword, this, rconDebug);
m_remoteControl->setLookups(m_ridLookup, m_tidLookup);
bool ret = m_remoteControl->open();
if (restApiEnable) {
m_RESTAPI = new RESTAPI(restApiAddress, restApiPort, restApiPassword, this, restApiDebug);
m_RESTAPI->setLookups(m_ridLookup, m_tidLookup);
bool ret = m_RESTAPI->open();
if (!ret) {
delete m_remoteControl;
m_remoteControl = nullptr;
LogError(LOG_HOST, "failed to initialize remote command networking! remote command control will be unavailable!");
// remote command control failing isn't fatal -- we'll allow this to return normally
delete m_RESTAPI;
m_RESTAPI = nullptr;
LogError(LOG_HOST, "failed to initialize REST API networking! REST API will be unavailable!");
// REST API failing isn't fatal -- we'll allow this to return normally
}
}
else {
m_remoteControl = nullptr;
m_RESTAPI = nullptr;
}
return true;
@ -2564,9 +2561,9 @@ void Host::setState(uint8_t state)
delete m_network;
}
if (m_remoteControl != nullptr) {
m_remoteControl->close();
delete m_remoteControl;
if (m_RESTAPI != nullptr) {
m_RESTAPI->close();
delete m_RESTAPI;
}
}
else {

@ -33,7 +33,7 @@
#include "Defines.h"
#include "network/Network.h"
#include "network/RemoteControl.h"
#include "network/RESTAPI.h"
#include "modem/Modem.h"
#include "Timer.h"
#include "lookups/AffiliationLookup.h"
@ -49,7 +49,7 @@
// Class Prototypes
// ---------------------------------------------------------------------------
class HOST_SW_API RemoteControl;
class HOST_SW_API RESTAPI;
// ---------------------------------------------------------------------------
// Class Declaration
@ -152,8 +152,8 @@ private:
uint8_t m_activeTickDelay;
uint8_t m_idleTickDelay;
friend class RemoteControl;
RemoteControl* m_remoteControl;
friend class RESTAPI;
RESTAPI* m_RESTAPI;
/// <summary>Reads basic configuration parameters from the INI.</summary>
bool readParams();

@ -54,9 +54,9 @@ namespace lookups
}
/// <summary>Initializes a new instance of the VoiceChData class.</summary>
/// <param name="chNo">Voice Channel Number.</param>
/// <param name="address">RCON Address.</param>
/// <param name="port">RCON Port.</param>
/// <param name="password">RCON Password.</param>
/// <param name="address">REST API Address.</param>
/// <param name="port">REST API Port.</param>
/// <param name="password">REST API Password.</param>
VoiceChData(uint32_t chNo, std::string address, uint16_t port, std::string password) :
m_chNo(chNo),
m_address(address),
@ -87,11 +87,11 @@ namespace lookups
public:
/// <summary>Voice Channel Number.</summary>
__READONLY_PROPERTY_PLAIN(uint32_t, chNo, chNo);
/// <summary>RCON Address.</summary>
/// <summary>REST API Address.</summary>
__READONLY_PROPERTY_PLAIN(std::string, address, address);
/// <summary>RCON Port.</summary>
/// <summary>REST API Port.</summary>
__READONLY_PROPERTY_PLAIN(uint16_t, port, port);
/// <summary>RCON Password.</summary>
/// <summary>REST API Password.</summary>
__READONLY_PROPERTY_PLAIN(std::string, password, password);
};

@ -34,7 +34,7 @@
#include "Defines.h"
#include "modem/port/IModemPort.h"
#include "network/RemoteControl.h"
#include "network/RESTAPI.h"
#include "RingBuffer.h"
#include "Timer.h"
@ -56,7 +56,7 @@
// ---------------------------------------------------------------------------
class HOST_SW_API HostCal;
class HOST_SW_API RemoteControl;
class HOST_SW_API RESTAPI;
namespace modem
{
@ -349,7 +349,7 @@ namespace modem
private:
friend class ::HostCal;
friend class ::RemoteControl;
friend class ::RESTAPI;
port::IModemPort* m_port;

@ -86,8 +86,8 @@ Network::Network(const std::string& address, uint16_t port, uint16_t local, uint
m_longitude(0.0F),
m_height(0),
m_location(),
m_rconPassword(),
m_rconPort(0)
m_restApiPassword(),
m_restApiPort(0)
{
assert(!address.empty());
assert(port > 0U);
@ -148,14 +148,14 @@ void Network::setMetadata(const std::string& identity, uint32_t rxFrequency, uin
}
/// <summary>
/// Sets RCON configuration settings from the modem.
/// Sets REST API configuration settings from the modem.
/// </summary>
/// <param name="password"></param>
/// <param name="port"></param>
void Network::setRconData(const std::string& password, uint16_t port)
void Network::setRESTAPIData(const std::string& password, uint16_t port)
{
m_rconPassword = password;
m_rconPort = port;
m_restApiPassword = password;
m_restApiPort = port;
}
/// <summary>
@ -539,8 +539,8 @@ bool Network::writeConfig()
// RCON
json::object rcon = json::object();
rcon["password"].set<std::string>(m_rconPassword); // RCON Password
rcon["port"].set<uint16_t>(m_rconPort); // RCON Port
rcon["password"].set<std::string>(m_restApiPassword); // REST API Password
rcon["port"].set<uint16_t>(m_restApiPort); // REST API Port
config["rcon"].set<json::object>(rcon);
config["software"].set<std::string>(std::string(software)); // Software ID

@ -59,8 +59,8 @@ namespace network
/// <summary>Sets metadata configuration settings from the modem.</summary>
void setMetadata(const std::string& callsign, uint32_t rxFrequency, uint32_t txFrequency, float txOffsetMhz, float chBandwidthKhz,
uint8_t channelId, uint32_t channelNo, uint32_t power, float latitude, float longitude, int height, const std::string& location);
/// <summary>Sets RCON configuration settings from the modem.</summary>
void setRconData(const std::string& password, uint16_t port);
/// <summary>Sets REST API configuration settings from the modem.</summary>
void setRESTAPIData(const std::string& password, uint16_t port);
/// <summary>Gets the current status of the network.</summary>
uint8_t getStatus();
@ -109,8 +109,8 @@ namespace network
int m_height;
std::string m_location;
std::string m_rconPassword;
uint16_t m_rconPort;
std::string m_restApiPassword;
uint16_t m_restApiPort;
/// <summary>Writes login request to the network.</summary>
bool writeLogin();

File diff suppressed because it is too large Load Diff

@ -0,0 +1,230 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
/*
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(__REST_API_H__)
#define __REST_API_H__
#include "Defines.h"
#include "network/UDPSocket.h"
#include "network/rest/RequestDispatcher.h"
#include "network/rest/http/HTTPServer.h"
#include "lookups/RadioIdLookup.h"
#include "lookups/TalkgroupIdLookup.h"
#include "Thread.h"
#include <vector>
#include <string>
#include <random>
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#define DVM_REST_RAND_MAX 0xfffffffffffffffe
#define PUT_AUTHENTICATE "/auth"
#define GET_VERSION "/version"
#define GET_STATUS "/status"
#define GET_VOICE_CH "/voice-ch"
#define PUT_MDM_MODE "/mdm/mode"
#define MODE_OPT_IDLE "idle"
#define MODE_OPT_LCKOUT "lockout"
#define MODE_OPT_FDMR "dmr"
#define MODE_OPT_FP25 "p25"
#define MODE_OPT_FNXDN "nxdn"
#define PUT_MDM_KILL "/mdm/kill"
#define PUT_PERMIT_TG "/permit-tg"
#define PUT_GRANT_TG "/grant-tg"
#define GET_RELEASE_GRNTS "/release-grants"
#define GET_RELEASE_AFFS "/release-affs"
#define GET_RID_WHITELIST "/rid-whitelist/(\\d+)"
#define GET_RID_BLACKLIST "/rid-blacklist/(\\d+)"
#define GET_DMR_BEACON "/dmr/beacon"
#define GET_DMR_DEBUG "/dmr/debug/(\\d+)/(\\d+)"
#define GET_DMR_DUMP_CSBK "/dmr/dump-csbk/(\\d+)"
#define PUT_DMR_RID "/dmr/rid"
#define GET_DMR_CC_DEDICATED "/dmr/cc-enable/(\\d+)"
#define GET_DMR_CC_BCAST "/dmr/cc-broadcast/(\\d+)"
#define GET_P25_CC "/p25/cc"
//#define GET_P25_CC_FALLBACK "/p25/cc-fallback/(\\d+)"
#define GET_P25_DEBUG "/p25/debug/(\\d+)/(\\d+)"
#define GET_P25_DUMP_TSBK "/p25/dump-tsbk/(\\d+)"
#define PUT_P25_RID "/p25/rid"
#define GET_P25_CC_DEDICATED "/p25/cc-enable/(\\d+)"
#define GET_P25_CC_BCAST "/p25/cc-broadcast/(\\d+)"
#define GET_NXDN_DEBUG "/nxdn/debug/(\\d+)/(\\d+)"
#define GET_NXDN_DUMP_RCCH "/nxdn/dump-rcch/(\\d+)"
// ---------------------------------------------------------------------------
// Class Prototypes
// ---------------------------------------------------------------------------
class HOST_SW_API Host;
namespace dmr { class HOST_SW_API Control; }
namespace p25 { class HOST_SW_API Control; }
namespace nxdn { class HOST_SW_API Control; }
// ---------------------------------------------------------------------------
// Class Declaration
// Implements the REST API server logic.
// ---------------------------------------------------------------------------
class HOST_SW_API RESTAPI : private Thread {
public:
/// <summary>Initializes a new instance of the RESTAPI class.</summary>
RESTAPI(const std::string& address, uint16_t port, const std::string& password, Host* host, bool debug);
/// <summary>Finalizes a instance of the RESTAPI class.</summary>
~RESTAPI();
/// <summary>Sets the instances of the Radio ID and Talkgroup ID lookup tables.</summary>
void setLookups(::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupIdLookup* tidLookup);
/// <summary>Sets the instances of the digital radio protocols.</summary>
void setProtocols(dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn);
/// <summary>Opens connection to the network.</summary>
bool open();
/// <summary>Closes connection to the network.</summary>
void close();
private:
typedef network::rest::RequestDispatcher<network::rest::http::HTTPRequest, network::rest::http::HTTPReply> RESTDispatcherType;
typedef network::rest::http::HTTPRequest HTTPRequest;
typedef network::rest::http::HTTPReply HTTPReply;
RESTDispatcherType m_dispatcher;
network::rest::http::HTTPServer<RESTDispatcherType> m_restServer;
std::mt19937 m_random;
uint8_t m_p25MFId;
std::string m_password;
uint8_t* m_passwordHash;
bool m_debug;
Host* m_host;
dmr::Control* m_dmr;
p25::Control* m_p25;
nxdn::Control* m_nxdn;
::lookups::RadioIdLookup* m_ridLookup;
::lookups::TalkgroupIdLookup* m_tidLookup;
typedef std::unordered_map<std::string, uint64_t>::value_type AuthTokenValueType;
std::unordered_map<std::string, uint64_t> m_authTokens;
/// <summary></summary>
virtual void entry();
/// <summary>Helper to initialize REST API endpoints.</summary>
void initializeEndpoints();
/// <summary></summary>
void invalidateHostToken(const std::string host);
/// <summary></summary>
bool validateAuth(const HTTPRequest& request, HTTPReply& reply);
/// <summary></summary>
void restAPI_PutAuth(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetVersion(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetStatus(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetVoiceCh(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_PutModemMode(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_PutModemKill(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_PutPermitTG(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_PutGrantTG(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetReleaseGrants(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetReleaseAffs(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetRIDWhitelist(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetRIDBlacklist(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/*
** Digital Mobile Radio
*/
/// <summary></summary>
void restAPI_GetDMRBeacon(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetDMRDebug(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetDMRDumpCSBK(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_PutDMRRID(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetDMRCCEnable(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetDMRCCBroadcast(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/*
** Project 25
*/
/// <summary></summary>
void restAPI_GetP25CC(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetP25Debug(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetP25DumpTSBK(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_PutP25RID(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetP25CCEnable(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetP25CCBroadcast(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/*
** Next Generation Digital Narrowband
*/
/// <summary></summary>
void restAPI_GetNXDNDebug(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
/// <summary></summary>
void restAPI_GetNXDNDumpRCCH(const HTTPRequest& request, HTTPReply& reply, const network::rest::RequestMatch& match);
};
#endif // __REST_API_H__

File diff suppressed because it is too large Load Diff

@ -1,214 +0,0 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
//
// Based on code from the MMDVMHost project. (https://github.com/g4klx/MMDVMHost)
// Licensed under the GPLv2 License (https://opensource.org/licenses/GPL-2.0)
//
/*
* Copyright (C) 2019 by Jonathan Naylor G4KLX
* Copyright (C) 2019-2023 by Bryan Biedenkapp N2PLL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#if !defined(__REMOTE_CONTROL_H__)
#define __REMOTE_CONTROL_H__
#include "Defines.h"
#include "network/UDPSocket.h"
#include "network/rest/RequestDispatcher.h"
#include "network/rest/http/HTTPServer.h"
#include "lookups/RadioIdLookup.h"
#include "lookups/TalkgroupIdLookup.h"
#include "Thread.h"
#include <vector>
#include <string>
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
#define RCD_GET_VERSION "version"
#define RCD_GET_HELP "help"
#define RCD_GET_STATUS "status"
#define RCD_GET_VOICE_CH "voice-ch"
#define RCD_MODE "mdm-mode"
#define RCD_MODE_OPT_IDLE "idle"
#define RCD_MODE_OPT_LCKOUT "lockout"
#define RCD_MODE_OPT_FDMR "dmr"
#define RCD_MODE_OPT_FP25 "p25"
#define RCD_MODE_OPT_FNXDN "nxdn"
#define RCD_KILL "mdm-kill"
#define RCD_FORCE_KILL "mdm-force-kill"
#define RCD_PERMIT_TG "permit-tg"
#define RCD_GRANT_TG "grant-tg"
#define RCD_RID_WLIST "rid-whitelist"
#define RCD_RID_BLIST "rid-blacklist"
#define RCD_DMR_BEACON "dmr-beacon"
#define RCD_P25_CC "p25-cc"
#define RCD_P25_CC_FALLBACK "p25-cc-fallback"
#define RCD_DMR_RID_PAGE "dmr-rid-page"
#define RCD_DMR_RID_CHECK "dmr-rid-check"
#define RCD_DMR_RID_INHIBIT "dmr-rid-inhibit"
#define RCD_DMR_RID_UNINHIBIT "dmr-rid-uninhibit"
#define RCD_P25_SET_MFID "p25-set-mfid"
#define RCD_P25_RID_PAGE "p25-rid-page"
#define RCD_P25_RID_CHECK "p25-rid-check"
#define RCD_P25_RID_INHIBIT "p25-rid-inhibit"
#define RCD_P25_RID_UNINHIBIT "p25-rid-uninhibit"
#define RCD_P25_RID_GAQ "p25-rid-gaq"
#define RCD_P25_RID_UREG "p25-rid-ureg"
#define RCD_P25_RELEASE_GRANTS "p25-rel-grnts"
#define RCD_P25_RELEASE_AFFS "p25-rel-affs"
#define RCD_DMR_CC_DEDICATED "dmr-cc-dedicated"
#define RCD_DMR_CC_BCAST "dmr-cc-bcast"
#define RCD_P25_CC_DEDICATED "p25-cc-dedicated"
#define RCD_P25_CC_BCAST "p25-cc-bcast"
#define RCD_DMR_DEBUG "dmr-debug"
#define RCD_DMR_DUMP_CSBK "dmr-dump-csbk"
#define RCD_P25_DEBUG "p25-debug"
#define RCD_P25_DUMP_TSBK "p25-dump-tsbk"
#define RCD_NXDN_DEBUG "nxdn-debug"
#define RCD_NXDN_DUMP_RCCH "nxdn-dump-rcch"
#define RCD_DMRD_MDM_INJ "debug-dmrd-mdm-inj"
#define RCD_P25D_MDM_INJ "debug-p25d-mdm-inj"
#define RCD_NXDD_MDM_INJ "debug-nxdd-mdm-inj"
#define RCD_PING "ping"
// ---------------------------------------------------------------------------
// Class Prototypes
// ---------------------------------------------------------------------------
class HOST_SW_API Host;
namespace dmr { class HOST_SW_API Control; }
namespace p25 { class HOST_SW_API Control; }
namespace nxdn { class HOST_SW_API Control; }
// ---------------------------------------------------------------------------
// Class Declaration
// Implements the remote control networking logic.
// ---------------------------------------------------------------------------
class HOST_SW_API RemoteControl : private Thread {
public:
/// <summary>Initializes a new instance of the RemoteControl class.</summary>
RemoteControl(const std::string& address, uint16_t port, const std::string& password, Host* host, bool debug);
/// <summary>Finalizes a instance of the RemoteControl class.</summary>
~RemoteControl();
/// <summary>Sets the instances of the Radio ID and Talkgroup ID lookup tables.</summary>
void setLookups(::lookups::RadioIdLookup* ridLookup, ::lookups::TalkgroupIdLookup* tidLookup);
/// <summary>Sets the instances of the digital radio protocols.</summary>
void setProtocols(dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn);
/// <summary>Process remote network command data.</summary>
void process(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn);
/// <summary>Opens connection to the network.</summary>
bool open();
/// <summary>Closes connection to the network.</summary>
void close();
private:
typedef network::rest::RequestDispatcher<network::rest::http::HTTPRequest, network::rest::http::HTTPReply> RESTDispatcherType;
RESTDispatcherType m_dispatcher;
network::rest::http::HTTPServer<RESTDispatcherType> m_restServer;
network::UDPSocket m_socket;
uint8_t m_p25MFId;
std::string m_password;
uint8_t* m_passwordHash;
bool m_debug;
Host* m_host;
dmr::Control* m_dmr;
p25::Control* m_p25;
nxdn::Control* m_nxdn;
::lookups::RadioIdLookup* m_ridLookup;
::lookups::TalkgroupIdLookup* m_tidLookup;
/// <summary>Helper to initialize REST API endpoints.</summary>
void initializeEndpoints();
/// <summary></summary>
virtual void entry();
/// <summary>Helper to send response to client.</summary>
void writeResponse(std::string reply, sockaddr_storage address, uint32_t addrLen);
/// <summary>Helper to print the remote control help.</summary>
std::string displayHelp();
/// <summary></summary>
std::string rcdGetStatus(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn);
/// <summary></summary>
std::string rcdGetVoiceCh(Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn);
/// <summary></summary>
std::string rcdMode(std::vector<std::string> args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn);
/// <summary></summary>
std::string rcdPermitTG(std::vector<std::string> args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn);
/// <summary></summary>
std::string rcdGrantTG(std::vector<std::string> args, Host* host, dmr::Control* dmr, p25::Control* p25, nxdn::Control* nxdn);
/// <summary></summary>
std::string rcdDMRModemInj(std::vector<std::string> args, Host* host, dmr::Control* dmr);
/// <summary></summary>
std::string rcdP25ModemInj(std::vector<std::string> args, Host* host, p25::Control* p25);
/// <summary></summary>
std::string rcdNXDNModemInj(std::vector<std::string> args, Host* host, nxdn::Control* nxdn);
/// <summary></summary>
std::string getArgString(std::vector<std::string> args, uint32_t n) const;
/// <summary></summary>
__forceinline uint64_t getArgUInt64(std::vector<std::string> args, uint32_t n) const { return (uint64_t)::atol(getArgString(args, n).c_str()); }
/// <summary></summary>
__forceinline uint32_t getArgUInt32(std::vector<std::string> args, uint32_t n) const { return (uint32_t)::atoi(getArgString(args, n).c_str()); }
/// <summary></summary>
__forceinline int32_t getArgInt32(std::vector<std::string> args, uint32_t n) const { return ::atoi(getArgString(args, n).c_str()); }
/// <summary></summary>
__forceinline uint16_t getArgUInt16(std::vector<std::string> args, uint32_t n) const { return (uint16_t)::atoi(getArgString(args, n).c_str()); }
/// <summary></summary>
__forceinline int16_t getArgInt16(std::vector<std::string> args, uint32_t n) const { return (int16_t)::atoi(getArgString(args, n).c_str()); }
/// <summary></summary>
__forceinline uint8_t getArgUInt8(std::vector<std::string> args, uint32_t n) const { return (uint8_t)::atoi(getArgString(args, n).c_str()); }
/// <summary></summary>
__forceinline int8_t getArgInt8(std::vector<std::string> args, uint32_t n) const { return (int8_t)::atoi(getArgString(args, n).c_str()); }
};
#endif // __REMOTE_CONTROL_H__

@ -49,9 +49,9 @@ namespace network
struct RequestMatch : std::smatch
{
/// <summary>Initializes a new instance of the RequestMatch structure.</summary>
RequestMatch(const std::smatch& m, const std::string& d) : std::smatch(m), data(d) { /* stub */ }
RequestMatch(const std::smatch& m, const std::string& c) : std::smatch(m), content(c) { /* stub */ }
std::string data;
std::string content;
};
// ---------------------------------------------------------------------------
@ -98,7 +98,7 @@ namespace network
/// <summary></summary>
void handleRequest(const Request& request, Reply& reply, const std::smatch &what) {
// dispatching to matching based on handler
RequestMatch match(what, request.data);
RequestMatch match(what, request.content);
auto& handler = m_handlers[request.method];
if (handler) {
handler(request, reply, match);
@ -128,20 +128,21 @@ namespace network
RequestDispatcher(const std::string& basePath, bool debug) : m_basePath(basePath), m_debug(debug) { /* stub */ }
/// <summary></summary>
MatcherType& match(const std::string& expression)
MatcherType& match(const std::string& expression, bool regex = false)
{
MatcherTypePtr& p = m_matchers[expression];
if (!p) {
if (m_debug) {
::LogDebug(LOG_RCON, "creating REST RequestDispatcher, expression = %s", expression.c_str());
::LogDebug(LOG_REST, "creating RequestDispatcher, expression = %s", expression.c_str());
}
p = std::make_shared<MatcherType>(expression);
} else {
if (m_debug) {
::LogDebug(LOG_RCON, "fetching REST RequestDispatcher, expression = %s", expression.c_str());
::LogDebug(LOG_REST, "fetching RequestDispatcher, expression = %s", expression.c_str());
}
}
p->setRegEx(regex);
return *p;
}
@ -153,7 +154,7 @@ namespace network
if (!matcher.second->regex()) {
if (request.uri.find(matcher.first) != std::string::npos) {
if (m_debug) {
::LogDebug(LOG_RCON, "non-regex endpoint, uri = %s, expression = %s", request.uri.c_str(), matcher.first.c_str());
::LogDebug(LOG_REST, "non-regex endpoint, uri = %s, expression = %s", request.uri.c_str(), matcher.first.c_str());
}
//what = matcher.first;
@ -163,7 +164,7 @@ namespace network
} else {
if (std::regex_match(request.uri, what, std::regex(matcher.first))) {
if (m_debug) {
::LogDebug(LOG_RCON, "regex endpoint, uri = %s, expression = %s", request.uri.c_str(), matcher.first.c_str());
::LogDebug(LOG_REST, "regex endpoint, uri = %s, expression = %s", request.uri.c_str(), matcher.first.c_str());
}
matcher.second->handleRequest(request, reply, what);
@ -172,7 +173,7 @@ namespace network
}
}
::LogError(LOG_RCON, "unknown endpoint, uri = %s", request.uri.c_str());
::LogError(LOG_REST, "unknown endpoint, uri = %s", request.uri.c_str());
reply = http::HTTPReply::stockReply(http::HTTPReply::BAD_REQUEST, "application/json");
}

@ -38,9 +38,9 @@
#define __REST_HTTP__CONNECTION_H__
#include "Defines.h"
#include "network/rest/http/HTTPReply.h"
#include "network/rest/http/HTTPRequest.h"
#include "network/rest/http/HTTPRequestLexer.h"
#include "network/rest/http/HTTPReply.h"
#include <array>
#include <memory>
@ -104,13 +104,14 @@ namespace network
m_socket.async_read_some(asio::buffer(m_buffer), [=](asio::error_code ec, std::size_t bytes_transferred) {
if (!ec) {
HTTPRequestLexer::ResultType result;
char* data;
char* content;
std::tie(result, content) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred);
std::tie(result, data) = m_lexer.parse(m_request, m_buffer.data(), m_buffer.data() + bytes_transferred);
auto header = std::find_if(m_request.headers.begin(), m_request.headers.end(), [](const HTTPHeader& h) { return h.name == "content-length"; });
if (header != m_request.headers.end()) {
size_t length = (size_t)::strtoul(header->value.c_str(), NULL, 10);
m_request.data = std::string(data, length);
std::string contentLength = m_request.headers.find("Content-Length");
if (contentLength != "") {
size_t length = (size_t)::strtoul(contentLength.c_str(), NULL, 10);
m_request.content = std::string(content, length);
}
if (result == HTTPRequestLexer::GOOD) {
@ -137,13 +138,13 @@ namespace network
if (!m_persistent) {
auto self(this->shared_from_this());
} else {
m_reply.headers.emplace_back("Connection:", "keep-alive");
m_reply.headers.add("Connection", "keep-alive");
}
asio::async_write(m_socket, m_reply.toBuffers(), [=](asio::error_code ec, std::size_t) {
if (m_persistent) {
m_lexer.reset();
m_reply.headers.resize(0);
m_reply.headers = HTTPHeaders();
m_reply.status = HTTPReply::OK;
m_reply.content = "";
m_request = HTTPRequest();

@ -1,71 +0,0 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
//
// Based on code from the CRUD project. (https://github.com/venediktov/CRUD)
// Licensed under the BPL-1.0 License (https://opensource.org/license/bsl1-0-html)
//
/*
* Copyright (c) 2003-2013 Christopher M. Kohlhoff
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
*
* Permission is hereby granted, free of charge, to any person or organization
* obtaining a copy of the software and accompanying documentation covered by
* this license (the Software) to use, reproduce, display, distribute, execute,
* and transmit the Software, and to prepare derivative works of the Software, and
* to permit third-parties to whom the Software is furnished to do so, all subject
* to the following:
*
* The copyright notices in the Software and this entire statement, including the
* above license grant, this restriction and the following disclaimer, must be included
* in all copies of the Software, in whole or in part, and all derivative works of the
* Software, unless such copies or derivative works are solely in the form of
* machine-executable object code generated by a source language processor.
*
* THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
* DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if !defined(__REST_HTTP__HTTP_HEADER_H__)
#define __REST_HTTP__HTTP_HEADER_H__
#include "Defines.h"
#include <string>
namespace network
{
namespace rest
{
namespace http
{
// ---------------------------------------------------------------------------
// Structure Declaration
// This class implements a model for an HTTP header.
// ---------------------------------------------------------------------------
struct HTTPHeader
{
std::string name;
std::string value;
/// <summary>Initializes a new instance of the HTTPHeader struct.</summary>
HTTPHeader() { /* stub */ }
/// <summary>Initializes a new instance of the HTTPHeader struct.</summary>
HTTPHeader(const std::string& name, const std::string& value) : name(name), value(value) { /* stub */ }
};
} // namespace http
} // namespace rest
} // namespace network
#endif // __REST_HTTP__HTTP_HEADER_H__

@ -0,0 +1,132 @@
/**
* Digital Voice Modem - Host Software
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Host Software
*
*/
//
// Based on code from the CRUD project. (https://github.com/venediktov/CRUD)
// Licensed under the BPL-1.0 License (https://opensource.org/license/bsl1-0-html)
//
/*
* Copyright (c) 2003-2013 Christopher M. Kohlhoff
* Copyright (C) 2023 by Bryan Biedenkapp N2PLL
*
* Permission is hereby granted, free of charge, to any person or organization
* obtaining a copy of the software and accompanying documentation covered by
* this license (the Software) to use, reproduce, display, distribute, execute,
* and transmit the Software, and to prepare derivative works of the Software, and
* to permit third-parties to whom the Software is furnished to do so, all subject
* to the following:
*
* The copyright notices in the Software and this entire statement, including the
* above license grant, this restriction and the following disclaimer, must be included
* in all copies of the Software, in whole or in part, and all derivative works of the
* Software, unless such copies or derivative works are solely in the form of
* machine-executable object code generated by a source language processor.
*
* THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
* PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR ANYONE
* DISTRIBUTING THE SOFTWARE BE LIABLE FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#if !defined(__REST_HTTP__HTTP_HEADERS_H__)
#define __REST_HTTP__HTTP_HEADERS_H__
#include "Defines.h"
#include "Log.h"
#include <string>
#include <vector>
namespace network
{
namespace rest
{
namespace http
{
// ---------------------------------------------------------------------------
// Class Prototypes
// ---------------------------------------------------------------------------
struct HTTPReply;
// ---------------------------------------------------------------------------
// Structure Declaration
//
// ---------------------------------------------------------------------------
struct HTTPHeaders
{
struct Header
{
std::string name;
std::string value;
Header() : name{}, value{} { /* stub */}
Header(std::string n, std::string v) : name{n}, value{v} { /* stub */ }
};
/// <summary>Gets the list of HTTP headers.</summary>
std::vector<Header> headers() const { return m_headers; }
/// <summary>Returns true if the headers are empty.</summary>
bool empty() const { return m_headers.empty(); }
/// <summary>Returns the number of headers.</summary>
std::size_t size() const { return m_headers.size(); }
/// <summary>Clears the list of HTTP headers.</summary>
void clearHeaders() { m_headers = std::vector<Header>(); }
/// <summary>Helper to add a HTTP header.</summary>
void add(const std::string& name, const std::string& value)
{
//::LogDebug(LOG_REST, "HTTPHeaders::add(), header = %s, value = %s", name.c_str(), value.c_str());
for (auto& header : m_headers) {
if (::strtolower(header.name) == ::strtolower(name)) {
header.value = value;
return;
}
}
m_headers.push_back(Header(name, value));
//for (auto header : m_headers)
// ::LogDebug(LOG_REST, "HTTPHeaders::add() m_headers.header = %s, m_headers.value = %s", header.name.c_str(), header.value.c_str());
}
/// <summary>Helper to add a HTTP header.</summary>
void remove(const std::string headerName)
{
auto header = std::find_if(m_headers.begin(), m_headers.end(), [&](const Header& h) {
return ::strtolower(h.name) == ::strtolower(headerName);
});
if (header != m_headers.end()) {
m_headers.erase(header);
}
}
/// <summary>Helper to find the named HTTP header.</summary>
std::string find(const std::string headerName) const
{
auto header = std::find_if(m_headers.begin(), m_headers.end(), [&](const Header& h) {
return ::strtolower(h.name) == ::strtolower(headerName);
});
if (header != m_headers.end()) {
return header->value;
}
else {
return "";
}
}
private:
friend struct HTTPReply;
std::vector<Header> m_headers;
};
} // namespace http
} // namespace rest
} // namespace network
#endif // __REST_HTTP__HTTP_HEADERS_H__

@ -36,6 +36,7 @@
*/
#include "Defines.h"
#include "network/rest/http/HTTPReply.h"
#include "Log.h"
using namespace network::rest::http;
@ -299,7 +300,9 @@ std::vector<asio::const_buffer> HTTPReply::toBuffers()
buffers.push_back(status_strings::toBuffer(status));
for (std::size_t i = 0; i < headers.size(); ++i) {
HTTPHeader& h = headers[i];
HTTPHeaders::Header& h = headers.m_headers[i];
//::LogDebug(LOG_REST, "HTTPReply::toBuffers() header = %s, value = %s", h.name.c_str(), h.value.c_str());
buffers.push_back(asio::buffer(h.name));
buffers.push_back(asio::buffer(misc_strings::name_value_separator));
buffers.push_back(asio::buffer(h.value));
@ -368,7 +371,10 @@ HTTPReply HTTPReply::stockReply(HTTPReply::StatusType status, const std::string
/// <param name="contentType"></param>
void HTTPReply::ensureDefaultHeaders(std::string contentType)
{
headers.push_back(HTTPHeader("Content-Length", std::to_string(content.size())));
headers.push_back(HTTPHeader("Content-Type", contentType));
headers.push_back(HTTPHeader("Server", std::string((__EXE_NAME__ "/" __VER__))));
headers.add("Content-Type", contentType);
headers.add("Content-Length", std::to_string(content.size()));
headers.add("Server", std::string((__EXE_NAME__ "/" __VER__)));
//for (auto header : headers.headers())
// ::LogDebug(LOG_REST, "HTTPReply::ensureDefaultHeaders() header = %s, value = %s", header.name.c_str(), header.value.c_str());
}

@ -39,7 +39,7 @@
#include "Defines.h"
#include "network/json/json.h"
#include "network/rest/http/HTTPHeader.h"
#include "network/rest/http/HTTPHeaders.h"
#include <string>
#include <vector>
@ -82,7 +82,7 @@ namespace network
SERVICE_UNAVAILABLE = 503
} status;
std::vector<HTTPHeader> headers;
HTTPHeaders headers;
std::string content;
/// <summary>Convert the reply into a vector of buffers. The buffers do not own the

@ -38,7 +38,8 @@
#define __REST_HTTP__HTTP_REQUEST_H__
#include "Defines.h"
#include "network/rest/http/HTTPHeader.h"
#include "network/rest/http/HTTPHeaders.h"
#include "Log.h"
#include <string>
#include <vector>
@ -49,7 +50,6 @@ namespace network
{
namespace http
{
// ---------------------------------------------------------------------------
// Structure Declaration
// This struct implements a model of a request received from a HTTP client.
@ -60,11 +60,12 @@ namespace network
std::string method;
std::string uri;
HTTPHeaders headers;
std::string content;
int httpVersionMajor;
int httpVersionMinor;
std::vector<HTTPHeader> headers;
std::string data;
};
} // namespace http
} // namespace rest

@ -106,11 +106,9 @@ void HTTPRequestHandler::handleRequest(const HTTPRequest& request, HTTPReply& re
while (is.read(buf, sizeof(buf)).gcount() > 0)
reply.content.append(buf, is.gcount());
reply.headers.resize(2);
reply.headers[0].name = "Content-Length";
reply.headers[0].value = std::to_string(reply.content.size());
reply.headers[1].name = "Content-Type";
reply.headers[1].value = "application/octet-stream";
reply.headers.clearHeaders();
reply.headers.add("Content-Length", std::to_string(reply.content.size()));
reply.headers.add("Content-Type", "application/octet-stream");
}
/// <summary>

@ -37,6 +37,7 @@
#include "Defines.h"
#include "network/rest/http/HTTPRequestLexer.h"
#include "network/rest/http/HTTPRequest.h"
#include "Log.h"
using namespace network::rest::http;
@ -51,6 +52,7 @@ using namespace network::rest::http;
/// </summary>
HTTPRequestLexer::HTTPRequestLexer() :
m_headers(),
m_state(METHOD_START)
{
/* stub */
@ -60,6 +62,7 @@ HTTPRequestLexer::HTTPRequestLexer() :
void HTTPRequestLexer::reset()
{
m_state = METHOD_START;
m_headers = std::vector<LexedHeader>();
}
// ---------------------------------------------------------------------------
@ -234,8 +237,8 @@ HTTPRequestLexer::ResultType HTTPRequestLexer::consume(HTTPRequest& req, char in
return BAD;
}
else {
req.headers.push_back(HTTPHeader());
req.headers.back().name.push_back(std::tolower(input));
m_headers.push_back(LexedHeader());
m_headers.back().name.push_back(std::tolower(input));
m_state = HEADER_NAME;
return INDETERMINATE;
}
@ -253,7 +256,7 @@ HTTPRequestLexer::ResultType HTTPRequestLexer::consume(HTTPRequest& req, char in
}
else {
m_state = HEADER_VALUE;
req.headers.back().value.push_back(input);
m_headers.back().value.push_back(input);
return INDETERMINATE;
}
@ -267,7 +270,7 @@ HTTPRequestLexer::ResultType HTTPRequestLexer::consume(HTTPRequest& req, char in
}
else
{
req.headers.back().name.push_back(std::tolower(input));
m_headers.back().name.push_back(std::tolower(input));
return INDETERMINATE;
}
@ -290,7 +293,7 @@ HTTPRequestLexer::ResultType HTTPRequestLexer::consume(HTTPRequest& req, char in
return BAD;
}
else {
req.headers.back().value.push_back(input);
m_headers.back().value.push_back(input);
return INDETERMINATE;
}
@ -304,7 +307,16 @@ HTTPRequestLexer::ResultType HTTPRequestLexer::consume(HTTPRequest& req, char in
}
case EXPECTING_NEWLINE_3:
return (input == '\n') ? GOOD : BAD;
if (input == '\n') {
for (auto header : m_headers) {
//::LogDebug(LOG_REST, "HTTPRequestLexer::consume(), header = %s, value = %s", header.name.c_str(), header.value.c_str());
req.headers.add(header.name, header.value);
}
return GOOD;
} else {
return BAD;
}
default:
return BAD;

@ -38,6 +38,7 @@
#define __REST_HTTP__HTTP_REQUEST_PARSER_H__
#include <tuple>
#include <vector>
namespace network
{
@ -96,6 +97,17 @@ namespace network
/// <summary>Check if a byte is an digit.</summary>
static bool isDigit(int c);
struct LexedHeader
{
std::string name;
std::string value;
LexedHeader() { /* stub */ }
LexedHeader(const std::string& name, const std::string& value) : name(name), value(value) {}
};
std::vector<LexedHeader> m_headers;
enum state
{
METHOD_START,

@ -40,7 +40,6 @@
#include "nxdn/packet/Data.h"
#include "nxdn/SiteData.h"
#include "network/BaseNetwork.h"
#include "network/RemoteControl.h"
#include "lookups/RSSIInterpolator.h"
#include "lookups/IdenTableLookup.h"
#include "lookups/RadioIdLookup.h"
@ -107,6 +106,9 @@ namespace nxdn
/// <summary>Permits a TGID on a non-authoritative host.</summary>
void permittedTG(uint32_t dstId);
/// <summary>Gets instance of the AffiliationLookup class.</summary>
lookups::AffiliationLookup affiliations() { return m_affiliations; }
/// <summary>Flag indicating whether the processor or is busy or not.</summary>
bool isBusy() const;

@ -38,7 +38,6 @@
#include "p25/packet/Voice.h"
#include "p25/packet/Trunk.h"
#include "network/BaseNetwork.h"
#include "network/RemoteControl.h"
#include "lookups/RSSIInterpolator.h"
#include "lookups/IdenTableLookup.h"
#include "lookups/RadioIdLookup.h"

@ -34,7 +34,6 @@
#include "p25/lc/TDULC.h"
#include "p25/Control.h"
#include "network/BaseNetwork.h"
#include "network/RemoteControl.h"
#include "Timer.h"
#include <cstdio>

@ -186,7 +186,7 @@ int RemoteCommand::send(const std::string& address, uint32_t port, const std::st
break;
if (debug)
::LogDebug(LOG_RCON, "RemoteCommand::send() block len = %u, offs = %u", len - 3, offs);
::LogDebug(LOG_HOST, "RemoteCommand::send() block len = %u, offs = %u", len - 3, offs);
buffer[len] = '\0';

@ -65,7 +65,7 @@ using namespace network;
static std::string g_progExe = std::string(__EXE_NAME__);
static std::string g_remoteAddress = std::string("127.0.0.1");
static uint32_t g_remotePort = RCON_DEFAULT_PORT;
static uint32_t g_remotePort = REST_API_DEFAULT_PORT;
static std::string g_remotePassword = std::string();
static bool g_debug = false;

Loading…
Cancel
Save

Powered by TurnKey Linux.