parent
21a4a97ec3
commit
6b18089d75
@ -0,0 +1,396 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Conference FNE Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Conference FNE Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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.
|
||||||
|
*/
|
||||||
|
#include "fne/Defines.h"
|
||||||
|
#include "common/edac/SHA256.h"
|
||||||
|
#include "common/lookups/AffiliationLookup.h"
|
||||||
|
#include "common/network/json/json.h"
|
||||||
|
#include "common/Log.h"
|
||||||
|
#include "common/Thread.h"
|
||||||
|
#include "common/Utils.h"
|
||||||
|
#include "fne/network/RESTAPI.h"
|
||||||
|
#include "HostFNE.h"
|
||||||
|
#include "FNEMain.h"
|
||||||
|
|
||||||
|
using namespace network;
|
||||||
|
using namespace network::rest;
|
||||||
|
using namespace network::rest::http;
|
||||||
|
|
||||||
|
#include <cstdio>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Macros
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define REST_API_BIND(funcAddr, classInstance) std::bind(&funcAddr, classInstance, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Global Functions
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
template<typename ... FormatArgs>
|
||||||
|
std::string string_format(const std::string& format, FormatArgs ... args)
|
||||||
|
{
|
||||||
|
int size_s = std::snprintf(nullptr, 0, format.c_str(), args ...) + 1; // extra space for '\0'
|
||||||
|
if (size_s <= 0)
|
||||||
|
throw std::runtime_error("Error during string formatting.");
|
||||||
|
|
||||||
|
auto size = static_cast<size_t>(size_s);
|
||||||
|
std::unique_ptr<char[]> buf(new char[ size ]);
|
||||||
|
std::snprintf(buf.get(), size, format.c_str(), args ...);
|
||||||
|
|
||||||
|
return std::string(buf.get(), buf.get() + size - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
void setResponseDefaultStatus(json::object& obj)
|
||||||
|
{
|
||||||
|
int s = (int)HTTPPayload::OK;
|
||||||
|
obj["status"].set<int>(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="reply"></param>
|
||||||
|
/// <param name="message"></param>
|
||||||
|
/// <param name="status"></param>
|
||||||
|
void errorPayload(HTTPPayload& reply, std::string message, HTTPPayload::StatusType status = HTTPPayload::BAD_REQUEST)
|
||||||
|
{
|
||||||
|
HTTPPayload rep;
|
||||||
|
rep.status = status;
|
||||||
|
|
||||||
|
json::object response = json::object();
|
||||||
|
|
||||||
|
int s = (int)rep.status;
|
||||||
|
response["status"].set<int>(s);
|
||||||
|
response["message"].set<std::string>(message);
|
||||||
|
|
||||||
|
reply.payload(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="reply"></param>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool parseRequestBody(const HTTPPayload& request, HTTPPayload& reply, json::object& obj)
|
||||||
|
{
|
||||||
|
std::string contentType = request.headers.find("Content-Type");
|
||||||
|
if (contentType != "application/json") {
|
||||||
|
reply = HTTPPayload::statusPayload(HTTPPayload::BAD_REQUEST, "application/json");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse JSON body
|
||||||
|
json::value v;
|
||||||
|
std::string err = json::parse(v, request.content);
|
||||||
|
if (!err.empty()) {
|
||||||
|
errorPayload(reply, err);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure parsed JSON is an object
|
||||||
|
if (!v.is<json::object>()) {
|
||||||
|
errorPayload(reply, "Request was not a valid JSON object.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
obj = v.get<json::object>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Public Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the RESTAPI class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="address">Network Hostname/IP address to connect to.</param>
|
||||||
|
/// <param name="port">Network port number.</param>
|
||||||
|
/// <param name="password">Authentication password.</param>
|
||||||
|
/// <param name="host">Instance of the Host class.</param>
|
||||||
|
/// <param name="debug"></param>
|
||||||
|
RESTAPI::RESTAPI(const std::string& address, uint16_t port, const std::string& password, HostFNE* host, bool debug) :
|
||||||
|
m_dispatcher(debug),
|
||||||
|
m_restServer(address, port),
|
||||||
|
m_random(),
|
||||||
|
m_password(password),
|
||||||
|
m_passwordHash(nullptr),
|
||||||
|
m_debug(debug),
|
||||||
|
m_host(host),
|
||||||
|
m_ridLookup(nullptr),
|
||||||
|
m_tidLookup(nullptr),
|
||||||
|
m_authTokens()
|
||||||
|
{
|
||||||
|
assert(!address.empty());
|
||||||
|
assert(port > 0U);
|
||||||
|
assert(!password.empty());
|
||||||
|
|
||||||
|
size_t size = password.size();
|
||||||
|
|
||||||
|
uint8_t* in = new uint8_t[size];
|
||||||
|
for (size_t i = 0U; i < size; i++)
|
||||||
|
in[i] = password.at(i);
|
||||||
|
|
||||||
|
m_passwordHash = new uint8_t[32U];
|
||||||
|
::memset(m_passwordHash, 0x00U, 32U);
|
||||||
|
|
||||||
|
edac::SHA256 sha256;
|
||||||
|
sha256.buffer(in, (uint32_t)(size), m_passwordHash);
|
||||||
|
|
||||||
|
delete[] in;
|
||||||
|
|
||||||
|
if (m_debug) {
|
||||||
|
Utils::dump("REST Password Hash", m_passwordHash, 32U);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::random_device rd;
|
||||||
|
std::mt19937 mt(rd());
|
||||||
|
m_random = mt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizes a instance of the RESTAPI class.
|
||||||
|
/// </summary>
|
||||||
|
RESTAPI::~RESTAPI()
|
||||||
|
{
|
||||||
|
/* stub */
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the instances of the Radio ID and Talkgroup ID lookup tables.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="ridLookup">Radio ID Lookup Table Instance</param>
|
||||||
|
/// <param name="tidLookup">Talkgroup Rules Lookup Table Instance</param>
|
||||||
|
void RESTAPI::setLookups(lookups::RadioIdLookup* ridLookup, lookups::TalkgroupRulesLookup* tidLookup)
|
||||||
|
{
|
||||||
|
m_ridLookup = ridLookup;
|
||||||
|
m_tidLookup = tidLookup;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opens connection to the network.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
bool RESTAPI::open()
|
||||||
|
{
|
||||||
|
initializeEndpoints();
|
||||||
|
m_restServer.setHandler(m_dispatcher);
|
||||||
|
|
||||||
|
return run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Closes connection to the network.
|
||||||
|
/// </summary>
|
||||||
|
void RESTAPI::close()
|
||||||
|
{
|
||||||
|
m_restServer.stop();
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Private Class Members
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
void RESTAPI::entry()
|
||||||
|
{
|
||||||
|
m_restServer.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Helper to initialize REST API endpoints.
|
||||||
|
/// </summary>
|
||||||
|
void RESTAPI::initializeEndpoints()
|
||||||
|
{
|
||||||
|
m_dispatcher.match(PUT_AUTHENTICATE).put(REST_API_BIND(RESTAPI::restAPI_PutAuth, this));
|
||||||
|
|
||||||
|
m_dispatcher.match(GET_VERSION).get(REST_API_BIND(RESTAPI::restAPI_GetVersion, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="host"></param>
|
||||||
|
void RESTAPI::invalidateHostToken(const std::string host)
|
||||||
|
{
|
||||||
|
auto token = std::find_if(m_authTokens.begin(), m_authTokens.end(), [&](const AuthTokenValueType& tok) { return tok.first == host; });
|
||||||
|
if (token != m_authTokens.end()) {
|
||||||
|
m_authTokens.erase(host);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
bool RESTAPI::validateAuth(const HTTPPayload& request, HTTPPayload& reply)
|
||||||
|
{
|
||||||
|
std::string host = request.headers.find("RemoteHost");
|
||||||
|
std::string headerToken = request.headers.find("X-DVM-Auth-Token");
|
||||||
|
#if DEBUG_HTTP_PAYLOAD
|
||||||
|
::LogDebug(LOG_REST, "RESTAPI::validateAuth() token, host = %s, token = %s", host.c_str(), headerToken.c_str());
|
||||||
|
#endif
|
||||||
|
if (headerToken == "") {
|
||||||
|
errorPayload(reply, "no authentication token", HTTPPayload::UNAUTHORIZED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& token : m_authTokens) {
|
||||||
|
#if DEBUG_HTTP_PAYLOAD
|
||||||
|
::LogDebug(LOG_REST, "RESTAPI::validateAuth() valid list, host = %s, token = %s", token.first.c_str(), std::to_string(token.second).c_str());
|
||||||
|
#endif
|
||||||
|
if (token.first.compare(host) == 0) {
|
||||||
|
#if DEBUG_HTTP_PAYLOAD
|
||||||
|
::LogDebug(LOG_REST, "RESTAPI::validateAuth() storedToken = %s, passedToken = %s", std::to_string(token.second).c_str(), headerToken.c_str());
|
||||||
|
#endif
|
||||||
|
if (std::to_string(token.second).compare(headerToken) == 0) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
m_authTokens.erase(host); // devalidate host
|
||||||
|
errorPayload(reply, "invalid authentication token", HTTPPayload::UNAUTHORIZED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
errorPayload(reply, "illegal authentication token", HTTPPayload::UNAUTHORIZED);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="reply"></param>
|
||||||
|
/// <param name="match"></param>
|
||||||
|
void RESTAPI::restAPI_PutAuth(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match)
|
||||||
|
{
|
||||||
|
std::string host = request.headers.find("RemoteHost");
|
||||||
|
json::object response = json::object();
|
||||||
|
setResponseDefaultStatus(response);
|
||||||
|
|
||||||
|
json::object req = json::object();
|
||||||
|
if (!parseRequestBody(request, reply, req)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// validate auth is a string within the JSON blob
|
||||||
|
if (!req["auth"].is<std::string>()) {
|
||||||
|
invalidateHostToken(host);
|
||||||
|
errorPayload(reply, "password was not a valid string");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string auth = req["auth"].get<std::string>();
|
||||||
|
if (auth.empty()) {
|
||||||
|
invalidateHostToken(host);
|
||||||
|
errorPayload(reply, "auth cannot be empty");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth.size() > 64) {
|
||||||
|
invalidateHostToken(host);
|
||||||
|
errorPayload(reply, "auth cannot be longer than 64 characters");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(auth.find_first_not_of("0123456789abcdefABCDEF", 2) == std::string::npos)) {
|
||||||
|
invalidateHostToken(host);
|
||||||
|
errorPayload(reply, "auth contains invalid characters");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_debug) {
|
||||||
|
::LogDebug(LOG_REST, "/auth auth = %s", auth.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* authPtr = auth.c_str();
|
||||||
|
uint8_t* passwordHash = new uint8_t[32U];
|
||||||
|
::memset(passwordHash, 0x00U, 32U);
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < 32U; i++) {
|
||||||
|
char t[4] = {authPtr[0], authPtr[1], 0};
|
||||||
|
passwordHash[i] = (uint8_t)::strtoul(t, NULL, 16);
|
||||||
|
authPtr += 2 * sizeof(char);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_debug) {
|
||||||
|
Utils::dump("Password Hash", passwordHash, 32U);
|
||||||
|
}
|
||||||
|
|
||||||
|
// compare hashes
|
||||||
|
if (::memcmp(m_passwordHash, passwordHash, 32U) != 0) {
|
||||||
|
invalidateHostToken(host);
|
||||||
|
errorPayload(reply, "invalid password");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
delete[] passwordHash;
|
||||||
|
|
||||||
|
invalidateHostToken(host);
|
||||||
|
std::uniform_int_distribution<uint64_t> dist(DVM_RAND_MIN, DVM_REST_RAND_MAX);
|
||||||
|
uint64_t salt = dist(m_random);
|
||||||
|
|
||||||
|
m_authTokens[host] = salt;
|
||||||
|
response["token"].set<std::string>(std::to_string(salt));
|
||||||
|
reply.payload(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="request"></param>
|
||||||
|
/// <param name="reply"></param>
|
||||||
|
/// <param name="match"></param>
|
||||||
|
void RESTAPI::restAPI_GetVersion(const HTTPPayload& request, HTTPPayload& reply, const RequestMatch& match)
|
||||||
|
{
|
||||||
|
if (!validateAuth(request, reply)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
json::object response = json::object();
|
||||||
|
setResponseDefaultStatus(response);
|
||||||
|
response["version"].set<std::string>(std::string((__PROG_NAME__ " " __VER__ " (built " __BUILD__ ")")));
|
||||||
|
|
||||||
|
reply.payload(response);
|
||||||
|
}
|
||||||
@ -0,0 +1,107 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Conference FNE Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Conference FNE Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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 "fne/Defines.h"
|
||||||
|
#include "common/network/UDPSocket.h"
|
||||||
|
#include "common/network/rest/RequestDispatcher.h"
|
||||||
|
#include "common/network/rest/http/HTTPServer.h"
|
||||||
|
#include "common/lookups/RadioIdLookup.h"
|
||||||
|
#include "common/lookups/TalkgroupRulesLookup.h"
|
||||||
|
#include "common/Thread.h"
|
||||||
|
#include "fne/network/RESTDefines.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Class Prototypes
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
class HOST_SW_API HostFNE;
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// 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, HostFNE* 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::TalkgroupRulesLookup* tidLookup);
|
||||||
|
|
||||||
|
/// <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::HTTPPayload, network::rest::http::HTTPPayload> RESTDispatcherType;
|
||||||
|
typedef network::rest::http::HTTPPayload HTTPPayload;
|
||||||
|
RESTDispatcherType m_dispatcher;
|
||||||
|
network::rest::http::HTTPServer<RESTDispatcherType> m_restServer;
|
||||||
|
|
||||||
|
std::mt19937 m_random;
|
||||||
|
|
||||||
|
std::string m_password;
|
||||||
|
uint8_t* m_passwordHash;
|
||||||
|
bool m_debug;
|
||||||
|
|
||||||
|
HostFNE* m_host;
|
||||||
|
|
||||||
|
::lookups::RadioIdLookup* m_ridLookup;
|
||||||
|
::lookups::TalkgroupRulesLookup* 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 HTTPPayload& request, HTTPPayload& reply);
|
||||||
|
|
||||||
|
/// <summary></summary>
|
||||||
|
void restAPI_PutAuth(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
|
||||||
|
|
||||||
|
/// <summary></summary>
|
||||||
|
void restAPI_GetVersion(const HTTPPayload& request, HTTPPayload& reply, const network::rest::RequestMatch& match);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // __REST_API_H__
|
||||||
@ -0,0 +1,41 @@
|
|||||||
|
/**
|
||||||
|
* Digital Voice Modem - Conference FNE Software
|
||||||
|
* GPLv2 Open Source. Use is subject to license terms.
|
||||||
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
|
*
|
||||||
|
* @package DVM / Conference FNE Software
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2024 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_DEFINES_H__)
|
||||||
|
#define __REST_DEFINES_H__
|
||||||
|
|
||||||
|
#include "fne/Defines.h"
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Constants
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#define DVM_REST_RAND_MAX 0xfffffffffffffffe
|
||||||
|
|
||||||
|
#define PUT_AUTHENTICATE "/auth"
|
||||||
|
|
||||||
|
#define GET_VERSION "/version"
|
||||||
|
|
||||||
|
#endif // __REST_API_H__
|
||||||
Loading…
Reference in new issue