mirror of https://github.com/nostar/urfd.git
parent
f001a2fbbd
commit
6e7ba8c5a0
@ -0,0 +1,928 @@
|
||||
/*
|
||||
* Copyright (c) 2023 by Thomas A. Early N7TAE
|
||||
*
|
||||
* 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 <curl/curl.h>
|
||||
#include <stdlib.h>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <cstring>
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <algorithm>
|
||||
#include <regex>
|
||||
|
||||
#include "Configure.h"
|
||||
|
||||
// ini file keywords
|
||||
#define JAUTOLINKMODULE "AutoLinkModule"
|
||||
#define JBLACKLISTPATH "BlacklistPath"
|
||||
#define JBRANDMEISTER "Brandmeister"
|
||||
#define JCALLSIGN "Callsign"
|
||||
#define JCLIENTSPATH "ClientFilePath"
|
||||
#define JCOUNTRY "Country"
|
||||
#define JDCS "DCS"
|
||||
#define JDEFAULTCALLSIGN "DefaultCallsign"
|
||||
#define JDEFAULTID "DefaultId"
|
||||
#define JDEFAULTRXFREQ "DefaultRxFreq"
|
||||
#define JDEFAULTTXFREQ "DefaultTxFreq"
|
||||
#define JDESCRIPTION "Description"
|
||||
#define JDEXTRA "DExtra"
|
||||
#define JDMRIDDB "DMR ID DB"
|
||||
#define JDMRPLUS "DMRPlus"
|
||||
#define JDPLUS "DPlus"
|
||||
#define JFILES "Files"
|
||||
#define JFILEPATH "FilePath"
|
||||
#define JG3TERMINALPATH "G3TerminalPath"
|
||||
#define JHOSTNAME "Hostname"
|
||||
#define JINTERLINKPATH "InterlinkPath"
|
||||
#define JIPADDRESSES "IpAddresses"
|
||||
#define JIPV4BINDING "IPv4Binding"
|
||||
#define JIPV4EXTERNAL "IPv4External"
|
||||
#define JIPV6BINDING "IPv6Binding"
|
||||
#define JIPV6EXTERNAL "IPv6External"
|
||||
#define JJSONPATH "JsonPath"
|
||||
#define JM17 "M17"
|
||||
#define JMMDVM "MMDVM"
|
||||
#define JMODE "Mode"
|
||||
#define JMODULES "Modules"
|
||||
#define JNAMES "Names"
|
||||
#define JNXDNIDDB "NXDN ID DB"
|
||||
#define JNXDN "NXDN"
|
||||
#define JP25 "P25"
|
||||
#define JPIDPATH "PidPath"
|
||||
#define JPORT "Port"
|
||||
#define JREFLECTORID "ReflectorID"
|
||||
#define JREFRESHMIN "RefreshMin"
|
||||
#define JREGISTRATIONDESCRIPTION "RegistrationDescription"
|
||||
#define JREGISTRATIONID "RegistrationID"
|
||||
#define JREGISTRATIONNAME "RegistrationName"
|
||||
#define JSPONSOR "Sponsor"
|
||||
#define JSUFFIX "Suffix"
|
||||
#define JSYSOPEMAIL "SysopEmail"
|
||||
#define JTRANSCODED "Transcoded"
|
||||
#define JTRANSCODER "Transcoder"
|
||||
#define JURF "URF"
|
||||
#define JUSRP "USRP"
|
||||
#define JWHITELISTPATH "WhitelistPath"
|
||||
#define JYSF "YSF"
|
||||
#define JYSFTXRXDB "YSF TX/RX DB"
|
||||
|
||||
static inline void split(const std::string &s, char delim, std::vector<std::string> &v)
|
||||
{
|
||||
std::istringstream iss(s);
|
||||
std::string item;
|
||||
while (std::getline(iss, item, delim))
|
||||
v.push_back(item);
|
||||
}
|
||||
|
||||
// trim from start (in place)
|
||||
static inline void ltrim(std::string &s) {
|
||||
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
|
||||
return !std::isspace(ch);
|
||||
}));
|
||||
}
|
||||
|
||||
// trim from end (in place)
|
||||
static inline void rtrim(std::string &s) {
|
||||
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
|
||||
return !std::isspace(ch);
|
||||
}).base(), s.end());
|
||||
}
|
||||
|
||||
// trim from both ends (in place)
|
||||
static inline void trim(std::string &s) {
|
||||
ltrim(s);
|
||||
rtrim(s);
|
||||
}
|
||||
|
||||
// callback function writes data to a std::ostream
|
||||
static size_t data_write(void* buf, size_t size, size_t nmemb, void* userp)
|
||||
{
|
||||
if(userp)
|
||||
{
|
||||
std::ostream& os = *static_cast<std::ostream*>(userp);
|
||||
std::streamsize len = size * nmemb;
|
||||
if(os.write(static_cast<char*>(buf), len))
|
||||
return len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static CURLcode curl_read(const std::string& url, std::ostream& os, long timeout = 30)
|
||||
{
|
||||
CURLcode code(CURLE_FAILED_INIT);
|
||||
CURL* curl = curl_easy_init();
|
||||
|
||||
if(curl)
|
||||
{
|
||||
if(CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, &data_write))
|
||||
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_NOPROGRESS, 1L))
|
||||
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L))
|
||||
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_FILE, &os))
|
||||
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout))
|
||||
&& CURLE_OK == (code = curl_easy_setopt(curl, CURLOPT_URL, url.c_str())))
|
||||
{
|
||||
code = curl_easy_perform(curl);
|
||||
}
|
||||
curl_easy_cleanup(curl);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
void CConfigure::CurlAddresses(std::string &ipv4, std::string &ipv6) const
|
||||
{
|
||||
std::ostringstream oss;
|
||||
|
||||
curl_global_init(CURL_GLOBAL_ALL);
|
||||
|
||||
if(CURLE_OK == curl_read("https://ipv4.icanhazip.com", oss))
|
||||
{
|
||||
// Web page successfully written to string
|
||||
ipv4.assign(oss.str());
|
||||
trim(ipv4);
|
||||
oss.str(std::string());
|
||||
}
|
||||
|
||||
if(CURLE_OK == curl_read("https://ipv6.icanhazip.com", oss))
|
||||
{
|
||||
ipv6.assign(oss.str());
|
||||
trim(ipv6);
|
||||
}
|
||||
|
||||
curl_global_cleanup();
|
||||
|
||||
// std::cout << "IPv4=" << ipv4 << " IPv6=" << ipv6 << std::endl;
|
||||
}
|
||||
|
||||
CConfigure::CConfigure()
|
||||
{
|
||||
IPv4RegEx = std::regex("^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3,3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9]){1,1}$", std::regex::extended);
|
||||
IPv6RegEx = std::regex("^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}(:[0-9a-fA-F]{1,4}){1,1}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|([0-9a-fA-F]{1,4}:){1,1}(:[0-9a-fA-F]{1,4}){1,6}|:((:[0-9a-fA-F]{1,4}){1,7}|:))$", std::regex::extended);
|
||||
}
|
||||
|
||||
bool CConfigure::ReadData(const std::string &path)
|
||||
// returns true on failure
|
||||
{
|
||||
bool rval = false;
|
||||
ESection section = ESection::none;
|
||||
counter = 0;
|
||||
REFLECTOR::DB *pdb;
|
||||
|
||||
//data.ysfalmodule = 0;
|
||||
//data.DPlusPort = data.DCSPort = data.DExtraPort = data.BMPort = data.DMRPlusPort = 0;
|
||||
std::ifstream cfgfile(path.c_str(), std::ifstream::in);
|
||||
if (! cfgfile.is_open()) {
|
||||
std::cerr << "ERROR: '" << path << "' was not found!" << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string ipv4, ipv6;
|
||||
CurlAddresses(ipv4, ipv6);
|
||||
|
||||
std::string line;
|
||||
while (std::getline(cfgfile, line))
|
||||
{
|
||||
counter++;
|
||||
trim(line);
|
||||
if (3 > line.size())
|
||||
continue; // can't be anything
|
||||
if ('#' == line.at(0))
|
||||
continue; // skip comments
|
||||
|
||||
// check for next section
|
||||
if ('[' == line.at(0))
|
||||
{
|
||||
std::string hname(line.substr(1));
|
||||
auto pos = hname.find(']');
|
||||
if (std::string::npos != pos)
|
||||
hname.resize(pos);
|
||||
section = ESection::none;
|
||||
if (0 == hname.compare(JNAMES))
|
||||
section = ESection::names;
|
||||
else if (0 == hname.compare(JIPADDRESSES))
|
||||
section = ESection::ip;
|
||||
else if (0 == hname.compare(JMODULES))
|
||||
section = ESection::modules;
|
||||
else if (0 == hname.compare(JDPLUS))
|
||||
section = ESection::dplus;
|
||||
else if (0 == hname.compare(JDEXTRA))
|
||||
section = ESection::dextra;
|
||||
else if (0 == hname.compare(JDMRPLUS))
|
||||
section = ESection::dmrplus;
|
||||
else if (0 == hname.compare(JMMDVM))
|
||||
section = ESection::mmdvm;
|
||||
else if (0 == hname.compare(JNXDN))
|
||||
section = ESection::nxdn;
|
||||
else if (0 == hname.compare(JBRANDMEISTER))
|
||||
section = ESection::bm;
|
||||
else if (0 == hname.compare(JYSF))
|
||||
section = ESection::ysf;
|
||||
else if (0 == hname.compare(JDCS))
|
||||
section = ESection::dcs;
|
||||
else if (0 == hname.compare(JP25))
|
||||
section = ESection::p25;
|
||||
else if (0 == hname.compare(JM17))
|
||||
section = ESection::m17;
|
||||
else if (0 == hname.compare(JUSRP))
|
||||
section = ESection::usrp;
|
||||
else if (0 == hname.compare(JURF))
|
||||
section = ESection::urf;
|
||||
else if (0 == hname.compare(JDMRIDDB))
|
||||
section = ESection::dmrid;
|
||||
else if (0 == hname.compare(JNXDNIDDB))
|
||||
section = ESection::nxdnid;
|
||||
else if (0 == hname.compare(JYSFTXRXDB))
|
||||
section = ESection::ysffreq;
|
||||
else if (0 == hname.compare(JFILES))
|
||||
section = ESection::files;
|
||||
else
|
||||
{
|
||||
std::cerr << "WARNING: unknown ini file section: " << line << std::endl;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
std::vector<std::string> tokens;
|
||||
split(line, '=', tokens);
|
||||
// check value for end-of-line comment
|
||||
if (2 > tokens.size())
|
||||
{
|
||||
std::cout << "WARNING: line #" << counter << ": '" << line << "' does not contain an equal sign, skipping" << std::endl;
|
||||
continue;
|
||||
}
|
||||
auto pos = tokens[1].find('#');
|
||||
if (std::string::npos != pos)
|
||||
{
|
||||
tokens[1].assign(tokens[1].substr(0, pos));
|
||||
rtrim(tokens[1]); // whitespace between the value and the end-of-line comment
|
||||
}
|
||||
// trim whitespace from around the '='
|
||||
rtrim(tokens[0]);
|
||||
ltrim(tokens[1]);
|
||||
const std::string key(tokens[0]);
|
||||
const std::string value(tokens[1]);
|
||||
if (key.empty() || value.empty())
|
||||
{
|
||||
std::cout << "WARNING: line #" << counter << ": missing key or value: '" << line << "'" << std::endl;
|
||||
continue;
|
||||
}
|
||||
switch (section)
|
||||
{
|
||||
case ESection::names:
|
||||
if (0 == key.compare(JCALLSIGN))
|
||||
data[j.names.cs] = value;
|
||||
else if (0 == key.compare(JSYSOPEMAIL))
|
||||
data[j.names.email] = value;
|
||||
else if (0 == key.compare(JCOUNTRY))
|
||||
data[j.names.country] = value.substr(0,2);
|
||||
else if (0 == key.compare(JSPONSOR))
|
||||
data[j.names.sponsor] = value;
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::ip:
|
||||
if (0 == key.compare(JIPV4BINDING))
|
||||
{
|
||||
data[j.ip.ipv4bind] = value;
|
||||
}
|
||||
else if (0 == key.compare(JIPV6BINDING))
|
||||
{
|
||||
data[j.ip.ipv6bind] = value;
|
||||
}
|
||||
else if (0 == key.compare(JIPV4EXTERNAL))
|
||||
{
|
||||
data[j.ip.ipv4address] = value;
|
||||
}
|
||||
else if (0 == key.compare(JIPV6EXTERNAL))
|
||||
{
|
||||
data[j.ip.ipv6address] = value;
|
||||
}
|
||||
else if (0 == key.compare(JTRANSCODER))
|
||||
{
|
||||
if (value.compare("local"))
|
||||
{
|
||||
std::cout << "WARNING: Line #" << counter << ": malformed transcoder address, '" << value << "', resetting..." << std::endl;
|
||||
}
|
||||
data[j.ip.transcoder] = "local";
|
||||
}
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::modules:
|
||||
if (0 == key.compare(JMODULES))
|
||||
{
|
||||
std::string m(value);
|
||||
if (checkModules(m))
|
||||
{
|
||||
std::cerr << "ERROR: line #" << counter << ": no letters found in Modules: '" << m << "'" << std::endl;
|
||||
rval = true;
|
||||
} else
|
||||
data[j.modules.modules] = m;
|
||||
}
|
||||
else if (0 == key.compare(JTRANSCODED))
|
||||
{
|
||||
std::string m(value);
|
||||
if (checkModules(m))
|
||||
{
|
||||
std::cerr << "ERROR: line #" << counter << ": no letters found in Transcoded: '" << m << "'" << std::endl;
|
||||
rval = true;
|
||||
} else
|
||||
data[j.modules.tcmodules] = m;
|
||||
}
|
||||
else if (0 == key.compare(0, 11, "Description"))
|
||||
{
|
||||
if (12 == key.size() && isupper(key[11]))
|
||||
data[key] = value;
|
||||
else
|
||||
badParam(key);
|
||||
}
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::bm:
|
||||
if (0 == key.compare(JPORT))
|
||||
data[j.bm.port] = getUnsigned(value, "Brandmeister Port", 1024, 65535, 10002);
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::dcs:
|
||||
if (0 == key.compare(JPORT))
|
||||
data[j.dcs.port] = getUnsigned(value, "DCS Port", 1024, 65535, 30051);
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::dextra:
|
||||
if (0 == key.compare(JPORT))
|
||||
data[j.dextra.port] = getUnsigned(value, "DExtra Port", 1024, 65535, 30001);
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::dmrplus:
|
||||
if (0 == key.compare(JPORT))
|
||||
data[j.dmrplus.port] = getUnsigned(value, "DMRPlus Port", 1024, 65535, 8880);
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::dplus:
|
||||
if (0 == key.compare(JPORT))
|
||||
data[j.dplus.port] = getUnsigned(value, "DPlus Port", 1024, 65535, 20001);
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::m17:
|
||||
if (0 == key.compare(JPORT))
|
||||
data[j.m17.port] = getUnsigned(value, "M17 Port", 1024, 65535, 17000);
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::mmdvm:
|
||||
if (0 == key.compare(JPORT))
|
||||
data[j.mmdvm.port] = getUnsigned(value, "MMDVM Port", 1024, 65535, 62030);
|
||||
else if (0 == key.compare(JDEFAULTID))
|
||||
data[j.mmdvm.defaultid] = getUnsigned(value, "MMDVM DefaultID", 0, 9999999, 0);
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::nxdn:
|
||||
if (0 == key.compare(JPORT))
|
||||
data[j.nxdn.port] = getUnsigned(value, "NDXN Port", 1024, 65535, 41400);
|
||||
else if (0 == key.compare(JAUTOLINKMODULE))
|
||||
setAutolink(JNXDN, j.nxdn.autolinkmod, value);
|
||||
else if (0 == key.compare(JREFLECTORID))
|
||||
data[j.nxdn.reflectorid] = getUnsigned(value, "NXDN ReflectorID", 0, 65535, 0);
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::p25:
|
||||
if (0 == key.compare(JPORT))
|
||||
data[j.p25.port] = getUnsigned(value, "P25 Port", 1024, 65535, 41000);
|
||||
else if (0 == key.compare(JAUTOLINKMODULE))
|
||||
setAutolink(JP25, j.p25.autolinkmod, value);
|
||||
else if (0 == key.compare(JREFLECTORID))
|
||||
data[j.p25.reflectorid] = getUnsigned(value, "P25 ReflectorID", 0, 16777215, 0);
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::urf:
|
||||
if (0 == key.compare(JPORT))
|
||||
data[j.urf.port] = getUnsigned(value, "URF Port", 1024, 65535, 10017);
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::usrp:
|
||||
if (0 == key.compare(JPORT))
|
||||
data[j.usrp.port] = getUnsigned(value, "USRP Port", 1024, 65535, 34001);
|
||||
else if (0 == key.compare(JAUTOLINKMODULE))
|
||||
setAutolink(JUSRP, j.usrp.autolinkmod, value);
|
||||
else if (0 == key.compare(JDEFAULTCALLSIGN))
|
||||
{
|
||||
std::string cs;
|
||||
for (auto &c : value)
|
||||
if (isalnum(c))
|
||||
cs.append(1, toupper(c));
|
||||
if (cs.size() > 7) cs.resize(7);
|
||||
data[j.usrp.defaultcallsign] = cs;
|
||||
}
|
||||
else if (0 == key.compare(JCLIENTSPATH))
|
||||
data[j.usrp.clientfilepath] == value;
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::ysf:
|
||||
if (0 == key.compare(JPORT))
|
||||
data[j.ysf.port] = getUnsigned(value, "YSF Port", 1024, 65535, 42000);
|
||||
else if (0 == key.compare(JAUTOLINKMODULE))
|
||||
setAutolink(JYSF, j.ysf.autolinkmod, value);
|
||||
else if (0 == key.compare(JDEFAULTTXFREQ))
|
||||
data[j.ysf.defaulttxfreq] = getUnsigned(value, "YSF DefaultTxFreq", 40000000, 2600000000, 439000000);
|
||||
else if (0 == key.compare(JDEFAULTRXFREQ))
|
||||
data[j.ysf.defaultrxfreq] = getUnsigned(value, "YSF DefaultRxFreq", 40000000, 2600000000, 439000000);
|
||||
else if (0 == key.compare(JREGISTRATIONID))
|
||||
data[j.ysf.ysfreflectordb.id] = getUnsigned(value, "YSF RegistrationID", 0, 9999999, 0);
|
||||
else if (0 == key.compare(JREGISTRATIONNAME))
|
||||
{
|
||||
std::string name(value);
|
||||
if (name.size() > 13) name.resize(13);
|
||||
data[j.ysf.ysfreflectordb.name] = name;
|
||||
}
|
||||
else if (0 == key.compare(JREGISTRATIONDESCRIPTION))
|
||||
{
|
||||
std::string desc(value);
|
||||
if (desc.size() > 16) desc.resize(16);
|
||||
data[j.ysf.ysfreflectordb.description] = desc;
|
||||
}
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::dmrid:
|
||||
case ESection::nxdnid:
|
||||
case ESection::ysffreq:
|
||||
switch (section)
|
||||
{
|
||||
case ESection::dmrid: pdb = &j.dmriddb; break;
|
||||
case ESection::nxdnid: pdb = &j.nxdniddb; break;
|
||||
case ESection::ysffreq: pdb = &j.ysftxrxdb; break;
|
||||
}
|
||||
if (0 == key.compare(JHOSTNAME))
|
||||
data[pdb->hostname] = value;
|
||||
else if (0 == key.compare(JSUFFIX))
|
||||
data[pdb->suffix] = value;
|
||||
else if (0 == key.compare(JMODE))
|
||||
{
|
||||
if ((0==value.compare("file")) || (0==value.compare("http")) || (0==value.compare("both")))
|
||||
data[pdb->mode] = value;
|
||||
else
|
||||
{
|
||||
std::cout << "WARNING: line #" << counter << ": Mode, '" << value << "' not recognized. Setting to 'http'" << std::endl;
|
||||
data[pdb->mode] = "http";
|
||||
}
|
||||
}
|
||||
else if (0 == key.compare(JREFRESHMIN))
|
||||
data[pdb->refreshmin] = getUnsigned(value, JREFRESHMIN, 15, 14400, 180);
|
||||
else if (0 == key.compare(JFILEPATH))
|
||||
data[pdb->filepath] = value;
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
case ESection::files:
|
||||
if (0 == key.compare(JPIDPATH))
|
||||
data[j.files.pid] = value;
|
||||
else if (0 == key.compare(JJSONPATH))
|
||||
data[j.files.json] = value;
|
||||
else if (0 == key.compare(JWHITELISTPATH))
|
||||
data[j.files.white] = value;
|
||||
else if (0 == key.compare(JBLACKLISTPATH))
|
||||
data[j.files.black] = value;
|
||||
else if (0 == key.compare(JINTERLINKPATH))
|
||||
data[j.files.interlink] = value;
|
||||
else if (0 == key.compare(JG3TERMINALPATH))
|
||||
data[j.files.terminal] = value;
|
||||
else
|
||||
badParam(key);
|
||||
break;
|
||||
default:
|
||||
std::cout << "WARNING: parameter '" << line << "' defined befor any [section]" << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
cfgfile.close();
|
||||
|
||||
////////////////////////////// check the input
|
||||
// Names section
|
||||
if (isDefined(ErrorLevel::fatal, JNAMES, JCALLSIGN, j.names.cs, rval))
|
||||
{
|
||||
const auto cs = data[j.names.cs].get<std::string>();
|
||||
auto RefRegEx = std::regex("^URF([A-Z0-9]){3,3}$", std::regex::extended);
|
||||
if (! std::regex_match(cs, RefRegEx))
|
||||
{
|
||||
std::cerr << "ERROR: [" << JNAMES << "] " << JCALLSIGN << " '" << cs << "' is malformed" << std::endl;
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
|
||||
isDefined(ErrorLevel::mild, JNAMES, JSYSOPEMAIL, j.names.email, rval);
|
||||
isDefined(ErrorLevel::mild, JNAMES, JCOUNTRY, j.names.country, rval);
|
||||
isDefined(ErrorLevel::mild, JNAMES, JSPONSOR, j.names.sponsor, rval);
|
||||
|
||||
// IP Address section
|
||||
// ipv4 bind and external
|
||||
if (isDefined(ErrorLevel::fatal, JIPADDRESSES, JIPV4BINDING, j.ip.ipv4bind, rval))
|
||||
{
|
||||
if (std::regex_match(data[j.ip.ipv4bind].get<std::string>(), IPv4RegEx))
|
||||
{
|
||||
if (data.contains(j.ip.ipv4address))
|
||||
{
|
||||
auto v4 = data[j.ip.ipv4address].get<std::string>();
|
||||
if (std::regex_match(v4, IPv4RegEx))
|
||||
{
|
||||
if (ipv4.compare(v4))
|
||||
std::cout << "WARNING: specified IPv4 external address, " << v4 << ", is different than detected address, " << ipv4 << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "ERROR: specifed IPv4 external address, " << v4 << ", is malformed" << std::endl;
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// make sure curl worked!
|
||||
if (std::regex_match(ipv4, IPv4RegEx))
|
||||
data[j.ip.ipv4address] = ipv4;
|
||||
else
|
||||
{
|
||||
std::cerr << "ERROR: could not detect IPv4 address at this time" << std::endl;
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "ERROR: IPv4 binding address, " << data[j.ip.ipv4address].get<std::string>() << ", is malformed" << std::endl;
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
|
||||
// ipv6 bind and external
|
||||
if (data.contains(j.ip.ipv6bind))
|
||||
{
|
||||
if (std::regex_match(data[j.ip.ipv6bind].get<std::string>(), IPv6RegEx))
|
||||
{
|
||||
if (data.contains(j.ip.ipv6address))
|
||||
{
|
||||
auto v6 = data[j.ip.ipv6address].get<std::string>();
|
||||
if (std::regex_match(v6, IPv6RegEx))
|
||||
{
|
||||
if (ipv6.compare(v6))
|
||||
std::cout << "WARNING: specified IPv6 external address [" << v6 << "], is different than detected address [" << ipv6 << ']' << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "ERROR: the specifed IPv6 address [" << v6 << "] is malformed" << std::endl;
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// make sure curl worked!
|
||||
if (std::regex_match(ipv6, IPv6RegEx))
|
||||
data[j.ip.ipv6address] = ipv6;
|
||||
else
|
||||
{
|
||||
std::cerr << "ERROR: could not detect IPv6 address at this time" << std::endl;
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "ERROR: IPv6 binding address, " << data[j.ip.ipv6address].get<std::string>() << ", is malformed" << std::endl;
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
data[j.ip.ipv4bind] = nullptr;
|
||||
data[j.ip.ipv6address] = nullptr;
|
||||
}
|
||||
|
||||
// Modules section
|
||||
if (isDefined(ErrorLevel::fatal, JMODULES, JMODULES, j.modules.modules, rval))
|
||||
{
|
||||
const auto mods(data[j.modules.modules].get<std::string>());
|
||||
if (data.contains(j.modules.tcmodules))
|
||||
{
|
||||
const auto tcmods(data[j.modules.tcmodules].get<std::string>());
|
||||
|
||||
// how many transcoded modules
|
||||
auto size = tcmods.size();
|
||||
if (3 != size && 1 != size)
|
||||
std::cout << "WARNING: [" << JMODULES << "] " << JTRANSCODED << " doesn't define three (or one) modules" << std::endl;
|
||||
|
||||
// make sure each transcoded module is configured
|
||||
for (auto c : data[j.modules.tcmodules])
|
||||
{
|
||||
if (std::string::npos == mods.find(c))
|
||||
{
|
||||
std::cerr << "ERROR: transcoded module '" << c << "' not found in defined modules" << std::endl;
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
data[j.modules.tcmodules] = nullptr;
|
||||
|
||||
// finally, check the module descriptions
|
||||
for (unsigned i=0; i<26; i++)
|
||||
{
|
||||
if (std::string::npos == mods.find('A'+i))
|
||||
{
|
||||
if (data.contains(j.modules.descriptor[i]))
|
||||
{
|
||||
std::cout << "WARNING: " << j.modules.descriptor[i] << " defined for an unconfigured module. Deleting..." << std::endl;
|
||||
data.erase(j.modules.descriptor[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (! data.contains(j.modules.descriptor[i]))
|
||||
{
|
||||
std::string value("Module ");
|
||||
value.append(1, 'A'+i);
|
||||
std::cout << "WARNING: " << j.modules.descriptor[i] << " not found. Setting to " << value << std::endl;
|
||||
data[j.modules.descriptor[i]] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// "simple" protocols with only a Port
|
||||
isDefined(ErrorLevel::fatal, JBRANDMEISTER, JPORT, j.bm.port, rval);
|
||||
isDefined(ErrorLevel::fatal, JDCS, JPORT, j.dcs.port, rval);
|
||||
isDefined(ErrorLevel::fatal, JDEXTRA, JPORT, j.dextra.port, rval);
|
||||
isDefined(ErrorLevel::fatal, JDMRPLUS, JPORT, j.dmrplus.port, rval);
|
||||
isDefined(ErrorLevel::fatal, JDPLUS, JPORT, j.dplus.port, rval);
|
||||
isDefined(ErrorLevel::fatal, JM17, JPORT, j.m17.port, rval);
|
||||
isDefined(ErrorLevel::fatal, JURF, JPORT, j.urf.port, rval);
|
||||
// MMDVM
|
||||
isDefined(ErrorLevel::fatal, JMMDVM, JPORT, j.mmdvm.port, rval);
|
||||
isDefined(ErrorLevel::fatal, JMMDVM, JDEFAULTID, j.mmdvm.defaultid, rval);
|
||||
// NXDN
|
||||
isDefined(ErrorLevel::fatal, JNXDN, JPORT, j.nxdn.port, rval);
|
||||
checkAutoLink(JNXDN, JAUTOLINKMODULE, j.nxdn.autolinkmod, rval);
|
||||
isDefined(ErrorLevel::fatal, JNXDN, JREFLECTORID, j.nxdn.reflectorid, rval);
|
||||
// P25
|
||||
isDefined(ErrorLevel::fatal, JP25, JPORT, j.p25.port, rval);
|
||||
checkAutoLink(JP25, JAUTOLINKMODULE, j.p25.autolinkmod, rval);
|
||||
isDefined(ErrorLevel::fatal, JP25, JREFLECTORID, j.p25.reflectorid, rval);
|
||||
// USRP
|
||||
isDefined(ErrorLevel::fatal, JUSRP, JPORT, j.usrp.port, rval);
|
||||
checkAutoLink(JUSRP, JAUTOLINKMODULE, j.usrp.autolinkmod, rval);
|
||||
isDefined(ErrorLevel::fatal, JUSRP, JDEFAULTCALLSIGN, j.usrp.defaultcallsign, rval);
|
||||
isDefined(ErrorLevel::fatal, JUSRP, JCLIENTSPATH, j.usrp.clientfilepath, rval);
|
||||
// YSF
|
||||
isDefined(ErrorLevel::fatal, JYSF, JPORT, j.ysf.port, rval);
|
||||
checkAutoLink(JYSF, JAUTOLINKMODULE, j.ysf.autolinkmod, rval);
|
||||
isDefined(ErrorLevel::fatal, JYSF, JDEFAULTRXFREQ, j.ysf.defaultrxfreq, rval);
|
||||
isDefined(ErrorLevel::fatal, JYSF, JDEFAULTTXFREQ, j.ysf.defaulttxfreq, rval);
|
||||
isDefined(ErrorLevel::mild, JYSF, JREGISTRATIONID, j.ysf.ysfreflectordb.id, rval);
|
||||
isDefined(ErrorLevel::mild, JYSF, JREGISTRATIONNAME, j.ysf.ysfreflectordb.name, rval);
|
||||
isDefined(ErrorLevel::mild, JYSF, JREGISTRATIONDESCRIPTION, j.ysf.ysfreflectordb.description, rval);
|
||||
// Databases
|
||||
std::list<std::pair<const std::string, const struct REFLECTOR::DB *>> dbs = {
|
||||
{ JDMRIDDB, &j.dmriddb },
|
||||
{ JNXDNIDDB, &j.nxdniddb },
|
||||
{ JYSFTXRXDB, &j.ysftxrxdb }
|
||||
};
|
||||
for ( auto &item : dbs )
|
||||
{
|
||||
isDefined(ErrorLevel::fatal, item.first, JHOSTNAME, item.second->hostname, rval);
|
||||
isDefined(ErrorLevel::fatal, item.first, JSUFFIX, item.second->suffix, rval);
|
||||
isDefined(ErrorLevel::fatal, item.first, JMODE, item.second->mode, rval);
|
||||
isDefined(ErrorLevel::fatal, item.first, JREFRESHMIN, item.second->refreshmin, rval);
|
||||
isDefined(ErrorLevel::fatal, item.first, JFILEPATH, item.second->filepath, rval);
|
||||
}
|
||||
// Other files
|
||||
isDefined(ErrorLevel::fatal, JFILES, JPIDPATH, j.files.pid, rval);
|
||||
isDefined(ErrorLevel::fatal, JFILES, JJSONPATH, j.files.json, rval);
|
||||
isDefined(ErrorLevel::fatal, JFILES, JWHITELISTPATH, j.files.white, rval);
|
||||
isDefined(ErrorLevel::fatal, JFILES, JBLACKLISTPATH, j.files.black, rval);
|
||||
isDefined(ErrorLevel::fatal, JFILES, JINTERLINKPATH, j.files.interlink, rval);
|
||||
isDefined(ErrorLevel::fatal, JFILES, JG3TERMINALPATH, j.files.terminal, rval);
|
||||
|
||||
return rval;
|
||||
}
|
||||
|
||||
bool CConfigure::isDefined(ErrorLevel level, const std::string §ion, const std::string &pname, const std::string &key, bool &rval)
|
||||
{
|
||||
if (data.contains(key))
|
||||
return true;
|
||||
|
||||
if (ErrorLevel::mild == level)
|
||||
{
|
||||
std::cout << "WARNING: [" << section << "] " << pname << " is not defined" << std::endl;
|
||||
data[key] = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "ERROR: [" << section << "] " << pname << " is not defined" << std::endl;
|
||||
rval = true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void CConfigure::checkAutoLink(const std::string §ion, const std::string &pname, const std::string &key, bool &rval)
|
||||
{
|
||||
if (data.contains(key))
|
||||
{
|
||||
auto ismods = data.contains(j.modules.modules);
|
||||
const auto mods(ismods ? data[j.modules.modules].get<std::string>() : "");
|
||||
const auto c = data[key].get<std::string>().at(0);
|
||||
if (std::string::npos == mods.find(c))
|
||||
{
|
||||
std::cerr << "ERROR: [" << section << "] " << pname << " module '" << c << "' not a configured module" << std::endl;
|
||||
rval = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
data[key] = nullptr;
|
||||
}
|
||||
|
||||
std::string CConfigure::getDataRefreshType(ERefreshType type) const
|
||||
{
|
||||
if (ERefreshType::both == type)
|
||||
return std::string("both");
|
||||
else if (ERefreshType::file == type)
|
||||
return std::string("file");
|
||||
else
|
||||
return std::string("http");
|
||||
}
|
||||
|
||||
unsigned CConfigure::getUnsigned(const std::string &valuestr, const std::string &label, unsigned min, unsigned max, unsigned def) const
|
||||
{
|
||||
auto i = unsigned(std::stoul(valuestr.c_str()));
|
||||
if ( i < min || i > max )
|
||||
{
|
||||
std::cout << "WARNING: line #" << counter << ": " << label << " is out of range. Reset to " << def << std::endl;
|
||||
i = def;
|
||||
}
|
||||
return (unsigned)i;
|
||||
}
|
||||
|
||||
void CConfigure::badParam(const std::string &key) const
|
||||
{
|
||||
std::cout << "WARNING: line #" << counter << ": Unexpected parameter: '" << key << "'" << std::endl;
|
||||
}
|
||||
|
||||
bool CConfigure::checkModules(std::string &m) const
|
||||
{
|
||||
bool rval = false; // return true on error
|
||||
for(unsigned i=0; i<m.size(); i++)
|
||||
if (islower(m[i]))
|
||||
m[i] = toupper(m[i]);
|
||||
|
||||
const std::string all("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
|
||||
std::string found;
|
||||
for (auto c : all)
|
||||
if (std::string::npos != m.find(c) && std::string::npos == found.find(c))
|
||||
found.append(1, c);
|
||||
m.assign(found);
|
||||
return m.empty();
|
||||
}
|
||||
|
||||
void CConfigure::setAutolink(const std::string §ion, const std::string &key, const std::string &value)
|
||||
{
|
||||
auto c = toupper(value.at(0));
|
||||
if (isupper(c))
|
||||
data[key] = std::string(1, c);
|
||||
else
|
||||
std::cout << "WARNING: line #" << counter << ": " << section << " AutoLinkModule is invalid: '" << value.substr(0, 1) << "'" << std::endl;
|
||||
}
|
||||
|
||||
void CConfigure::Dump(bool justpublic) const
|
||||
{
|
||||
nlohmann::json tmpjson = data;
|
||||
if (justpublic)
|
||||
{
|
||||
for (auto &it : data.items())
|
||||
{
|
||||
if (islower(it.key().at(0)))
|
||||
tmpjson.erase(it.key());
|
||||
}
|
||||
}
|
||||
std::cout << tmpjson.dump(4) << std::endl;
|
||||
}
|
||||
|
||||
ERefreshType CConfigure::GetRefreshType(const std::string &key) const
|
||||
{
|
||||
ERefreshType type = ERefreshType::http;
|
||||
if (data.contains(key))
|
||||
{
|
||||
if (data[key].is_string())
|
||||
{
|
||||
auto s = data[key].get<std::string>();
|
||||
if (0 == s.compare("both"))
|
||||
type = ERefreshType::both;
|
||||
else if (0 == s.compare("file"))
|
||||
type = ERefreshType::file;
|
||||
else
|
||||
type = ERefreshType::http;
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
std::string CConfigure::GetString(const std::string &key) const
|
||||
{
|
||||
std::string str;
|
||||
if (data.contains(key))
|
||||
{
|
||||
if (data[key].is_null())
|
||||
{
|
||||
// null is the same thing as an empty string
|
||||
return str;
|
||||
}
|
||||
else if (data[key].is_string())
|
||||
{
|
||||
str.assign(data[key].get<std::string>());
|
||||
}
|
||||
else
|
||||
std::cerr << "ERROR: GetString(): '" << key << "' is not a string" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "ERROR: GetString(): item at '" << key << "' is not defined" << std::endl;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
unsigned CConfigure::GetUnsigned(const std::string &key) const
|
||||
{
|
||||
unsigned u = 0;
|
||||
if (data.contains(key))
|
||||
{
|
||||
if (data[key].is_number_unsigned())
|
||||
{
|
||||
u = data[key].get<unsigned>();
|
||||
}
|
||||
else
|
||||
std::cerr << "ERROR: GetString(): '" << key << "' is not an unsigned value" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "ERROR: GetString(): item at '" << key << "' is not defined" << std::endl;
|
||||
}
|
||||
return u;
|
||||
}
|
||||
|
||||
char CConfigure::GetAutolinkModule(const std::string &key) const
|
||||
{
|
||||
char c = 0;
|
||||
if (data.contains(key))
|
||||
{
|
||||
if (data[key].is_string())
|
||||
{
|
||||
c = data[key].get<std::string>().at(0);
|
||||
}
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
bool CConfigure::IsString(const std::string &key) const
|
||||
{
|
||||
if (data.contains(key))
|
||||
return data[key].is_string();
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef INICHECK
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
if (argc == 2)
|
||||
{
|
||||
CConfigure d;
|
||||
auto rval = d.ReadData(argv[1]);
|
||||
d.Dump(true);
|
||||
return rval ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
std::cerr << "Usage: " << argv[0] << " FILENAME" << std::endl;
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2023 by Thomas A. Early N7TAE
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
enum class ErrorLevel { fatal, mild };
|
||||
enum class ERefreshType { file, http, both };
|
||||
enum class ESection { none, names, ip, modules, urf, dplus, dextra, dcs, dmrplus, mmdvm, nxdn, bm, ysf, p25, m17, usrp, dmrid, nxdnid, ysffreq, files };
|
||||
|
||||
#define IS_TRUE(a) ((a)=='t' || (a)=='T' || (a)=='1')
|
||||
|
||||
class CConfigure
|
||||
{
|
||||
public:
|
||||
// json names
|
||||
struct REFLECTOR {
|
||||
struct BM { const std::string port; } bm;
|
||||
struct DCS { const std::string port; } dcs;
|
||||
struct DEXTRA { const std::string port; } dextra;
|
||||
struct DMRPLUS { const std::string port; } dmrplus;
|
||||
struct DPLUS { const std::string port; } dplus;
|
||||
struct M17 { const std::string port; } m17;
|
||||
struct URF { const std::string port; } urf;
|
||||
struct NAMES { const std::string cs, email, country, sponsor; } names;
|
||||
struct IP { const std::string ipv4bind, ipv4address, ipv6bind, ipv6address, transcoder; } ip;
|
||||
struct MODULES { const std::string modules, tcmodules, descriptor[26]; } modules;
|
||||
struct USRP { const std::string port, autolinkmod, defaultcallsign, clientfilepath; } usrp;
|
||||
struct P25NXDN { const std::string port, autolinkmod, reflectorid; } p25, nxdn;
|
||||
struct YSF {
|
||||
const std::string port, autolinkmod, defaulttxfreq, defaultrxfreq;
|
||||
struct YSLREG { const std::string id, name, description; } ysfreflectordb;
|
||||
} ysf;
|
||||
struct MMDVM { const std::string port, defaultid; } mmdvm;
|
||||
struct DB { const std::string hostname, suffix, mode, refreshmin, filepath; } dmriddb, nxdniddb, ysftxrxdb;
|
||||
struct FILES { const std::string pid, json, white, black, interlink, terminal; } files;
|
||||
} j = {
|
||||
{ "BrandMeisterPort" },
|
||||
{ "DCSPort" },
|
||||
{ "DExtraPort" },
|
||||
{ "DMRPlusPort" },
|
||||
{ "DPlusPort" },
|
||||
{ "M17Port" },
|
||||
{ "URFPort" },
|
||||
{ "Callsign", "SysopEmail", "Country", "Sponsor" },
|
||||
{ "ipv4bind", "IPv4Address", "ipv6bind", "IPv6Address", "tcaddress" },
|
||||
{ "Modules", "TranscodedModules", "DescriptionA", "DescriptionB", "DescriptionC",
|
||||
"DescriptionD", "DescriptionE", "DescriptionF", "DescriptionG", "DescriptionH",
|
||||
"DescriptionI", "DescriptionJ", "DescriptionK", "DescriptionL", "DescriptionM",
|
||||
"DescriptionN", "DescriptionO", "DescriptionP", "DescriptionQ", "DescriptionR",
|
||||
"DescriptionS", "DescriptionT", "DescriptionU", "DescriptionV", "DescriptionW",
|
||||
"DescriptionX", "DescriptionY", "DescriptionZ" },
|
||||
{ "USRPPort", "USRPAutolinkMod", "usrpDefaultCallsign", "usrpClientfilePath" },
|
||||
{ "P25Port", "P25AutolinkMod", "P25ReflectorID" },
|
||||
{ "NXDNPort", "NXDNAutolinkMod", "NXDNReflectorID" },
|
||||
{ "YSFPort", "YSFAutoLinkMod", "YSFDefaultTxFreq", "YSFDefaultRxFreq", { "ysfrefdbid", "ysfrefdbname", "ysfrefdbdesc" } },
|
||||
{ "MMDVMPort", "mmdvmdefaultid" },
|
||||
{ "dmrIdDbHost", "dmrIdDbSuffix", "dmrIdDbMode", "dmrIdDbRefresh", "dmrIdDbFilePath" },
|
||||
{ "nxdnIdDbHost", "nxdnIdDbSuffix", "nxdnIdDbMode", "nxdnIdDbRefresh", "nxdnIdDbFilePath" },
|
||||
{ "ysfIdDbHost", "ysfIdDbSuffix", "ysfIdDbMode", "ysfIdDbRefresh", "ysfIdDbFilePath" },
|
||||
{ "pidFilePath", "jsonFilePath", "whitelistFilePath", "blacklistFilePath", "interlinkFilePath", "g3TerminalFilePath" }
|
||||
};
|
||||
|
||||
CConfigure();
|
||||
bool ReadData(const std::string &path);
|
||||
void Dump(bool justpublic) const;
|
||||
std::string GetString(const std::string &key) const;
|
||||
unsigned GetUnsigned(const std::string &key) const;
|
||||
ERefreshType GetRefreshType(const std::string &key) const;
|
||||
bool IsString(const std::string &key) const;
|
||||
char GetAutolinkModule(const std::string &key) const;
|
||||
|
||||
private:
|
||||
// CFGDATA data;
|
||||
unsigned counter;
|
||||
nlohmann::json data;
|
||||
std::regex IPv4RegEx, IPv6RegEx;
|
||||
|
||||
void CurlAddresses(std::string &v4, std::string &v6) const;
|
||||
std::string getDataRefreshType(ERefreshType t) const;
|
||||
unsigned getUnsigned(const std::string &value, const std::string &label, unsigned min, unsigned max, unsigned defaultvalue) const;
|
||||
void badParam(const std::string ¶m) const;
|
||||
bool checkModules(std::string &m) const;
|
||||
void setAutolink(const std::string §ion, const std::string &key, const std::string &value);
|
||||
bool isDefined(ErrorLevel level, const std::string §ion, const std::string &pname, const std::string &key, bool &rval);
|
||||
void checkAutoLink(const std::string §ion, const std::string &pname, const std::string &key, bool &rval);
|
||||
};
|
||||
@ -1,145 +0,0 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string.h>
|
||||
#include "Main.h"
|
||||
#include "Reflector.h"
|
||||
#include "DMRIdDir.h"
|
||||
#include "DMRIdDirFile.h"
|
||||
#include "DMRIdDirHttp.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor & destructor
|
||||
|
||||
CDmridDir::CDmridDir()
|
||||
{
|
||||
keep_running = true;
|
||||
}
|
||||
|
||||
CDmridDir::~CDmridDir()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// init & close
|
||||
|
||||
bool CDmridDir::Init(void)
|
||||
{
|
||||
// load content
|
||||
Reload();
|
||||
|
||||
// reset run flag
|
||||
keep_running = true;
|
||||
|
||||
// start thread;
|
||||
m_Future = std::async(std::launch::async, &CDmridDir::Thread, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDmridDir::Close(void)
|
||||
{
|
||||
keep_running = false;
|
||||
if ( m_Future.valid() )
|
||||
{
|
||||
m_Future.get();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// thread
|
||||
|
||||
void CDmridDir::Thread()
|
||||
{
|
||||
while (keep_running)
|
||||
{
|
||||
// Wait DMRIDDB_REFRESH_RATE minutes
|
||||
for (int i=0; i<30*DMRIDDB_REFRESH_RATE && keep_running; i++)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
|
||||
// have lists files changed ?
|
||||
if ( NeedReload() )
|
||||
{
|
||||
Reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Reload
|
||||
|
||||
bool CDmridDir::Reload(void)
|
||||
{
|
||||
CBuffer buffer;
|
||||
bool ok = false;
|
||||
|
||||
if ( LoadContent(&buffer) )
|
||||
{
|
||||
Lock();
|
||||
{
|
||||
ok = RefreshContent(buffer);
|
||||
}
|
||||
Unlock();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// find
|
||||
|
||||
const CCallsign *CDmridDir::FindCallsign(uint32_t dmrid)
|
||||
{
|
||||
auto found = m_CallsignMap.find(dmrid);
|
||||
if ( found != m_CallsignMap.end() )
|
||||
{
|
||||
return &(found->second);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t CDmridDir::FindDmrid(const CCallsign &callsign)
|
||||
{
|
||||
auto found = m_DmridMap.find(callsign);
|
||||
if ( found != m_DmridMap.end() )
|
||||
{
|
||||
return (found->second);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// syntax helpers
|
||||
|
||||
bool CDmridDir::IsValidDmrid(const char *sz)
|
||||
{
|
||||
bool ok = false;
|
||||
size_t n = ::strlen(sz);
|
||||
if ( (n > 0) && (n <= 8) )
|
||||
{
|
||||
ok = true;
|
||||
for ( size_t i = 0; (i < n) && ok; i++ )
|
||||
{
|
||||
ok &= ::isdigit(sz[i]);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
@ -1,159 +0,0 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include "Main.h"
|
||||
#include "DMRIdDirFile.h"
|
||||
|
||||
|
||||
#if (DMRIDDB_USE_RLX_SERVER == 0)
|
||||
CDmridDirFile g_DmridDir;
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor & destructor
|
||||
|
||||
CDmridDirFile::CDmridDirFile()
|
||||
{
|
||||
memset(&m_LastModTime, 0, sizeof(time_t));
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// init & close
|
||||
|
||||
bool CDmridDirFile::Init(void)
|
||||
{
|
||||
return CDmridDir::Init();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// refresh
|
||||
|
||||
bool CDmridDirFile::NeedReload(void)
|
||||
{
|
||||
bool needReload = false;
|
||||
|
||||
time_t time;
|
||||
if ( GetLastModTime(&time) )
|
||||
{
|
||||
needReload = time != m_LastModTime;
|
||||
}
|
||||
return needReload;
|
||||
}
|
||||
|
||||
bool CDmridDirFile::LoadContent(CBuffer *buffer)
|
||||
{
|
||||
bool ok = false;
|
||||
std::ifstream file;
|
||||
std::streampos size;
|
||||
|
||||
// open file
|
||||
file.open(DMRIDDB_PATH, std::ios::in | std::ios::binary | std::ios::ate);
|
||||
if ( file.is_open() )
|
||||
{
|
||||
// read file
|
||||
size = file.tellg();
|
||||
if ( size > 0 )
|
||||
{
|
||||
// read file into buffer
|
||||
buffer->resize((int)size+1);
|
||||
file.seekg (0, std::ios::beg);
|
||||
file.read((char *)buffer->data(), (int)size);
|
||||
|
||||
// close file
|
||||
file.close();
|
||||
|
||||
// update time
|
||||
GetLastModTime(&m_LastModTime);
|
||||
|
||||
// done
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool CDmridDirFile::RefreshContent(const CBuffer &buffer)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
// clear directory
|
||||
m_CallsignMap.clear();
|
||||
m_DmridMap.clear();
|
||||
|
||||
// scan buffer
|
||||
if ( buffer.size() > 0 )
|
||||
{
|
||||
// crack it
|
||||
char *ptr1 = (char *)buffer.data();
|
||||
char *ptr2;
|
||||
|
||||
// get next line
|
||||
while ( (ptr2 = ::strchr(ptr1, '\n')) != nullptr )
|
||||
{
|
||||
*ptr2 = 0;
|
||||
// get items
|
||||
char *dmrid;
|
||||
char *callsign;
|
||||
if ( ((dmrid = ::strtok(ptr1, ";")) != nullptr) && IsValidDmrid(dmrid) )
|
||||
{
|
||||
if ( ((callsign = ::strtok(nullptr, ";")) != nullptr) )
|
||||
{
|
||||
// new entry
|
||||
uint32_t ui = atoi(dmrid);
|
||||
CCallsign cs(callsign, ui);
|
||||
if ( cs.IsValid() )
|
||||
{
|
||||
m_CallsignMap.insert(std::pair<uint32_t,CCallsign>(ui, cs));
|
||||
m_DmridMap.insert(std::pair<CCallsign,uint32_t>(cs,ui));
|
||||
}
|
||||
}
|
||||
}
|
||||
// next line
|
||||
ptr1 = ptr2+1;
|
||||
}
|
||||
|
||||
// done
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// report
|
||||
std::cout << "Read " << m_DmridMap.size() << " DMR ids from file " << DMRIDDB_PATH << std::endl;
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
bool CDmridDirFile::GetLastModTime(time_t *time)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
struct stat fileStat;
|
||||
if( ::stat(DMRIDDB_PATH, &fileStat) != -1 )
|
||||
{
|
||||
*time = fileStat.st_mtime;
|
||||
ok = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
@ -1,177 +0,0 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
// Copyright © 2021 Doug McLain AD8DP
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string.h>
|
||||
#include "Main.h"
|
||||
#include "Reflector.h"
|
||||
#include "DMRIdDirHttp.h"
|
||||
|
||||
#if (DMRIDDB_USE_RLX_SERVER == 1)
|
||||
CDmridDirHttp g_DmridDir;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// refresh
|
||||
|
||||
bool CDmridDirHttp::LoadContent(CBuffer *buffer)
|
||||
{
|
||||
// get file from xlxapi server
|
||||
return HttpGet("xlxapi.rlx.lu", "api/exportdmr.php", 80, buffer);
|
||||
}
|
||||
|
||||
bool CDmridDirHttp::RefreshContent(const CBuffer &buffer)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
// clear directory
|
||||
m_CallsignMap.clear();
|
||||
m_DmridMap.clear();
|
||||
|
||||
// scan file
|
||||
if ( buffer.size() > 0 )
|
||||
{
|
||||
char *ptr1 = (char *)buffer.data();
|
||||
char *ptr2;
|
||||
// get next line
|
||||
while ( (ptr2 = ::strchr(ptr1, '\n')) != nullptr )
|
||||
{
|
||||
*ptr2 = 0;
|
||||
// get items
|
||||
char *dmrid;
|
||||
char *callsign;
|
||||
if ( ((dmrid = ::strtok(ptr1, ";")) != nullptr) && IsValidDmrid(dmrid) )
|
||||
{
|
||||
if ( ((callsign = ::strtok(nullptr, ";")) != nullptr) )
|
||||
{
|
||||
// new entry
|
||||
uint32_t ui = atoi(dmrid);
|
||||
CCallsign cs(callsign, ui);
|
||||
if ( cs.IsValid() )
|
||||
{
|
||||
m_CallsignMap.insert(std::pair<uint32_t,CCallsign>(ui, cs));
|
||||
m_DmridMap.insert(std::pair<CCallsign,uint32_t>(cs,ui));
|
||||
}
|
||||
}
|
||||
}
|
||||
// next line
|
||||
ptr1 = ptr2+1;
|
||||
}
|
||||
// done
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// report
|
||||
std::cout << "Read " << m_DmridMap.size() << " DMR ids from xlxapi.rlx.lu database " << std::endl;
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// httpd helpers
|
||||
|
||||
#define DMRID_HTTPGET_SIZEMAX (256)
|
||||
|
||||
bool CDmridDirHttp::HttpGet(const char *hostname, const char *filename, int port, CBuffer *buffer)
|
||||
{
|
||||
bool ok = false;
|
||||
int sock_id;
|
||||
|
||||
// open socket
|
||||
if ( (sock_id = ::socket(AF_INET, SOCK_STREAM, 0)) >= 0 )
|
||||
{
|
||||
// get hostname address
|
||||
struct sockaddr_in servaddr;
|
||||
struct hostent *hp;
|
||||
memset(&servaddr,0,sizeof(servaddr));
|
||||
if( (hp = gethostbyname(hostname)) != nullptr )
|
||||
{
|
||||
// dns resolved
|
||||
memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length);
|
||||
servaddr.sin_port = htons(port);
|
||||
servaddr.sin_family = AF_INET;
|
||||
|
||||
// connect
|
||||
if ( ::connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0)
|
||||
{
|
||||
// send the GET request
|
||||
char request[DMRID_HTTPGET_SIZEMAX];
|
||||
::sprintf(request, "GET /%s HTTP/1.0\r\nFrom: %s\r\nUser-Agent: urfd\r\n\r\n",
|
||||
filename, (const char *)g_Reflector.GetCallsign());
|
||||
::write(sock_id, request, strlen(request));
|
||||
|
||||
// config receive timeouts
|
||||
fd_set read_set;
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 5;
|
||||
timeout.tv_usec = 0;
|
||||
FD_ZERO(&read_set);
|
||||
FD_SET(sock_id, &read_set);
|
||||
|
||||
// get the reply back
|
||||
buffer->clear();
|
||||
bool done = false;
|
||||
do
|
||||
{
|
||||
char buf[1440];
|
||||
ssize_t len = 0;
|
||||
select(sock_id+1, &read_set, nullptr, nullptr, &timeout);
|
||||
//if ( (ret > 0) || ((ret < 0) && (errno == EINPROGRESS)) )
|
||||
//if ( ret >= 0 )
|
||||
//{
|
||||
usleep(5000);
|
||||
len = read(sock_id, buf, 1440);
|
||||
if ( len > 0 )
|
||||
{
|
||||
buffer->Append((uint8_t *)buf, (int)len);
|
||||
ok = true;
|
||||
}
|
||||
//}
|
||||
done = (len <= 0);
|
||||
|
||||
}
|
||||
while (!done);
|
||||
buffer->Append((uint8_t)0);
|
||||
|
||||
// and disconnect
|
||||
close(sock_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Cannot establish connection with host " << hostname << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Host " << hostname << " not found" << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Failed to open wget socket" << std::endl;
|
||||
}
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
@ -0,0 +1,125 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <ctime>
|
||||
#include <cctype>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// defines
|
||||
|
||||
//// Module configuration
|
||||
#define DSTAR_IPV4 true
|
||||
#define DMR_IPV4 true
|
||||
#define YSF_IPV4 true
|
||||
#define BM_IPV4 true
|
||||
#define M17_IPV4 true
|
||||
#define URF_IPV4 true
|
||||
|
||||
#define DSTAR_IPV6 true
|
||||
#define DMR_IPV6 false
|
||||
#define YSF_IPV6 false
|
||||
#define BM_IPV6 false
|
||||
#define M17_IPV6 true
|
||||
#define URF_IPV6 true
|
||||
|
||||
// protocols ---------------------------------------------------
|
||||
|
||||
enum class EProtocol { any, none, dextra, dplus, dcs, bm, urf, dmrplus, dmrmmdvm, ysf, m17 };
|
||||
|
||||
// DExtra
|
||||
#define DEXTRA_KEEPALIVE_PERIOD 3 // in seconds
|
||||
#define DEXTRA_KEEPALIVE_TIMEOUT (DEXTRA_KEEPALIVE_PERIOD*10) // in seconds
|
||||
#define DEXTRA_RECONNECT_PERIOD 5 // in seconds
|
||||
|
||||
// DPlus
|
||||
#define DPLUS_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define DPLUS_KEEPALIVE_TIMEOUT (DPLUS_KEEPALIVE_PERIOD*10) // in seconds
|
||||
#define DPLUS_DEFAULT_RPTR1_SUFFIX 'Y'
|
||||
|
||||
// DCS
|
||||
#define DCS_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define DCS_KEEPALIVE_TIMEOUT (DCS_KEEPALIVE_PERIOD*30) // in seconds
|
||||
|
||||
// XLX, used for BM
|
||||
#define BM_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define BM_KEEPALIVE_TIMEOUT (XLX_KEEPALIVE_PERIOD*30) // in seconds
|
||||
#define BM_RECONNECT_PERIOD 5 // in seconds
|
||||
|
||||
// URF
|
||||
#define URF_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define URF_KEEPALIVE_TIMEOUT (URF_KEEPALIVE_PERIOD*30) // in seconds
|
||||
#define URF_RECONNECT_PERIOD 5 // in seconds
|
||||
|
||||
// DMRPlus (dongle)
|
||||
#define DMRPLUS_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define DMRPLUS_KEEPALIVE_TIMEOUT (DMRPLUS_KEEPALIVE_PERIOD*10) // in seconds
|
||||
#define DMRPLUS_REFLECTOR_SLOT DMR_SLOT2
|
||||
|
||||
// DMRMmdvm
|
||||
#define DMRMMDVM_KEEPALIVE_PERIOD 10 // in seconds
|
||||
#define DMRMMDVM_KEEPALIVE_TIMEOUT (DMRMMDVM_KEEPALIVE_PERIOD*10) // in seconds
|
||||
#define DMRMMDVM_REFLECTOR_SLOT DMR_SLOT2
|
||||
#define DMRMMDVM_REFLECTOR_COLOUR 1
|
||||
|
||||
// YSF
|
||||
#define YSF_KEEPALIVE_PERIOD 3 // in seconds
|
||||
#define YSF_KEEPALIVE_TIMEOUT (YSF_KEEPALIVE_PERIOD*10) // in seconds
|
||||
|
||||
// M17
|
||||
#define M17_KEEPALIVE_PERIOD 3
|
||||
#define M17_KEEPALIVE_TIMEOUT (M17_KEEPALIVE_PERIOD*10)
|
||||
|
||||
// G3 Terminal
|
||||
#define G3_PRESENCE_PORT 12346 // UDP port
|
||||
#define G3_CONFIG_PORT 12345 // UDP port
|
||||
#define G3_DV_PORT 40000 // UDP port
|
||||
#define G3_KEEPALIVE_PERIOD 10 // in seconds
|
||||
#define G3_KEEPALIVE_TIMEOUT 3600 // in seconds, 1 hour
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// macros
|
||||
|
||||
#define MIN(a,b) ((a)<(b))?(a):(b)
|
||||
#define MAX(a,b) ((a)>(b))?(a):(b)
|
||||
#define MAKEWORD(low, high) ((uint16_t)(((uint8_t)(low)) | (((uint16_t)((uint8_t)(high))) << 8)))
|
||||
#define MAKEDWORD(low, high) ((uint32_t)(((uint16_t)(low)) | (((uint32_t)((uint16_t)(high))) << 16)))
|
||||
#define LOBYTE(w) ((uint8_t)(uint16_t)(w & 0x00FF))
|
||||
#define HIBYTE(w) ((uint8_t)((((uint16_t)(w)) >> 8) & 0xFF))
|
||||
#define LOWORD(dw) ((uint16_t)(uint32_t)(dw & 0x0000FFFF))
|
||||
#define HIWORD(dw) ((uint16_t)((((uint32_t)(dw)) >> 16) & 0xFFFF))
|
||||
@ -0,0 +1,95 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2023 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
#include <sys/stat.h>
|
||||
#include "Lookup.h"
|
||||
|
||||
CLookup::~CLookup()
|
||||
{
|
||||
LookupClose();
|
||||
}
|
||||
|
||||
void CLookup::LookupClose()
|
||||
{
|
||||
keep_running = false;
|
||||
if (m_Future.valid())
|
||||
m_Future.get();
|
||||
}
|
||||
|
||||
std::time_t CLookup::GetLastModTime()
|
||||
{
|
||||
struct stat fileStat;
|
||||
if(0 == stat(m_Path.c_str(), &fileStat))
|
||||
{
|
||||
return fileStat.st_mtime;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CLookup::LookupInit()
|
||||
{
|
||||
LoadParameters();
|
||||
|
||||
m_Future = std::async(std::launch::async, &CLookup::Thread, this);
|
||||
}
|
||||
|
||||
void CLookup::Thread()
|
||||
{
|
||||
while (keep_running)
|
||||
{
|
||||
bool loaded = false;
|
||||
|
||||
if (m_Type != ERefreshType::file) // get the HTTP contents
|
||||
{
|
||||
CBuffer buf;
|
||||
loaded = LoadContentHttp(buf);
|
||||
if (loaded)
|
||||
{
|
||||
Lock();
|
||||
ClearContents();
|
||||
RefreshContentHttp(buf);
|
||||
Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (m_Type != ERefreshType::http) // get the file contents
|
||||
{
|
||||
auto lastTime = GetLastModTime();
|
||||
if (lastTime > m_LastModTime)
|
||||
{
|
||||
CBuffer buf;
|
||||
if (LoadContentFile(buf))
|
||||
{
|
||||
Lock();
|
||||
if (! loaded)
|
||||
ClearContents();
|
||||
RefreshContentFile(buf);
|
||||
Unlock();
|
||||
m_LastModTime = lastTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// now wait for a while...
|
||||
for (unsigned i=0; i<20u*m_Refresh && keep_running; i++)
|
||||
std::this_thread::sleep_for(std::chrono::seconds(3));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,262 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2023 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include "Reflector.h"
|
||||
#include "LookupDmr.h"
|
||||
|
||||
extern CReflector g_ref;
|
||||
extern CConfigure g_cfg;
|
||||
|
||||
void CLookupDmr::ClearContents()
|
||||
{
|
||||
m_CallsignMap.clear();
|
||||
m_DmridMap.clear();
|
||||
}
|
||||
|
||||
void CLookupDmr::LoadParameters()
|
||||
{
|
||||
g_cfg.GetRefreshType(g_cfg.j.dmriddb.mode, m_Type);
|
||||
g_cfg.GetUnsigned(g_cfg.j.dmriddb.refreshmin, m_Refresh);
|
||||
g_cfg.GetString(g_cfg.j.dmriddb.filepath, m_Path);
|
||||
g_cfg.GetString(g_cfg.j.dmriddb.hostname, m_Host);
|
||||
g_cfg.GetString(g_cfg.j.dmriddb.suffix, m_Suffix);
|
||||
}
|
||||
|
||||
uint32_t CLookupDmr::FindDmrid(const CCallsign &callsign)
|
||||
{
|
||||
|
||||
auto found = m_DmridMap.find(callsign);
|
||||
if ( found != m_DmridMap.end() )
|
||||
{
|
||||
return (found->second);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const CCallsign *CLookupDmr::FindCallsign(uint32_t dmrid)
|
||||
{
|
||||
auto found = m_CallsignMap.find(dmrid);
|
||||
if ( found != m_CallsignMap.end() )
|
||||
{
|
||||
return &(found->second);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool CLookupDmr::LoadContentFile(CBuffer &buffer)
|
||||
{
|
||||
buffer.clear();
|
||||
std::ifstream file;
|
||||
std::streampos size;
|
||||
|
||||
// open file
|
||||
file.open(m_Path, std::ios::in | std::ios::binary | std::ios::ate);
|
||||
if ( file.is_open() )
|
||||
{
|
||||
// read file
|
||||
size = file.tellg();
|
||||
if ( size > 0 )
|
||||
{
|
||||
// read file into buffer
|
||||
buffer.resize((int)size+1);
|
||||
file.seekg(0, std::ios::beg);
|
||||
file.read((char *)buffer.data(), (int)size);
|
||||
|
||||
// close file
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return buffer.size() > 0;
|
||||
}
|
||||
|
||||
bool CLookupDmr::LoadContentHttp(CBuffer &buf)
|
||||
{
|
||||
// get file from xlxapi server
|
||||
return HttpGet(m_Host.c_str(), m_Suffix.c_str(), 80, buf);
|
||||
}
|
||||
|
||||
void CLookupDmr::RefreshContentFile(const CBuffer &buffer)
|
||||
{
|
||||
// crack it
|
||||
char *ptr1 = (char *)buffer.data();
|
||||
char *ptr2;
|
||||
|
||||
// get next line
|
||||
while ( (ptr2 = strchr(ptr1, '\n')) != nullptr )
|
||||
{
|
||||
*ptr2 = 0;
|
||||
// get items
|
||||
char *dmrid;
|
||||
char *callsign;
|
||||
if ( ((dmrid = strtok(ptr1, ";")) != nullptr) && IsValidDmrId(dmrid) )
|
||||
{
|
||||
if ( ((callsign = ::strtok(nullptr, ";")) != nullptr) )
|
||||
{
|
||||
// new entry
|
||||
uint32_t ui = atoi(dmrid);
|
||||
CCallsign cs(callsign, ui);
|
||||
if ( cs.IsValid() )
|
||||
{
|
||||
m_CallsignMap.insert(std::pair<uint32_t,CCallsign>(ui, cs));
|
||||
m_DmridMap.insert(std::pair<CCallsign,uint32_t>(cs,ui));
|
||||
}
|
||||
}
|
||||
}
|
||||
// next line
|
||||
ptr1 = ptr2+1;
|
||||
}
|
||||
|
||||
std::cout << "Read " << m_DmridMap.size() << " DMR ids from file " << m_Refresh << std::endl;
|
||||
}
|
||||
|
||||
void CLookupDmr::RefreshContentHttp(const CBuffer &buffer)
|
||||
{
|
||||
char *ptr1 = (char *)buffer.data();
|
||||
char *ptr2;
|
||||
// get next line
|
||||
while ( (ptr2 = strchr(ptr1, '\n')) != nullptr )
|
||||
{
|
||||
*ptr2 = 0;
|
||||
// get items
|
||||
char *dmrid;
|
||||
char *callsign;
|
||||
if ( ((dmrid = strtok(ptr1, ";")) != nullptr) && IsValidDmrId(dmrid) )
|
||||
{
|
||||
if ( ((callsign = strtok(nullptr, ";")) != nullptr) )
|
||||
{
|
||||
// new entry
|
||||
uint32_t ui = atoi(dmrid);
|
||||
CCallsign cs(callsign, ui);
|
||||
if ( cs.IsValid() )
|
||||
{
|
||||
m_CallsignMap.insert(std::pair<uint32_t, CCallsign>(ui, cs));
|
||||
m_DmridMap.insert(std::pair<CCallsign, uint32_t>(cs,ui));
|
||||
}
|
||||
}
|
||||
}
|
||||
// next line
|
||||
ptr1 = ptr2+1;
|
||||
}
|
||||
|
||||
std::cout << "Read " << m_DmridMap.size() << " DMR ids from " << m_Host << " database " << std::endl;
|
||||
}
|
||||
|
||||
bool CLookupDmr::IsValidDmrId(const char *sz)
|
||||
{
|
||||
bool ok = false;
|
||||
size_t n = strlen(sz);
|
||||
if ( (n > 0) && (n <= 8) )
|
||||
{
|
||||
ok = true;
|
||||
for ( size_t i = 0; (i < n) && ok; i++ )
|
||||
{
|
||||
ok = ok && isdigit(sz[i]);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
#define DMRID_HTTPGET_SIZEMAX (256)
|
||||
|
||||
bool CLookupDmr::HttpGet(const char *hostname, const char *filename, int port, CBuffer &buffer)
|
||||
{
|
||||
buffer.clear();
|
||||
int sock_id;
|
||||
|
||||
// open socket
|
||||
if ( (sock_id = socket(AF_INET, SOCK_STREAM, 0)) >= 0 )
|
||||
{
|
||||
// get hostname address
|
||||
struct sockaddr_in servaddr;
|
||||
struct hostent *hp;
|
||||
memset(&servaddr, 0, sizeof(servaddr));
|
||||
if( (hp = gethostbyname(hostname)) != nullptr)
|
||||
{
|
||||
// dns resolved
|
||||
memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length);
|
||||
servaddr.sin_port = htons(port);
|
||||
servaddr.sin_family = AF_INET;
|
||||
|
||||
// connect
|
||||
if (connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0)
|
||||
{
|
||||
// send the GET request
|
||||
char request[DMRID_HTTPGET_SIZEMAX];
|
||||
::sprintf(request, "GET /%s HTTP/1.0\r\nFrom: %s\r\nUser-Agent: urfd\r\n\r\n", filename, g_ref.GetCallsign().GetCS().c_str());
|
||||
::write(sock_id, request, strlen(request));
|
||||
|
||||
// config receive timeouts
|
||||
fd_set read_set;
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 5;
|
||||
timeout.tv_usec = 0;
|
||||
FD_ZERO(&read_set);
|
||||
FD_SET(sock_id, &read_set);
|
||||
|
||||
// get the reply back
|
||||
bool done = false;
|
||||
do
|
||||
{
|
||||
char buf[1440];
|
||||
ssize_t len = 0;
|
||||
select(sock_id+1, &read_set, nullptr, nullptr, &timeout);
|
||||
//if ( (ret > 0) || ((ret < 0) && (errno == EINPROGRESS)) )
|
||||
//if ( ret >= 0 )
|
||||
//{
|
||||
usleep(5000);
|
||||
len = read(sock_id, buf, 1440);
|
||||
if ( len > 0 )
|
||||
{
|
||||
buffer.Append((uint8_t *)buf, (int)len);
|
||||
}
|
||||
//}
|
||||
done = (len <= 0);
|
||||
|
||||
}
|
||||
while (!done);
|
||||
buffer.Append((uint8_t)0);
|
||||
|
||||
// and disconnect
|
||||
close(sock_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Cannot establish connection with host " << hostname << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Host " << hostname << " not found" << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Failed to open wget socket" << std::endl;
|
||||
}
|
||||
|
||||
// done
|
||||
return buffer.size() > 1;
|
||||
}
|
||||
@ -0,0 +1,264 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2023 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include "Reflector.h"
|
||||
#include "LookupNxdn.h"
|
||||
|
||||
extern CReflector g_ref;
|
||||
extern CConfigure g_cfg;
|
||||
|
||||
void CLookupNxdn::ClearContents()
|
||||
{
|
||||
m_CallsignMap.clear();
|
||||
m_NxdnidMap.clear();
|
||||
}
|
||||
|
||||
void CLookupNxdn::LoadParameters()
|
||||
{
|
||||
g_cfg.GetRefreshType(g_cfg.j.nxdniddb.mode, m_Type);
|
||||
g_cfg.GetUnsigned(g_cfg.j.nxdniddb.refreshmin, m_Refresh);
|
||||
g_cfg.GetString(g_cfg.j.nxdniddb.filepath, m_Path);
|
||||
g_cfg.GetString(g_cfg.j.dmriddb.hostname, m_Host);
|
||||
g_cfg.GetString(g_cfg.j.nxdniddb.suffix, m_Suffix);
|
||||
}
|
||||
|
||||
const CCallsign *CLookupNxdn::FindCallsign(uint16_t nxdnid)
|
||||
{
|
||||
auto found = m_CallsignMap.find(nxdnid);
|
||||
if ( found != m_CallsignMap.end() )
|
||||
{
|
||||
return &(found->second);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint16_t CLookupNxdn::FindNXDNid(const CCallsign &callsign)
|
||||
{
|
||||
auto found = m_NxdnidMap.find(callsign);
|
||||
if ( found != m_NxdnidMap.end() )
|
||||
{
|
||||
return (found->second);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CLookupNxdn::LoadContentFile(CBuffer &buffer)
|
||||
{
|
||||
buffer.clear();
|
||||
std::ifstream file;
|
||||
std::streampos size;
|
||||
|
||||
// open file
|
||||
file.open(m_Path, std::ios::in | std::ios::binary | std::ios::ate);
|
||||
if ( file.is_open() )
|
||||
{
|
||||
// read file
|
||||
size = file.tellg();
|
||||
if ( size > 0 )
|
||||
{
|
||||
// read file into buffer
|
||||
buffer.resize((int)size+1);
|
||||
file.seekg (0, std::ios::beg);
|
||||
file.read((char *)buffer.data(), (int)size);
|
||||
|
||||
// close file
|
||||
file.close();
|
||||
|
||||
// done
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return buffer.size() > 0;
|
||||
}
|
||||
|
||||
bool CLookupNxdn::LoadContentHttp(CBuffer &buffer)
|
||||
{
|
||||
// get file from xlxapi server
|
||||
return HttpGet(m_Host.c_str(), m_Suffix.c_str(), 80, buffer);
|
||||
}
|
||||
|
||||
void CLookupNxdn::RefreshContentFile(const CBuffer &buffer)
|
||||
{
|
||||
// crack it
|
||||
char *ptr1 = (char *)buffer.data();
|
||||
char *ptr2;
|
||||
|
||||
// get next line
|
||||
while ( (ptr2 = ::strchr(ptr1, '\n')) != nullptr )
|
||||
{
|
||||
*ptr2 = 0;
|
||||
// get items
|
||||
char *nxdnid;
|
||||
char *callsign;
|
||||
if ( ((nxdnid = ::strtok(ptr1, ",")) != nullptr) && IsValidNxdnId(nxdnid) )
|
||||
{
|
||||
if ( ((callsign = ::strtok(nullptr, ",")) != nullptr) )
|
||||
{
|
||||
// new entry
|
||||
uint16_t us = atoi(nxdnid);
|
||||
CCallsign cs(callsign, 0, us);
|
||||
if ( cs.IsValid() )
|
||||
{
|
||||
m_CallsignMap.insert(std::pair<uint32_t,CCallsign>(us, cs));
|
||||
m_NxdnidMap.insert(std::pair<CCallsign,uint32_t>(cs,us));
|
||||
}
|
||||
}
|
||||
}
|
||||
// next line
|
||||
ptr1 = ptr2+1;
|
||||
}
|
||||
|
||||
std::cout << "Read " << m_NxdnidMap.size() << " NXDN ids from file " << m_Path << std::endl;
|
||||
}
|
||||
|
||||
void CLookupNxdn::RefreshContentHttp(const CBuffer &buffer)
|
||||
{
|
||||
char *ptr1 = (char *)buffer.data();
|
||||
char *ptr2;
|
||||
// get next line
|
||||
while ( (ptr2 = strchr(ptr1, '\n')) != nullptr )
|
||||
{
|
||||
std::cout << "newline: " << std::string(ptr2) << std::endl;
|
||||
*ptr2 = 0;
|
||||
// get items
|
||||
char *nxdnid;
|
||||
char *callsign;
|
||||
if ( ((nxdnid = ::strtok(ptr1, ",")) != nullptr) && IsValidNxdnId(nxdnid) )
|
||||
{
|
||||
if ( ((callsign = ::strtok(nullptr, ",")) != nullptr) )
|
||||
{
|
||||
// new entry
|
||||
uint16_t us = atoi(nxdnid);
|
||||
CCallsign cs(callsign, 0, us);
|
||||
if ( cs.IsValid() )
|
||||
{
|
||||
m_CallsignMap.insert(std::pair<uint16_t,CCallsign>(us, cs));
|
||||
m_NxdnidMap.insert(std::pair<CCallsign,uint16_t>(cs,us));
|
||||
}
|
||||
}
|
||||
}
|
||||
// next line
|
||||
ptr1 = ptr2+1;
|
||||
}
|
||||
|
||||
std::cout << "Read " << m_NxdnidMap.size() << " NXDN ids from " << m_Host << std::endl;
|
||||
}
|
||||
|
||||
bool CLookupNxdn::IsValidNxdnId(const char *sz)
|
||||
{
|
||||
bool ok = false;
|
||||
size_t n = ::strlen(sz);
|
||||
if ( (n > 0) && (n <= 5) )
|
||||
{
|
||||
ok = true;
|
||||
for ( size_t i = 0; (i < n) && ok; i++ )
|
||||
{
|
||||
ok = ok && isdigit(sz[i]);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
#define NXDNID_HTTPGET_SIZEMAX (256)
|
||||
|
||||
bool CLookupNxdn::HttpGet(const char *hostname, const char *filename, int port, CBuffer &buffer)
|
||||
{
|
||||
int sock_id;
|
||||
buffer.clear();
|
||||
|
||||
// open socket
|
||||
if ( (sock_id = socket(AF_INET, SOCK_STREAM, 0)) >= 0 )
|
||||
{
|
||||
// get hostname address
|
||||
struct sockaddr_in servaddr;
|
||||
struct hostent *hp;
|
||||
memset(&servaddr,0,sizeof(servaddr));
|
||||
if( (hp = gethostbyname(hostname)) != nullptr )
|
||||
{
|
||||
// dns resolved
|
||||
memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length);
|
||||
servaddr.sin_port = htons(port);
|
||||
servaddr.sin_family = AF_INET;
|
||||
|
||||
// connect
|
||||
if ( ::connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0)
|
||||
{
|
||||
// send the GET request
|
||||
char request[NXDNID_HTTPGET_SIZEMAX];
|
||||
::sprintf(request, "GET /%s HTTP/1.0\r\nFrom: %s\r\nUser-Agent: urfd\r\n\r\n", filename, (const char *)g_ref.GetCallsign());
|
||||
::write(sock_id, request, strlen(request));
|
||||
|
||||
// config receive timeouts
|
||||
fd_set read_set;
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 5;
|
||||
timeout.tv_usec = 0;
|
||||
FD_ZERO(&read_set);
|
||||
FD_SET(sock_id, &read_set);
|
||||
|
||||
// get the reply back
|
||||
bool done = false;
|
||||
do
|
||||
{
|
||||
char buf[1440];
|
||||
ssize_t len = 0;
|
||||
select(sock_id+1, &read_set, nullptr, nullptr, &timeout);
|
||||
//if ( (ret > 0) || ((ret < 0) && (errno == EINPROGRESS)) )
|
||||
//if ( ret >= 0 )
|
||||
//{
|
||||
usleep(5000);
|
||||
len = read(sock_id, buf, 1440);
|
||||
if ( len > 0 )
|
||||
{
|
||||
buffer.Append((uint8_t *)buf, (int)len);
|
||||
}
|
||||
//}
|
||||
done = (len <= 0);
|
||||
|
||||
}
|
||||
while (!done);
|
||||
buffer.Append((uint8_t)0);
|
||||
|
||||
// and disconnect
|
||||
close(sock_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Cannot establish connection with host " << hostname << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Host " << hostname << " not found" << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Failed to open wget socket" << std::endl;
|
||||
}
|
||||
|
||||
// done
|
||||
return buffer.size() > 1;
|
||||
}
|
||||
@ -0,0 +1,256 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2023 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <iostream>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <mysql/mysql.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#include "Reflector.h"
|
||||
#include "LookupYsf.h"
|
||||
|
||||
extern CReflector g_ref;
|
||||
extern CConfigure g_cfg;
|
||||
|
||||
void CLookupYsf::ClearContents()
|
||||
{
|
||||
m_map.clear();
|
||||
}
|
||||
|
||||
void CLookupYsf::LoadParameters()
|
||||
{
|
||||
g_cfg.GetRefreshType(g_cfg.j.ysftxrxdb.mode, m_Type);
|
||||
g_cfg.GetUnsigned(g_cfg.j.ysftxrxdb.refreshmin, m_Refresh);
|
||||
g_cfg.GetString(g_cfg.j.ysftxrxdb.filepath, m_Path);
|
||||
g_cfg.GetString(g_cfg.j.ysftxrxdb.hostname, m_Host);
|
||||
g_cfg.GetString(g_cfg.j.ysftxrxdb.suffix, m_Suffix);
|
||||
g_cfg.GetUnsigned(g_cfg.j.ysf.defaulttxfreq, m_DefaultTx);
|
||||
g_cfg.GetUnsigned(g_cfg.j.ysf.defaultrxfreq, m_DefaultRx);
|
||||
}
|
||||
|
||||
bool CLookupYsf::LoadContentFile(CBuffer &buffer)
|
||||
{
|
||||
buffer.clear();
|
||||
std::ifstream file;
|
||||
std::streampos size;
|
||||
|
||||
// open file
|
||||
file.open(m_Path, std::ios::in | std::ios::binary | std::ios::ate);
|
||||
if (file.is_open())
|
||||
{
|
||||
// read file
|
||||
size = file.tellg();
|
||||
if (size > 0)
|
||||
{
|
||||
// read file into buffer
|
||||
buffer.resize((int)size + 1);
|
||||
file.seekg(0, std::ios::beg);
|
||||
file.read((char *)buffer.data(), (int)size);
|
||||
|
||||
// close file
|
||||
file.close();
|
||||
}
|
||||
}
|
||||
return buffer.size() > 0;
|
||||
}
|
||||
|
||||
bool CLookupYsf::LoadContentHttp(CBuffer &buffer)
|
||||
{
|
||||
// get file from http://xlxapi.rlx.lu/api/exportysfrepeaters.php
|
||||
return HttpGet(m_Host.c_str(), m_Suffix.c_str(), 80, buffer);
|
||||
}
|
||||
|
||||
void CLookupYsf::RefreshContentFile(const CBuffer &buffer)
|
||||
{
|
||||
// scan buffer
|
||||
if (buffer.size() > 0)
|
||||
{
|
||||
// crack it
|
||||
char *ptr1 = (char *)buffer.data();
|
||||
char *ptr2;
|
||||
|
||||
// get next line
|
||||
while ((ptr2 = ::strchr(ptr1, '\n')) != nullptr)
|
||||
{
|
||||
*ptr2 = 0;
|
||||
// get items
|
||||
char *callsign;
|
||||
char *txfreq;
|
||||
char *rxfreq;
|
||||
if (((callsign = ::strtok(ptr1, ";")) != nullptr))
|
||||
{
|
||||
if (((txfreq = ::strtok(nullptr, ";")) != nullptr))
|
||||
{
|
||||
if (((rxfreq = ::strtok(nullptr, ";")) != nullptr))
|
||||
{
|
||||
// new entry
|
||||
CCallsign cs(callsign);
|
||||
CYsfNode node(atoi(txfreq), atoi(rxfreq));
|
||||
if (cs.IsValid() && node.IsValid())
|
||||
{
|
||||
m_map.insert(std::pair<CCallsign, CYsfNode>(cs, node));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// next line
|
||||
ptr1 = ptr2 + 1;
|
||||
}
|
||||
}
|
||||
|
||||
// report
|
||||
std::cout << "Read " << m_map.size() << " YSF nodes from file " << m_Path << std::endl;
|
||||
}
|
||||
|
||||
void CLookupYsf::RefreshContentHttp(const CBuffer &buffer)
|
||||
{
|
||||
// crack it
|
||||
char *ptr1 = (char *)buffer.data();
|
||||
char *ptr2;
|
||||
|
||||
// get next line
|
||||
while ((ptr2 = ::strchr(ptr1, '\n')) != nullptr)
|
||||
{
|
||||
*ptr2 = 0;
|
||||
// get items
|
||||
char *callsign;
|
||||
char *txfreq;
|
||||
char *rxfreq;
|
||||
if (((callsign = ::strtok(ptr1, ";")) != nullptr))
|
||||
{
|
||||
if (((txfreq = ::strtok(nullptr, ";")) != nullptr))
|
||||
{
|
||||
if (((rxfreq = ::strtok(nullptr, ";")) != nullptr))
|
||||
{
|
||||
// new entry
|
||||
CCallsign cs(callsign);
|
||||
CYsfNode node(atoi(txfreq), atoi(rxfreq));
|
||||
if (cs.IsValid() && node.IsValid())
|
||||
{
|
||||
m_map.insert(std::pair<CCallsign, CYsfNode>(cs, node));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// next line
|
||||
ptr1 = ptr2 + 1;
|
||||
}
|
||||
|
||||
// report
|
||||
std::cout << "Read " << m_map.size() << " YSF nodes from " << m_Host << " database " << std::endl;
|
||||
}
|
||||
|
||||
#define YSFNODE_HTTPGET_SIZEMAX (256)
|
||||
|
||||
bool CLookupYsf::HttpGet(const char *hostname, const char *filename, int port, CBuffer &buffer)
|
||||
{
|
||||
buffer.clear();
|
||||
int sock_id;
|
||||
|
||||
// open socket
|
||||
if ((sock_id = socket(AF_INET, SOCK_STREAM, 0)) >= 0)
|
||||
{
|
||||
// get hostname address
|
||||
struct sockaddr_in servaddr;
|
||||
struct hostent *hp;
|
||||
memset(&servaddr, 0, sizeof(servaddr));
|
||||
if ((hp = gethostbyname(hostname)) != nullptr)
|
||||
{
|
||||
// dns resolved
|
||||
memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length);
|
||||
servaddr.sin_port = htons(port);
|
||||
servaddr.sin_family = AF_INET;
|
||||
|
||||
// connect
|
||||
if (connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0)
|
||||
{
|
||||
// send the GET request
|
||||
char request[YSFNODE_HTTPGET_SIZEMAX];
|
||||
sprintf(request, "GET /%s HTTP/1.0\r\nFrom: %s\r\nUser-Agent: urfd\r\n\r\n", filename, g_ref.GetCallsign().GetCS().c_str());
|
||||
write(sock_id, request, strlen(request));
|
||||
|
||||
// config receive timeouts
|
||||
fd_set read_set;
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 5;
|
||||
timeout.tv_usec = 0;
|
||||
FD_ZERO(&read_set);
|
||||
FD_SET(sock_id, &read_set);
|
||||
|
||||
// get the reply back
|
||||
bool done = false;
|
||||
do
|
||||
{
|
||||
char buf[1440];
|
||||
ssize_t len = 0;
|
||||
select(sock_id + 1, &read_set, nullptr, nullptr, &timeout);
|
||||
// if ( (ret > 0) || ((ret < 0) && (errno == EINPROGRESS)) )
|
||||
// if ( ret >= 0 )
|
||||
//{
|
||||
usleep(5000);
|
||||
len = read(sock_id, buf, 1440);
|
||||
if (len > 0)
|
||||
{
|
||||
buffer.Append((uint8_t *)buf, (int)len);
|
||||
}
|
||||
//}
|
||||
done = (len <= 0);
|
||||
|
||||
} while (!done);
|
||||
|
||||
buffer.Append((uint8_t)0);
|
||||
|
||||
// and disconnect
|
||||
close(sock_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Cannot establish connection with host " << hostname << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Host " << hostname << " not found" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Failed to open wget socket" << std::endl;
|
||||
}
|
||||
|
||||
// done
|
||||
return buffer.size() > 1;
|
||||
}
|
||||
|
||||
bool CLookupYsf::FindFrequencies(const CCallsign &callsign, uint32_t *txfreq, uint32_t *rxfreq)
|
||||
{
|
||||
auto found = m_map.find(callsign);
|
||||
if (found != m_map.end())
|
||||
{
|
||||
*txfreq = found->second.GetTxFrequency();
|
||||
*rxfreq = found->second.GetRxFrequency();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*txfreq = m_DefaultTx;
|
||||
*rxfreq = m_DefaultRx;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -1,276 +0,0 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <memory>
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <ctime>
|
||||
#include <cctype>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "configure.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// defines
|
||||
|
||||
//// Module configuration
|
||||
#define DSTAR_IPV4 true
|
||||
#define DMR_IPV4 true
|
||||
#define YSF_IPV4 true
|
||||
#define XLX_IPV4 true
|
||||
#define M17_IPV4 true
|
||||
#define P25_IPV4 true
|
||||
#define NXDN_IPV4 true
|
||||
#define USRP_IPV4 true
|
||||
#define URF_IPV4 true
|
||||
|
||||
#define DSTAR_IPV6 false
|
||||
#define DMR_IPV6 false
|
||||
#define YSF_IPV6 false
|
||||
#define XLX_IPV6 false
|
||||
#define M17_IPV6 true
|
||||
#define P25_IPV6 false
|
||||
#define NXDN_IPV6 false
|
||||
#define USRP_IPV6 false
|
||||
#define URF_IPV6 true
|
||||
|
||||
// version -----------------------------------------------------
|
||||
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 0
|
||||
#define VERSION_REVISION 7
|
||||
|
||||
// global ------------------------------------------------------
|
||||
|
||||
//#define JSON_MONITOR
|
||||
|
||||
// debug -------------------------------------------------------
|
||||
|
||||
//#define DEBUG_NO_ERROR_ON_XML_OPEN_FAIL
|
||||
//#define DEBUG_DUMPFILE
|
||||
|
||||
// protocols ---------------------------------------------------
|
||||
|
||||
#ifndef NO_G3
|
||||
enum class EProtocol { any, none, dextra, dplus, dcs, bm, urf, dmrplus, dmrmmdvm, ysf, m17, g3, p25, nxdn, usrp };
|
||||
#else
|
||||
enum class EProtocol { any, none, dextra, dplus, dcs, bm, urf, dmrplus, dmrmmdvm, ysf, m17, p25, nxdn, usrp };
|
||||
#endif
|
||||
|
||||
// DExtra
|
||||
#define DEXTRA_PORT 30001 // UDP port
|
||||
#define DEXTRA_KEEPALIVE_PERIOD 3 // in seconds
|
||||
#define DEXTRA_KEEPALIVE_TIMEOUT (DEXTRA_KEEPALIVE_PERIOD*10) // in seconds
|
||||
#define DEXTRA_RECONNECT_PERIOD 5 // in seconds
|
||||
|
||||
// DPlus
|
||||
#define DPLUS_PORT 20001 // UDP port
|
||||
#define DPLUS_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define DPLUS_KEEPALIVE_TIMEOUT (DPLUS_KEEPALIVE_PERIOD*10) // in seconds
|
||||
#define DPLUS_DEFAULT_RPTR1_SUFFIX 'Y'
|
||||
|
||||
// DCS
|
||||
#define DCS_PORT 30051 // UDP port
|
||||
#define DCS_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define DCS_KEEPALIVE_TIMEOUT (DCS_KEEPALIVE_PERIOD*30) // in seconds
|
||||
|
||||
// XLX, used for BM
|
||||
#define XLX_PORT 10002 // UDP port
|
||||
#define XLX_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define XLX_KEEPALIVE_TIMEOUT (XLX_KEEPALIVE_PERIOD*30) // in seconds
|
||||
#define XLX_RECONNECT_PERIOD 5 // in seconds
|
||||
|
||||
// URF
|
||||
#define URF_PORT 10017 // UDP port
|
||||
#define URF_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define URF_KEEPALIVE_TIMEOUT (URF_KEEPALIVE_PERIOD*30) // in seconds
|
||||
#define URF_RECONNECT_PERIOD 5 // in seconds
|
||||
|
||||
// DMRPlus (dongle)
|
||||
#define DMRPLUS_PORT 8880 // UDP port
|
||||
#define DMRPLUS_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define DMRPLUS_KEEPALIVE_TIMEOUT (DMRPLUS_KEEPALIVE_PERIOD*10) // in seconds
|
||||
#define DMRPLUS_REFLECTOR_SLOT DMR_SLOT2
|
||||
#define DMRPLUS_REFLECTOR_COLOUR 1
|
||||
|
||||
// DMRMmdvm
|
||||
#define DMRMMDVM_PORT 62030 // UDP port
|
||||
#define DMRMMDVM_KEEPALIVE_PERIOD 10 // in seconds
|
||||
#define DMRMMDVM_KEEPALIVE_TIMEOUT (DMRMMDVM_KEEPALIVE_PERIOD*10) // in seconds
|
||||
#define DMRMMDVM_REFLECTOR_SLOT DMR_SLOT2
|
||||
#define DMRMMDVM_REFLECTOR_COLOUR 1
|
||||
#define DMRMMDVM_DEFAULTID 0
|
||||
|
||||
// YSF
|
||||
#define YSF_PORT 42000 // UDP port
|
||||
#define YSF_KEEPALIVE_PERIOD 3 // in seconds
|
||||
#define YSF_KEEPALIVE_TIMEOUT (YSF_KEEPALIVE_PERIOD*10) // in seconds
|
||||
#define YSF_DEFAULT_NODE_TX_FREQ 445500000 // in Hz
|
||||
#define YSF_DEFAULT_NODE_RX_FREQ 445500000 // in Hz
|
||||
//#define YSF_REFLECTOR_ID 12345 // 5 digit YSF ID, assigned by registry when not defined
|
||||
// the following 4 defines are now in configure.h
|
||||
//#define YSF_REFLECTOR_NAME "URF000" // Max 16 characters, use reflector callsign when not defined
|
||||
//#define YSF_REFLECTOR_DESCRIPTION "XLX reflector" // Max 14 characters
|
||||
// #define YSF_AUTOLINK_ENABLE 0 // 1 = enable, 0 = disable auto-link
|
||||
// #define YSF_AUTOLINK_MODULE 'B' // module for client to auto-link to
|
||||
|
||||
// M17
|
||||
#define M17_PORT 17000
|
||||
#define M17_KEEPALIVE_PERIOD 3
|
||||
#define M17_KEEPALIVE_TIMEOUT (M17_KEEPALIVE_PERIOD*10)
|
||||
#define M17_RECONNECT_PERIOD 5
|
||||
|
||||
// P25
|
||||
#define P25_REFID 12345 // Reflector ID
|
||||
#define P25_PORT 41000 // UDP port
|
||||
#define P25_KEEPALIVE_PERIOD 3 // in seconds
|
||||
#define P25_KEEPALIVE_TIMEOUT (P25_KEEPALIVE_PERIOD*10) // in seconds
|
||||
#define P25_AUTOLINK_ENABLE 1 // 1 = enable, 0 = disable auto-link
|
||||
#define P25_AUTOLINK_MODULE 'A' // module for client to auto-link to
|
||||
|
||||
// NXDN
|
||||
#define NXDN_REFID 12345 // Reflector ID
|
||||
#define NXDN_PORT 41400 // UDP port
|
||||
#define NXDN_KEEPALIVE_PERIOD 3 // in seconds
|
||||
#define NXDN_KEEPALIVE_TIMEOUT (NXDN_KEEPALIVE_PERIOD*10) // in seconds
|
||||
#define NXDN_AUTOLINK_ENABLE 1 // 1 = enable, 0 = disable auto-link
|
||||
#define NXDN_AUTOLINK_MODULE 'A' // module for client to auto-link to
|
||||
|
||||
// USRP
|
||||
#define USRP_PORT 34001 // UDP port
|
||||
#define USRP_KEEPALIVE_PERIOD 1 // in seconds
|
||||
#define USRP_KEEPALIVE_TIMEOUT (USRP_KEEPALIVE_PERIOD*10) // in seconds
|
||||
#define USRP_AUTOLINK_ENABLE 1 // 1 = enable, 0 = disable auto-link
|
||||
#define USRP_AUTOLINK_MODULE 'A' // module for client to auto-link to
|
||||
#define USRP_DEFAULT_CALLSIGN "ALLSTAR"
|
||||
#define USRPCLIENTS_PATH "/home/pi/USRPClients.txt" // format ip;port; per line for each ALLSTAR/USRP node
|
||||
|
||||
#ifndef NO_G3
|
||||
// G3 Terminal
|
||||
#define G3_PRESENCE_PORT 12346 // UDP port
|
||||
#define G3_CONFIG_PORT 12345 // UDP port
|
||||
#define G3_DV_PORT 40000 // UDP port
|
||||
#define G3_KEEPALIVE_PERIOD 10 // in seconds
|
||||
#define G3_KEEPALIVE_TIMEOUT 3600 // in seconds, 1 hour
|
||||
#endif
|
||||
|
||||
// Transcoder server --------------------------------------------
|
||||
|
||||
#ifdef TRANSCODER_IP
|
||||
#define TRANSCODER_PORT 10100 // UDP port
|
||||
#endif
|
||||
#define TRANSCODER_KEEPALIVE_PERIOD 5 // in seconds
|
||||
#define TRANSCODER_KEEPALIVE_TIMEOUT 30 // in seconds
|
||||
#define TRANSCODER_AMBEPACKET_TIMEOUT 400 // in ms
|
||||
|
||||
// DMRid database -----------------------------------------------
|
||||
|
||||
// these values are now set in configure.h by ./rconfig
|
||||
//#define DMRIDDB_USE_RLX_SERVER 1 // 1 = use http, 0 = use local file
|
||||
//#define DMRIDDB_PATH "/usr/local/etc/dmrid.dat" // local file path
|
||||
//#define DMRIDDB_REFRESH_RATE 180 // in minutes
|
||||
|
||||
//NXDNid database
|
||||
#define NXDNIDDB_USE_RLX_SERVER 0 // 1 = use http, 0 = use local file
|
||||
#define NXDNIDDB_PATH "/home/pi/NXDN.csv" // local file path
|
||||
#define NXDNIDDB_REFRESH_RATE 180 // in minutes
|
||||
|
||||
// Wires-X node database ----------------------------------------
|
||||
|
||||
#define YSFNODEDB_USE_RLX_SERVER 1 // 1 = use http, 0 = use local file
|
||||
#define YSFNODEDB_PATH "/usr/local/etc/ysfnode.dat" // local file path
|
||||
#define YSFNODEDB_REFRESH_RATE 180 // in minutes
|
||||
|
||||
// xml & json reporting -----------------------------------------
|
||||
|
||||
#define LASTHEARD_USERS_MAX_SIZE 100
|
||||
#define XML_UPDATE_PERIOD 10 // in seconds
|
||||
#ifdef JSON_MONITOR
|
||||
#define JSON_UPDATE_PERIOD 10 // in seconds
|
||||
#define JSON_PORT 10001
|
||||
#endif
|
||||
|
||||
// system paths -------------------------------------------------
|
||||
#define XML_PATH "/var/log/xlxd.xml"
|
||||
#define WHITELIST_PATH "/usr/local/etc/urfd.whitelist"
|
||||
#define BLACKLIST_PATH "/usr/local/etc/urfd.blacklist"
|
||||
#define INTERLINKLIST_PATH "/usr/local/etc/urfd.interlink"
|
||||
#define TERMINALOPTIONS_PATH "/usr/local/etc/urfd.terminal"
|
||||
#define PIDFILE_PATH "/var/run/xlxd.pid"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// macros
|
||||
|
||||
#define MIN(a,b) ((a)<(b))?(a):(b)
|
||||
#define MAX(a,b) ((a)>(b))?(a):(b)
|
||||
#define MAKEWORD(low, high) ((uint16_t)(((uint8_t)(low)) | (((uint16_t)((uint8_t)(high))) << 8)))
|
||||
#define MAKEDWORD(low, high) ((uint32_t)(((uint16_t)(low)) | (((uint32_t)((uint16_t)(high))) << 16)))
|
||||
#define LOBYTE(w) ((uint8_t)(uint16_t)(w & 0x00FF))
|
||||
#define HIBYTE(w) ((uint8_t)((((uint16_t)(w)) >> 8) & 0xFF))
|
||||
#define LOWORD(dw) ((uint16_t)(uint32_t)(dw & 0x0000FFFF))
|
||||
#define HIWORD(dw) ((uint16_t)((((uint32_t)(dw)) >> 16) & 0xFFFF))
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// global objects
|
||||
|
||||
class CReflector;
|
||||
extern CReflector g_Reflector;
|
||||
|
||||
class CGateKeeper;
|
||||
extern CGateKeeper g_GateKeeper;
|
||||
|
||||
#if (DMRIDDB_USE_RLX_SERVER == 1)
|
||||
class CDmridDirHttp;
|
||||
extern CDmridDirHttp g_DmridDir;
|
||||
#else
|
||||
class CDmridDirFile;
|
||||
extern CDmridDirFile g_DmridDir;
|
||||
#endif
|
||||
|
||||
#if (NXDNIDDB_USE_RLX_SERVER == 1)
|
||||
class CNXDNidDirHttp;
|
||||
extern CNXDNidDirHttp g_NXDNidDir;
|
||||
#else
|
||||
class CNXDNidDirFile;
|
||||
extern CNXDNidDirFile g_NXDNidDir;
|
||||
#endif
|
||||
|
||||
#if (YSFNODEDB_USE_RLX_SERVER == 1)
|
||||
class CYsfNodeDirHttp;
|
||||
extern CYsfNodeDirHttp g_YsfNodeDir;
|
||||
#else
|
||||
class CYsfNodeDirFile;
|
||||
extern CYsfNodeDirFile g_YsfNodeDir;
|
||||
#endif
|
||||
@ -1,145 +0,0 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string.h>
|
||||
#include "Main.h"
|
||||
#include "Reflector.h"
|
||||
#include "NXDNIdDir.h"
|
||||
#include "NXDNIdDirFile.h"
|
||||
#include "NXDNIdDirHttp.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor & destructor
|
||||
|
||||
CNXDNidDir::CNXDNidDir()
|
||||
{
|
||||
keep_running = true;
|
||||
}
|
||||
|
||||
CNXDNidDir::~CNXDNidDir()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// init & close
|
||||
|
||||
bool CNXDNidDir::Init(void)
|
||||
{
|
||||
// load content
|
||||
Reload();
|
||||
|
||||
// reset run flag
|
||||
keep_running = true;
|
||||
|
||||
// start thread;
|
||||
m_Future = std::async(std::launch::async, &CNXDNidDir::Thread, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CNXDNidDir::Close(void)
|
||||
{
|
||||
keep_running = false;
|
||||
if ( m_Future.valid() )
|
||||
{
|
||||
m_Future.get();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// thread
|
||||
|
||||
void CNXDNidDir::Thread()
|
||||
{
|
||||
while (keep_running)
|
||||
{
|
||||
// Wait DMRIDDB_REFRESH_RATE minutes
|
||||
for (int i=0; i<30*NXDNIDDB_REFRESH_RATE && keep_running; i++)
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
|
||||
// have lists files changed ?
|
||||
if ( NeedReload() )
|
||||
{
|
||||
Reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Reload
|
||||
|
||||
bool CNXDNidDir::Reload(void)
|
||||
{
|
||||
CBuffer buffer;
|
||||
bool ok = false;
|
||||
|
||||
if ( LoadContent(&buffer) )
|
||||
{
|
||||
Lock();
|
||||
{
|
||||
ok = RefreshContent(buffer);
|
||||
}
|
||||
Unlock();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// find
|
||||
|
||||
const CCallsign *CNXDNidDir::FindCallsign(uint16_t nxdnid)
|
||||
{
|
||||
auto found = m_CallsignMap.find(nxdnid);
|
||||
if ( found != m_CallsignMap.end() )
|
||||
{
|
||||
return &(found->second);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint16_t CNXDNidDir::FindNXDNid(const CCallsign &callsign)
|
||||
{
|
||||
auto found = m_NXDNidMap.find(callsign);
|
||||
if ( found != m_NXDNidMap.end() )
|
||||
{
|
||||
return (found->second);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// syntax helpers
|
||||
|
||||
bool CNXDNidDir::IsValidNXDNid(const char *sz)
|
||||
{
|
||||
bool ok = false;
|
||||
size_t n = ::strlen(sz);
|
||||
if ( (n > 0) && (n <= 5) )
|
||||
{
|
||||
ok = true;
|
||||
for ( size_t i = 0; (i < n) && ok; i++ )
|
||||
{
|
||||
ok &= ::isdigit(sz[i]);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
@ -1,86 +0,0 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
// Copyright © 2021 Doug McLain AD8DP
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include "Buffer.h"
|
||||
#include "Callsign.h"
|
||||
|
||||
// compare function for std::map::find
|
||||
|
||||
struct CNXDNidDirCallsignCompare
|
||||
{
|
||||
bool operator() (const CCallsign &cs1, const CCallsign &cs2) const
|
||||
{ return cs1.HasLowerCallsign(cs2);}
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
class CNXDNidDir
|
||||
{
|
||||
public:
|
||||
// constructor
|
||||
CNXDNidDir();
|
||||
|
||||
// destructor
|
||||
~CNXDNidDir();
|
||||
|
||||
// init & close
|
||||
virtual bool Init(void);
|
||||
virtual void Close(void);
|
||||
|
||||
// locks
|
||||
void Lock(void) { m_Mutex.lock(); }
|
||||
void Unlock(void) { m_Mutex.unlock(); }
|
||||
|
||||
// refresh
|
||||
virtual bool LoadContent(CBuffer *) { return false; }
|
||||
virtual bool RefreshContent(const CBuffer &) { return false; }
|
||||
|
||||
// find
|
||||
const CCallsign *FindCallsign(uint16_t);
|
||||
uint16_t FindNXDNid(const CCallsign &);
|
||||
|
||||
protected:
|
||||
// thread
|
||||
void Thread();
|
||||
|
||||
// reload helpers
|
||||
bool Reload(void);
|
||||
virtual bool NeedReload(void) { return false; }
|
||||
bool IsValidNXDNid(const char *);
|
||||
|
||||
protected:
|
||||
// data
|
||||
std::map <uint16_t, CCallsign> m_CallsignMap;
|
||||
std::map <CCallsign, uint16_t, CNXDNidDirCallsignCompare> m_NXDNidMap;
|
||||
|
||||
// Lock()
|
||||
std::mutex m_Mutex;
|
||||
|
||||
// thread
|
||||
std::atomic<bool> keep_running;
|
||||
std::future<void> m_Future;
|
||||
|
||||
};
|
||||
@ -1,159 +0,0 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include "Main.h"
|
||||
#include "NXDNIdDirFile.h"
|
||||
|
||||
|
||||
#if (NXDNIDDB_USE_RLX_SERVER == 0)
|
||||
CNXDNidDirFile g_NXDNidDir;
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor & destructor
|
||||
|
||||
CNXDNidDirFile::CNXDNidDirFile()
|
||||
{
|
||||
memset(&m_LastModTime, 0, sizeof(time_t));
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// init & close
|
||||
|
||||
bool CNXDNidDirFile::Init(void)
|
||||
{
|
||||
return CNXDNidDir::Init();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// refresh
|
||||
|
||||
bool CNXDNidDirFile::NeedReload(void)
|
||||
{
|
||||
bool needReload = false;
|
||||
|
||||
time_t time;
|
||||
if ( GetLastModTime(&time) )
|
||||
{
|
||||
needReload = time != m_LastModTime;
|
||||
}
|
||||
return needReload;
|
||||
}
|
||||
|
||||
bool CNXDNidDirFile::LoadContent(CBuffer *buffer)
|
||||
{
|
||||
bool ok = false;
|
||||
std::ifstream file;
|
||||
std::streampos size;
|
||||
|
||||
// open file
|
||||
file.open(NXDNIDDB_PATH, std::ios::in | std::ios::binary | std::ios::ate);
|
||||
if ( file.is_open() )
|
||||
{
|
||||
// read file
|
||||
size = file.tellg();
|
||||
if ( size > 0 )
|
||||
{
|
||||
// read file into buffer
|
||||
buffer->resize((int)size+1);
|
||||
file.seekg (0, std::ios::beg);
|
||||
file.read((char *)buffer->data(), (int)size);
|
||||
|
||||
// close file
|
||||
file.close();
|
||||
|
||||
// update time
|
||||
GetLastModTime(&m_LastModTime);
|
||||
|
||||
// done
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool CNXDNidDirFile::RefreshContent(const CBuffer &buffer)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
// clear directory
|
||||
m_CallsignMap.clear();
|
||||
m_NXDNidMap.clear();
|
||||
|
||||
// scan buffer
|
||||
if ( buffer.size() > 0 )
|
||||
{
|
||||
// crack it
|
||||
char *ptr1 = (char *)buffer.data();
|
||||
char *ptr2;
|
||||
|
||||
// get next line
|
||||
while ( (ptr2 = ::strchr(ptr1, '\n')) != nullptr )
|
||||
{
|
||||
*ptr2 = 0;
|
||||
// get items
|
||||
char *nxdnid;
|
||||
char *callsign;
|
||||
if ( ((nxdnid = ::strtok(ptr1, ",")) != nullptr) && IsValidNXDNid(nxdnid) )
|
||||
{
|
||||
if ( ((callsign = ::strtok(nullptr, ",")) != nullptr) )
|
||||
{
|
||||
// new entry
|
||||
uint16_t us = atoi(nxdnid);
|
||||
CCallsign cs(callsign, 0, us);
|
||||
if ( cs.IsValid() )
|
||||
{
|
||||
m_CallsignMap.insert(std::pair<uint32_t,CCallsign>(us, cs));
|
||||
m_NXDNidMap.insert(std::pair<CCallsign,uint32_t>(cs,us));
|
||||
}
|
||||
}
|
||||
}
|
||||
// next line
|
||||
ptr1 = ptr2+1;
|
||||
}
|
||||
|
||||
// done
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// report
|
||||
std::cout << "Read " << m_NXDNidMap.size() << " NXDN ids from file " << NXDNIDDB_PATH << std::endl;
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
bool CNXDNidDirFile::GetLastModTime(time_t *time)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
struct stat fileStat;
|
||||
if( ::stat(NXDNIDDB_PATH, &fileStat) != -1 )
|
||||
{
|
||||
*time = fileStat.st_mtime;
|
||||
ok = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
@ -1,177 +0,0 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string.h>
|
||||
#include "Main.h"
|
||||
#include "Reflector.h"
|
||||
#include "NXDNIdDirHttp.h"
|
||||
|
||||
#if (NXDNIDDB_USE_RLX_SERVER == 1)
|
||||
CNXDNidDirHttp g_NXDNidDir;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// refresh
|
||||
|
||||
bool CNXDNidDirHttp::LoadContent(CBuffer *buffer)
|
||||
{
|
||||
// get file from xlxapi server
|
||||
return HttpGet("www.dudetronics.com", "ar-dns/NXDN.csv", 80, buffer);
|
||||
}
|
||||
|
||||
bool CNXDNidDirHttp::RefreshContent(const CBuffer &buffer)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
// clear directory
|
||||
m_CallsignMap.clear();
|
||||
m_NXDNidMap.clear();
|
||||
|
||||
// scan file
|
||||
if ( buffer.size() > 0 )
|
||||
{
|
||||
char *ptr1 = (char *)buffer.data();
|
||||
char *ptr2;
|
||||
// get next line
|
||||
while ( (ptr2 = ::strchr(ptr1, '\n')) != nullptr )
|
||||
{
|
||||
std::cout << "newline: " << std::string(ptr2) << std::endl;
|
||||
*ptr2 = 0;
|
||||
// get items
|
||||
char *nxdnid;
|
||||
char *callsign;
|
||||
if ( ((nxdnid = ::strtok(ptr1, ",")) != nullptr) && IsValidNXDNid(nxdnid) )
|
||||
{
|
||||
if ( ((callsign = ::strtok(nullptr, ",")) != nullptr) )
|
||||
{
|
||||
// new entry
|
||||
uint16_t us = atoi(nxdnid);
|
||||
CCallsign cs(callsign, 0, us);
|
||||
if ( cs.IsValid() )
|
||||
{
|
||||
m_CallsignMap.insert(std::pair<uint16_t,CCallsign>(us, cs));
|
||||
m_NXDNidMap.insert(std::pair<CCallsign,uint16_t>(cs,us));
|
||||
}
|
||||
}
|
||||
}
|
||||
// next line
|
||||
ptr1 = ptr2+1;
|
||||
}
|
||||
// done
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// report
|
||||
std::cout << "Read " << m_NXDNidMap.size() << " NXDN ids from xlxapi.rlx.lu database " << std::endl;
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// httpd helpers
|
||||
|
||||
#define NXDNID_HTTPGET_SIZEMAX (256)
|
||||
|
||||
bool CNXDNidDirHttp::HttpGet(const char *hostname, const char *filename, int port, CBuffer *buffer)
|
||||
{
|
||||
bool ok = false;
|
||||
int sock_id;
|
||||
|
||||
// open socket
|
||||
if ( (sock_id = ::socket(AF_INET, SOCK_STREAM, 0)) >= 0 )
|
||||
{
|
||||
// get hostname address
|
||||
struct sockaddr_in servaddr;
|
||||
struct hostent *hp;
|
||||
memset(&servaddr,0,sizeof(servaddr));
|
||||
if( (hp = gethostbyname(hostname)) != nullptr )
|
||||
{
|
||||
// dns resolved
|
||||
memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length);
|
||||
servaddr.sin_port = htons(port);
|
||||
servaddr.sin_family = AF_INET;
|
||||
|
||||
// connect
|
||||
if ( ::connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0)
|
||||
{
|
||||
// send the GET request
|
||||
char request[NXDNID_HTTPGET_SIZEMAX];
|
||||
::sprintf(request, "GET /%s HTTP/1.0\r\nFrom: %s\r\nUser-Agent: urfd\r\n\r\n",
|
||||
filename, (const char *)g_Reflector.GetCallsign());
|
||||
::write(sock_id, request, strlen(request));
|
||||
|
||||
// config receive timeouts
|
||||
fd_set read_set;
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 5;
|
||||
timeout.tv_usec = 0;
|
||||
FD_ZERO(&read_set);
|
||||
FD_SET(sock_id, &read_set);
|
||||
|
||||
// get the reply back
|
||||
buffer->clear();
|
||||
bool done = false;
|
||||
do
|
||||
{
|
||||
char buf[1440];
|
||||
ssize_t len = 0;
|
||||
select(sock_id+1, &read_set, nullptr, nullptr, &timeout);
|
||||
//if ( (ret > 0) || ((ret < 0) && (errno == EINPROGRESS)) )
|
||||
//if ( ret >= 0 )
|
||||
//{
|
||||
usleep(5000);
|
||||
len = read(sock_id, buf, 1440);
|
||||
if ( len > 0 )
|
||||
{
|
||||
buffer->Append((uint8_t *)buf, (int)len);
|
||||
ok = true;
|
||||
}
|
||||
//}
|
||||
done = (len <= 0);
|
||||
|
||||
}
|
||||
while (!done);
|
||||
buffer->Append((uint8_t)0);
|
||||
|
||||
// and disconnect
|
||||
close(sock_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Cannot establish connection with host " << hostname << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Host " << hostname << " not found" << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Failed to open wget socket" << std::endl;
|
||||
}
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
@ -1,73 +1,89 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
// Copyright © 2023 Thomas A. Early N7TAE.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of m17ref.
|
||||
//
|
||||
// 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
// m17ref 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 3 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.
|
||||
// m17ref 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, see <https://www.gnu.org/licenses/>.
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// with this software. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#include "Main.h"
|
||||
#include "Version.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor
|
||||
CVersion::CVersion() : version(0) {}
|
||||
|
||||
CVersion::CVersion(uint8_t maj, uint8_t min, uint8_t rev)
|
||||
{
|
||||
Set(maj, min, rev);
|
||||
}
|
||||
|
||||
int CVersion::GetMajor(void) const
|
||||
{
|
||||
return version / 0x10000;
|
||||
}
|
||||
|
||||
int CVersion::GetMinor(void) const
|
||||
{
|
||||
return version / 0x100 % 0x100;
|
||||
}
|
||||
|
||||
int CVersion::GetRevision(void) const
|
||||
{
|
||||
return version % 0x100;
|
||||
}
|
||||
|
||||
CVersion::CVersion()
|
||||
int CVersion::GetVersion(void) const
|
||||
{
|
||||
m_iMajor = 0;
|
||||
m_iMinor = 0;
|
||||
m_iRevision = 0;
|
||||
return version;
|
||||
}
|
||||
|
||||
CVersion::CVersion(int iMajor, int iMinor, int iRevision)
|
||||
void CVersion::Set(uint8_t maj, uint8_t min, uint8_t rev)
|
||||
{
|
||||
m_iMajor = iMajor;
|
||||
m_iMinor = iMinor;
|
||||
m_iRevision = iRevision;
|
||||
version = 0x10000*maj + 0x100*min + rev;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// comparaison
|
||||
bool CVersion::operator ==(const CVersion &v) const
|
||||
{
|
||||
return v.version == version;
|
||||
};
|
||||
|
||||
bool CVersion::operator !=(const CVersion &v) const
|
||||
{
|
||||
return v.version != version;
|
||||
};
|
||||
|
||||
bool CVersion::operator >=(const CVersion &v) const
|
||||
{
|
||||
return v.version >= version;
|
||||
}
|
||||
|
||||
bool CVersion::IsEqualOrHigherTo(const CVersion &version) const
|
||||
bool CVersion::operator <=(const CVersion &v) const
|
||||
{
|
||||
if ( m_iMajor > version.m_iMajor )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if ( m_iMajor == version.m_iMajor )
|
||||
{
|
||||
if ( m_iMinor > version.m_iMinor )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if ( m_iMinor == version.m_iMinor )
|
||||
{
|
||||
if ( m_iRevision >= version.m_iRevision )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return v.version <= version;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// operator
|
||||
bool CVersion::operator >(const CVersion &v) const
|
||||
{
|
||||
return v.version > version;
|
||||
}
|
||||
|
||||
bool CVersion::operator ==(const CVersion &Version) const
|
||||
bool CVersion::operator <(const CVersion &v) const
|
||||
{
|
||||
return ( (Version.m_iMajor == m_iMajor) &&
|
||||
(Version.m_iMinor == m_iMinor) &&
|
||||
(Version.m_iRevision == m_iRevision )) ;
|
||||
return v.version < version;
|
||||
}
|
||||
|
||||
// output
|
||||
std::ostream &operator <<(std::ostream &os, const CVersion &v)
|
||||
{
|
||||
os << v.GetMajor() << '.' << v.GetMinor() << '.' << v.GetRevision();
|
||||
return os;
|
||||
};
|
||||
|
||||
@ -1,44 +1,56 @@
|
||||
// Copyright © 2015 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
// Copyright © 2023 Thomas A. Early N7TAE.
|
||||
//
|
||||
// ----------------------------------------------------------------------------
|
||||
// This file is part of m17ref.
|
||||
//
|
||||
// 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 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
// m17ref 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 3 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.
|
||||
// m17ref 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, see <https://www.gnu.org/licenses/>.
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// with this software. If not, see <http://www.gnu.org/licenses/>.
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
|
||||
class CVersion
|
||||
{
|
||||
public:
|
||||
// constructor
|
||||
// constructors
|
||||
CVersion();
|
||||
CVersion(int, int, int);
|
||||
CVersion(uint8_t maj, uint8_t min, uint8_t rev);
|
||||
|
||||
// get
|
||||
int GetMajor(void) const { return m_iMajor; }
|
||||
int GetMinor(void) const { return m_iMinor; }
|
||||
int GetRevision(void) const { return m_iRevision; }
|
||||
int GetMajor(void) const;
|
||||
int GetMinor(void) const;
|
||||
int GetRevision(void) const;
|
||||
int GetVersion(void) const;
|
||||
|
||||
// set
|
||||
void Set(uint8_t, uint8_t, uint8_t);
|
||||
|
||||
// comparaison operators
|
||||
bool operator ==(const CVersion &v) const;
|
||||
bool operator !=(const CVersion &v) const;
|
||||
bool operator >=(const CVersion &v) const;
|
||||
bool operator <=(const CVersion &v) const;
|
||||
bool operator >(const CVersion &v) const;
|
||||
bool operator <(const CVersion &v) const;
|
||||
|
||||
// comparaison
|
||||
bool IsEqualOrHigherTo(const CVersion &) const;
|
||||
// output
|
||||
friend std::ostream &operator <<(std::ostream &os, const CVersion &v);
|
||||
|
||||
// operator
|
||||
bool operator ==(const CVersion &) const;
|
||||
|
||||
protected:
|
||||
// data
|
||||
int m_iMajor;
|
||||
int m_iMinor;
|
||||
int m_iRevision;
|
||||
int version;
|
||||
};
|
||||
|
||||
@ -1,176 +0,0 @@
|
||||
// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string.h>
|
||||
#include <mysql/mysql.h>
|
||||
#include "Main.h"
|
||||
#include "Reflector.h"
|
||||
#include "YSFNodeDir.h"
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor & destructor
|
||||
|
||||
CYsfNodeDir::CYsfNodeDir()
|
||||
{
|
||||
keep_running = true;
|
||||
}
|
||||
|
||||
CYsfNodeDir::~CYsfNodeDir()
|
||||
{
|
||||
// kill threads
|
||||
Close();
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// init & close
|
||||
|
||||
bool CYsfNodeDir::Init(void)
|
||||
{
|
||||
// load content
|
||||
Reload();
|
||||
|
||||
// reset run flag
|
||||
keep_running = true;
|
||||
|
||||
// start thread;
|
||||
m_Future = std::async(std::launch::async, &CYsfNodeDir::Thread, this);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CYsfNodeDir::Close(void)
|
||||
{
|
||||
keep_running = false;
|
||||
if ( m_Future.valid() )
|
||||
{
|
||||
m_Future.get();
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// thread
|
||||
|
||||
void CYsfNodeDir::Thread()
|
||||
{
|
||||
while (keep_running)
|
||||
{
|
||||
// Wait YSFNODEDB_REFRESH_RATE minutes
|
||||
for (int i=0; keep_running && (i < 30*YSFNODEDB_REFRESH_RATE); i++)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
|
||||
#if YSF_DB_SUPPORT==true
|
||||
if (keep_running && (0 == i % 450))
|
||||
{
|
||||
ReadDb(); // update from the db every 15 minutes
|
||||
}
|
||||
#endif
|
||||
}
|
||||
// have lists files changed ?
|
||||
if (keep_running && NeedReload())
|
||||
{
|
||||
Reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Reload
|
||||
|
||||
bool CYsfNodeDir::Reload(void)
|
||||
{
|
||||
CBuffer buffer;
|
||||
bool ok = false;
|
||||
|
||||
if ( LoadContent(&buffer) )
|
||||
{
|
||||
Lock();
|
||||
ok = RefreshContent(buffer);
|
||||
Unlock();
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// find
|
||||
|
||||
bool CYsfNodeDir::FindFrequencies(const CCallsign &callsign, uint32_t *txfreq, uint32_t *rxfreq)
|
||||
{
|
||||
auto found = find(callsign);
|
||||
if ( found != end() )
|
||||
{
|
||||
*txfreq = found->second.GetTxFrequency();
|
||||
*rxfreq = found->second.GetRxFrequency();
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
*txfreq = YSF_DEFAULT_NODE_TX_FREQ;
|
||||
*rxfreq = YSF_DEFAULT_NODE_RX_FREQ;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
#if YSF_DB_SUPPORT==true
|
||||
void CYsfNodeDir::ReadDb()
|
||||
{
|
||||
MYSQL *con = mysql_init(NULL);
|
||||
if (con)
|
||||
{
|
||||
if (mysql_real_connect(con, "localhost", YSF_DB_USER, YSF_DB_PASSWORD, YSF_DB_NAME, 0, NULL, 0))
|
||||
{
|
||||
if (0 == mysql_query(con, "SELECT callsign,txfreq,rxfreq FROM ysfnodes"))
|
||||
{
|
||||
MYSQL_RES *result = mysql_store_result(con);
|
||||
if (result)
|
||||
{
|
||||
std::cout << "Adding " << mysql_num_rows(result) << " registered YSF stations from database " << YSF_DB_NAME << std::endl;
|
||||
MYSQL_ROW row;
|
||||
|
||||
while ((row = mysql_fetch_row(result)))
|
||||
{
|
||||
CCallsign cs(row[0]);
|
||||
CYsfNode node(atoi(row[1]), atoi(row[2]));
|
||||
m_map[cs] = node;
|
||||
}
|
||||
|
||||
mysql_free_result(result);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Could not fetch MySQL rows" << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "MySQL query failed: " << mysql_error(con) << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Could not connect to database " << YSF_DB_NAME << ": " << mysql_error(con) << std::endl;
|
||||
}
|
||||
mysql_close(con);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Could not init mysql." << std::endl;;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -1,94 +0,0 @@
|
||||
// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include "Buffer.h"
|
||||
#include "Callsign.h"
|
||||
#include "YSFNode.h"
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// compare function for std::map::find
|
||||
|
||||
struct CYsfNodeDirCallsignCompare
|
||||
{
|
||||
bool operator() (const CCallsign &cs1, const CCallsign &cs2) const
|
||||
{ return cs1.HasLowerCallsign(cs2);}
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// class
|
||||
|
||||
using CsNodeMap = std::map<CCallsign, CYsfNode, CYsfNodeDirCallsignCompare>;
|
||||
|
||||
class CYsfNodeDir
|
||||
{
|
||||
public:
|
||||
// constructor
|
||||
CYsfNodeDir();
|
||||
// destructor
|
||||
virtual ~CYsfNodeDir();
|
||||
|
||||
// init & close
|
||||
virtual bool Init(void);
|
||||
virtual void Close(void);
|
||||
|
||||
// locks
|
||||
void Lock(void) { m_Mutex.lock(); }
|
||||
void Unlock(void) { m_Mutex.unlock(); }
|
||||
|
||||
// refresh
|
||||
virtual bool LoadContent(CBuffer *) { return false; }
|
||||
virtual bool RefreshContent(const CBuffer &) { return false; }
|
||||
|
||||
// find
|
||||
bool FindFrequencies(const CCallsign &, uint32_t *, uint32_t *);
|
||||
|
||||
// pass-thru
|
||||
void clear() { m_map.clear(); }
|
||||
size_t size() { return m_map.size(); }
|
||||
CsNodeMap::iterator find(const CCallsign &cs) { return m_map.find(cs); }
|
||||
CsNodeMap::iterator end() { return m_map.end(); }
|
||||
std::pair<CsNodeMap::iterator, bool> insert(const std::pair<CCallsign, CYsfNode> &pair) { return m_map.insert(pair); }
|
||||
|
||||
protected:
|
||||
// thread
|
||||
void Thread();
|
||||
|
||||
// reload helpers
|
||||
bool Reload(void);
|
||||
virtual bool NeedReload(void) { return false; }
|
||||
#if YSF_DB_SUPPORT==true
|
||||
void ReadDb(void);
|
||||
#endif
|
||||
//bool IsValidDmrid(const char *);
|
||||
|
||||
|
||||
protected:
|
||||
// Lock()
|
||||
std::mutex m_Mutex;
|
||||
|
||||
// thread
|
||||
std::atomic<bool> keep_running;
|
||||
std::future<void> m_Future;
|
||||
CsNodeMap m_map;
|
||||
};
|
||||
@ -1,161 +0,0 @@
|
||||
// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include "Main.h"
|
||||
#include "YSFNodeDirFile.h"
|
||||
|
||||
|
||||
#if (YSFNODEDB_USE_RLX_SERVER == 0)
|
||||
CYsfNodeDirFile g_YsfNodeDir;
|
||||
#endif
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// constructor & destructor
|
||||
|
||||
CYsfNodeDirFile::CYsfNodeDirFile()
|
||||
{
|
||||
memset(&m_LastModTime, 0, sizeof(time_t));
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// init & close
|
||||
|
||||
bool CYsfNodeDirFile::Init(void)
|
||||
{
|
||||
return CYsfNodeDir::Init();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// refresh
|
||||
|
||||
bool CYsfNodeDirFile::NeedReload(void)
|
||||
{
|
||||
bool needReload = false;
|
||||
|
||||
time_t time;
|
||||
if ( GetLastModTime(&time) )
|
||||
{
|
||||
needReload = time != m_LastModTime;
|
||||
}
|
||||
return needReload;
|
||||
}
|
||||
|
||||
bool CYsfNodeDirFile::LoadContent(CBuffer *buffer)
|
||||
{
|
||||
bool ok = false;
|
||||
std::ifstream file;
|
||||
std::streampos size;
|
||||
|
||||
// open file
|
||||
file.open(YSFNODEDB_PATH, std::ios::in | std::ios::binary | std::ios::ate);
|
||||
if ( file.is_open() )
|
||||
{
|
||||
// read file
|
||||
size = file.tellg();
|
||||
if ( size > 0 )
|
||||
{
|
||||
// read file into buffer
|
||||
buffer->resize((int)size+1);
|
||||
file.seekg (0, std::ios::beg);
|
||||
file.read((char *)buffer->data(), (int)size);
|
||||
|
||||
// close file
|
||||
file.close();
|
||||
|
||||
// update time
|
||||
GetLastModTime(&m_LastModTime);
|
||||
|
||||
// done
|
||||
ok = true;
|
||||
}
|
||||
}
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool CYsfNodeDirFile::RefreshContent(const CBuffer &buffer)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
// clear directory
|
||||
clear();
|
||||
|
||||
// scan buffer
|
||||
if ( buffer.size() > 0 )
|
||||
{
|
||||
// crack it
|
||||
char *ptr1 = (char *)buffer.data();
|
||||
char *ptr2;
|
||||
|
||||
// get next line
|
||||
while ( (ptr2 = ::strchr(ptr1, '\n')) != nullptr )
|
||||
{
|
||||
*ptr2 = 0;
|
||||
// get items
|
||||
char *callsign;
|
||||
char *txfreq;
|
||||
char *rxfreq;
|
||||
if ( ((callsign = ::strtok(ptr1, ";")) != nullptr) )
|
||||
{
|
||||
if ( ((txfreq = ::strtok(nullptr, ";")) != nullptr) )
|
||||
{
|
||||
if ( ((rxfreq = ::strtok(nullptr, ";")) != nullptr) )
|
||||
{
|
||||
// new entry
|
||||
CCallsign cs(callsign);
|
||||
CYsfNode node(atoi(txfreq), atoi(rxfreq));
|
||||
if ( cs.IsValid() && node.IsValid() )
|
||||
{
|
||||
insert(std::pair<CCallsign, CYsfNode>(cs, node));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// next line
|
||||
ptr1 = ptr2+1;
|
||||
}
|
||||
|
||||
// done
|
||||
ok = true;
|
||||
}
|
||||
|
||||
|
||||
// report
|
||||
std::cout << "Read " << size() << " YSF nodes from file " << YSFNODEDB_PATH << std::endl;
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
bool CYsfNodeDirFile::GetLastModTime(time_t *time)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
struct stat fileStat;
|
||||
if( ::stat(YSFNODEDB_PATH, &fileStat) != -1 )
|
||||
{
|
||||
*time = fileStat.st_mtime;
|
||||
ok = true;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "YSFNodeDir.h"
|
||||
|
||||
class CYsfNodeDirFile : public CYsfNodeDir
|
||||
{
|
||||
public:
|
||||
// constructor
|
||||
CYsfNodeDirFile();
|
||||
|
||||
// destructor
|
||||
~CYsfNodeDirFile() {}
|
||||
|
||||
// init & close
|
||||
bool Init(void);
|
||||
|
||||
// refresh
|
||||
bool LoadContent(CBuffer *);
|
||||
bool RefreshContent(const CBuffer &);
|
||||
|
||||
protected:
|
||||
// reload helpers
|
||||
bool NeedReload(void);
|
||||
bool GetLastModTime(time_t *);
|
||||
|
||||
protected:
|
||||
// data
|
||||
time_t m_LastModTime;
|
||||
};
|
||||
@ -1,179 +0,0 @@
|
||||
// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#include <string.h>
|
||||
#include "Main.h"
|
||||
#include "Reflector.h"
|
||||
#include "YSFNodeDirHttp.h"
|
||||
|
||||
#if (YSFNODEDB_USE_RLX_SERVER == 1)
|
||||
CYsfNodeDirHttp g_YsfNodeDir;
|
||||
#endif
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// refresh
|
||||
|
||||
bool CYsfNodeDirHttp::LoadContent(CBuffer *buffer)
|
||||
{
|
||||
// get file from xlxapi server
|
||||
return HttpGet("xlxapi.rlx.lu", "api/exportysfrepeaters.php", 80, buffer);
|
||||
}
|
||||
|
||||
bool CYsfNodeDirHttp::RefreshContent(const CBuffer &buffer)
|
||||
{
|
||||
bool ok = false;
|
||||
|
||||
// clear directory
|
||||
clear();
|
||||
|
||||
// scan buffer
|
||||
if ( buffer.size() > 0 )
|
||||
{
|
||||
// crack it
|
||||
char *ptr1 = (char *)buffer.data();
|
||||
char *ptr2;
|
||||
|
||||
// get next line
|
||||
while ( (ptr2 = ::strchr(ptr1, '\n')) != nullptr )
|
||||
{
|
||||
*ptr2 = 0;
|
||||
// get items
|
||||
char *callsign;
|
||||
char *txfreq;
|
||||
char *rxfreq;
|
||||
if ( ((callsign = ::strtok(ptr1, ";")) != nullptr) )
|
||||
{
|
||||
if ( ((txfreq = ::strtok(nullptr, ";")) != nullptr) )
|
||||
{
|
||||
if ( ((rxfreq = ::strtok(nullptr, ";")) != nullptr) )
|
||||
{
|
||||
// new entry
|
||||
CCallsign cs(callsign);
|
||||
CYsfNode node(atoi(txfreq), atoi(rxfreq));
|
||||
if ( cs.IsValid() && node.IsValid() )
|
||||
{
|
||||
insert(std::pair<CCallsign, CYsfNode>(cs, node));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// next line
|
||||
ptr1 = ptr2+1;
|
||||
}
|
||||
|
||||
// done
|
||||
ok = true;
|
||||
}
|
||||
|
||||
// report
|
||||
std::cout << "Read " << size() << " YSF nodes from xlxapi.rlx.lu database " << std::endl;
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////
|
||||
// httpd helpers
|
||||
|
||||
#define YSFNODE_HTTPGET_SIZEMAX (256)
|
||||
|
||||
bool CYsfNodeDirHttp::HttpGet(const char *hostname, const char *filename, int port, CBuffer *buffer)
|
||||
{
|
||||
bool ok = false;
|
||||
int sock_id;
|
||||
|
||||
// open socket
|
||||
if ( (sock_id = ::socket(AF_INET, SOCK_STREAM, 0)) >= 0 )
|
||||
{
|
||||
// get hostname address
|
||||
struct sockaddr_in servaddr;
|
||||
struct hostent *hp;
|
||||
memset(&servaddr,0,sizeof(servaddr));
|
||||
if( (hp = gethostbyname(hostname)) != nullptr )
|
||||
{
|
||||
// dns resolved
|
||||
memcpy((char *)&servaddr.sin_addr.s_addr, (char *)hp->h_addr, hp->h_length);
|
||||
servaddr.sin_port = htons(port);
|
||||
servaddr.sin_family = AF_INET;
|
||||
|
||||
// connect
|
||||
if ( ::connect(sock_id, (struct sockaddr *)&servaddr, sizeof(servaddr)) == 0)
|
||||
{
|
||||
// send the GET request
|
||||
char request[YSFNODE_HTTPGET_SIZEMAX];
|
||||
::sprintf(request, "GET /%s HTTP/1.0\r\nFrom: %s\r\nUser-Agent: urfd\r\n\r\n",
|
||||
filename, (const char *)g_Reflector.GetCallsign());
|
||||
::write(sock_id, request, strlen(request));
|
||||
|
||||
// config receive timeouts
|
||||
fd_set read_set;
|
||||
struct timeval timeout;
|
||||
timeout.tv_sec = 5;
|
||||
timeout.tv_usec = 0;
|
||||
FD_ZERO(&read_set);
|
||||
FD_SET(sock_id, &read_set);
|
||||
|
||||
// get the reply back
|
||||
buffer->clear();
|
||||
bool done = false;
|
||||
do
|
||||
{
|
||||
char buf[1440];
|
||||
ssize_t len = 0;
|
||||
select(sock_id+1, &read_set, nullptr, nullptr, &timeout);
|
||||
//if ( (ret > 0) || ((ret < 0) && (errno == EINPROGRESS)) )
|
||||
//if ( ret >= 0 )
|
||||
//{
|
||||
usleep(5000);
|
||||
len = read(sock_id, buf, 1440);
|
||||
if ( len > 0 )
|
||||
{
|
||||
buffer->Append((uint8_t *)buf, (int)len);
|
||||
ok = true;
|
||||
}
|
||||
//}
|
||||
done = (len <= 0);
|
||||
|
||||
}
|
||||
while (!done);
|
||||
buffer->Append((uint8_t)0);
|
||||
|
||||
// and disconnect
|
||||
close(sock_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Cannot establish connection with host " << hostname << std::endl;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Host " << hostname << " not found" << std::endl;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Failed to open wget socket" << std::endl;
|
||||
}
|
||||
|
||||
// done
|
||||
return ok;
|
||||
}
|
||||
@ -1,40 +0,0 @@
|
||||
// Copyright © 2019 Jean-Luc Deltombe (LX3JL). All rights reserved.
|
||||
|
||||
// urfd -- The universal reflector
|
||||
// Copyright © 2021 Thomas A. Early N7TAE
|
||||
//
|
||||
// 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 3 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, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "YSFNodeDir.h"
|
||||
|
||||
class CYsfNodeDirHttp : public CYsfNodeDir
|
||||
{
|
||||
public:
|
||||
// constructor
|
||||
CYsfNodeDirHttp() {}
|
||||
|
||||
// destructor
|
||||
~CYsfNodeDirHttp() {}
|
||||
|
||||
// refresh
|
||||
bool LoadContent(CBuffer *);
|
||||
bool RefreshContent(const CBuffer &);
|
||||
|
||||
protected:
|
||||
// reload helpers
|
||||
bool NeedReload(void) { return true; }
|
||||
bool HttpGet(const char *, const char *, int, CBuffer *);
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue