mirror of https://github.com/nostar/urfd.git
Merge pull request #1 from n7tae/main
run-time config nlohmann's json lib, codecstream improvements, readme reworkpull/8/head
commit
37efcb21fe
@ -1,13 +1,7 @@
|
|||||||
*.o
|
*.o
|
||||||
*.d
|
*.d
|
||||||
.vscode
|
.vscode
|
||||||
reflector/urfd.blacklist
|
reflector/urfd.*
|
||||||
reflector/urfd.whitelist
|
|
||||||
reflector/urfd.interlink
|
|
||||||
reflector/urfd.terminal
|
|
||||||
configure.mk
|
|
||||||
configure.h
|
|
||||||
configure.sql
|
|
||||||
reflector.cfg
|
|
||||||
wiresx/configure.php
|
|
||||||
urfd
|
urfd
|
||||||
|
inicheck
|
||||||
|
dbutil
|
||||||
|
|||||||
@ -0,0 +1,129 @@
|
|||||||
|
###### URFD CONFIGURATION Example ######
|
||||||
|
# comments begin with '#'
|
||||||
|
# Do not use quotes, unless in a comment!
|
||||||
|
|
||||||
|
[Names]
|
||||||
|
Callsign = URF??? # where ? is A-Z or 0-9
|
||||||
|
|
||||||
|
SysopEmail = me@somewhere.com
|
||||||
|
|
||||||
|
# 2-letter country codes are listed at https://www.iban.com/country-codes
|
||||||
|
Country = GB
|
||||||
|
|
||||||
|
Sponsor = My Home Club
|
||||||
|
|
||||||
|
[IP Addresses]
|
||||||
|
# Binding addresses are usually the 'any' address
|
||||||
|
IPv4Binding = 0.0.0.0
|
||||||
|
|
||||||
|
# define IPv6 if you want a "dual-stack" reflector
|
||||||
|
# IPv6Binding = ::
|
||||||
|
|
||||||
|
# define if you want to override what urfd finds using ipv4.icanhazip.com
|
||||||
|
# IPv4External = 4.3.2.1
|
||||||
|
|
||||||
|
# define if you want to override what urfd finds using ipv6.icanhazip.com
|
||||||
|
# IPv6External = f:e:d:c:b:a:9:0
|
||||||
|
|
||||||
|
Transcoder = local # SORRY, but only local TC's are supported right now!
|
||||||
|
|
||||||
|
[Modules]
|
||||||
|
# Modules = ABCDEFGHIJKLMNOPQRSTUVWXYZ
|
||||||
|
Modules = ADMSZ
|
||||||
|
Transcoded = A # comment out if you don't have transcoding hardware
|
||||||
|
# Create Descriptions as needed...
|
||||||
|
DescriptionA = Transcoded
|
||||||
|
DescriptionD = DMR Chat
|
||||||
|
DescriptionM = M17 Chat
|
||||||
|
DescriptionS = DStar Chat
|
||||||
|
DescriptionZ = Temp Meeting
|
||||||
|
|
||||||
|
# Protocols
|
||||||
|
[Brandmeister]
|
||||||
|
Enable = false # Set to true if you've configured BM connections in your urfd.interlink file.
|
||||||
|
Port = 10002
|
||||||
|
|
||||||
|
[DCS]
|
||||||
|
Port = 30051
|
||||||
|
|
||||||
|
[DExtra]
|
||||||
|
Port = 30001
|
||||||
|
|
||||||
|
[DPlus]
|
||||||
|
Port = 20001
|
||||||
|
|
||||||
|
[G3]
|
||||||
|
Enable = true
|
||||||
|
|
||||||
|
[DMRPlus]
|
||||||
|
Port = 8880
|
||||||
|
|
||||||
|
[M17]
|
||||||
|
Port = 17000
|
||||||
|
|
||||||
|
[MMDVM]
|
||||||
|
Port = 62030
|
||||||
|
DefaultId = 0
|
||||||
|
|
||||||
|
[NXDN]
|
||||||
|
Port = 41400
|
||||||
|
AutoLinkModule = A # comment out if you want to disable AL
|
||||||
|
ReflectorID = 12345
|
||||||
|
|
||||||
|
[P25]
|
||||||
|
Port = 41000
|
||||||
|
AutoLinkModule = A # comment out if you want to disable AL
|
||||||
|
ReflectorID = 12345
|
||||||
|
|
||||||
|
[URF]
|
||||||
|
Port = 10017
|
||||||
|
|
||||||
|
[USRP]
|
||||||
|
Enable = false
|
||||||
|
Callsign = ALLSTAR # set to NONE if you don't want to create this client
|
||||||
|
IPAddress = 1.2.3.4 # the IP address of the USRP client (the Allstar node)
|
||||||
|
RxPort = 34000
|
||||||
|
TxPort = 32000
|
||||||
|
Module = A # this has to be a transcoded module!
|
||||||
|
#FilePath = /home/usr/clients.txt # a list of listen-only clients
|
||||||
|
|
||||||
|
[YSF]
|
||||||
|
Port = 42000
|
||||||
|
AutoLinkModule = A # comment out if you want to disable AL
|
||||||
|
DefaultTxFreq = 446500000
|
||||||
|
DefaultRxFreq = 446500000
|
||||||
|
# if you've registered your reflector at register.ysfreflector.de:
|
||||||
|
RegistrationID = 12345
|
||||||
|
RegistrationName = US URF???
|
||||||
|
RegistrationDescription = URF Reflector
|
||||||
|
|
||||||
|
######## Database files
|
||||||
|
[DMR ID DB]
|
||||||
|
Mode = http #### Mode is "http", "file", or "both"
|
||||||
|
#### if "both", the url will be read first
|
||||||
|
FilePath = /home/user/dmrid.dat # for you to add your own values
|
||||||
|
# will be reloaded within 10s
|
||||||
|
URL = http://xlxapi.rlx.lu/api/exportdmr.php # if Mode "http" or "both"
|
||||||
|
RefreshMin = 179
|
||||||
|
|
||||||
|
[NXDN ID DB]
|
||||||
|
Mode = http
|
||||||
|
FilePath = /home/user/nxdn.dat
|
||||||
|
URL = https://radioid.net/static/nxdn.csv
|
||||||
|
RefreshMin = 1440 # radioid.net says this file is updated once/day
|
||||||
|
|
||||||
|
[YSF TX/RX DB]
|
||||||
|
Mode = http
|
||||||
|
FilePath = /home/user/ysfnode.dat
|
||||||
|
URL = http://xlxapi.rlx.lu/api/exportysfrepeaters.php
|
||||||
|
RefreshMin = 191
|
||||||
|
|
||||||
|
######### Other File locations
|
||||||
|
[Files]
|
||||||
|
PidPath = /var/run/xlxd.pid
|
||||||
|
XmlPath = /var/log/xlxd.xml
|
||||||
|
#JsonPath = /var/tmp/urfd.json # for future development
|
||||||
|
WhitelistPath = /home/user/urfd.whitelist
|
||||||
|
BlacklistPath = /home/user/urfd.blacklist
|
||||||
|
InterlinkPath = /home/user/urfd.interlink
|
||||||
|
G3TerminalPath = /home/user/urfd.terminal
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
# this is where the binary will be installed
|
||||||
|
BINDIR = /usr/local/bin
|
||||||
|
|
||||||
|
# besides making an executable that gdb can use,
|
||||||
|
# this will also provide some additional log messsage
|
||||||
|
debug = false
|
||||||
@ -1,653 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
#
|
|
||||||
# Copyright (c) 2021 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 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 <http://www.gnu.org/licenses/>.
|
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
CharCount () {
|
|
||||||
local haystack=$1
|
|
||||||
local test="\${haystack//[^$2]}"
|
|
||||||
eval local result=$test
|
|
||||||
return ${#result}
|
|
||||||
}
|
|
||||||
|
|
||||||
RemoveDupes () {
|
|
||||||
local -n s=$1
|
|
||||||
local i j
|
|
||||||
for (( i=0; i<${#s}-1; i++)); do
|
|
||||||
for (( j=$i+1; j<${#s}; j++)); do
|
|
||||||
if [[ ${s:$i:1} != "." ]]; then
|
|
||||||
if [[ ${s:$i:1} == ${s:$j:1} ]]; then
|
|
||||||
local l=$(($j + 1))
|
|
||||||
s="${s:0:$j}.${s:$l}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
done
|
|
||||||
s=${s//.}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckModules () {
|
|
||||||
# only A through Z
|
|
||||||
modules=${1//[^A-Z]}
|
|
||||||
if (( ${#modules} < 1 )); then
|
|
||||||
unset modules
|
|
||||||
clear
|
|
||||||
echo "ERROR: You must specify at least one module, A to Z!"
|
|
||||||
echo
|
|
||||||
read -p "<Enter> to continue: " ans
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
RemoveDupes modules
|
|
||||||
tcmodules=${modules:0:1}
|
|
||||||
}
|
|
||||||
|
|
||||||
CheckTranscodedModules () {
|
|
||||||
local tc=${1//[^A-Z]}
|
|
||||||
local tcorig=$tc
|
|
||||||
if (( ${#tc} < 1 )); then
|
|
||||||
tcmodules=${modules:0:1}
|
|
||||||
clear
|
|
||||||
echo "ERROR: You must specify at least one module!"
|
|
||||||
echo "If you aren't using a transcoder, set the transcoder"
|
|
||||||
echo "address value to the default 'none'!"
|
|
||||||
echo
|
|
||||||
read -p "<Enter> to continue: " ans
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
RemoveDupes tc
|
|
||||||
|
|
||||||
local m=$modules_d
|
|
||||||
if [ ! -z ${modules+x} ]; then
|
|
||||||
m=$modules
|
|
||||||
fi
|
|
||||||
|
|
||||||
local i
|
|
||||||
for ((i=0; i<${#tc}; i++)); do
|
|
||||||
CharCount $m ${tc:$i:1}
|
|
||||||
local count=$?
|
|
||||||
if (( $count < 1 )); then
|
|
||||||
local j=$((i+1))
|
|
||||||
tc="${tc:0:$i}.${tc:$j}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
tc=${tc//.}
|
|
||||||
|
|
||||||
if (( ${#tc} < 1 )); then
|
|
||||||
echo "ERROR: Module(s), '$tcorig', are not in the configure modules, '$m'!"
|
|
||||||
echo
|
|
||||||
read -p "<Enter> to continue: " ans
|
|
||||||
tcmodules=${modules:0:1}
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
tcmodules=$tc
|
|
||||||
}
|
|
||||||
|
|
||||||
SetBooleanValue ()
|
|
||||||
{
|
|
||||||
local dvname
|
|
||||||
local cv
|
|
||||||
if [ -z $2 ]; then
|
|
||||||
if [ -z ${!1+x} ]; then
|
|
||||||
if [[ "$1" == module_[abc]_* ]]; then
|
|
||||||
echo matches
|
|
||||||
dvname=${1//_[abc]_/_x_}
|
|
||||||
else
|
|
||||||
echo does not match
|
|
||||||
dvname=${1}_d
|
|
||||||
fi
|
|
||||||
cv=${!dvname}
|
|
||||||
else
|
|
||||||
cv=${!1}
|
|
||||||
fi
|
|
||||||
if [[ $cv == [tT]* ]]; then
|
|
||||||
eval ${1}=false
|
|
||||||
else
|
|
||||||
eval ${1}=true
|
|
||||||
fi
|
|
||||||
elif [[ "$2" == [tT]* ]]; then
|
|
||||||
eval ${1}=true
|
|
||||||
else
|
|
||||||
eval ${1}=false
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
EvaluateVar ()
|
|
||||||
{
|
|
||||||
if [ -z ${!1+x} ]; then
|
|
||||||
if [ -z "${!2}" ]; then
|
|
||||||
echo "'' <DEFAULT>"
|
|
||||||
else
|
|
||||||
echo "${!2} <DEFAULT>"
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
if [ -z "${!1}" ]; then
|
|
||||||
echo "''"
|
|
||||||
else
|
|
||||||
echo "${!1}"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
TestGainValue ()
|
|
||||||
{
|
|
||||||
if [[ "$1" =~ ^[-]?[0-9]+$ ]]; then
|
|
||||||
if (( $1 >= -24 && $1 <= 24 )); then
|
|
||||||
return 0
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
AudioGainMenu ()
|
|
||||||
{
|
|
||||||
while [[ "$key" != q* ]]
|
|
||||||
do
|
|
||||||
clear
|
|
||||||
echo
|
|
||||||
echo " DVSI AMBE Chip Audio Gain Menu"
|
|
||||||
echo
|
|
||||||
echo " All values are in dB and can be from -24 to 24"
|
|
||||||
echo " 'input' means the audio amplitude is adjusted before encoding"
|
|
||||||
echo " 'output' means the audio amplitude is adjusted after decoding"
|
|
||||||
echo
|
|
||||||
echo " Non-zero values are generally not required!"
|
|
||||||
echo " USE AT YOUR OWN RISK!"
|
|
||||||
echo
|
|
||||||
echo -n "si : D-Star audio input = "; EvaluateVar dstar_in_gain{,_d}
|
|
||||||
echo -n "so : D-Star audio output = "; EvaluateVar dstar_out_gain{,_d}
|
|
||||||
echo -n "mi : DMR/YSF audio input = "; EvaluateVar dmr_in_gain{,_d}
|
|
||||||
echo -n "mo : DMR/YSF audio output = "; EvaluateVar dmr_out_gain{,_d}
|
|
||||||
echo
|
|
||||||
echo "q : Return to Main Menu"
|
|
||||||
echo "u : Unset the value of <key> (revert to the default value)."
|
|
||||||
echo
|
|
||||||
read -p "Please input <key> <value> : " key value
|
|
||||||
if [[ "$key" == si* ]]; then
|
|
||||||
TestGainValue "$value" && dstar_in_gain="$value"
|
|
||||||
elif [[ "$key" == so* ]]; then
|
|
||||||
TestGainValue "$value" && dstar_out_gain="$value"
|
|
||||||
elif [[ "$key" == mi* ]]; then
|
|
||||||
TestGainValue "$value" && dmr_in_gain="$value"
|
|
||||||
elif [[ "$key" == mo* ]]; then
|
|
||||||
TestGainValue "$value" && dmr_out_gain="$value"
|
|
||||||
elif [[ "$key" == u* ]]; then
|
|
||||||
if [[ "$value" == si* ]]; then
|
|
||||||
unset dstar_in_gain
|
|
||||||
elif [[ "$value" == so* ]]; then
|
|
||||||
unset dstar_out_gain
|
|
||||||
elif [[ "$value" == mi* ]]; then
|
|
||||||
unset dmr_in_gain
|
|
||||||
elif [[ "$value" == mo* ]]; then
|
|
||||||
unset dmr_out_gain
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteMemFile ()
|
|
||||||
{
|
|
||||||
local file
|
|
||||||
file="$rcfg"
|
|
||||||
echo "# created on `date`" > $file
|
|
||||||
[ -z ${callsign+x} ] || echo "callsign='$callsign'" >> $file
|
|
||||||
[ -z ${modules+x} ] || echo "modules='$modules'" >> $file
|
|
||||||
[ -z ${ip4addr+x} ] || echo "ip4addr='$ip4addr'" >> $file
|
|
||||||
[ -z ${ip6addr+x} ] || echo "ip6addr='$ip6addr'" >> $file
|
|
||||||
[ -z ${tcaddress+x} ] || echo "tcaddress='$tcaddress'" >> $file
|
|
||||||
[ -z ${tcmodules+x} ] || echo "tcmodules='$tcmodules'" >> $file
|
|
||||||
[ -z ${dstar_in_gain+x} ] || echo "dstar_in_gain=$dstar_in_gain" >> $file
|
|
||||||
[ -z ${dstar_out_gain+x} ] || echo "dstar_out_gain=$dstar_out_gain" >> $file
|
|
||||||
[ -z ${dmr_in_gain+x} ] || echo "dmr_in_gain=$dmr_in_gain" >> $file
|
|
||||||
[ -z ${dmr_out_gain+x} ] || echo "dmr_out_gain=$dmr_out_gain" >> $file
|
|
||||||
[ -z ${dmrdbuseserver+x} ] || echo "dmrdbuseserver=$dmrdbuseserver" >> $file
|
|
||||||
[ -z ${dmrdbrefresh+x} ] || echo "dmrdbrefresh=$dmrdbrefresh" >> $file
|
|
||||||
[ -z ${dmrdbpath+x} ] || echo "dmrdbpath='$dmrdbpath'" >> $file
|
|
||||||
[ -z ${ysfautolink+x} ] || echo "ysfautolink=$ysfautolink" >> $file
|
|
||||||
[ -z ${ysfmodule+x} ] || echo "ysfmodule='$ysfmodule'" >> $file
|
|
||||||
[ -z ${ysflocaldb+x} ] || echo "ysflocaldb=$ysflocaldb" >> $file
|
|
||||||
[ -z ${ysfdbname+x} ] || echo "ysfdbname='$ysfdbname'" >> $file
|
|
||||||
[ -z ${ysfdbuser+x} ] || echo "ysfdbuser='$ysfdbuser'" >> $file
|
|
||||||
[ -z ${ysfrname+x} ] || echo "ysfrname='$ysfrname'" >> $file
|
|
||||||
[ -z ${ysfrdesc+x} ] || echo "ysfrdesc='$ysfrdesc'" >> $file
|
|
||||||
[ -z ${ysfdbpw+x} ] || echo "ysfdbpw='$ysfdbpw'" >> $file
|
|
||||||
[ -z ${g3support+x} ] || echo "g3support=$g3support" >> $file
|
|
||||||
[ -z ${dbsupport+x} ] || echo "dbsupport=$dbsupport" >> $file
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteSRCHFile ()
|
|
||||||
{
|
|
||||||
local file m
|
|
||||||
file="$srch"
|
|
||||||
echo "// Created on `date`" > $file
|
|
||||||
echo "#define CALLSIGN \"${callsign}\"" >> $file
|
|
||||||
if [[ "$callsign" == XRF* ]]; then
|
|
||||||
echo "#define NO_XLX" >> $file
|
|
||||||
fi
|
|
||||||
if [ -z ${modules+x} ]; then
|
|
||||||
echo "#define ACTIVE_MODULES \"${modules_d}\"" >> $file
|
|
||||||
else
|
|
||||||
echo "#define ACTIVE_MODULES \"${modules}\"" >> $file
|
|
||||||
fi
|
|
||||||
if [ ! -z ${ip4addr+x} ]; then
|
|
||||||
echo "#define LISTEN_IPV4 \"${ip4addr}\"" >> $file
|
|
||||||
fi
|
|
||||||
if [ ! -z ${ip6addr+x} ]; then
|
|
||||||
echo "#define LISTEN_IPV6 \"${ip6addr}\"" >> $file
|
|
||||||
fi
|
|
||||||
if [ -z ${ysfautolink+x} ]; then
|
|
||||||
echo "#define YSF_AUTOLINK_ENABLE ${ysfautolink_d}" >> $file
|
|
||||||
else
|
|
||||||
echo "#define YSF_AUTOLINK_ENABLE ${ysfautolink}" >> $file
|
|
||||||
fi
|
|
||||||
if [ -z ${ysfmodule+x} ]; then
|
|
||||||
echo "#define YSF_AUTOLINK_MODULE '${ysfmodule_d}'" >> $file
|
|
||||||
else
|
|
||||||
echo "#define YSF_AUTOLINK_MODULE '${ysfmodule}'" >> $file
|
|
||||||
fi
|
|
||||||
if [ ! -z ${dmrdbuseserver+x} ]; then
|
|
||||||
if [[ "$dmrdbuseserver" == true ]]; then
|
|
||||||
m=1
|
|
||||||
else
|
|
||||||
m=0
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
m=1
|
|
||||||
fi
|
|
||||||
echo "#define DMRIDDB_USE_RLX_SERVER $m" >> $file
|
|
||||||
if [ ! -z ${dmrdbrefresh+x} ]; then
|
|
||||||
echo "#define DMRIDDB_REFRESH_RATE $dmrdbrefresh" >> $file
|
|
||||||
else
|
|
||||||
echo "#define DMRIDDB_REFRESH_RATE $dmrdbrefresh_d" >> $file
|
|
||||||
fi
|
|
||||||
if [ ! -z ${dmrdbpath+x} ]; then
|
|
||||||
echo "#define DMRIDDB_PATH \"$dmrdbpath\"" >> $file
|
|
||||||
else
|
|
||||||
echo "#define DMRIDDB_PATH \"$dmrdbpath_d\"" >> $file
|
|
||||||
fi
|
|
||||||
if [[ "$ysflocaldb" == true ]]; then
|
|
||||||
echo '#define YSF_DB_SUPPORT true' >> $file
|
|
||||||
echo "#define YSF_DB_NAME \"$ysfdbname\"" >> $file
|
|
||||||
echo "#define YSF_DB_USER \"$ysfdbuser\"" >> $file
|
|
||||||
echo "#define YSF_DB_PASSWORD \"$ysfdbpw\"" >> $file
|
|
||||||
else
|
|
||||||
echo '#define YSF_DB_SUPPORT false' >> $file
|
|
||||||
fi
|
|
||||||
if [ ! -z ${ysfrname+x} ]; then
|
|
||||||
echo "#define YSF_REFLECTOR_NAME \"$ysfrname\"" >> $file
|
|
||||||
fi
|
|
||||||
if [ ! -z ${ysfrdesc+x} ]; then
|
|
||||||
echo "#define YSF_REFLECTOR_DESCRIPTION \"$ysfrdesc\"" >> $file
|
|
||||||
fi
|
|
||||||
if [ ! -z ${tcaddress+x} ]; then
|
|
||||||
echo "#define TRANSCODER_IP \"${tcaddress}\"" >> $file
|
|
||||||
echo "#define TRANSCODED_MODULES \"${tcmodules}\"" >> $file
|
|
||||||
fi
|
|
||||||
if [ -z ${g3support+x} ]; then
|
|
||||||
m=${g3support_d}
|
|
||||||
else
|
|
||||||
m=${g3support}
|
|
||||||
fi
|
|
||||||
if [[ "$m" != true ]]; then
|
|
||||||
echo "#define NO_G3" >> $file
|
|
||||||
fi
|
|
||||||
if [ -n ${dbsupport+x} ]; then
|
|
||||||
if [ "$dbsupport" == true ]; then
|
|
||||||
echo "#define DEBUG" >> $file
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteSRCMKFile ()
|
|
||||||
{
|
|
||||||
local file
|
|
||||||
file="$srcm"
|
|
||||||
echo "# Created on `date`" > $file
|
|
||||||
[ -z ${ip4addr+x} ] || echo "ipv4 = $ip4addr" >> $file
|
|
||||||
[ -z ${ip6addr+x} ] || echo "ipv6 = $ip6addr" >> $file
|
|
||||||
[ -z ${tcaddress+x} ] || echo "tc_ip = $tcaddress" >> $file
|
|
||||||
if [ -z ${g3support+x} ]; then
|
|
||||||
echo "use_g3 = $g3support_d" >> $file
|
|
||||||
else
|
|
||||||
echo "use_g3 = $g3support" >> $file
|
|
||||||
fi
|
|
||||||
if [ -z ${dbsupport+x} ]; then
|
|
||||||
echo "debug = $dbsupport_d" >> $file
|
|
||||||
else
|
|
||||||
echo "debug = $dbsupport" >> $file
|
|
||||||
fi
|
|
||||||
if [ -z ${ysflocaldb+x} ]; then
|
|
||||||
echo "ysf_db = $ysflocaldb_d" >> $file
|
|
||||||
else
|
|
||||||
echo "ysf_db = $ysflocaldb" >> $file
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteTranscoderHFile ()
|
|
||||||
{
|
|
||||||
local file
|
|
||||||
file="$tcdh"
|
|
||||||
echo "// Created on `date`" > $file
|
|
||||||
if [ -n ${tcaddress+x} ] && [[ "$tcaddress" != "local" ]]; then
|
|
||||||
echo "#define TRANSCODER_IP \"${tcaddress}\"" >> $file
|
|
||||||
fi
|
|
||||||
if [ -n ${tcmodules+x} ]; then
|
|
||||||
echo "#define TRANSCODED_MODULES \"${tcmodules}\"" >> $file
|
|
||||||
fi
|
|
||||||
if [ -z ${dstar_in_gain+x} ]; then
|
|
||||||
echo "#define DSTAR_IN_GAIN $dstar_in_gain_d" >> $file
|
|
||||||
else
|
|
||||||
echo "#define DSTAR_IN_GAIN $dstar_in_gain" >> $file
|
|
||||||
fi
|
|
||||||
if [ -z ${dstar_out_gain+x} ]; then
|
|
||||||
echo "#define DSTAR_OUT_GAIN $dstar_out_gain_d" >> $file
|
|
||||||
else
|
|
||||||
echo "#define DSTAR_OUT_GAIN $dstar_out_gain" >> $file
|
|
||||||
fi
|
|
||||||
if [ -z ${dmr_in_gain+x} ]; then
|
|
||||||
echo "#define DMR_IN_GAIN $dmr_in_gain_d" >> $file
|
|
||||||
else
|
|
||||||
echo "#define DMR_IN_GAIN $dmr_in_gain" >> $file
|
|
||||||
fi
|
|
||||||
if [ -z ${dmr_out_gain+x} ]; then
|
|
||||||
echo "#define DMR_OUT_GAIN $dmr_out_gain_d" >> $file
|
|
||||||
else
|
|
||||||
echo "#define DMR_OUT_GAIN $dmr_out_gain" >> $file
|
|
||||||
fi
|
|
||||||
if [ -n ${dbsupport+x} ]; then
|
|
||||||
if [ "$dbsupport" == true ]; then
|
|
||||||
echo "#define DEBUG" >> $file
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteTranscoderMKFile ()
|
|
||||||
{
|
|
||||||
local file
|
|
||||||
file="$tcdm"
|
|
||||||
echo "# created on `date`" > $file
|
|
||||||
if [ -z ${dbsupport+x} ]; then
|
|
||||||
echo "debug = $dbsupport_d" >> $file
|
|
||||||
else
|
|
||||||
echo "debug = $dbsupport" >> $file
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteCfgPhpFile ()
|
|
||||||
{
|
|
||||||
cat << EOF > $ysfs
|
|
||||||
<?php
|
|
||||||
define('DB_SERVER', 'localhost');
|
|
||||||
define('DB_USERNAME', '${ysfdbuser}');
|
|
||||||
define('DB_PASSWORD', '${ysfdbpw}');
|
|
||||||
define('DB_NAME', '${ysfdbname}');
|
|
||||||
// Attempt to connect to MySQL database
|
|
||||||
\$link = mysqli_connect(DB_SERVER, DB_USERNAME, DB_PASSWORD, DB_NAME);
|
|
||||||
// Check connection
|
|
||||||
if (\$link === false) { die("ERROR: Could not connect. " . mysqli_connect_error()); }
|
|
||||||
?>
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteDBCreateFile ()
|
|
||||||
{
|
|
||||||
cat << EOF > $dbcr
|
|
||||||
CREATE DATABASE IF NOT EXISTS ${ysfdbname};
|
|
||||||
USE ${ysfdbname};
|
|
||||||
CREATE TABLE IF NOT EXISTS ysfnodes (
|
|
||||||
callsign VARCHAR(7) PRIMARY KEY,
|
|
||||||
password VARCHAR(255) NOT NULL,
|
|
||||||
txfreq INT NOT NULL DEFAULT 446500000,
|
|
||||||
rxfreq INT NOT NULL DEFAULT 446500000,
|
|
||||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
||||||
);
|
|
||||||
CREATE USER IF NOT EXISTS '${ysfdbuser}'@'localhost' IDENTIFIED BY '${ysfdbpw}';
|
|
||||||
GRANT ALL PRIVILEGES ON $ysfdbname . ysfnodes TO '${ysfdbuser}'@'localhost';
|
|
||||||
FLUSH PRIVILEGES;
|
|
||||||
EOF
|
|
||||||
}
|
|
||||||
|
|
||||||
WriteCFGFiles ()
|
|
||||||
{
|
|
||||||
WriteMemFile
|
|
||||||
WriteSRCHFile
|
|
||||||
WriteSRCMKFile
|
|
||||||
if [ -z ${tcaddress+x} ]; then
|
|
||||||
rm -f $tcdh $tcdm
|
|
||||||
else
|
|
||||||
WriteTranscoderHFile
|
|
||||||
WriteTranscoderMKFile
|
|
||||||
fi
|
|
||||||
if [[ "$ysflocaldb" == true ]]; then
|
|
||||||
WriteCfgPhpFile
|
|
||||||
WriteDBCreateFile
|
|
||||||
else
|
|
||||||
rm -f $ysfs $dbcr
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
ListCFGFiles ()
|
|
||||||
{
|
|
||||||
echo "===========${rcfg}============="
|
|
||||||
cat $rcfg
|
|
||||||
echo "===========${srch}============="
|
|
||||||
cat $srch
|
|
||||||
echo "===========${srcm}============="
|
|
||||||
cat $srcm
|
|
||||||
if [ ! -z ${tcaddress+x} ]; then
|
|
||||||
echo "===========${tcdh}============="
|
|
||||||
cat $tcdh
|
|
||||||
echo "===========${tcdm}============="
|
|
||||||
cat $tcdm
|
|
||||||
fi
|
|
||||||
if [[ "$ysflocaldb" == true ]]; then
|
|
||||||
echo "===========${ysfs}============="
|
|
||||||
cat $ysfs
|
|
||||||
echo "===========${dbcr}============="
|
|
||||||
cat $dbcr
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# Execution starts here!
|
|
||||||
# file locations
|
|
||||||
rcfg='reflector.cfg'
|
|
||||||
srch='reflector/configure.h'
|
|
||||||
srcm='reflector/configure.mk'
|
|
||||||
tcdh='../tcd/configure.h'
|
|
||||||
tcdm='../tcd/configure.mk'
|
|
||||||
ysfs='wiresx/configure.php'
|
|
||||||
dbcr='configure.sql'
|
|
||||||
urfserv='/etc/systemd/system/urfd.service'
|
|
||||||
|
|
||||||
# expert mode
|
|
||||||
if [[ "$1" == ex* ]]; then
|
|
||||||
expertmode="expertMode"
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ -e reflector.cfg ]; then
|
|
||||||
source reflector.cfg
|
|
||||||
else
|
|
||||||
echo 'No configuration file found...'
|
|
||||||
sleep 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# default values
|
|
||||||
callsign_d='CHNGME'
|
|
||||||
modules_d='ABCF'
|
|
||||||
ip4addr_d='none'
|
|
||||||
ip6addr_d='none'
|
|
||||||
tcaddress_d='none'
|
|
||||||
ysfautolink_d=false
|
|
||||||
ysfmodule_d='D'
|
|
||||||
g3support_d=false
|
|
||||||
dbsupport_d=false
|
|
||||||
dmrdbuseserver_d=true
|
|
||||||
dmrdbrefresh_d=180
|
|
||||||
dmrdbpath_d='/usr/local/etc/dmrid.dat'
|
|
||||||
ysflocaldb_d=false
|
|
||||||
ysfdbname_d=''
|
|
||||||
ysfdbuser_d=''
|
|
||||||
ysfdbpw_d=''
|
|
||||||
dstar_in_gain_d=0
|
|
||||||
dstar_out_gain_d=0
|
|
||||||
dmr_in_gain_d=0
|
|
||||||
dmr_out_gain_d=0
|
|
||||||
if [ -z ${callsign+x} ];then
|
|
||||||
ysfrname_d="$callsign_d"
|
|
||||||
else
|
|
||||||
ysfrname_d="$callsign"
|
|
||||||
fi
|
|
||||||
ysfrdesc_d='URF Reflector'
|
|
||||||
|
|
||||||
if [ -z ${expertmode+x} ]; then
|
|
||||||
if [ -e $urfserv ]; then
|
|
||||||
echo -n "You cannot change the configuration right now beacuse there is an "
|
|
||||||
if [ -e $urfserv ]; then
|
|
||||||
echo "URFD server running."
|
|
||||||
fi;
|
|
||||||
echo "===========${rcfg}============="
|
|
||||||
cat $rcfg
|
|
||||||
echo
|
|
||||||
echo "Please use radmin to uninstall the running server before attempting to modify the configuration."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
key='x'
|
|
||||||
# main loop
|
|
||||||
while [[ "$key" != q* ]]
|
|
||||||
do
|
|
||||||
clear
|
|
||||||
echo
|
|
||||||
echo " Reflector Configuration, Version #220326"
|
|
||||||
echo
|
|
||||||
echo " ******* REFLECTOR ********"
|
|
||||||
echo -n "cs : Reflector Callsign = "; EvaluateVar callsign{,_d}
|
|
||||||
echo -n "am : Active Modules = "; EvaluateVar modules{,_d}
|
|
||||||
echo -n "g3 : Icom G3 Support = "; EvaluateVar g3support{,_d}
|
|
||||||
echo " ******* ADDRESSES ********"
|
|
||||||
echo -n "i4 : IPv4 Listen Address = "; EvaluateVar ip4addr{,_d}
|
|
||||||
echo -n "i6 : IPv6 Listen Address = "; EvaluateVar ip6addr{,_d}
|
|
||||||
echo " ******* TRANSCODER ********"
|
|
||||||
echo " The only TC address supported is 'local' or the default 'none'"
|
|
||||||
echo -n "tc : Transcoder Address = "; EvaluateVar tcaddress{,_d}
|
|
||||||
if [ ! -z ${tcaddress+x} ]; then
|
|
||||||
echo -n "tm : Transcoder Modules = "; EvaluateVar tcmodules{,_d}
|
|
||||||
echo "ag : AMBE Audio Gain Sub-Menu"
|
|
||||||
if [ ! -z ${dstar_in_gain+x} ]; then
|
|
||||||
echo -n " D-Star Input gain = "; EvaluateVar dstar_in_gain{,_d}
|
|
||||||
fi
|
|
||||||
if [ ! -z ${dstar_out_gain+x} ]; then
|
|
||||||
echo -n " D-Star Output gain = "; EvaluateVar dstar_out_gain{,_d}
|
|
||||||
fi
|
|
||||||
if [ ! -z ${dmr_in_gain+x} ]; then
|
|
||||||
echo -n " DMR Input gain = "; EvaluateVar dmr_in_gain{,_d}
|
|
||||||
fi
|
|
||||||
if [ ! -z ${dmr_out_gain+x} ]; then
|
|
||||||
echo -n " DMR Output gain = "; EvaluateVar dmr_out_gain{,_d}
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo " ******* DMR Database ********"
|
|
||||||
echo -n "ds : Use RLX Server = "; EvaluateVar dmrdbuseserver{,_d}
|
|
||||||
echo -n "dt : Refresh time (in min) = "; EvaluateVar dmrdbrefresh{,_d}
|
|
||||||
echo -n "dp : Database path = "; EvaluateVar dmrdbpath{,_d}
|
|
||||||
echo " ******* SYSTEM FUSION ********"
|
|
||||||
echo -n "ye : YSF Autolink Enable = "; EvaluateVar ysfautolink{,_d}
|
|
||||||
if [ ! -z ${ysfautolink+x} ]; then
|
|
||||||
if [[ "$ysfautolink" == true ]]; then
|
|
||||||
echo -n "ym : YSF Autolink Module = "; EvaluateVar ysfmodule{,_d}
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo -n "yl : YSF Local Database = "; EvaluateVar ysflocaldb{,_d}
|
|
||||||
if [[ "$ysflocaldb" == true ]]; then
|
|
||||||
echo -n "yd : YSF Database Name = "; EvaluateVar ysfdbname{,_d}
|
|
||||||
echo -n "yu : YSF Database User = "; EvaluateVar ysfdbuser{,_d}
|
|
||||||
echo -n "yp : YSF Database Password = "; EvaluateVar ysfdbpw{,_d}
|
|
||||||
fi
|
|
||||||
echo " ******* YSFReflector Registry *******"
|
|
||||||
echo -n "rn : Registry Name = "; EvaluateVar ysfrname{,_d}
|
|
||||||
echo -n "rd : Registry Description = "; EvaluateVar ysfrdesc{,_d}
|
|
||||||
echo " ******* DEBUGGING ********"
|
|
||||||
echo -n "db : Debugging Support = "; EvaluateVar dbsupport{,_d}
|
|
||||||
echo
|
|
||||||
if [[ "$callsign" == URF* ]]; then
|
|
||||||
echo "w : Write configuration files (overwrites any existing files) and quit"
|
|
||||||
fi
|
|
||||||
echo "q : Quit without saving"
|
|
||||||
echo "u : Unset the value of <key> (revert to the default value)."
|
|
||||||
echo
|
|
||||||
read -p "Please input <key> <value> - omit value to toggle a true/false : " key value
|
|
||||||
|
|
||||||
if [[ "$key" == cs* && ${value^^} == URF* ]]; then
|
|
||||||
callsign="${value^^}"
|
|
||||||
callsign="${callsign:0:6}"
|
|
||||||
ysfrname_d="${callsign}"
|
|
||||||
unset tcaddress tcmodules ysf{autolink,module,localdb,dbname,dbuser,dbpw}
|
|
||||||
elif [[ "$key" == am* ]]; then CheckModules "${value^^}"
|
|
||||||
elif [[ "$key" == i4* ]]; then ip4addr="$value"
|
|
||||||
elif [[ "$key" == i6* ]]; then ip6addr="$value"
|
|
||||||
elif [[ "$key" == tc* ]]; then tcaddress="local"
|
|
||||||
elif [[ "$key" == tm* ]]; then CheckTranscodedModules "${value^^}"
|
|
||||||
elif [[ "$key" == ag* ]]; then
|
|
||||||
AudioGainMenu
|
|
||||||
key=x
|
|
||||||
elif [[ "$key" == ds* ]]; then SetBooleanValue dmrdbuseserver "$value"
|
|
||||||
elif [[ "$key" == dt* ]]; then dmrdbrefresh="$value"
|
|
||||||
elif [[ "$key" == dp* ]]; then dmrdbpath="$value"
|
|
||||||
elif [[ "$key" == ye* ]]; then SetBooleanValue ysfautolink "$value"
|
|
||||||
elif [[ "$key" == ym* ]]; then
|
|
||||||
ysfmodule="${value^^}"
|
|
||||||
ysfmodule="${ysfmodule:0:1}"
|
|
||||||
elif [[ "$key" == g3* ]]; then SetBooleanValue g3support "$value"
|
|
||||||
elif [[ "$key" == db* ]]; then SetBooleanValue dbsupport "$value"
|
|
||||||
elif [[ "$key" == yl* ]]; then SetBooleanValue ysflocaldb "$value"
|
|
||||||
elif [[ "$key" == yd* ]]; then ysfdbname="$value"
|
|
||||||
elif [[ "$key" == yu* ]]; then ysfdbuser="$value"
|
|
||||||
elif [[ "$key" == yp* ]]; then ysfdbpw="$value"
|
|
||||||
elif [[ "$key" == rn* ]]; then ysfrname="${value:0:16}"
|
|
||||||
elif [[ "$key" == rd* ]]; then ysfrdesc="${value:0:14}"
|
|
||||||
elif [[ "$key" == w* ]]; then
|
|
||||||
WriteCFGFiles
|
|
||||||
ListCFGFiles
|
|
||||||
exit 0
|
|
||||||
elif [[ "$key" == u* ]]; then
|
|
||||||
if [[ "$value" == cs* ]]; then unset callsign
|
|
||||||
elif [[ "$value" == am* ]]; then unset modules
|
|
||||||
elif [[ "$value" == i4* ]]; then unset ip4addr
|
|
||||||
elif [[ "$value" == i6* ]]; then unset ip6addr
|
|
||||||
elif [[ "$value" == tc* ]]; then unset tcaddress
|
|
||||||
elif [[ "$value" == tm* ]]; then tcmodules=${modules:0:1}
|
|
||||||
elif [[ "$value" == ds* ]]; then unset dmrdbuseserver
|
|
||||||
elif [[ "$value" == dt* ]]; then unset dmrdbrefresh
|
|
||||||
elif [[ "$value" == dp* ]]; then unset dmrdbpath
|
|
||||||
elif [[ "$value" == ye* ]]; then unset ysfautolink ysfmodule
|
|
||||||
elif [[ "$value" == ym* ]]; then unset ysfmodule
|
|
||||||
elif [[ "$value" == g3* ]]; then unset g3support
|
|
||||||
elif [[ "$value" == db* ]]; then unset dbsupport
|
|
||||||
elif [[ "$value" == yl* ]]; then unset ysflocaldb ysfdbname ysfdbuser ysfdbpw
|
|
||||||
elif [[ "$value" == yd* ]]; then unset ysfdbname
|
|
||||||
elif [[ "$value" == yu* ]]; then unset ysfdbuser
|
|
||||||
elif [[ "$value" == yp* ]]; then unset ysfdbpw
|
|
||||||
elif [[ "$value" == rn* ]]; then unset ysfrname
|
|
||||||
elif [[ "$value" == rd* ]]; then unset ysfrdesc
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
exit 0
|
|
||||||
@ -0,0 +1,988 @@
|
|||||||
|
/*
|
||||||
|
* 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 <sys/stat.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <fstream>
|
||||||
|
#include <cstring>
|
||||||
|
#include <vector>
|
||||||
|
#include <list>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
#include "Global.h"
|
||||||
|
#include "CurlGet.h"
|
||||||
|
|
||||||
|
// ini file keywords
|
||||||
|
#define JAUTOLINKMODULE "AutoLinkModule"
|
||||||
|
#define JBLACKLISTPATH "BlacklistPath"
|
||||||
|
#define JBRANDMEISTER "Brandmeister"
|
||||||
|
#define JCALLSIGN "Callsign"
|
||||||
|
#define JCOUNTRY "Country"
|
||||||
|
#define JDCS "DCS"
|
||||||
|
#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 JENABLE "Enable"
|
||||||
|
#define JFILES "Files"
|
||||||
|
#define JFILEPATH "FilePath"
|
||||||
|
#define JG3 "G3"
|
||||||
|
#define JG3TERMINALPATH "G3TerminalPath"
|
||||||
|
#define JINTERLINKPATH "InterlinkPath"
|
||||||
|
#define JIPADDRESS "IPAddress"
|
||||||
|
#define JIPADDRESSES "IP Addresses"
|
||||||
|
#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 JMODULE "Module"
|
||||||
|
#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 JRXPORT "RxPort"
|
||||||
|
#define JSPONSOR "Sponsor"
|
||||||
|
#define JSYSOPEMAIL "SysopEmail"
|
||||||
|
#define JTRANSCODED "Transcoded"
|
||||||
|
#define JTRANSCODER "Transcoder"
|
||||||
|
#define JTXPORT "TxPort"
|
||||||
|
#define JURF "URF"
|
||||||
|
#define JURL "URL"
|
||||||
|
#define JUSRP "USRP"
|
||||||
|
#define JWHITELISTPATH "WhitelistPath"
|
||||||
|
#define JXMLPATH "XmlPath"
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
SJsonKeys::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;
|
||||||
|
|
||||||
|
{
|
||||||
|
CCurlGet curl;
|
||||||
|
std::stringstream ss;
|
||||||
|
if (CURLE_OK == curl.GetURL("https://ipv4.icanhazip.com", ss))
|
||||||
|
{
|
||||||
|
ipv4.assign(ss.str());
|
||||||
|
trim(ipv4);
|
||||||
|
}
|
||||||
|
ss.str(std::string());
|
||||||
|
if (CURLE_OK == curl.GetURL("https://ipv6.icanhazip.com", ss))
|
||||||
|
{
|
||||||
|
ipv6.assign(ss.str());
|
||||||
|
trim(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(JG3))
|
||||||
|
section = ESection::g3;
|
||||||
|
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[g_Keys.names.callsign] = value;
|
||||||
|
else if (0 == key.compare(JSYSOPEMAIL))
|
||||||
|
data[g_Keys.names.email] = value;
|
||||||
|
else if (0 == key.compare(JCOUNTRY))
|
||||||
|
data[g_Keys.names.country] = value.substr(0,2);
|
||||||
|
else if (0 == key.compare(JSPONSOR))
|
||||||
|
data[g_Keys.names.sponsor] = value;
|
||||||
|
else
|
||||||
|
badParam(key);
|
||||||
|
break;
|
||||||
|
case ESection::ip:
|
||||||
|
if (0 == key.compare(JIPV4BINDING))
|
||||||
|
{
|
||||||
|
data[g_Keys.ip.ipv4bind] = value;
|
||||||
|
}
|
||||||
|
else if (0 == key.compare(JIPV6BINDING))
|
||||||
|
{
|
||||||
|
data[g_Keys.ip.ipv6bind] = value;
|
||||||
|
}
|
||||||
|
else if (0 == key.compare(JIPV4EXTERNAL))
|
||||||
|
{
|
||||||
|
data[g_Keys.ip.ipv4address] = value;
|
||||||
|
}
|
||||||
|
else if (0 == key.compare(JIPV6EXTERNAL))
|
||||||
|
{
|
||||||
|
data[g_Keys.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[g_Keys.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[g_Keys.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[g_Keys.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[g_Keys.bm.port] = getUnsigned(value, "Brandmeister Port", 1024, 65535, 10002);
|
||||||
|
else if (0 == key.compare(JENABLE))
|
||||||
|
data[g_Keys.bm.enable] = IS_TRUE(value[0]);
|
||||||
|
else
|
||||||
|
badParam(key);
|
||||||
|
break;
|
||||||
|
case ESection::dcs:
|
||||||
|
if (0 == key.compare(JPORT))
|
||||||
|
data[g_Keys.dcs.port] = getUnsigned(value, "DCS Port", 1024, 65535, 30051);
|
||||||
|
else
|
||||||
|
badParam(key);
|
||||||
|
break;
|
||||||
|
case ESection::dextra:
|
||||||
|
if (0 == key.compare(JPORT))
|
||||||
|
data[g_Keys.dextra.port] = getUnsigned(value, "DExtra Port", 1024, 65535, 30001);
|
||||||
|
else
|
||||||
|
badParam(key);
|
||||||
|
break;
|
||||||
|
case ESection::g3:
|
||||||
|
if (0 == key.compare(JENABLE))
|
||||||
|
data[g_Keys.g3.enable] = IS_TRUE(value[0]);
|
||||||
|
else
|
||||||
|
badParam(key);
|
||||||
|
break;
|
||||||
|
case ESection::dmrplus:
|
||||||
|
if (0 == key.compare(JPORT))
|
||||||
|
data[g_Keys.dmrplus.port] = getUnsigned(value, "DMRPlus Port", 1024, 65535, 8880);
|
||||||
|
else
|
||||||
|
badParam(key);
|
||||||
|
break;
|
||||||
|
case ESection::dplus:
|
||||||
|
if (0 == key.compare(JPORT))
|
||||||
|
data[g_Keys.dplus.port] = getUnsigned(value, "DPlus Port", 1024, 65535, 20001);
|
||||||
|
else
|
||||||
|
badParam(key);
|
||||||
|
break;
|
||||||
|
case ESection::m17:
|
||||||
|
if (0 == key.compare(JPORT))
|
||||||
|
data[g_Keys.m17.port] = getUnsigned(value, "M17 Port", 1024, 65535, 17000);
|
||||||
|
else
|
||||||
|
badParam(key);
|
||||||
|
break;
|
||||||
|
case ESection::mmdvm:
|
||||||
|
if (0 == key.compare(JPORT))
|
||||||
|
data[g_Keys.mmdvm.port] = getUnsigned(value, "MMDVM Port", 1024, 65535, 62030);
|
||||||
|
else if (0 == key.compare(JDEFAULTID))
|
||||||
|
data[g_Keys.mmdvm.defaultid] = getUnsigned(value, "MMDVM DefaultID", 0, 9999999, 0);
|
||||||
|
else
|
||||||
|
badParam(key);
|
||||||
|
break;
|
||||||
|
case ESection::nxdn:
|
||||||
|
if (0 == key.compare(JPORT))
|
||||||
|
data[g_Keys.nxdn.port] = getUnsigned(value, "NDXN Port", 1024, 65535, 41400);
|
||||||
|
else if (0 == key.compare(JAUTOLINKMODULE))
|
||||||
|
setAutolink(JNXDN, g_Keys.nxdn.autolinkmod, value);
|
||||||
|
else if (0 == key.compare(JREFLECTORID))
|
||||||
|
data[g_Keys.nxdn.reflectorid] = getUnsigned(value, "NXDN ReflectorID", 0, 65535, 0);
|
||||||
|
else
|
||||||
|
badParam(key);
|
||||||
|
break;
|
||||||
|
case ESection::p25:
|
||||||
|
if (0 == key.compare(JPORT))
|
||||||
|
data[g_Keys.p25.port] = getUnsigned(value, "P25 Port", 1024, 65535, 41000);
|
||||||
|
else if (0 == key.compare(JAUTOLINKMODULE))
|
||||||
|
setAutolink(JP25, g_Keys.p25.autolinkmod, value);
|
||||||
|
else if (0 == key.compare(JREFLECTORID))
|
||||||
|
data[g_Keys.p25.reflectorid] = getUnsigned(value, "P25 ReflectorID", 0, 16777215, 0);
|
||||||
|
else
|
||||||
|
badParam(key);
|
||||||
|
break;
|
||||||
|
case ESection::urf:
|
||||||
|
if (0 == key.compare(JPORT))
|
||||||
|
data[g_Keys.urf.port] = getUnsigned(value, "URF Port", 1024, 65535, 10017);
|
||||||
|
else
|
||||||
|
badParam(key);
|
||||||
|
break;
|
||||||
|
case ESection::usrp:
|
||||||
|
if (0 == key.compare(JENABLE))
|
||||||
|
data[g_Keys.usrp.enable] = IS_TRUE(value[0]);
|
||||||
|
else if (0 == key.compare(JIPADDRESS))
|
||||||
|
data[g_Keys.usrp.ip] = value;
|
||||||
|
else if (0 == key.compare(JTXPORT))
|
||||||
|
data[g_Keys.usrp.txport] = getUnsigned(value, "USRP TxPort", 1024, 65535, 32000);
|
||||||
|
else if (0 == key.compare(JRXPORT))
|
||||||
|
data[g_Keys.usrp.rxport] = getUnsigned(value, "USRP RxPort", 1024, 65535, 34000);
|
||||||
|
else if (0 == key.compare(JMODULE))
|
||||||
|
data[g_Keys.usrp.module] = value.substr(0, 1);
|
||||||
|
else if (0 == key.compare(JCALLSIGN))
|
||||||
|
data[g_Keys.usrp.callsign] = value;
|
||||||
|
else if (0 == key.compare(JFILEPATH))
|
||||||
|
data[g_Keys.usrp.filepath] = value;
|
||||||
|
else
|
||||||
|
badParam(key);
|
||||||
|
break;
|
||||||
|
case ESection::ysf:
|
||||||
|
if (0 == key.compare(JPORT))
|
||||||
|
data[g_Keys.ysf.port] = getUnsigned(value, "YSF Port", 1024, 65535, 42000);
|
||||||
|
else if (0 == key.compare(JAUTOLINKMODULE))
|
||||||
|
setAutolink(JYSF, g_Keys.ysf.autolinkmod, value);
|
||||||
|
else if (0 == key.compare(JDEFAULTTXFREQ))
|
||||||
|
data[g_Keys.ysf.defaulttxfreq] = getUnsigned(value, "YSF DefaultTxFreq", 40000000, 2600000000, 439000000);
|
||||||
|
else if (0 == key.compare(JDEFAULTRXFREQ))
|
||||||
|
data[g_Keys.ysf.defaultrxfreq] = getUnsigned(value, "YSF DefaultRxFreq", 40000000, 2600000000, 439000000);
|
||||||
|
else if (0 == key.compare(JREGISTRATIONID))
|
||||||
|
data[g_Keys.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[g_Keys.ysf.ysfreflectordb.name] = name;
|
||||||
|
}
|
||||||
|
else if (0 == key.compare(JREGISTRATIONDESCRIPTION))
|
||||||
|
{
|
||||||
|
std::string desc(value);
|
||||||
|
if (desc.size() > 16) desc.resize(16);
|
||||||
|
data[g_Keys.ysf.ysfreflectordb.description] = desc;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
badParam(key);
|
||||||
|
break;
|
||||||
|
case ESection::dmrid:
|
||||||
|
case ESection::nxdnid:
|
||||||
|
case ESection::ysffreq:
|
||||||
|
switch (section)
|
||||||
|
{
|
||||||
|
case ESection::dmrid: pdb = &g_Keys.dmriddb; break;
|
||||||
|
case ESection::nxdnid: pdb = &g_Keys.nxdniddb; break;
|
||||||
|
case ESection::ysffreq: pdb = &g_Keys.ysftxrxdb; break;
|
||||||
|
}
|
||||||
|
if (0 == key.compare(JURL))
|
||||||
|
data[pdb->url] = 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[g_Keys.files.pid] = value;
|
||||||
|
else if (0 == key.compare(JXMLPATH))
|
||||||
|
data[g_Keys.files.xml] = value;
|
||||||
|
else if (0 == key.compare(JJSONPATH))
|
||||||
|
data[g_Keys.files.json] = value;
|
||||||
|
else if (0 == key.compare(JWHITELISTPATH))
|
||||||
|
data[g_Keys.files.white] = value;
|
||||||
|
else if (0 == key.compare(JBLACKLISTPATH))
|
||||||
|
data[g_Keys.files.black] = value;
|
||||||
|
else if (0 == key.compare(JINTERLINKPATH))
|
||||||
|
data[g_Keys.files.interlink] = value;
|
||||||
|
else if (0 == key.compare(JG3TERMINALPATH))
|
||||||
|
data[g_Keys.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, g_Keys.names.callsign, rval))
|
||||||
|
{
|
||||||
|
const auto cs = data[g_Keys.names.callsign].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, g_Keys.names.email, rval);
|
||||||
|
isDefined(ErrorLevel::mild, JNAMES, JCOUNTRY, g_Keys.names.country, rval);
|
||||||
|
isDefined(ErrorLevel::mild, JNAMES, JSPONSOR, g_Keys.names.sponsor, rval);
|
||||||
|
|
||||||
|
// IP Address section
|
||||||
|
// ipv4 bind and external
|
||||||
|
if (isDefined(ErrorLevel::fatal, JIPADDRESSES, JIPV4BINDING, g_Keys.ip.ipv4bind, rval))
|
||||||
|
{
|
||||||
|
if (std::regex_match(data[g_Keys.ip.ipv4bind].get<std::string>(), IPv4RegEx))
|
||||||
|
{
|
||||||
|
if (data.contains(g_Keys.ip.ipv4address))
|
||||||
|
{
|
||||||
|
auto v4 = data[g_Keys.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[g_Keys.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[g_Keys.ip.ipv4address].get<std::string>() << ", is malformed" << std::endl;
|
||||||
|
rval = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ipv6 bind and external
|
||||||
|
if (data.contains(g_Keys.ip.ipv6bind))
|
||||||
|
{
|
||||||
|
if (std::regex_match(data[g_Keys.ip.ipv6bind].get<std::string>(), IPv6RegEx))
|
||||||
|
{
|
||||||
|
if (data.contains(g_Keys.ip.ipv6address))
|
||||||
|
{
|
||||||
|
auto v6 = data[g_Keys.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[g_Keys.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[g_Keys.ip.ipv6address].get<std::string>() << ", is malformed" << std::endl;
|
||||||
|
rval = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data[g_Keys.ip.ipv6bind] = nullptr;
|
||||||
|
data[g_Keys.ip.ipv6address] = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Modules section
|
||||||
|
if (isDefined(ErrorLevel::fatal, JMODULES, JMODULES, g_Keys.modules.modules, rval))
|
||||||
|
{
|
||||||
|
const auto mods(data[g_Keys.modules.modules].get<std::string>());
|
||||||
|
if (data.contains(g_Keys.modules.tcmodules))
|
||||||
|
{
|
||||||
|
const auto tcmods(data[g_Keys.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 one (or three) modules" << std::endl;
|
||||||
|
|
||||||
|
// make sure each transcoded module is configured
|
||||||
|
for (auto c : tcmods)
|
||||||
|
{
|
||||||
|
if (std::string::npos == mods.find(c))
|
||||||
|
{
|
||||||
|
std::cerr << "ERROR: transcoded module '" << c << "' not found in defined modules" << std::endl;
|
||||||
|
rval = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
data[g_Keys.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(g_Keys.modules.descriptor[i]))
|
||||||
|
{
|
||||||
|
std::cout << "WARNING: " << g_Keys.modules.descriptor[i] << " defined for an unconfigured module. Deleting..." << std::endl;
|
||||||
|
data.erase(g_Keys.modules.descriptor[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (! data.contains(g_Keys.modules.descriptor[i]))
|
||||||
|
{
|
||||||
|
std::string value("Module ");
|
||||||
|
value.append(1, 'A'+i);
|
||||||
|
std::cout << "WARNING: " << g_Keys.modules.descriptor[i] << " not found. Setting to " << value << std::endl;
|
||||||
|
data[g_Keys.modules.descriptor[i]] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// "simple" protocols with only a Port
|
||||||
|
isDefined(ErrorLevel::fatal, JDCS, JPORT, g_Keys.dcs.port, rval);
|
||||||
|
isDefined(ErrorLevel::fatal, JDEXTRA, JPORT, g_Keys.dextra.port, rval);
|
||||||
|
isDefined(ErrorLevel::fatal, JDMRPLUS, JPORT, g_Keys.dmrplus.port, rval);
|
||||||
|
isDefined(ErrorLevel::fatal, JDPLUS, JPORT, g_Keys.dplus.port, rval);
|
||||||
|
isDefined(ErrorLevel::fatal, JM17, JPORT, g_Keys.m17.port, rval);
|
||||||
|
isDefined(ErrorLevel::fatal, JURF, JPORT, g_Keys.urf.port, rval);
|
||||||
|
|
||||||
|
// BM
|
||||||
|
if (isDefined(ErrorLevel::fatal, JBRANDMEISTER, JENABLE, g_Keys.bm.enable, rval))
|
||||||
|
{
|
||||||
|
if (GetBoolean(g_Keys.bm.enable))
|
||||||
|
{
|
||||||
|
isDefined(ErrorLevel::fatal, JBRANDMEISTER, JPORT, g_Keys.bm.port, rval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// G3
|
||||||
|
isDefined(ErrorLevel::fatal, JG3, JENABLE, g_Keys.g3.enable, rval);
|
||||||
|
|
||||||
|
// MMDVM
|
||||||
|
isDefined(ErrorLevel::fatal, JMMDVM, JPORT, g_Keys.mmdvm.port, rval);
|
||||||
|
isDefined(ErrorLevel::fatal, JMMDVM, JDEFAULTID, g_Keys.mmdvm.defaultid, rval);
|
||||||
|
|
||||||
|
// NXDN
|
||||||
|
isDefined(ErrorLevel::fatal, JNXDN, JPORT, g_Keys.nxdn.port, rval);
|
||||||
|
checkAutoLink(JNXDN, JAUTOLINKMODULE, g_Keys.nxdn.autolinkmod, rval);
|
||||||
|
isDefined(ErrorLevel::fatal, JNXDN, JREFLECTORID, g_Keys.nxdn.reflectorid, rval);
|
||||||
|
|
||||||
|
// P25
|
||||||
|
isDefined(ErrorLevel::fatal, JP25, JPORT, g_Keys.p25.port, rval);
|
||||||
|
checkAutoLink(JP25, JAUTOLINKMODULE, g_Keys.p25.autolinkmod, rval);
|
||||||
|
isDefined(ErrorLevel::fatal, JP25, JREFLECTORID, g_Keys.p25.reflectorid, rval);
|
||||||
|
|
||||||
|
// USRP
|
||||||
|
if (isDefined(ErrorLevel::fatal, JUSRP, JENABLE, g_Keys.usrp.enable, rval))
|
||||||
|
{
|
||||||
|
if (GetBoolean(g_Keys.usrp.enable))
|
||||||
|
{
|
||||||
|
if (IsString(g_Keys.modules.tcmodules))
|
||||||
|
{
|
||||||
|
if (isDefined(ErrorLevel::fatal, JUSRP, JMODULE, g_Keys.usrp.module, rval))
|
||||||
|
{
|
||||||
|
if (std::string::npos == GetString(g_Keys.modules.tcmodules).find(GetString(g_Keys.usrp.module).at(0)))
|
||||||
|
{
|
||||||
|
std::cerr << "ERROR: [" << JUSRP << ']' << JMODULE << " is not a transcoded module" << std::endl;
|
||||||
|
rval = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isDefined(ErrorLevel::fatal, JUSRP, JIPADDRESS, g_Keys.usrp.ip, rval))
|
||||||
|
{
|
||||||
|
// check for syntax
|
||||||
|
if (! std::regex_match(data[g_Keys.usrp.ip].get<std::string>(), IPv4RegEx))
|
||||||
|
{
|
||||||
|
std::cerr << "ERROR: [" << JUSRP << ']' << JIPADDRESS " '" << data[g_Keys.usrp.ip] << "' is malformed" << std::endl;
|
||||||
|
rval = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
isDefined(ErrorLevel::fatal, JUSRP, JTXPORT, g_Keys.usrp.txport, rval);
|
||||||
|
isDefined(ErrorLevel::fatal, JUSRP, JRXPORT, g_Keys.usrp.rxport, rval);
|
||||||
|
isDefined(ErrorLevel::fatal, JUSRP, JCALLSIGN, g_Keys.usrp.callsign, rval);
|
||||||
|
//if (isDefined(ErrorLevel::fatal, JUSRP, JFILEPATH, g_Keys.usrp.filepath, rval))
|
||||||
|
if (data.contains(g_Keys.usrp.filepath))
|
||||||
|
checkFile(JUSRP, JFILEPATH, data[g_Keys.usrp.filepath]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "ERROR: " << JUSRP << " requires a transoder" << std::endl;
|
||||||
|
rval = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// YSF
|
||||||
|
isDefined(ErrorLevel::fatal, JYSF, JPORT, g_Keys.ysf.port, rval);
|
||||||
|
checkAutoLink(JYSF, JAUTOLINKMODULE, g_Keys.ysf.autolinkmod, rval);
|
||||||
|
isDefined(ErrorLevel::fatal, JYSF, JDEFAULTRXFREQ, g_Keys.ysf.defaultrxfreq, rval);
|
||||||
|
isDefined(ErrorLevel::fatal, JYSF, JDEFAULTTXFREQ, g_Keys.ysf.defaulttxfreq, rval);
|
||||||
|
isDefined(ErrorLevel::mild, JYSF, JREGISTRATIONID, g_Keys.ysf.ysfreflectordb.id, rval);
|
||||||
|
isDefined(ErrorLevel::mild, JYSF, JREGISTRATIONNAME, g_Keys.ysf.ysfreflectordb.name, rval);
|
||||||
|
isDefined(ErrorLevel::mild, JYSF, JREGISTRATIONDESCRIPTION, g_Keys.ysf.ysfreflectordb.description, rval);
|
||||||
|
|
||||||
|
// Databases
|
||||||
|
std::list<std::pair<const std::string, const struct SJsonKeys::DB *>> dbs = {
|
||||||
|
{ JDMRIDDB, &g_Keys.dmriddb },
|
||||||
|
{ JNXDNIDDB, &g_Keys.nxdniddb },
|
||||||
|
{ JYSFTXRXDB, &g_Keys.ysftxrxdb }
|
||||||
|
};
|
||||||
|
for ( auto &item : dbs )
|
||||||
|
{
|
||||||
|
if (isDefined(ErrorLevel::fatal, item.first, JMODE, item.second->mode, rval))
|
||||||
|
{
|
||||||
|
if (ERefreshType::file != GetRefreshType(item.second->mode))
|
||||||
|
{
|
||||||
|
isDefined(ErrorLevel::fatal, item.first, JURL, item.second->url, rval);
|
||||||
|
isDefined(ErrorLevel::fatal, item.first, JREFRESHMIN, item.second->refreshmin, rval);
|
||||||
|
}
|
||||||
|
if (ERefreshType::http != GetRefreshType(item.second->mode))
|
||||||
|
{
|
||||||
|
if (isDefined(ErrorLevel::fatal, item.first, JFILEPATH, item.second->filepath, rval))
|
||||||
|
checkFile(item.first, JFILEPATH, data[item.second->filepath]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Other files
|
||||||
|
isDefined(ErrorLevel::fatal, JFILES, JPIDPATH, g_Keys.files.pid, rval);
|
||||||
|
isDefined(ErrorLevel::fatal, JFILES, JXMLPATH, g_Keys.files.xml, rval);
|
||||||
|
if (isDefined(ErrorLevel::fatal, JFILES, JWHITELISTPATH, g_Keys.files.white, rval))
|
||||||
|
checkFile(JFILES, JWHITELISTPATH, data[g_Keys.files.white]);
|
||||||
|
if (isDefined(ErrorLevel::fatal, JFILES, JBLACKLISTPATH, g_Keys.files.black, rval))
|
||||||
|
checkFile(JFILES, JBLACKLISTPATH, data[g_Keys.files.black]);
|
||||||
|
if (isDefined(ErrorLevel::fatal, JFILES, JINTERLINKPATH, g_Keys.files.interlink, rval))
|
||||||
|
checkFile(JFILES, JINTERLINKPATH, data[g_Keys.files.interlink]);
|
||||||
|
if (data.contains(g_Keys.g3.enable) && GetBoolean(g_Keys.g3.enable))
|
||||||
|
{
|
||||||
|
if (isDefined(ErrorLevel::fatal, JFILES, JG3TERMINALPATH, g_Keys.files.terminal, rval))
|
||||||
|
checkFile(JFILES, JG3TERMINALPATH, data[g_Keys.files.terminal]);
|
||||||
|
}
|
||||||
|
|
||||||
|
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(g_Keys.modules.modules);
|
||||||
|
const auto mods(ismods ? data[g_Keys.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::checkFile(const std::string §ion, const std::string &key, const std::string &filepath) const
|
||||||
|
{
|
||||||
|
struct stat sstat;
|
||||||
|
auto rval = stat(filepath.c_str(), &sstat);
|
||||||
|
if (rval)
|
||||||
|
{
|
||||||
|
std::cout << "WARNING: [" << section << ']' << key << " \"" << filepath << "\": " << strerror(errno) << 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CConfigure::Contains(const std::string &key) const
|
||||||
|
{
|
||||||
|
return data.contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
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: GetUnsigned(): '" << key << "' is not an unsigned value" << std::endl;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::cerr << "ERROR: GetUnsigned(): item at '" << key << "' is not defined" << std::endl;
|
||||||
|
}
|
||||||
|
return u;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CConfigure::GetBoolean(const std::string &key) const
|
||||||
|
{
|
||||||
|
if (data[key].is_boolean())
|
||||||
|
return data[key];
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
SJsonKeys g_Keys;
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
if (3 == argc && strlen(argv[1]) > 1 && '-' == argv[1][0])
|
||||||
|
{
|
||||||
|
CConfigure d;
|
||||||
|
auto rval = d.ReadData(argv[2]);
|
||||||
|
if ('q' != argv[1][1])
|
||||||
|
d.Dump(('n' == argv[1][1]) ? true : false);
|
||||||
|
return rval ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
std::cerr << "Usage: " << argv[0] << " -(q|n|v) FILENAME\nWhere:\n\t-q just prints warnings and errors.\n\t-n also prints keys that begin with an uppercase letter.\n\t-v prints all keys, warnings and errors." << std::endl;
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
/*
|
||||||
|
* 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, g3, 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:
|
||||||
|
CConfigure();
|
||||||
|
bool ReadData(const std::string &path);
|
||||||
|
bool Contains(const std::string &key) const;
|
||||||
|
void Dump(bool justpublic) const;
|
||||||
|
std::string GetString(const std::string &key) const;
|
||||||
|
unsigned GetUnsigned(const std::string &key) const;
|
||||||
|
bool GetBoolean(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;
|
||||||
|
const nlohmann::json &GetData() { return data; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// CFGDATA data;
|
||||||
|
unsigned counter;
|
||||||
|
nlohmann::json data;
|
||||||
|
std::regex IPv4RegEx, IPv6RegEx;
|
||||||
|
|
||||||
|
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 checkFile(const std::string §ion, const std::string &key, const std::string &filepath) 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);
|
||||||
|
};
|
||||||
@ -0,0 +1,68 @@
|
|||||||
|
/*
|
||||||
|
* 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 "CurlGet.h"
|
||||||
|
|
||||||
|
CCurlGet::CCurlGet()
|
||||||
|
{
|
||||||
|
curl_global_init(CURL_GLOBAL_ALL);
|
||||||
|
}
|
||||||
|
|
||||||
|
CCurlGet::~CCurlGet()
|
||||||
|
{
|
||||||
|
curl_global_cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
// callback function writes data to a std::ostream
|
||||||
|
size_t CCurlGet::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;
|
||||||
|
}
|
||||||
|
|
||||||
|
CURLcode CCurlGet::GetURL(const std::string &url, std::stringstream &ss, long timeout)
|
||||||
|
{
|
||||||
|
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, &ss))
|
||||||
|
&& 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);
|
||||||
|
}
|
||||||
|
if (code != CURLE_OK)
|
||||||
|
{
|
||||||
|
std::cout << "ERROR: was not able retrieve data at '" << url << "'\nCurl returned: " << code << std::endl;
|
||||||
|
}
|
||||||
|
return code;
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
/*
|
||||||
|
* 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 <curl/curl.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
class CCurlGet
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
CCurlGet();
|
||||||
|
~CCurlGet();
|
||||||
|
// the contents of the URL will be appended to the stringstream.
|
||||||
|
CURLcode GetURL(const std::string &url, std::stringstream &ss, long timeout = 30);
|
||||||
|
private:
|
||||||
|
static size_t data_write(void* buf, size_t size, size_t nmemb, void* userp);
|
||||||
|
};
|
||||||
@ -1,67 +0,0 @@
|
|||||||
// Created by Antony Chazapis (SV9OAN) on 25/2/2018.
|
|
||||||
// Copyright © 2016 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 "Main.h"
|
|
||||||
#include <string.h>
|
|
||||||
#include "Reflector.h"
|
|
||||||
#include "DExtraPeer.h"
|
|
||||||
#include "DExtraClient.h"
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// constructor
|
|
||||||
|
|
||||||
|
|
||||||
CDextraPeer::CDextraPeer()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
CDextraPeer::CDextraPeer(const CCallsign &callsign, const CIp &ip, const char *modules, const CVersion &version)
|
|
||||||
: CPeer(callsign, ip, modules, version)
|
|
||||||
{
|
|
||||||
std::cout << "Adding DExtra peer" << std::endl;
|
|
||||||
|
|
||||||
// and construct the DExtra clients
|
|
||||||
for ( unsigned i = 0; i < ::strlen(modules); i++ )
|
|
||||||
{
|
|
||||||
// create and append to list
|
|
||||||
m_Clients.push_back(std::make_shared<CDextraClient>(callsign, ip, modules[i], EProtoRev::ambe));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// status
|
|
||||||
|
|
||||||
bool CDextraPeer::IsAlive(void) const
|
|
||||||
{
|
|
||||||
for ( auto it=cbegin(); it!=cend(); it++ )
|
|
||||||
{
|
|
||||||
if (! (*it)->IsAlive())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// revision helper
|
|
||||||
|
|
||||||
int CDextraPeer::GetProtocolRevision(const CVersion &version)
|
|
||||||
{
|
|
||||||
return version.GetMajor();
|
|
||||||
}
|
|
||||||
@ -1,42 +0,0 @@
|
|||||||
// Created by Antony Chazapis (SV9OAN) on 25/2/2018.
|
|
||||||
// Copyright © 2016 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 "Peer.h"
|
|
||||||
#include "DExtraClient.h"
|
|
||||||
|
|
||||||
class CDextraPeer : public CPeer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// constructors
|
|
||||||
CDextraPeer();
|
|
||||||
CDextraPeer(const CCallsign &, const CIp &, const char *, const CVersion &);
|
|
||||||
CDextraPeer(const CDextraPeer &) = delete;
|
|
||||||
|
|
||||||
// status
|
|
||||||
bool IsAlive(void) const;
|
|
||||||
|
|
||||||
// identity
|
|
||||||
EProtocol GetProtocol(void) const { return EProtocol::dextra; }
|
|
||||||
const char *GetProtocolName(void) const { return "DExtra"; }
|
|
||||||
|
|
||||||
// revision helper
|
|
||||||
static int GetProtocolRevision(const CVersion &);
|
|
||||||
};
|
|
||||||
@ -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,47 +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 "DMRIdDir.h"
|
|
||||||
|
|
||||||
class CDmridDirFile : public CDmridDir
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
// constructor
|
|
||||||
CDmridDirFile();
|
|
||||||
|
|
||||||
// destructor
|
|
||||||
~CDmridDirFile() {}
|
|
||||||
|
|
||||||
// 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,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,143 @@
|
|||||||
|
// 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 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 true // QnetGateway can use IPv6
|
||||||
|
#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
|
||||||
|
|
||||||
|
// protocols ---------------------------------------------------
|
||||||
|
|
||||||
|
enum class EProtocol { any, none, dextra, dplus, dcs, g3, bm, urf, dmrplus, dmrmmdvm, nxdn, p25, usrp, ysf, m17 };
|
||||||
|
|
||||||
|
// DExtra
|
||||||
|
#define DEXTRA_KEEPALIVE_PERIOD 3 // in seconds
|
||||||
|
#define DEXTRA_KEEPALIVE_TIMEOUT (DEXTRA_KEEPALIVE_PERIOD*10) // 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 (BM_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
|
||||||
|
#define DMRPLUS_REFLECTOR_COLOUR 1
|
||||||
|
|
||||||
|
// 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)
|
||||||
|
|
||||||
|
// P25
|
||||||
|
#define P25_KEEPALIVE_PERIOD 3 // in seconds
|
||||||
|
#define P25_KEEPALIVE_TIMEOUT (P25_KEEPALIVE_PERIOD*10) // in seconds
|
||||||
|
|
||||||
|
// NXDN
|
||||||
|
#define NXDN_KEEPALIVE_PERIOD 3 // in seconds
|
||||||
|
#define NXDN_KEEPALIVE_TIMEOUT (NXDN_KEEPALIVE_PERIOD*10) // in seconds
|
||||||
|
|
||||||
|
// USRP
|
||||||
|
#define USRP_KEEPALIVE_PERIOD 1 // in seconds
|
||||||
|
#define USRP_KEEPALIVE_TIMEOUT (USRP_KEEPALIVE_PERIOD*10) // in seconds
|
||||||
|
|
||||||
|
// 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 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,71 @@
|
|||||||
|
/*
|
||||||
|
* 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 <string>
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// configuration key names
|
||||||
|
struct SJsonKeys {
|
||||||
|
struct PORTONLY { const std::string port; }
|
||||||
|
dcs { "DCSPort" },
|
||||||
|
dextra { "DExtraPort" },
|
||||||
|
dmrplus { "DMRPlusPort" },
|
||||||
|
dplus { "DPlusPort" },
|
||||||
|
m17 { "M17Port" },
|
||||||
|
urf { "URFPort" };
|
||||||
|
|
||||||
|
struct G3 { const std::string enable; }
|
||||||
|
g3 { "G3Enable" };
|
||||||
|
|
||||||
|
struct BM { const std::string enable, port; }
|
||||||
|
bm { "bmEnable", "bmPort" };
|
||||||
|
|
||||||
|
struct MMDVM { const std::string port, defaultid; }
|
||||||
|
mmdvm { "MMDVMPort", "mmdvmdefaultid" };
|
||||||
|
|
||||||
|
struct NAMES { const std::string callsign, email, country, sponsor; }
|
||||||
|
names { "Callsign", "SysopEmail", "Country", "Sponsor" };
|
||||||
|
|
||||||
|
struct IP { const std::string ipv4bind, ipv4address, ipv6bind, ipv6address, transcoder; }
|
||||||
|
ip { "ipv4bind", "IPv4Address", "ipv6bind", "IPv6Address", "tcaddress" };
|
||||||
|
|
||||||
|
struct MODULES { const std::string modules, tcmodules, descriptor[26]; }
|
||||||
|
modules { "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" };
|
||||||
|
|
||||||
|
struct USRP { const std::string enable, ip, txport, rxport, module, callsign, filepath; }
|
||||||
|
usrp { "usrpEnable", "usrpIpAddress", "urspTxPort", "usrpRxPort", "usrpModule", "usrpCallsign", "usrpFilePath" };
|
||||||
|
|
||||||
|
struct P25NXDN { const std::string port, autolinkmod, reflectorid; }
|
||||||
|
p25 { "P25Port", "P25AutolinkMod", "P25ReflectorID" },
|
||||||
|
nxdn { "NXDNPort", "NXDNAutolinkMod", "NXDNReflectorID" };
|
||||||
|
|
||||||
|
struct YSF { const std::string port, autolinkmod, defaulttxfreq, defaultrxfreq;
|
||||||
|
struct YSLREG { const std::string id, name, description; } ysfreflectordb; }
|
||||||
|
ysf { "YSFPort", "YSFAutoLinkMod", "YSFDefaultTxFreq", "YSFDefaultRxFreq",
|
||||||
|
{ "ysfrefdbid", "ysfrefdbname", "ysfrefdbdesc" } };
|
||||||
|
|
||||||
|
struct DB { const std::string url, mode, refreshmin, filepath; }
|
||||||
|
dmriddb { "dmrIdDbUrl", "dmrIdDbMode", "dmrIdDbRefresh", "dmrIdDbFilePath" },
|
||||||
|
nxdniddb { "nxdnIdDbUrl", "nxdnIdDbMode", "nxdnIdDbRefresh", "nxdnIdDbFilePath" },
|
||||||
|
ysftxrxdb { "ysfIdDbUrl", "ysfIdDbMode", "ysfIdDbRefresh", "ysfIdDbFilePath" };
|
||||||
|
|
||||||
|
struct FILES { const std::string pid, xml, json, white, black, interlink, terminal; }
|
||||||
|
files { "pidFilePath", "xmlFilePath", "jsonFilePath", "whitelistFilePath", "blacklistFilePath", "interlinkFilePath", "g3TerminalFilePath" };
|
||||||
|
};
|
||||||
@ -0,0 +1,125 @@
|
|||||||
|
// 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 <fstream>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <thread>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include "CurlGet.h"
|
||||||
|
#include "Lookup.h"
|
||||||
|
|
||||||
|
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()
|
||||||
|
{
|
||||||
|
const unsigned long wait_cycles = m_Refresh * 6u; // the number of while loops in m_Refresh
|
||||||
|
unsigned long count = 0;
|
||||||
|
while (keep_running)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
bool http_loaded = false;
|
||||||
|
bool file_loaded = false;
|
||||||
|
|
||||||
|
// load http section first, if configured and m_Refresh minutes have lapsed
|
||||||
|
// on the first pass through this while loop (count == 0)
|
||||||
|
if (ERefreshType::file != m_Type && 0ul == count++ % wait_cycles)
|
||||||
|
{
|
||||||
|
// if SIG_INT was received at this point in time,
|
||||||
|
// in might take a bit more than 10 seconds to soft close
|
||||||
|
http_loaded = LoadContentHttp(ss);
|
||||||
|
}
|
||||||
|
|
||||||
|
// load the file if http was loaded or if we haven't loaded since the last mod time
|
||||||
|
if (ERefreshType::http != m_Type)
|
||||||
|
{
|
||||||
|
if (http_loaded || m_LastLoadTime < GetLastModTime())
|
||||||
|
{
|
||||||
|
file_loaded = LoadContentFile(ss);
|
||||||
|
time(&m_LastLoadTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now update the map(s) if anything was loaded
|
||||||
|
if (http_loaded || file_loaded)
|
||||||
|
{
|
||||||
|
Lock();
|
||||||
|
// if m_Type == ERefreshType::both, and if something was deleted from the file,
|
||||||
|
// it won't be purged from the map(s) until http is loaded
|
||||||
|
// It would be a lot of work (iterating on an unordered_map) to do otherwise!
|
||||||
|
if (http_loaded || ERefreshType::file == m_Type)
|
||||||
|
ClearContents();
|
||||||
|
UpdateContent(ss, Eaction::normal);
|
||||||
|
Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// now wait for 10 seconds
|
||||||
|
std::this_thread::sleep_for(std::chrono::seconds(10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLookup::LoadContentHttp(std::stringstream &ss)
|
||||||
|
{
|
||||||
|
CCurlGet get;
|
||||||
|
auto code = get.GetURL(m_Url, ss);
|
||||||
|
return CURLE_OK == code;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLookup::LoadContentFile(std::stringstream &ss)
|
||||||
|
{
|
||||||
|
bool rval = false;
|
||||||
|
std::ifstream file(m_Path);
|
||||||
|
if ( file )
|
||||||
|
{
|
||||||
|
ss << file.rdbuf();
|
||||||
|
file.close();
|
||||||
|
rval = true;
|
||||||
|
}
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLookup::Utility(Eaction action, Esource source)
|
||||||
|
{
|
||||||
|
std::stringstream ss;
|
||||||
|
LoadParameters();
|
||||||
|
auto rval = (Esource::http == source) ? LoadContentHttp(ss) : LoadContentFile(ss);
|
||||||
|
if (rval)
|
||||||
|
UpdateContent(ss, action);
|
||||||
|
return rval;
|
||||||
|
}
|
||||||
@ -0,0 +1,103 @@
|
|||||||
|
// 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>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
#include "Global.h"
|
||||||
|
|
||||||
|
void CLookupDmr::ClearContents()
|
||||||
|
{
|
||||||
|
m_CallsignMap.clear();
|
||||||
|
m_DmridMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLookupDmr::LoadParameters()
|
||||||
|
{
|
||||||
|
m_Type = g_Configure.GetRefreshType(g_Keys.dmriddb.mode);
|
||||||
|
m_Refresh = g_Configure.GetUnsigned(g_Keys.dmriddb.refreshmin);
|
||||||
|
m_Path.assign(g_Configure.GetString(g_Keys.dmriddb.filepath));
|
||||||
|
m_Url.assign(g_Configure.GetString(g_Keys.dmriddb.url));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t CLookupDmr::FindDmrid(const UCallsign &ucs) const
|
||||||
|
{
|
||||||
|
auto found = m_DmridMap.find(ucs);
|
||||||
|
if ( found != m_DmridMap.end() )
|
||||||
|
{
|
||||||
|
return (found->second);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UCallsign *CLookupDmr::FindCallsign(const uint32_t dmrid) const
|
||||||
|
{
|
||||||
|
auto found = m_CallsignMap.find(dmrid);
|
||||||
|
if ( found != m_CallsignMap.end() )
|
||||||
|
{
|
||||||
|
return &(found->second);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLookupDmr::UpdateContent(std::stringstream &ss, Eaction action)
|
||||||
|
{
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(ss, line))
|
||||||
|
{
|
||||||
|
bool failed = true;
|
||||||
|
auto l = atol(line.c_str()); // no throw guarantee
|
||||||
|
if (0L < l && l <= 9999999L)
|
||||||
|
{
|
||||||
|
auto id = uint32_t(l);
|
||||||
|
auto p1 = line.find(';');
|
||||||
|
if (std::string::npos != p1)
|
||||||
|
{
|
||||||
|
auto p2 = line.find(';', ++p1);
|
||||||
|
if (std::string::npos != p2)
|
||||||
|
{
|
||||||
|
const auto cs_str(line.substr(p1, p2-p1));
|
||||||
|
CCallsign cs;
|
||||||
|
cs.SetCallsign(cs_str, false);
|
||||||
|
if (cs.IsValid())
|
||||||
|
{
|
||||||
|
failed = false;
|
||||||
|
if (Eaction::normal == action)
|
||||||
|
{
|
||||||
|
auto key = cs.GetKey();
|
||||||
|
m_DmridMap[key] = id;
|
||||||
|
m_CallsignMap[id] = key;
|
||||||
|
}
|
||||||
|
else if (Eaction::parse == action)
|
||||||
|
{
|
||||||
|
std::cout << id << ';' << cs_str << ";\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Eaction::error_only == action && failed)
|
||||||
|
{
|
||||||
|
std::cout << line << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Eaction::normal == action)
|
||||||
|
std::cout << "DMR Id database size: " << m_DmridMap.size() << std::endl;
|
||||||
|
}
|
||||||
@ -0,0 +1,103 @@
|
|||||||
|
// 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 "Global.h"
|
||||||
|
|
||||||
|
void CLookupNxdn::ClearContents()
|
||||||
|
{
|
||||||
|
m_CallsignMap.clear();
|
||||||
|
m_NxdnidMap.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLookupNxdn::LoadParameters()
|
||||||
|
{
|
||||||
|
m_Type = g_Configure.GetRefreshType(g_Keys.nxdniddb.mode);
|
||||||
|
m_Refresh = g_Configure.GetUnsigned(g_Keys.nxdniddb.refreshmin);
|
||||||
|
m_Path.assign(g_Configure.GetString(g_Keys.nxdniddb.filepath));
|
||||||
|
m_Url.assign(g_Configure.GetString(g_Keys.nxdniddb.url));
|
||||||
|
}
|
||||||
|
|
||||||
|
const UCallsign *CLookupNxdn::FindCallsign(uint16_t nxdnid) const
|
||||||
|
{
|
||||||
|
auto found = m_CallsignMap.find(nxdnid);
|
||||||
|
if ( found != m_CallsignMap.end() )
|
||||||
|
{
|
||||||
|
return &(found->second);
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t CLookupNxdn::FindNXDNid(const UCallsign &ucs) const
|
||||||
|
{
|
||||||
|
auto found = m_NxdnidMap.find(ucs);
|
||||||
|
if ( found != m_NxdnidMap.end() )
|
||||||
|
{
|
||||||
|
return found->second;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLookupNxdn::UpdateContent(std::stringstream &ss, Eaction action)
|
||||||
|
{
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(ss, line))
|
||||||
|
{
|
||||||
|
bool failed = true;
|
||||||
|
auto l = atol(line.c_str()); // no throw guarantee
|
||||||
|
if (0 < l && l < 0x10000)
|
||||||
|
{
|
||||||
|
auto id = uint32_t(l);
|
||||||
|
auto p1 = line.find(',');
|
||||||
|
if (std::string::npos != p1)
|
||||||
|
{
|
||||||
|
auto p2 = line.find(',', ++p1);
|
||||||
|
if (std::string::npos != p2)
|
||||||
|
{
|
||||||
|
const auto cs_str = line.substr(p1, p2-p1);
|
||||||
|
CCallsign cs;
|
||||||
|
cs.SetCallsign(cs_str, false);
|
||||||
|
if (cs.IsValid())
|
||||||
|
{
|
||||||
|
failed = false;
|
||||||
|
if (Eaction::normal == action)
|
||||||
|
{
|
||||||
|
auto key = cs.GetKey();
|
||||||
|
m_NxdnidMap[key] = id;
|
||||||
|
m_CallsignMap[id] = key;
|
||||||
|
}
|
||||||
|
else if (Eaction::parse == action)
|
||||||
|
{
|
||||||
|
std::cout << id << ',' << cs_str << ",\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Eaction::error_only == action && failed)
|
||||||
|
{
|
||||||
|
std::cout << line << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Eaction::normal == action)
|
||||||
|
std::cout << "NXDN Id database size: " << m_NxdnidMap.size() << std::endl;
|
||||||
|
}
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
// 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>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
|
||||||
|
#include "Global.h"
|
||||||
|
|
||||||
|
void CLookupYsf::ClearContents()
|
||||||
|
{
|
||||||
|
m_map.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLookupYsf::LoadParameters()
|
||||||
|
{
|
||||||
|
m_Type = g_Configure.GetRefreshType(g_Keys.ysftxrxdb.mode);
|
||||||
|
m_Refresh = g_Configure.GetUnsigned(g_Keys.ysftxrxdb.refreshmin);
|
||||||
|
m_Path.assign(g_Configure.GetString(g_Keys.ysftxrxdb.filepath));
|
||||||
|
m_Url.assign(g_Configure.GetString(g_Keys.ysftxrxdb.url));
|
||||||
|
m_DefaultTx = g_Configure.GetUnsigned(g_Keys.ysf.defaulttxfreq);
|
||||||
|
m_DefaultRx = g_Configure.GetUnsigned(g_Keys.ysf.defaultrxfreq);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLookupYsf::UpdateContent(std::stringstream &ss, Eaction action)
|
||||||
|
{
|
||||||
|
std::string line;
|
||||||
|
while (std::getline(ss, line))
|
||||||
|
{
|
||||||
|
CCallsign cs;
|
||||||
|
std::string cs_str, tx_str, rx_str;
|
||||||
|
std::istringstream iss(line);
|
||||||
|
std::getline(iss, cs_str, ';');
|
||||||
|
std::getline(iss, tx_str, ';');
|
||||||
|
std::getline(iss, rx_str, ';');
|
||||||
|
cs.SetCallsign(cs_str, false);
|
||||||
|
auto ltx = atoll(tx_str.c_str());
|
||||||
|
auto lrx = atoll(rx_str.c_str());
|
||||||
|
unsigned x = sizeof(lrx);
|
||||||
|
if (ltx > 40000000 && ltx < 0x100000000 && lrx > 40000000 && lrx < 0x100000000 && cs.IsValid())
|
||||||
|
{
|
||||||
|
if (Eaction::parse == action)
|
||||||
|
{
|
||||||
|
std::cout << cs_str << ';' << tx_str << ';' << rx_str << ";\n";
|
||||||
|
}
|
||||||
|
else if (Eaction::normal == action)
|
||||||
|
{
|
||||||
|
m_map[cs.GetKey()] = CYsfNode(ltx, lrx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (Eaction::error_only == action)
|
||||||
|
{
|
||||||
|
std::cout << line << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Eaction::normal == action)
|
||||||
|
std::cout << "YSF frequency database size now is " << m_map.size() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLookupYsf::FindFrequencies(const CCallsign &cs, uint32_t &txfreq, uint32_t &rxfreq)
|
||||||
|
{
|
||||||
|
auto found = m_map.find(cs.GetKey());
|
||||||
|
if (found != m_map.end())
|
||||||
|
{
|
||||||
|
txfreq = found->second.GetTxFrequency();
|
||||||
|
rxfreq = found->second.GetRxFrequency();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
txfreq = m_DefaultTx;
|
||||||
|
rxfreq = m_DefaultRx;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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;
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue