Initial Commit

feature/IndividualCalls
Geoffrey Merck 6 years ago
parent 72f12795c6
commit 0daba1fb0e

@ -0,0 +1,632 @@
/*
* Copyright (C) 2010-2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <cassert>
#include <string>
#include <cstring>
#include "AMBEData.h"
#include "DStarDefines.h"
#include "Utils.h"
CAMBEData::CAMBEData() :
m_rptSeq(0U),
m_outSeq(0U),
m_id(0U),
m_band1(0x00U),
m_band2(0x02U),
m_band3(0x01U),
m_data(NULL),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U),
m_errors(0U),
m_text(),
m_header()
{
m_data = new unsigned char[DV_FRAME_LENGTH_BYTES];
}
CAMBEData::CAMBEData(const CAMBEData& data) :
m_rptSeq(data.m_rptSeq),
m_outSeq(data.m_outSeq),
m_id(data.m_id),
m_band1(data.m_band1),
m_band2(data.m_band2),
m_band3(data.m_band3),
m_data(NULL),
m_yourAddress(data.m_yourAddress),
m_yourPort(data.m_yourPort),
m_myPort(data.m_myPort),
m_errors(data.m_errors),
m_text(data.m_text),
m_header(data.m_header)
{
m_data = new unsigned char[DV_FRAME_LENGTH_BYTES];
::memcpy(m_data, data.m_data, DV_FRAME_LENGTH_BYTES);
}
CAMBEData::~CAMBEData()
{
delete[] m_data;
}
bool CAMBEData::setIcomRepeaterData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort)
{
assert(data != NULL);
assert(length >= 29U);
m_rptSeq = data[4] * 256U + data[5];
m_band1 = data[11];
m_band2 = data[12];
m_band3 = data[13];
m_id = data[14] * 256U + data[15];
m_outSeq = data[16];
// A repeater end packet is longer than usual, so we substitute a normal length set of data
if (isEnd()) {
::memset(m_data, 0x00U, DV_FRAME_LENGTH_BYTES);
::memcpy(m_data, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES);
} else {
::memcpy(m_data, data + 17U, DV_FRAME_LENGTH_BYTES);
}
m_yourAddress = yourAddress;
m_yourPort = yourPort;
return true;
}
bool CAMBEData::setHBRepeaterData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort)
{
assert(data != NULL);
assert(length >= 21U);
m_id = data[5U] * 256U + data[6U];
m_outSeq = data[7U];
m_errors = data[8U];
// A repeater end packet is longer than usual, so we substitute a normal length set of data
if (isEnd()) {
::memset(m_data, 0x00U, DV_FRAME_LENGTH_BYTES);
::memcpy(m_data, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES);
} else {
::memcpy(m_data, data + 9U, DV_FRAME_LENGTH_BYTES);
}
m_yourAddress = yourAddress;
m_yourPort = yourPort;
return true;
}
bool CAMBEData::setG2Data(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort)
{
assert(data != NULL);
assert(length >= 27U);
m_band1 = data[9];
m_band2 = data[10];
m_band3 = data[11];
m_id = data[12] * 256U + data[13];
m_outSeq = data[14];
::memcpy(m_data, data + 15U, DV_FRAME_LENGTH_BYTES);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
return true;
}
bool CAMBEData::setDExtraData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(length >= 27U);
m_band1 = data[9];
m_band2 = data[10];
m_band3 = data[11];
m_id = data[12] * 256U + data[13];
m_outSeq = data[14];
::memcpy(m_data, data + 15U, DV_FRAME_LENGTH_BYTES);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
bool CAMBEData::setDPlusData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(length >= 29U);
if ((data[0] != 0x1D && data[0] != 0x20) || data[1] != 0x80) {
CUtils::dump("Invalid AMBE length from D-Plus", data, length);
return false;
}
m_band1 = data[11];
m_band2 = data[12];
m_band3 = data[13];
m_id = data[14] * 256U + data[15];
m_outSeq = data[16];
if (isEnd()) {
::memset(m_data, 0x00U, DV_FRAME_LENGTH_BYTES);
::memcpy(m_data, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES);
} else {
::memcpy(m_data, data + 17U, DV_FRAME_LENGTH_BYTES);
}
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
bool CAMBEData::setDCSData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(length >= 100U);
m_header.setDCSData(data, length, yourAddress, yourPort, myPort);
m_id = data[44] * 256U + data[43];
m_outSeq = data[45];
::memcpy(m_data, data + 46U, DV_FRAME_LENGTH_BYTES);
m_rptSeq = data[60] * 65536U + data[59] * 256U + data[58];
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
bool CAMBEData::setCCSData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(length >= 100U);
m_header.setCCSData(data, length, yourAddress, yourPort, myPort);
m_id = data[44] * 256U + data[43];
m_outSeq = data[45];
::memcpy(m_data, data + 46U, DV_FRAME_LENGTH_BYTES);
m_rptSeq = data[60] * 65536U + data[59] * 256U + data[58];
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
unsigned int CAMBEData::getIcomRepeaterData(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 32U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'T';
data[3] = 'R';
data[4] = m_rptSeq / 256U; // Packet sequence number
data[5] = m_rptSeq % 256U;
data[6] = 0x73; // Not a response
data[7] = 0x12; // Data type
data[8] = 0x00; // Length MSB
data[9] = 0x13U; // Length LSB
data[10] = 0x20; // AMBE plus Slow Data following
data[11] = m_band1;
data[12] = m_band2;
data[13] = m_band3;
data[14] = m_id / 256U; // Unique session id
data[15] = m_id % 256U;
data[16] = m_outSeq;
::memcpy(data + 17U, m_data, DV_FRAME_LENGTH_BYTES);
return 17U + DV_FRAME_LENGTH_BYTES;
}
unsigned int CAMBEData::getHBRepeaterData(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 21U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'R';
data[3] = 'P';
data[4] = 0x21U;
data[5] = m_id / 256U; // Unique session id
data[6] = m_id % 256U;
data[7] = m_outSeq;
data[8] = 0U;
::memcpy(data + 9U, m_data, DV_FRAME_LENGTH_BYTES);
return 9U + DV_FRAME_LENGTH_BYTES;
}
unsigned int CAMBEData::getG2Data(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 30U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'V';
data[3] = 'T';
data[4] = 0x20;
data[5] = 0x00;
data[6] = 0x15;
data[7] = 0x09;
data[8] = 0x20;
data[9] = m_band1;
data[10] = m_band2;
data[11] = m_band3;
data[12] = m_id / 256U; // Unique session id
data[13] = m_id % 256U;
data[14] = m_outSeq;
::memcpy(data + 15U, m_data, DV_FRAME_LENGTH_BYTES);
return 15U + DV_FRAME_LENGTH_BYTES;
}
unsigned int CAMBEData::getDExtraData(unsigned char* data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 30U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'V';
data[3] = 'T';
data[4] = 0x20;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x00;
data[8] = 0x20;
data[9] = m_band1;
data[10] = m_band2;
data[11] = m_band3;
data[12] = m_id % 256U; // Unique session id
data[13] = m_id / 256U;
data[14] = m_outSeq;
::memcpy(data + 15U, m_data, DV_FRAME_LENGTH_BYTES);
return 15U + DV_FRAME_LENGTH_BYTES;
}
unsigned int CAMBEData::getDPlusData(unsigned char* data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 32U);
if (isEnd()) {
data[0] = 0x20;
data[1] = 0x80;
} else {
data[0] = 0x1D;
data[1] = 0x80;
}
data[2] = 'D';
data[3] = 'S';
data[4] = 'V';
data[5] = 'T';
data[6] = 0x20;
data[7] = 0x00;
data[8] = 0x00;
data[9] = 0x00;
data[10] = 0x20;
data[11] = m_band1;
data[12] = m_band2;
data[13] = m_band3;
data[14] = m_id % 256U; // Unique session id
data[15] = m_id / 256U;
data[16] = m_outSeq;
if (isEnd()) {
::memcpy(data + 17U, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
::memcpy(data + 26U, END_PATTERN_BYTES, END_PATTERN_LENGTH_BYTES); // Add the end flag
return 17U + DV_FRAME_MAX_LENGTH_BYTES;
} else {
// All other cases, just copy the payload
::memcpy(data + 17U, m_data, DV_FRAME_LENGTH_BYTES);
return 17U + DV_FRAME_LENGTH_BYTES;
}
}
unsigned int CAMBEData::getDCSData(unsigned char* data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 100U);
::memset(data, 0x00U, 100U);
data[0] = '0';
data[1] = '0';
data[2] = '0';
data[3] = '1';
data[43] = m_id % 256U; // Unique session id
data[44] = m_id / 256U;
data[45] = m_outSeq;
::memcpy(data + 46U, m_data, DV_FRAME_LENGTH_BYTES);
if (isEnd()) {
data[55] = 0x55U;
data[56] = 0x55U;
data[57] = 0x55U;
}
data[58] = (m_rptSeq >> 0) & 0xFFU;
data[59] = (m_rptSeq >> 8) & 0xFFU;
data[60] = (m_rptSeq >> 16) & 0xFFU;
data[61] = 0x01U;
data[62] = 0x00U;
data[63] = 0x21U;
for (unsigned int i = 0U; i < m_text.size(); i++)
data[64 + i] = m_text[i];
m_header.getDCSData(data, 100U);
return 100U;
}
unsigned int CAMBEData::getCCSData(unsigned char* data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 100U);
::memset(data, 0x00U, 100U);
data[0] = '0';
data[1] = '0';
data[2] = '0';
data[3] = '1';
data[43] = m_id % 256U; // Unique session id
data[44] = m_id / 256U;
data[45] = m_outSeq;
::memcpy(data + 46U, m_data, DV_FRAME_LENGTH_BYTES);
if (isEnd()) {
data[55] = 0x55U;
data[56] = 0x55U;
data[57] = 0x55U;
}
data[58] = (m_rptSeq >> 0) & 0xFFU;
data[59] = (m_rptSeq >> 8) & 0xFFU;
data[60] = (m_rptSeq >> 16) & 0xFFU;
data[61] = 0x01U;
data[62] = 0x00U;
data[63] = 0x21U;
for (unsigned int i = 0U; i < m_text.size(); i++)
data[64 + i] = m_text[i];
data[93U] = 0x36U;
m_header.getCCSData(data, 100U);
return 100U;
}
unsigned int CAMBEData::getId() const
{
return m_id;
}
void CAMBEData::setId(unsigned int id)
{
m_id = id;
}
unsigned char CAMBEData::getBand1() const
{
return m_band1;
}
unsigned char CAMBEData::getBand2() const
{
return m_band2;
}
unsigned char CAMBEData::getBand3() const
{
return m_band3;
}
void CAMBEData::setBand1(unsigned char band)
{
m_band1 = band;
}
void CAMBEData::setBand2(unsigned char band)
{
m_band2 = band;
}
void CAMBEData::setBand3(unsigned char band)
{
m_band3 = band;
}
unsigned int CAMBEData::getRptSeq() const
{
return m_rptSeq;
}
void CAMBEData::setRptSeq(unsigned int seqNo)
{
m_rptSeq = seqNo;
}
unsigned int CAMBEData::getSeq() const
{
return m_outSeq & 0x1FU;
}
void CAMBEData::setSeq(unsigned int seqNo)
{
m_outSeq = seqNo;
}
bool CAMBEData::isEnd() const
{
return (m_outSeq & 0x40U) == 0x40U;
}
void CAMBEData::setEnd(bool end)
{
if (end)
m_outSeq |= 0x40U;
else
m_outSeq &= ~0x40U;
}
bool CAMBEData::isSync() const
{
return (m_outSeq & 0x1FU) == 0x00U;
}
void CAMBEData::setDestination(const in_addr& address, unsigned int port)
{
m_yourAddress = address;
m_yourPort = port;
}
void CAMBEData::setText(const std::string& text)
{
m_text = text;
}
in_addr CAMBEData::getYourAddress() const
{
return m_yourAddress;
}
unsigned int CAMBEData::getYourPort() const
{
return m_yourPort;
}
unsigned int CAMBEData::getMyPort() const
{
return m_myPort;
}
CHeaderData& CAMBEData::getHeader()
{
return m_header;
}
unsigned int CAMBEData::getErrors() const
{
return m_errors;
}
void CAMBEData::setData(const unsigned char *data, unsigned int length)
{
assert(data != NULL);
assert(length >= DV_FRAME_LENGTH_BYTES);
::memcpy(m_data, data, DV_FRAME_LENGTH_BYTES);
}
unsigned int CAMBEData::getData(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= DV_FRAME_LENGTH_BYTES);
::memcpy(data, m_data, DV_FRAME_LENGTH_BYTES);
return DV_FRAME_LENGTH_BYTES;
}
CAMBEData& CAMBEData::operator=(const CAMBEData& data)
{
if (&data != this) {
m_rptSeq = data.m_rptSeq;
m_outSeq = data.m_outSeq;
m_id = data.m_id;
m_band1 = data.m_band1;
m_band2 = data.m_band2;
m_band3 = data.m_band3;
m_yourAddress = data.m_yourAddress;
m_yourPort = data.m_yourPort;
m_myPort = data.m_myPort;
m_errors = data.m_errors;
m_text = data.m_text;
m_header = data.m_header;
::memcpy(m_data, data.m_data, DV_FRAME_LENGTH_BYTES);
}
return *this;
}

@ -0,0 +1,101 @@
/*
* Copyright (C) 2010-2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <netinet/in.h>
#include "HeaderData.h"
class CAMBEData {
public:
CAMBEData();
CAMBEData(const CAMBEData& data);
~CAMBEData();
bool setIcomRepeaterData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort);
bool setHBRepeaterData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort);
bool setG2Data(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort);
bool setDExtraData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setDPlusData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
unsigned int getIcomRepeaterData(unsigned char* data, unsigned int length) const;
unsigned int getHBRepeaterData(unsigned char* data, unsigned int length) const;
unsigned int getDExtraData(unsigned char* data, unsigned int length) const;
unsigned int getDPlusData(unsigned char* data, unsigned int length) const;
unsigned int getDCSData(unsigned char* data, unsigned int length) const;
unsigned int getCCSData(unsigned char* data, unsigned int length) const;
unsigned int getG2Data(unsigned char* data, unsigned int length) const;
unsigned int getId() const;
void setId(unsigned int id);
unsigned char getBand1() const;
unsigned char getBand2() const;
unsigned char getBand3() const;
void setBand1(unsigned char band);
void setBand2(unsigned char band);
void setBand3(unsigned char band);
unsigned int getRptSeq() const;
void setRptSeq(unsigned int seqNo);
unsigned int getSeq() const;
void setSeq(unsigned int seqNo);
bool isEnd() const;
void setEnd(bool end);
bool isSync() const;
void setData(const unsigned char* data, unsigned int length);
unsigned int getData(unsigned char* data, unsigned int length) const;
void setDestination(const in_addr& address, unsigned int port);
void setText(const std::string& text);
in_addr getYourAddress() const;
unsigned int getYourPort() const;
unsigned int getMyPort() const;
unsigned int getErrors() const;
CHeaderData& getHeader();
CAMBEData& operator=(const CAMBEData& data);
private:
unsigned int m_rptSeq;
unsigned char m_outSeq;
unsigned int m_id;
unsigned char m_band1;
unsigned char m_band2;
unsigned char m_band3;
unsigned char* m_data;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
unsigned int m_errors;
std::string m_text;
CHeaderData m_header;
};

@ -0,0 +1,157 @@
/*
* Copyright (C) 2014 by Jonathan Naylor G4KLX
* Copyright (c) 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 <cassert>
#include "DStarDefines.h"
#include "HeaderData.h"
#include "AnnouncementUnit.h"
#include "Utils.h"
CAnnouncementUnit::CAnnouncementUnit(IRepeaterCallback* handler, const std::string& callsign, const std::string& fileName, const std::string& name) :
m_handler(handler),
m_callsign(callsign),
m_fileName(fileName),
m_name(name),
m_reader(NULL),
m_status(NS_IDLE),
m_timer(1000U, REPLY_TIME),
m_out(0U),
m_id(0U)
{
assert(handler != NULL);
m_name.resize(SHORT_CALLSIGN_LENGTH, ' ');
printf("Connected '%s' to %s using file - %s\n", name.c_str(), callsign.c_str(), fileName.c_str());
}
CAnnouncementUnit::~CAnnouncementUnit()
{
}
void CAnnouncementUnit::sendAnnouncement()
{
if (m_status != NS_IDLE)
return;
struct stat sbuf;
if (stat(m_fileName.c_str(), &sbuf))
return;
m_reader = new CDVTOOLFileReader;
bool ret = m_reader->open(m_fileName);
if (!ret) {
delete m_reader;
m_reader = NULL;
return;
}
m_status = NS_WAIT;
m_timer.start();
}
void CAnnouncementUnit::clock(unsigned int ms)
{
m_timer.clock(ms);
if (m_status == NS_WAIT && m_timer.hasExpired()) {
m_reader->read(); // Pop the first record off the file
m_id = CHeaderData::createId();
CHeaderData header;
header.setMyCall1(m_callsign);
header.setMyCall2(m_name);
header.setYourCall("CQCQCQ ");
header.setId(m_id);
m_handler->process(header, DIR_INCOMING, AS_INFO);
m_timer.stop();
m_out = 0U;
m_status = NS_TRANSMIT;
// m_time.Start();
m_start = std::chrono::high_resolution_clock::now();
return;
}
if (m_status == NS_TRANSMIT) {
unsigned int needed = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - m_start).count();
needed /= DSTAR_FRAME_TIME_MS;
// unsigned int needed = m_time.Time() / DSTAR_FRAME_TIME_MS;
while (m_out < needed) {
DVTFR_TYPE type = m_reader->read();
if (type != DVTFR_DATA) {
m_out = 0U;
m_status = NS_IDLE;
m_timer.stop();
m_reader->close();
delete m_reader;
m_reader = NULL;
return;
}
CAMBEData* data = m_reader->readData();
data->setId(m_id);
m_out++;
m_handler->process(*data, DIR_INCOMING, AS_INFO);
bool end = data->isEnd();
delete data;
if (end) {
m_out = 0U;
m_status = NS_IDLE;
m_reader->close();
delete m_reader;
m_reader = NULL;
m_timer.stop();
return;
}
}
return;
}
}
void CAnnouncementUnit::cancel()
{
m_status = NS_IDLE;
m_out = 0U;
m_id = 0U;
if (m_reader != NULL) {
m_reader->close();
delete m_reader;
m_reader = NULL;
}
m_timer.stop();
}

@ -0,0 +1,59 @@
/*
* Copyright (C) 2014 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <chrono>
#include "DVTOOLFileReader.h"
#include "RepeaterCallback.h"
#include "AMBEData.h"
#include "Timer.h"
#include "Defs.h"
enum ANNOUNCEMENT_STATUS {
NS_IDLE,
NS_WAIT,
NS_TRANSMIT
};
class CAnnouncementUnit {
public:
CAnnouncementUnit(IRepeaterCallback* handler, const std::string& callsign, const std::string& fileName, const std::string& name);
~CAnnouncementUnit();
void sendAnnouncement();
void cancel();
void clock(unsigned int ms);
private:
IRepeaterCallback* m_handler;
std::string m_callsign;
std::string m_fileName;
std::string m_name;
CDVTOOLFileReader* m_reader;
ANNOUNCEMENT_STATUS m_status;
CTimer m_timer;
unsigned int m_out;
unsigned int m_id;
std::chrono::high_resolution_clock::time_point m_start;
};

@ -0,0 +1,504 @@
/*
* Copyright (C) 2011-2014 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <cassert>
#include <cstring>
#include <cstdlib>
#include <stdio.h>
#include "DStarDefines.h"
#include "HeaderData.h"
#include "AudioUnit.h"
#include "Utils.h"
unsigned char* CAudioUnit::m_ambe = NULL;
unsigned int CAudioUnit::m_ambeLength = 0U;
std::map<std::string, CIndexRecord *> CAudioUnit::m_index;
TEXT_LANG CAudioUnit::m_language = TL_ENGLISH_UK;
const unsigned int MAX_FRAMES = 60U * DSTAR_FRAMES_PER_SEC;
const unsigned int SILENCE_LENGTH = 10U;
void CAudioUnit::initialise()
{
}
void CAudioUnit::setLanguage(TEXT_LANG language)
{
m_language = language;
std::string ambeFileName;
std::string indxFileName;
switch (language) {
case TL_DEUTSCH:
ambeFileName = "de_DE.ambe";
indxFileName = "de_DE.indx";
break;
case TL_DANSK:
ambeFileName = "dk_DK.ambe";
indxFileName = "dk_DK.indx";
break;
case TL_ITALIANO:
ambeFileName = "it_IT.ambe";
indxFileName = "it_IT.indx";
break;
case TL_FRANCAIS:
ambeFileName = "fr_FR.ambe";
indxFileName = "fr_FR.indx";
break;
case TL_ESPANOL:
ambeFileName = "es_ES.ambe";
indxFileName = "es_ES.indx";
break;
case TL_SVENSKA:
ambeFileName = "se_SE.ambe";
indxFileName = "se_SE.indx";
break;
case TL_POLSKI:
ambeFileName = "pl_PL.ambe";
indxFileName = "pl_PL.indx";
break;
case TL_ENGLISH_US:
ambeFileName = "en_US.ambe";
indxFileName = "en_US.indx";
break;
case TL_NORSK:
ambeFileName = "no_NO.ambe";
indxFileName = "no_NO.indx";
break;
default:
ambeFileName = "en_GB.ambe";
indxFileName = "en_GB.indx";
break;
}
bool ret = readAMBE(ambeFileName);
if (!ret) {
delete[] m_ambe;
m_ambe = NULL;
return;
}
ret = readIndex(indxFileName);
if (!ret) {
delete[] m_ambe;
m_ambe = NULL;
}
}
void CAudioUnit::finalise()
{
for (std::map<std::string, CIndexRecord *>::iterator it = m_index.begin(); it != m_index.end(); ++it)
delete it->second;
delete[] m_ambe;
}
CAudioUnit::CAudioUnit(IRepeaterCallback* handler, const std::string& callsign) :
m_handler(handler),
m_callsign(callsign),
m_encoder(),
m_status(AS_IDLE),
m_linkStatus(LS_NONE),
m_tempLinkStatus(LS_NONE),
m_text(),
m_tempText(),
m_reflector(),
m_tempReflector(),
m_hasTemporary(false),
m_timer(1000U, REPLY_TIME),
m_data(NULL),
m_in(0U),
m_out(0U),
m_seqNo(0U) //,
//m_time()
{
assert(handler != NULL);
m_data = new CAMBEData*[MAX_FRAMES];
for (unsigned int i = 0U; i < MAX_FRAMES; i++)
m_data[i] = NULL;
}
CAudioUnit::~CAudioUnit()
{
delete[] m_data;
}
void CAudioUnit::sendStatus()
{
if (m_ambe == NULL)
return;
if (m_status != AS_IDLE)
return;
m_status = AS_WAIT;
m_timer.start();
}
void CAudioUnit::setStatus(LINK_STATUS status, const std::string& reflector, const std::string& text)
{
m_linkStatus = status;
m_reflector = reflector;
m_text = text;
}
void CAudioUnit::setTempStatus(LINK_STATUS status, const std::string& reflector, const std::string& text)
{
m_tempLinkStatus = status;
m_tempReflector = reflector;
m_tempText = text;
m_hasTemporary = true;
}
void CAudioUnit::clock(unsigned int ms)
{
m_timer.clock(ms);
if (m_status == AS_WAIT && m_timer.hasExpired()) {
if (m_hasTemporary) {
sendStatus(m_tempLinkStatus, m_tempReflector, m_tempText);
m_hasTemporary = false;
} else {
sendStatus(m_linkStatus, m_reflector, m_text);
}
m_timer.stop();
m_out = 0U;
m_seqNo = 0U;
m_status = AS_TRANSMIT;
m_time = std::chrono::high_resolution_clock::now();
return;
}
if (m_status == AS_TRANSMIT) {
std::chrono::high_resolution_clock::time_point hrctp = std::chrono::high_resolution_clock::now();
auto elapse = std::chrono::duration_cast<std::chrono::milliseconds>(hrctp - m_time);
unsigned int needed = elapse.count() / DSTAR_FRAME_TIME_MS;
while (m_out < needed) {
CAMBEData* data = m_data[m_out];
m_data[m_out] = NULL;
m_out++;
if (m_in == m_out)
data->setEnd(true);
m_handler->process(*data, DIR_INCOMING, AS_INFO);
delete data;
if (m_in == m_out) {
m_in = 0U;
m_out = 0U;
m_status = AS_IDLE;
m_timer.stop();
return;
}
}
return;
}
}
void CAudioUnit::cancel()
{
for (unsigned int i = 0U; i < MAX_FRAMES; i++) {
if (m_data[i] != NULL) {
delete m_data[i];
m_data[i] = NULL;
}
}
m_status = AS_IDLE;
m_out = 0U;
m_in = 0U;
m_timer.stop();
}
bool CAudioUnit::lookup(unsigned int id, const std::string &name)
{
CIndexRecord* info = m_index[name];
if (info == NULL) {
// wxLogError(wxT("Cannot find the AMBE index for *%s*"), name.c_str());
return false;
}
unsigned int start = info->getStart();
unsigned int length = info->getLength();
for (unsigned int i = 0U; i < length; i++) {
unsigned char* dataIn = m_ambe + (start + i) * VOICE_FRAME_LENGTH_BYTES;
CAMBEData* dataOut = new CAMBEData;
dataOut->setSeq(m_seqNo);
dataOut->setId(id);
unsigned char buffer[DV_FRAME_LENGTH_BYTES];
memcpy(buffer + 0U, dataIn, VOICE_FRAME_LENGTH_BYTES);
// Insert sync bytes when the sequence number is zero, slow data otherwise
if (m_seqNo == 0U) {
memcpy(buffer + VOICE_FRAME_LENGTH_BYTES, DATA_SYNC_BYTES, DATA_FRAME_LENGTH_BYTES);
m_encoder.sync();
} else {
m_encoder.getTextData(buffer + VOICE_FRAME_LENGTH_BYTES);
}
dataOut->setData(buffer, DV_FRAME_LENGTH_BYTES);
m_seqNo++;
if (m_seqNo == 21)
m_seqNo = 0;
m_data[m_in] = dataOut;
m_in++;
}
return true;
}
void CAudioUnit::spellReflector(unsigned int id, const std::string &reflector)
{
unsigned int length = reflector.size();
for (unsigned int i = 0; i < (length - 1); i++) {
std::string c = reflector.substr(i, 1);
if (c.compare(" "))
lookup(id, c);
}
char c = reflector.at(length - 1);
if (c == ' ')
return;
std::string cstr;
cstr.push_back(c);
if (m_linkStatus == LS_LINKING_DCS || m_linkStatus == LS_LINKED_DCS ||
m_linkStatus == LS_LINKING_CCS || m_linkStatus == LS_LINKED_CCS) {
lookup(id, cstr);
return;
}
switch (c) {
case 'A':
lookup(id, "alpha");
break;
case 'B':
lookup(id, "bravo");
break;
case 'C':
lookup(id, "charlie");
break;
case 'D':
lookup(id, "delta");
break;
default:
lookup(id, cstr);
break;
}
}
bool CAudioUnit::readAMBE(const std::string& name)
{
std::string dir = std::getenv("HOME");
std::string fileName = dir + "/" + name;
struct stat sbuf;
if (stat(fileName.c_str(), &sbuf)) {
printf("File %s not readable\n", fileName.c_str());
fileName.assign(CFG_DIR);
fileName.append("/data/");
fileName += name;
if (stat(fileName.c_str(), &sbuf)) {
printf("File %s not readable\n", fileName.c_str());
return false;
}
}
unsigned int fsize = sbuf.st_size;
FILE *file = fopen(fileName.c_str(), "rb");
if (NULL == file) {
printf("Cannot open %s for reading\n", fileName.c_str());
return false;
}
printf("Reading %s\n", fileName.c_str());
unsigned char buffer[VOICE_FRAME_LENGTH_BYTES];
size_t n = fread(buffer, 4, 1, file);
if (n != 4) {
printf("Unable to read the header from %s\n", fileName.c_str());
fclose(file);
return false;
}
if (memcmp(buffer, "AMBE", 4)) {
printf("Invalid header from %s\n", fileName.c_str());
fclose(file);
return false;
}
// Length of the file minus the header
unsigned int length = fsize - 4U;
// Hold the file data plus silence at the end
m_ambe = new unsigned char[length + SILENCE_LENGTH * VOICE_FRAME_LENGTH_BYTES];
m_ambeLength = length / VOICE_FRAME_LENGTH_BYTES;
// Add silence to the beginning of the buffer
unsigned char* p = m_ambe;
for (unsigned int i = 0U; i < SILENCE_LENGTH; i++, p += VOICE_FRAME_LENGTH_BYTES)
memcpy(p, NULL_AMBE_DATA_BYTES, VOICE_FRAME_LENGTH_BYTES);
n = fread(p, length, 1, file);
if (n != length) {
printf("Unable to read the AMBE data from %s\n", fileName.c_str());
fclose(file);
delete[] m_ambe;
m_ambe = NULL;
return false;
}
fclose(file);
return true;
}
bool CAudioUnit::readIndex(const std::string& name)
{
std::string dir = std::getenv("HOME");
std::string fileName = dir + "/" + name;
struct stat sbuf;
if (stat(fileName.c_str(), &sbuf)) {
printf("File %s not readable\n", fileName.c_str());
fileName.assign(CFG_DIR);
fileName.append("/data/");
fileName += name;
if (stat(fileName.c_str(), &sbuf)) {
printf("File %s not readable\n", fileName.c_str());
return false;
}
}
FILE *file = fopen(fileName.c_str(), "r");
if (NULL == file) {
printf("Cannot open %s for reading\n", fileName.c_str());
return false;
}
// Add a silence entry at the beginning
m_index[" "] = new CIndexRecord(" ", 0, SILENCE_LENGTH);
printf("Reading %s\n", fileName.c_str());
char line[128];
while (fgets(line, 128, file)) {
if (strlen(line) && '#'!=line[0]) {
const std::string space(" \t\r\n");
std::string name(strtok(line, space.c_str()));
std::string strt(strtok(NULL, space.c_str()));
std::string leng(strtok(NULL, space.c_str()));
if (name.size() && strt.size() && leng.size()) {
unsigned long start = std::stoul(strt);
unsigned long length = std::stoul(leng);
if (start >= m_ambeLength || (start + length) >= m_ambeLength)
printf("The start or end for *%s* is out of range, start: %lu, end: %lu\n", name.c_str(), start, start + length);
else
m_index[name] = new CIndexRecord(name, start + SILENCE_LENGTH, length);
}
}
}
fclose(file);
return true;
}
void CAudioUnit::sendStatus(LINK_STATUS status, const std::string& reflector, const std::string &text)
{
m_encoder.setTextData(text);
// Create the message
unsigned int id = CHeaderData::createId();
lookup(id, " ");
lookup(id, " ");
lookup(id, " ");
lookup(id, " ");
bool found;
switch (status) {
case LS_NONE:
lookup(id, "notlinked");
break;
case LS_LINKED_CCS:
case LS_LINKED_DCS:
// case LS_LINKED_DPLUS:
case LS_LINKED_DEXTRA:
case LS_LINKED_LOOPBACK:
found = lookup(id, "linkedto");
if (!found) {
lookup(id, "linked");
lookup(id, "2");
}
spellReflector(id, reflector);
break;
default:
found = lookup(id, "linkingto");
if (!found) {
lookup(id, "linking");
lookup(id, "2");
}
spellReflector(id, reflector);
break;
}
lookup(id, " ");
lookup(id, " ");
lookup(id, " ");
lookup(id, " ");
// RPT1 and RPT2 will be filled in later
CHeaderData header;
header.setMyCall1(m_callsign);
header.setMyCall2("INFO");
header.setYourCall("CQCQCQ ");
header.setId(id);
m_handler->process(header, DIR_INCOMING, AS_INFO);
}

@ -0,0 +1,119 @@
/*
* Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <map>
#include <chrono>
#include "RepeaterCallback.h"
#include "SlowDataEncoder.h"
#include "AMBEData.h"
#include "Timer.h"
#include "Defs.h"
class CIndexRecord {
public:
CIndexRecord(const std::string& name, unsigned int start, unsigned int length) :
m_name(name),
m_start(start),
m_length(length)
{
}
std::string getName() const
{
return m_name;
}
unsigned int getStart() const
{
return m_start;
}
unsigned int getLength() const
{
return m_length;
}
private:
std::string m_name;
unsigned int m_start;
unsigned int m_length;
};
enum AUDIO_STATUS {
AS_IDLE,
AS_WAIT,
AS_TRANSMIT
};
class CAudioUnit {
public:
CAudioUnit(IRepeaterCallback* handler, const std::string& callsign);
~CAudioUnit();
void sendStatus();
void setStatus(LINK_STATUS status, const std::string& reflector, const std::string& text);
void setTempStatus(LINK_STATUS status, const std::string& reflector, const std::string& text);
void cancel();
void clock(unsigned int ms);
static void initialise();
static void setLanguage(TEXT_LANG language);
static void finalise();
private:
static std::map<std::string, CIndexRecord *> m_index;
static unsigned char* m_ambe;
static unsigned int m_ambeLength;
static TEXT_LANG m_language;
IRepeaterCallback* m_handler;
std::string m_callsign;
CSlowDataEncoder m_encoder;
AUDIO_STATUS m_status;
LINK_STATUS m_linkStatus;
LINK_STATUS m_tempLinkStatus;
std::string m_text;
std::string m_tempText;
std::string m_reflector;
std::string m_tempReflector;
bool m_hasTemporary;
CTimer m_timer;
CAMBEData** m_data;
unsigned int m_in;
unsigned int m_out;
unsigned int m_seqNo;
std::chrono::high_resolution_clock::time_point m_time;
bool lookup(unsigned int id, const std::string& name);
void spellReflector(unsigned int id, const std::string& reflector);
void sendStatus(LINK_STATUS status, const std::string& reflector, const std::string& text);
static bool readAMBE(const std::string& name);
static bool readIndex(const std::string& name);
};

@ -0,0 +1,144 @@
/*
* Copyright (C) 2009 by Jonathan Naylor, G4KLX
*
* 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; version 2 of the License.
*
* 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.
*/
#include <cassert>
#include "CCITTChecksum.h"
#include "Utils.h"
static const unsigned short ccittTab[] = {
0x0000,0x1189,0x2312,0x329b,0x4624,0x57ad,0x6536,0x74bf,
0x8c48,0x9dc1,0xaf5a,0xbed3,0xca6c,0xdbe5,0xe97e,0xf8f7,
0x1081,0x0108,0x3393,0x221a,0x56a5,0x472c,0x75b7,0x643e,
0x9cc9,0x8d40,0xbfdb,0xae52,0xdaed,0xcb64,0xf9ff,0xe876,
0x2102,0x308b,0x0210,0x1399,0x6726,0x76af,0x4434,0x55bd,
0xad4a,0xbcc3,0x8e58,0x9fd1,0xeb6e,0xfae7,0xc87c,0xd9f5,
0x3183,0x200a,0x1291,0x0318,0x77a7,0x662e,0x54b5,0x453c,
0xbdcb,0xac42,0x9ed9,0x8f50,0xfbef,0xea66,0xd8fd,0xc974,
0x4204,0x538d,0x6116,0x709f,0x0420,0x15a9,0x2732,0x36bb,
0xce4c,0xdfc5,0xed5e,0xfcd7,0x8868,0x99e1,0xab7a,0xbaf3,
0x5285,0x430c,0x7197,0x601e,0x14a1,0x0528,0x37b3,0x263a,
0xdecd,0xcf44,0xfddf,0xec56,0x98e9,0x8960,0xbbfb,0xaa72,
0x6306,0x728f,0x4014,0x519d,0x2522,0x34ab,0x0630,0x17b9,
0xef4e,0xfec7,0xcc5c,0xddd5,0xa96a,0xb8e3,0x8a78,0x9bf1,
0x7387,0x620e,0x5095,0x411c,0x35a3,0x242a,0x16b1,0x0738,
0xffcf,0xee46,0xdcdd,0xcd54,0xb9eb,0xa862,0x9af9,0x8b70,
0x8408,0x9581,0xa71a,0xb693,0xc22c,0xd3a5,0xe13e,0xf0b7,
0x0840,0x19c9,0x2b52,0x3adb,0x4e64,0x5fed,0x6d76,0x7cff,
0x9489,0x8500,0xb79b,0xa612,0xd2ad,0xc324,0xf1bf,0xe036,
0x18c1,0x0948,0x3bd3,0x2a5a,0x5ee5,0x4f6c,0x7df7,0x6c7e,
0xa50a,0xb483,0x8618,0x9791,0xe32e,0xf2a7,0xc03c,0xd1b5,
0x2942,0x38cb,0x0a50,0x1bd9,0x6f66,0x7eef,0x4c74,0x5dfd,
0xb58b,0xa402,0x9699,0x8710,0xf3af,0xe226,0xd0bd,0xc134,
0x39c3,0x284a,0x1ad1,0x0b58,0x7fe7,0x6e6e,0x5cf5,0x4d7c,
0xc60c,0xd785,0xe51e,0xf497,0x8028,0x91a1,0xa33a,0xb2b3,
0x4a44,0x5bcd,0x6956,0x78df,0x0c60,0x1de9,0x2f72,0x3efb,
0xd68d,0xc704,0xf59f,0xe416,0x90a9,0x8120,0xb3bb,0xa232,
0x5ac5,0x4b4c,0x79d7,0x685e,0x1ce1,0x0d68,0x3ff3,0x2e7a,
0xe70e,0xf687,0xc41c,0xd595,0xa12a,0xb0a3,0x8238,0x93b1,
0x6b46,0x7acf,0x4854,0x59dd,0x2d62,0x3ceb,0x0e70,0x1ff9,
0xf78f,0xe606,0xd49d,0xc514,0xb1ab,0xa022,0x92b9,0x8330,
0x7bc7,0x6a4e,0x58d5,0x495c,0x3de3,0x2c6a,0x1ef1,0x0f78};
CCCITTChecksum::CCCITTChecksum() :
m_crc(0xFFFF)
{
}
CCCITTChecksum::~CCCITTChecksum()
{
}
void CCCITTChecksum::update(const unsigned char* data, unsigned int length)
{
assert(data != NULL);
for (unsigned int i = 0U; i < length; i++) {
unsigned short byte = data[i];
unsigned short tmp = (m_crc & 0x00FF) ^ byte;
m_crc = (m_crc >> 8) ^ ccittTab[tmp];
}
}
void CCCITTChecksum::update(const bool* data)
{
assert(data != NULL);
unsigned short byte = CUtils::bitsToByte(data);
unsigned short tmp = (m_crc & 0x00FF) ^ byte;
m_crc = (m_crc >> 8) ^ ccittTab[tmp];
}
void CCCITTChecksum::result(unsigned char* data) // XX FIXME
{
assert(data != NULL);
m_crc = ~m_crc;
unsigned short tmp = m_crc;
m_crc = (m_crc << 8) | (tmp >> 8 & 0xFF);
data[0] = (m_crc >> 8) & 0xFF;
data[1] = (m_crc >> 0) & 0xFF;
}
void CCCITTChecksum::result(bool* data)
{
assert(data != NULL);
m_crc = ~m_crc;
unsigned short tmp = m_crc;
m_crc = (m_crc << 8) | (tmp >> 8 & 0xFF);
unsigned short mask = 0x8000;
for (unsigned int i = 0U; i < 16U; i++, mask >>= 1)
data[i] = (m_crc & mask) ? true : false;
}
bool CCCITTChecksum::check(const unsigned char* data)
{
assert(data != NULL);
unsigned char sum[2];
result(sum);
return sum[0] == data[0] && sum[1] == data[1];
}
bool CCCITTChecksum::check(const bool* data)
{
assert(data != NULL);
bool sum[16];
result(sum);
for (unsigned int i = 0U; i < 16U; i++)
if (sum[i] != data[i])
return false;
return true;
}
void CCCITTChecksum::reset()
{
m_crc = 0xFFFF;
}

@ -0,0 +1,36 @@
/*
* Copyright (C) 2009,2013 by Jonathan Naylor, G4KLX
*
* 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; version 2 of the License.
*
* 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.
*/
#pragma once
#include <stdint.h>
class CCCITTChecksum {
public:
CCCITTChecksum();
~CCCITTChecksum();
void update(const unsigned char* data, unsigned int length);
void update(const bool* data);
void result(unsigned char* data);
void result(bool* data);
bool check(const unsigned char* data);
bool check(const bool* data);
void reset();
private:
uint16_t m_crc;
};

@ -0,0 +1,41 @@
/*
* Copyright (C) 2013 by Jonathan Naylor G4KLX
*
* 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 <string>
#include "DStarDefines.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "Defs.h"
class ICCSCallback {
public:
virtual bool process(CHeaderData& header, DIRECTION direction, AUDIO_SOURCE source) = 0;
virtual bool process(CAMBEData& data, DIRECTION direction, AUDIO_SOURCE source) = 0;
virtual void ccsLinkMade(const std::string& callsign, DIRECTION direction) = 0;
virtual void ccsLinkFailed(const std::string& dtmf, DIRECTION direction) = 0;
virtual void ccsLinkEnded(const std::string& callsign, DIRECTION direction) = 0;
private:
};

@ -0,0 +1,201 @@
/*
* Copyright (C) 2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <cassert>
#include <cstring>
#include "DStarDefines.h"
#include "CCSData.h"
#include "Utils.h"
CCCSData::CCCSData(const std::string& local, double latitude, double longitude, double frequency, double offset, const std::string& description1, const std::string& description2, const std::string& url, CC_TYPE type) :
m_local(local),
m_remote(),
m_latitude(latitude),
m_longitude(longitude),
m_frequency(frequency),
m_offset(offset),
m_description1(description1),
m_description2(description2),
m_url(url),
m_type(type),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U)
{
}
CCCSData::CCCSData(const std::string& local, const std::string& remote, CC_TYPE type) :
m_local(local),
m_remote(remote),
m_latitude(0.0),
m_longitude(0.0),
m_frequency(0.0),
m_offset(0.0),
m_description1(),
m_description2(),
m_url(),
m_type(type),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U)
{
}
CCCSData::CCCSData() :
m_local(),
m_remote(),
m_latitude(0.0),
m_longitude(0.0),
m_frequency(0.0),
m_offset(0.0),
m_description1(),
m_description2(),
m_url(),
m_type(),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U)
{
}
CCCSData::~CCCSData()
{
}
bool CCCSData::setCCSData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
char buffer[LONG_CALLSIGN_LENGTH+1];
switch (length) {
case 100U:
memcpy(buffer, data, LONG_CALLSIGN_LENGTH);
buffer[LONG_CALLSIGN_LENGTH] = (char)0;
m_remote = std::string(buffer);
if (0 == memcmp(data + 8U, "0001", 4U)) {
m_type = CT_TERMINATE;
} else {
// CUtils::dump(wxT("Invalid CCS packet"), data, length);
return false;
}
memcpy(buffer, data+12, LONG_CALLSIGN_LENGTH);
buffer[LONG_CALLSIGN_LENGTH] = (char)0;
m_local = std::string(buffer);
break;
case 20U:
if (0 == memcmp(data + 0U, "DTMF_CALL:", 10U)) {
m_type = CT_DTMFFOUND;
} else {
CUtils::dump("Invalid CCS packet", data, length);
return false;
}
memcpy(buffer, data+10, LONG_CALLSIGN_LENGTH);
buffer[LONG_CALLSIGN_LENGTH] = (char)0;
m_remote = std::string(buffer);
break;
case 17U:
if (0 == memcmp(data + 0U, "NODTMFCALL", 10U)) {
m_type = CT_DTMFNOTFOUND;
} else {
CUtils::dump("Invalid CCS packet", data, length);
return false;
}
break;
default:
CUtils::dump("Invalid CCS packet", data, length);
return false;
}
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
unsigned int CCCSData::getCCSData(unsigned char* data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 133U);
if (m_type == CT_TERMINATE) {
memset(data, ' ', 38U);
for (unsigned int i = 0U; i < m_remote.size() && i < LONG_CALLSIGN_LENGTH; i++)
data[i] = m_remote.at(i);
memcpy(data + 8U, "0001", 4U);
for (unsigned int i = 0U; i < m_local.size() && i < LONG_CALLSIGN_LENGTH; i++)
data[i + 12U] = m_local.at(i);
return 38U;
} else if (m_type == CT_INFO) {
char bstr[256];
snprintf(bstr, 256, "IRPT%.7s %s%-10.4lf%-10.4lf%-10.4lf%-10.4lf%-20s%-20s%-40s", m_local.substr(0U, LONG_CALLSIGN_LENGTH - 1U).c_str(), m_local.substr(LONG_CALLSIGN_LENGTH - 1U, 1U).c_str(), m_latitude, m_longitude, m_frequency, m_offset, m_description1.c_str(), m_description2.c_str(), m_url.c_str());
unsigned int len = strlen(bstr);
if (len > 133U)
len = 133u;
memcpy(data, bstr, len);
return 133U;
}
return 0U;
}
std::string CCCSData::getLocal() const
{
return m_local;
}
std::string CCCSData::getRemote() const
{
return m_remote;
}
CC_TYPE CCCSData::getType() const
{
return m_type;
}
void CCCSData::setDestination(const in_addr& address, unsigned int port)
{
m_yourAddress = address;
m_yourPort = port;
}
in_addr CCCSData::getYourAddress() const
{
return m_yourAddress;
}
unsigned int CCCSData::getYourPort() const
{
return m_yourPort;
}
unsigned int CCCSData::getMyPort() const
{
return m_myPort;
}

@ -0,0 +1,67 @@
/*
* Copyright (C) 2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <netinet/in.h>
#include <string>
enum CC_TYPE {
CT_TERMINATE,
CT_DTMFNOTFOUND,
CT_DTMFFOUND,
CT_INFO
};
class CCCSData {
public:
CCCSData(const std::string& local, double latitude, double longitude, double frequency, double offset, const std::string& description1, const std::string& description2, const std::string& url, CC_TYPE type);
CCCSData(const std::string& local, const std::string& remote, CC_TYPE type);
CCCSData();
~CCCSData();
bool setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
unsigned int getCCSData(unsigned char* data, unsigned int length) const;
void setDestination(const in_addr& address, unsigned int port);
std::string getLocal() const;
std::string getRemote() const;
CC_TYPE getType() const;
in_addr getYourAddress() const;
unsigned int getYourPort() const;
unsigned int getMyPort() const;
private:
std::string m_local;
std::string m_remote;
double m_latitude;
double m_longitude;
double m_frequency;
double m_offset;
std::string m_description1;
std::string m_description2;
std::string m_url;
CC_TYPE m_type;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
};

@ -0,0 +1,235 @@
/*
* Copyright (C) 2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 "CCSProtocolHandler.h"
#include "Utils.h"
// #define DUMP_TX
const unsigned int BUFFER_LENGTH = 2000U;
CCCSProtocolHandler::CCCSProtocolHandler(unsigned int port, const std::string& addr) :
m_socket(addr, port),
m_type(CT_NONE),
m_buffer(NULL),
m_length(0U),
m_yourAddress(),
m_yourPort(0U),
m_myPort(port)
{
m_buffer = new unsigned char[BUFFER_LENGTH];
}
CCCSProtocolHandler::~CCCSProtocolHandler()
{
delete[] m_buffer;
}
bool CCCSProtocolHandler::open()
{
return m_socket.open();
}
unsigned int CCCSProtocolHandler::getPort() const
{
return m_myPort;
}
bool CCCSProtocolHandler::writeData(const CAMBEData& data)
{
unsigned char buffer[100U];
unsigned int length = data.getCCSData(buffer, 100U);
#if defined(DUMP_TX)
CUtils::dump("Sending Data", buffer, length);
#endif
return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort());
}
bool CCCSProtocolHandler::writePoll(const CPollData& poll)
{
unsigned char buffer[30U];
unsigned int length = poll.getCCSData(buffer, 30U);
#if defined(DUMP_TX)
CUtils::dump(wxT"Sending Poll", buffer, length);
#endif
return m_socket.write(buffer, length, poll.getYourAddress(), poll.getYourPort());
}
bool CCCSProtocolHandler::writeHeard(const CHeardData& heard)
{
unsigned char buffer[100U];
unsigned int length = heard.getCCSData(buffer, 100U);
#if defined(DUMP_TX)
CUtils::dump("Sending Heard", buffer, length);
#endif
return m_socket.write(buffer, length, heard.getAddress(), heard.getPort());
}
bool CCCSProtocolHandler::writeConnect(const CConnectData& connect)
{
unsigned char buffer[40U];
unsigned int length = connect.getCCSData(buffer, 40U);
#if defined(DUMP_TX)
CUtils::dump("Sending Connect", buffer, length);
#endif
return m_socket.write(buffer, length, connect.getYourAddress(), connect.getYourPort());
}
bool CCCSProtocolHandler::writeMisc(const CCCSData& data)
{
unsigned char buffer[140U];
unsigned int length = data.getCCSData(buffer, 140U);
#if defined(DUMP_TX)
CUtils::dump("Sending Misc", buffer, length);
#endif
return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort());
}
CCS_TYPE CCCSProtocolHandler::read()
{
bool res = true;
// Loop until we have no more data from the socket or we have data for the higher layers
while (res)
res = readPackets();
return m_type;
}
bool CCCSProtocolHandler::readPackets()
{
m_type = CT_NONE;
// No more data?
int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_yourAddress, m_yourPort);
if (length <= 0)
return false;
m_length = length;
if (m_buffer[0] == '0' && m_buffer[1] == '0' && m_buffer[2] == '0' && m_buffer[3] == '1') {
m_type = CT_DATA;
return false;
} else if (m_buffer[0] == 'L' && m_buffer[1] == 'L' && m_buffer[2] == 'L') {
return true;
} else {
switch (m_length) {
case 14U:
m_type = CT_CONNECT;
return false;
case 25U:
m_type = CT_POLL;
return false;
case 100U:
case 20U:
case 17U:
m_type = CT_MISC;
return false;
case 39U:
return true;
default:
break;
}
}
// An unknown type
CUtils::dump("Unknown packet type from CCS", m_buffer, m_length);
return true;
}
CAMBEData* CCCSProtocolHandler::readData()
{
if (m_type != CT_DATA)
return NULL;
CAMBEData* data = new CAMBEData;
bool res = data->setCCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete data;
return NULL;
}
return data;
}
CConnectData* CCCSProtocolHandler::readConnect()
{
if (m_type != CT_CONNECT)
return NULL;
CConnectData* connect = new CConnectData;
bool res = connect->setCCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete connect;
return NULL;
}
return connect;
}
CPollData* CCCSProtocolHandler::readPoll()
{
if (m_type != CT_POLL)
return NULL;
CPollData* poll = new CPollData;
bool res = poll->setCCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete poll;
return NULL;
}
return poll;
}
CCCSData* CCCSProtocolHandler::readMisc()
{
if (m_type != CT_MISC)
return NULL;
CCCSData* data = new CCCSData;
bool res = data->setCCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete data;
return NULL;
}
return data;
}
void CCCSProtocolHandler::close()
{
m_socket.close();
}

@ -0,0 +1,74 @@
/*
* Copyright (C) 2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <netinet/in.h>
#include "UDPReaderWriter.h"
#include "DStarDefines.h"
#include "ConnectData.h"
#include "HeardData.h"
#include "AMBEData.h"
#include "PollData.h"
#include "CCSData.h"
enum CCS_TYPE {
CT_NONE,
CT_DATA,
CT_POLL,
CT_CONNECT,
CT_MISC
};
class CCCSProtocolHandler {
public:
CCCSProtocolHandler(unsigned int port, const std::string& addr = std::string(""));
~CCCSProtocolHandler();
bool open();
unsigned int getPort() const;
bool writeData(const CAMBEData& data);
bool writeConnect(const CConnectData& connect);
bool writePoll(const CPollData& poll);
bool writeHeard(const CHeardData& heard);
bool writeMisc(const CCCSData& data);
CCS_TYPE read();
CAMBEData* readData();
CPollData* readPoll();
CConnectData* readConnect();
CCCSData* readMisc();
void close();
private:
CUDPReaderWriter m_socket;
CCS_TYPE m_type;
unsigned char* m_buffer;
unsigned int m_length;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
bool readPackets();
};

@ -0,0 +1,135 @@
/*
* Copyright (C) 2010,2011,2012 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 "CacheManager.h"
#include "DStarDefines.h"
CCacheManager::CCacheManager() :
m_userCache(),
m_gatewayCache(),
m_repeaterCache()
{
}
CCacheManager::~CCacheManager()
{
}
// returns a new CUserData if there is a user and a and gateway
CUserData *CCacheManager::findUser(const std::string& user)
{
mux.lock();
CUserRecord *ur = m_userCache.find(user);
if (ur == NULL) {
mux.unlock();
return NULL;
}
std::string gateway(ur->getRepeater()); // it's not a gateway yet
CRepeaterRecord *rr = m_repeaterCache.find(gateway);
if (rr == NULL) {
gateway = ur->getRepeater();
gateway.resize(LONG_CALLSIGN_LENGTH - 1U, ' ');
gateway.push_back('G'); // now it's a gateway
} else
gateway = rr->getGateway();
CGatewayRecord *gr = m_gatewayCache.find(gateway);
if (gr == NULL) {
mux.unlock();
return NULL;
}
CUserData *userdata = new CUserData(user, ur->getRepeater(), gr->getGateway(), gr->getAddress());
mux.unlock();
return userdata;
}
CGatewayData *CCacheManager::findGateway(const std::string& gateway)
{
mux.lock();
CGatewayRecord *gr = m_gatewayCache.find(gateway);
if (gr == NULL)
return NULL;
CGatewayData *gatewaydata = new CGatewayData(gateway, gr->getAddress(), gr->getProtocol());
mux.unlock();
return gatewaydata;
}
CRepeaterData* CCacheManager::findRepeater(const std::string& repeater)
{
mux.lock();
CRepeaterRecord *rr = m_repeaterCache.find(repeater);
std::string gateway;
if (rr == NULL) {
gateway = repeater;
gateway.resize(LONG_CALLSIGN_LENGTH - 1U, ' ');
gateway.push_back('G');
} else {
gateway = rr->getGateway();
}
CGatewayRecord *gr = m_gatewayCache.find(gateway);
if (gr == NULL) {
mux.unlock();
return NULL;
}
CRepeaterData *repeaterdata = new CRepeaterData(repeater, gr->getGateway(), gr->getAddress(), gr->getProtocol());
mux.unlock();
return repeaterdata;
}
void CCacheManager::updateUser(const std::string& user, const std::string& repeater, const std::string& gateway, const std::string& address, const std::string& timestamp, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock)
{
mux.lock();
std::string repeater7 = repeater.substr(0, LONG_CALLSIGN_LENGTH - 1U);
std::string gateway7 = gateway.substr(0, LONG_CALLSIGN_LENGTH - 1U);
m_userCache.update(user, repeater, timestamp);
// Only store non-standard repeater-gateway pairs
if (repeater7.compare(gateway7))
m_repeaterCache.update(repeater, gateway);
m_gatewayCache.update(gateway, address, protocol, addrLock, protoLock);
mux.unlock();
}
void CCacheManager::updateRepeater(const std::string& repeater, const std::string& gateway, const std::string& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock)
{
mux.lock();
std::string repeater7 = repeater.substr(0, LONG_CALLSIGN_LENGTH - 1U);
std::string gateway7 = gateway.substr(0, LONG_CALLSIGN_LENGTH - 1U);
// Only store non-standard repeater-gateway pairs
if (repeater7.compare(gateway7))
m_repeaterCache.update(repeater, gateway);
m_gatewayCache.update(gateway, address, protocol, addrLock, protoLock);
mux.unlock();
}
void CCacheManager::updateGateway(const std::string& gateway, const std::string& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock)
{
mux.lock();
m_gatewayCache.update(gateway, address, protocol, addrLock, protoLock);
mux.unlock();
}

@ -0,0 +1,151 @@
/*
* Copyright (C) 2010,2011,2012 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <mutex>
#include "RepeaterCache.h"
#include "GatewayCache.h"
#include "UserCache.h"
class CUserData {
public:
CUserData(const std::string& user, const std::string& repeater, const std::string& gateway, in_addr address) :
m_user(user),
m_repeater(repeater),
m_gateway(gateway),
m_address(address)
{
}
std::string getUser() const
{
return m_user;
}
std::string getRepeater() const
{
return m_repeater;
}
std::string getGateway() const
{
return m_gateway;
}
in_addr getAddress() const
{
return m_address;
}
private:
std::string m_user;
std::string m_repeater;
std::string m_gateway;
in_addr m_address;
};
class CRepeaterData {
public:
CRepeaterData(const std::string& repeater, const std::string& gateway, in_addr address, DSTAR_PROTOCOL protocol) :
m_repeater(repeater),
m_gateway(gateway),
m_address(address),
m_protocol(protocol)
{
}
std::string getRepeater() const
{
return m_repeater;
}
std::string getGateway() const
{
return m_gateway;
}
in_addr getAddress() const
{
return m_address;
}
DSTAR_PROTOCOL getProtocol() const
{
return m_protocol;
}
private:
std::string m_repeater;
std::string m_gateway;
in_addr m_address;
DSTAR_PROTOCOL m_protocol;
};
class CGatewayData {
public:
CGatewayData(const std::string& gateway, in_addr address, DSTAR_PROTOCOL protocol) :
m_gateway(gateway),
m_address(address),
m_protocol(protocol)
{
}
std::string getGateway() const
{
return m_gateway;
}
in_addr getAddress() const
{
return m_address;
}
DSTAR_PROTOCOL getProtocol() const
{
return m_protocol;
}
private:
std::string m_gateway;
in_addr m_address;
DSTAR_PROTOCOL m_protocol;
};
class CCacheManager {
public:
CCacheManager();
~CCacheManager();
CUserData* findUser(const std::string& user);
CGatewayData* findGateway(const std::string& gateway);
CRepeaterData* findRepeater(const std::string& repeater);
void updateUser(const std::string& user, const std::string& repeater, const std::string& gateway, const std::string& address, const std::string& timeStamp, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock);
void updateRepeater(const std::string& repeater, const std::string& gateway, const std::string& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock);
void updateGateway(const std::string& gateway, const std::string& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock);
private:
CUserCache m_userCache;
CGatewayCache m_gatewayCache;
CRepeaterCache m_repeaterCache;
std::mutex mux;
};

@ -0,0 +1,66 @@
/*
* Copyright (C) 2011 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <cstdio>
#include "CallsignList.h"
#include "DStarDefines.h"
#include "Utils.h"
CCallsignList::CCallsignList(const std::string& filename) :
m_filename(filename),
m_callsigns()
{
}
CCallsignList::~CCallsignList()
{
m_callsigns.clear();
}
bool CCallsignList::load()
{
FILE *file = fopen(m_filename.c_str(), "r");
if (NULL == file)
return false;
char cstr[32];
while (fgets(cstr, 32, file)) {
std::string callsign(cstr);
CUtils::ToUpper(callsign);
callsign.resize(LONG_CALLSIGN_LENGTH, ' ');
m_callsigns.insert(callsign);
}
fclose(file);
return true;
}
unsigned int CCallsignList::getCount() const
{
return m_callsigns.size();
}
bool CCallsignList::isInList(const std::string& callsign) const
{
return m_callsigns.find(callsign) != m_callsigns.end();
}

@ -0,0 +1,40 @@
/*
* Copyright (C) 2011,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <set>
class CCallsignList {
public:
CCallsignList(const std::string& filename);
~CCallsignList();
bool load();
unsigned int getCount() const;
bool isInList(const std::string& callsign) const;
private:
std::string m_filename;
std::set<std::string> m_callsigns;
};

@ -0,0 +1,549 @@
/*
* Copyright (C) 2010,2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <cstdio>
#include <cassert>
#include <cstring>
#include <cctype>
#include "ConnectData.h"
#include "DStarDefines.h"
#include "Version.h"
#include "Utils.h"
const char *HTML = "<table border=\"0\" width=\"95%%\"><tr><td width=\"4%%\"><img border=\"0\" src=%s></td><td width=\"96%%\"><font size=\"2\"><b>%s</b> SGS %s</font></td></tr></table>";
CConnectData::CConnectData(GATEWAY_TYPE gatewayType, const std::string& repeater, const std::string& reflector, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) :
m_gatewayType(gatewayType),
m_repeater(repeater),
m_reflector(reflector),
m_type(type),
m_locator(),
m_yourAddress(yourAddress),
m_yourPort(yourPort),
m_myPort(myPort)
{
assert(yourPort > 0U);
assert(repeater.size());
assert(reflector.size());
}
CConnectData::CConnectData(const std::string& repeater, const std::string& reflector, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) :
m_gatewayType(GT_REPEATER),
m_repeater(repeater),
m_reflector(reflector),
m_type(type),
m_locator(),
m_yourAddress(yourAddress),
m_yourPort(yourPort),
m_myPort(myPort)
{
assert(yourPort > 0U);
assert(repeater.size());
assert(reflector.size());
}
CConnectData::CConnectData(const std::string& repeater, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) :
m_gatewayType(GT_REPEATER),
m_repeater(repeater),
m_reflector(),
m_type(type),
m_locator(),
m_yourAddress(yourAddress),
m_yourPort(yourPort),
m_myPort(myPort)
{
assert(yourPort > 0U);
assert(repeater.size());
}
CConnectData::CConnectData(const std::string& repeater, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) :
m_gatewayType(GT_REPEATER),
m_repeater(repeater),
m_reflector(),
m_type(CT_UNLINK),
m_locator(),
m_yourAddress(yourAddress),
m_yourPort(yourPort),
m_myPort(myPort)
{
assert(yourPort > 0U);
assert(repeater.size());
}
CConnectData::CConnectData(CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) :
m_gatewayType(GT_REPEATER),
m_repeater(),
m_reflector(),
m_type(type),
m_locator(),
m_yourAddress(yourAddress),
m_yourPort(yourPort),
m_myPort(myPort)
{
assert(yourPort > 0U);
}
CConnectData::CConnectData() :
m_gatewayType(GT_REPEATER),
m_repeater(" "),
m_reflector(),
m_type(CT_LINK1),
m_locator(),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U)
{
}
CConnectData::~CConnectData()
{
}
bool CConnectData::setDExtraData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(length >= 11U);
assert(yourPort > 0U);
m_repeater = std::string((const char*)data, LONG_CALLSIGN_LENGTH);
m_repeater[LONG_CALLSIGN_LENGTH - 1] = data[LONG_CALLSIGN_LENGTH];
m_reflector.assign(" ");
m_reflector[LONG_CALLSIGN_LENGTH - 1] = data[LONG_CALLSIGN_LENGTH + 1U];
switch (length) {
case 11U:
if (0 == m_reflector.compare(" "))
m_type = CT_UNLINK;
else
m_type = CT_LINK1;
break;
case 14U:
if (data[LONG_CALLSIGN_LENGTH + 2U] == 'A' &&
data[LONG_CALLSIGN_LENGTH + 3U] == 'C' &&
data[LONG_CALLSIGN_LENGTH + 4U] == 'K')
m_type = CT_ACK;
else if (data[LONG_CALLSIGN_LENGTH + 2U] == 'N' &&
data[LONG_CALLSIGN_LENGTH + 3U] == 'A' &&
data[LONG_CALLSIGN_LENGTH + 4U] == 'K')
m_type = CT_NAK;
else
return false;
break;
default:
return false;
}
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
bool CConnectData::setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(length >= 11U);
assert(yourPort > 0U);
m_repeater = std::string((const char*)data, LONG_CALLSIGN_LENGTH);
m_repeater[LONG_CALLSIGN_LENGTH - 1] = data[LONG_CALLSIGN_LENGTH];
switch (length) {
case 519U:
m_reflector = std::string((const char*)(data + LONG_CALLSIGN_LENGTH + 3), LONG_CALLSIGN_LENGTH);
m_reflector[LONG_CALLSIGN_LENGTH - 1] = data[LONG_CALLSIGN_LENGTH + 1];
m_type = CT_LINK1;
break;
case 19U:
m_reflector = std::string((const char*)(data + LONG_CALLSIGN_LENGTH + 3U), LONG_CALLSIGN_LENGTH);
m_reflector[LONG_CALLSIGN_LENGTH - 1] = data[LONG_CALLSIGN_LENGTH + 1];
m_type = CT_UNLINK;
break;
case 14U:
if (data[LONG_CALLSIGN_LENGTH + 2U] == 'A' &&
data[LONG_CALLSIGN_LENGTH + 3U] == 'C' &&
data[LONG_CALLSIGN_LENGTH + 4U] == 'K')
m_type = CT_ACK;
else if (data[LONG_CALLSIGN_LENGTH + 2U] == 'N' &&
data[LONG_CALLSIGN_LENGTH + 3U] == 'A' &&
data[LONG_CALLSIGN_LENGTH + 4U] == 'K')
m_type = CT_NAK;
else
return false;
break;
default:
return false;
}
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
bool CConnectData::setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(length >= 14U);
assert(yourPort > 0U);
m_repeater = std::string((const char*)data, LONG_CALLSIGN_LENGTH);
m_repeater[LONG_CALLSIGN_LENGTH - 1] = data[LONG_CALLSIGN_LENGTH];
if (data[LONG_CALLSIGN_LENGTH + 2U] == 'A' &&
data[LONG_CALLSIGN_LENGTH + 3U] == 'C' &&
data[LONG_CALLSIGN_LENGTH + 4U] == 'K')
m_type = CT_ACK;
else if (data[LONG_CALLSIGN_LENGTH + 2U] == 'N' &&
data[LONG_CALLSIGN_LENGTH + 3U] == 'A' &&
data[LONG_CALLSIGN_LENGTH + 4U] == 'K')
m_type = CT_NAK;
else
return false;
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
bool CConnectData::setDPlusData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(length >= 5U);
assert(yourPort > 0U);
switch (length) {
case 5U:
switch (data[4U]) {
case 0x01:
m_type = CT_LINK1;
break;
case 0x00:
m_type = CT_UNLINK;
break;
}
break;
case 8U: {
std::string reply((const char*)(data + 4U), 4U);
printf("D-Plus reply is %.4s\n", reply.c_str());
if (::memcmp(data + 4U, "OKRW", 4U) == 0)
m_type = CT_ACK;
else
m_type = CT_NAK;
}
break;
case 28U:
m_repeater = std::string((const char*)(data + 4U), LONG_CALLSIGN_LENGTH);
m_type = CT_LINK2;
break;
default:
return false;
}
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
unsigned int CConnectData::getDExtraData(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 11U);
::memset(data, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < m_repeater.size() && i < (LONG_CALLSIGN_LENGTH - 1U); i++)
data[i] = m_repeater.at(i);
data[LONG_CALLSIGN_LENGTH] = m_repeater.at(LONG_CALLSIGN_LENGTH - 1U);
switch (m_type) {
case CT_LINK1:
case CT_LINK2:
data[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.at(LONG_CALLSIGN_LENGTH - 1U);
data[LONG_CALLSIGN_LENGTH + 2U] = 0x00;
return 11U;
case CT_UNLINK:
data[LONG_CALLSIGN_LENGTH + 1U] = ' ';
data[LONG_CALLSIGN_LENGTH + 2U] = 0x00;
return 11U;
case CT_ACK:
data[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.at(LONG_CALLSIGN_LENGTH - 1U);
data[LONG_CALLSIGN_LENGTH + 2U] = 'A';
data[LONG_CALLSIGN_LENGTH + 3U] = 'C';
data[LONG_CALLSIGN_LENGTH + 4U] = 'K';
data[LONG_CALLSIGN_LENGTH + 5U] = 0x00;
return 14U;
case CT_NAK:
data[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.at(LONG_CALLSIGN_LENGTH - 1U);
data[LONG_CALLSIGN_LENGTH + 2U] = 'N';
data[LONG_CALLSIGN_LENGTH + 3U] = 'A';
data[LONG_CALLSIGN_LENGTH + 4U] = 'K';
data[LONG_CALLSIGN_LENGTH + 5U] = 0x00;
return 14U;
default:
return 0U;
}
}
unsigned int CConnectData::getDCSData(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 519U);
::memset(data, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < m_repeater.size() && i < (LONG_CALLSIGN_LENGTH - 1U); i++)
data[i] = m_repeater.at(i);
data[LONG_CALLSIGN_LENGTH] = m_repeater.at(LONG_CALLSIGN_LENGTH - 1U);
switch (m_type) {
case CT_LINK1:
case CT_LINK2: {
data[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.at(LONG_CALLSIGN_LENGTH - 1U);
data[LONG_CALLSIGN_LENGTH + 2U] = 0x00U;
::memset(data + 11U, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < m_reflector.size() && i < (LONG_CALLSIGN_LENGTH - 1U); i++)
data[i + 11U] = m_reflector.at(i);
char shtml[512];
switch (m_gatewayType) {
case GT_HOTSPOT:
snprintf(shtml, 512, HTML, "hotspot.jpg", "HOTSPOT", VERSION.c_str());
break;
case GT_DONGLE:
snprintf(shtml, 512, HTML, "dongle.jpg", "DONGLE", VERSION.c_str());
break;
case GT_SMARTGROUP:
snprintf(shtml, 512, HTML, "hf.jpg", "Smart Group", VERSION.c_str());
break;
default:
snprintf(shtml, 512, HTML, "hf.jpg", "REPEATER", VERSION.c_str());
break;
}
std::string html(shtml);
::memset(data + 19U, 0x00U, 500U);
for (unsigned int i = 0U; i < html.size(); i++)
data[i + 19U] = html.at(i);
}
return 519U;
case CT_UNLINK:
data[LONG_CALLSIGN_LENGTH + 1U] = 0x20U;
data[LONG_CALLSIGN_LENGTH + 2U] = 0x00U;
::memset(data + 11U, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < m_reflector.size() && i < (LONG_CALLSIGN_LENGTH - 1U); i++)
data[i + 11U] = m_reflector.at(i);
return 19U;
case CT_ACK:
data[LONG_CALLSIGN_LENGTH + 1U] = m_reflector.at(LONG_CALLSIGN_LENGTH - 1U);
data[LONG_CALLSIGN_LENGTH + 2U] = 'A';
data[LONG_CALLSIGN_LENGTH + 3U] = 'C';
data[LONG_CALLSIGN_LENGTH + 4U] = 'K';
data[LONG_CALLSIGN_LENGTH + 5U] = 0x00;
return 14U;
case CT_NAK:
data[LONG_CALLSIGN_LENGTH + 1U] = 0x20U;
data[LONG_CALLSIGN_LENGTH + 2U] = 'N';
data[LONG_CALLSIGN_LENGTH + 3U] = 'A';
data[LONG_CALLSIGN_LENGTH + 4U] = 'K';
data[LONG_CALLSIGN_LENGTH + 5U] = 0x00;
return 14U;
default:
return 0U;
}
}
unsigned int CConnectData::getCCSData(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 39U);
::memset(data, ' ', 39U);
for (unsigned int i = 0U; i < m_repeater.size() && i < (LONG_CALLSIGN_LENGTH - 1U); i++)
data[i] = m_repeater.at(i);
data[LONG_CALLSIGN_LENGTH + 0U] = m_repeater.at(LONG_CALLSIGN_LENGTH - 1U);
switch (m_type) {
case CT_LINK1:
case CT_LINK2: {
data[9U] = 0x41U;
data[10U] = '@';
for (unsigned int i = 0U; i < m_locator.size(); i++)
data[11U + i] = m_locator.at(i);
data[17U] = 0x20U;
data[18U] = '@';
std::string text("ircDDB_GW-");
text += VERSION.substr(0, 8);
for (unsigned int i = 0U; i < text.size(); i++)
data[19U + i] = text.at(i);
}
return 39U;
case CT_UNLINK:
return 19U;
default:
return 0U;
}
}
unsigned int CConnectData::getDPlusData(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 28U);
switch (m_type) {
case CT_LINK1:
data[0U] = 0x05;
data[1U] = 0x00;
data[2U] = 0x18;
data[3U] = 0x00;
data[4U] = 0x01;
return 5U;
case CT_LINK2: {
data[0U] = 0x1C;
data[1U] = 0xC0;
data[2U] = 0x04;
data[3U] = 0x00;
for (unsigned int i = 4U; i < 20U; i++)
data[i] = 0x00;
std::string callsign = m_repeater;
CUtils::Trim(callsign);
for (unsigned int i = 0U; i < callsign.size(); i++)
data[i + 4U] = callsign.at(i);
data[20U] = 'D';
data[21U] = 'V';
data[22U] = '0';
data[23U] = '1';
data[24U] = '9';
data[25U] = '9';
data[26U] = '9';
data[27U] = '9';
}
return 28U;
case CT_UNLINK:
data[0U] = 0x05;
data[1U] = 0x00;
data[2U] = 0x18;
data[3U] = 0x00;
data[4U] = 0x00;
return 5U;
case CT_ACK:
data[0U] = 0x08;
data[1U] = 0xC0;
data[2U] = 0x04;
data[3U] = 0x00;
data[4U] = 'O';
data[5U] = 'K';
data[6U] = 'R';
data[7U] = 'W';
return 8U;
case CT_NAK:
data[0U] = 0x08;
data[1U] = 0xC0;
data[2U] = 0x04;
data[3U] = 0x00;
data[4U] = 'B';
data[5U] = 'U';
data[6U] = 'S';
data[7U] = 'Y';
return 8U;
default:
return 0U;
}
}
in_addr CConnectData::getYourAddress() const
{
return m_yourAddress;
}
unsigned int CConnectData::getYourPort() const
{
return m_yourPort;
}
unsigned int CConnectData::getMyPort() const
{
return m_myPort;
}
std::string CConnectData::getRepeater() const
{
return m_repeater;
}
std::string CConnectData::getReflector() const
{
return m_reflector;
}
CD_TYPE CConnectData::getType() const
{
return m_type;
}
void CConnectData::setLocator(const std::string& locator)
{
m_locator = locator;
}

@ -0,0 +1,75 @@
/*
* Copyright (C) 2010,2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <netinet/in.h>
#include "Defs.h"
enum CD_TYPE {
CT_LINK1,
CT_LINK2,
CT_UNLINK,
CT_ACK,
CT_NAK
};
class CConnectData {
public:
CConnectData(GATEWAY_TYPE gatewayType, const std::string& repeater, const std::string& reflector, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U);
CConnectData(const std::string& repeater, const std::string& reflector, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U);
CConnectData(const std::string& repeater, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U);
CConnectData(const std::string& repeater, CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U);
CConnectData(CD_TYPE type, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U);
CConnectData();
~CConnectData();
bool setDExtraData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setDPlusData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
unsigned int getDExtraData(unsigned char* data, unsigned int length) const;
unsigned int getDPlusData(unsigned char* data, unsigned int length) const;
unsigned int getDCSData(unsigned char* data, unsigned int length) const;
unsigned int getCCSData(unsigned char* data, unsigned int length) const;
std::string getRepeater() const;
std::string getReflector() const;
CD_TYPE getType() const;
in_addr getYourAddress() const;
unsigned int getYourPort() const;
unsigned int getMyPort() const;
void setLocator(const std::string& locator);
private:
GATEWAY_TYPE m_gatewayType;
std::string m_repeater;
std::string m_reflector;
CD_TYPE m_type;
std::string m_locator;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
};

@ -0,0 +1,784 @@
/*
* Copyright (C) 2012-2015 by Jonathan Naylor G4KLX
* Copyright (c) 2017-2018 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 <cassert>
#include "DCSHandler.h"
#include "Utils.h"
CDCSProtocolHandlerPool *CDCSHandler::m_pool = NULL;
CDCSProtocolHandler *CDCSHandler::m_incoming = NULL;
bool CDCSHandler::m_stateChange = false;
GATEWAY_TYPE CDCSHandler::m_gatewayType = GT_REPEATER;
CCallsignList *CDCSHandler::m_whiteList = NULL;
CCallsignList *CDCSHandler::m_blackList = NULL;
std::list<CDCSHandler *> CDCSHandler::m_DCSHandlers;
CDCSHandler::CDCSHandler(IReflectorCallback *handler, const std::string &dcsHandler, const std::string &repeater, CDCSProtocolHandler *protoHandler, const in_addr &address, unsigned int port, DIRECTION direction) :
m_reflector(dcsHandler),
m_repeater(repeater),
m_handler(protoHandler),
m_yourAddress(address),
m_yourPort(port),
m_myPort(0U),
m_direction(direction),
m_linkState(DCS_LINKING),
m_destination(handler),
m_time(),
m_pollTimer(1000U, 5U),
m_pollInactivityTimer(1000U, 60U),
m_tryTimer(1000U, 1U),
m_tryCount(0U),
m_dcsId(0x00U),
m_dcsSeq(0x00U),
m_seqNo(0x00U),
m_inactivityTimer(1000U, NETWORK_TIMEOUT),
m_yourCall(),
m_myCall1(),
m_myCall2(),
m_rptCall1(),
m_rptCall2()
{
assert(protoHandler != NULL);
assert(handler != NULL);
assert(port > 0U);
m_myPort = protoHandler->getPort();
m_pollInactivityTimer.start();
m_time = ::time(NULL);
if (direction == DIR_INCOMING) {
m_pollTimer.start();
m_stateChange = true;
m_linkState = DCS_LINKED;
} else {
m_linkState = DCS_LINKING;
m_tryTimer.start();
}
}
CDCSHandler::~CDCSHandler()
{
if (m_direction == DIR_OUTGOING)
m_pool->release(m_handler);
}
void CDCSHandler::setDCSProtocolHandlerPool(CDCSProtocolHandlerPool *pool)
{
assert(pool != NULL);
m_pool = pool;
}
void CDCSHandler::setDCSProtocolIncoming(CDCSProtocolHandler *handler)
{
assert(handler != NULL);
m_incoming = handler;
}
void CDCSHandler::setGatewayType(GATEWAY_TYPE type)
{
m_gatewayType = type;
}
void CDCSHandler::setWhiteList(CCallsignList *list)
{
assert(list != NULL);
m_whiteList = list;
}
void CDCSHandler::setBlackList(CCallsignList *list)
{
assert(list != NULL);
m_blackList = list;
}
std::string CDCSHandler::getIncoming(const std::string &callsign)
{
std::string incoming;
for (auto it=m_DCSHandlers.begin(); it!=m_DCSHandlers.end(); it++) {
CDCSHandler *dcsHandler = *it;
if (dcsHandler->m_direction == DIR_INCOMING && 0==dcsHandler->m_repeater.compare(callsign)) {
incoming.append(dcsHandler->m_reflector);
incoming.append(" ");
}
}
return incoming;
}
void CDCSHandler::getInfo(IReflectorCallback *handler, CRemoteRepeaterData &data)
{
assert(handler != NULL);
for (auto it=m_DCSHandlers.begin(); it!=m_DCSHandlers.end(); it++) {
CDCSHandler *dcsHandler = *it;
if (dcsHandler->m_destination == handler) {
if (dcsHandler->m_direction == DIR_INCOMING && 0==dcsHandler->m_repeater.size()) {
if (dcsHandler->m_linkState != DCS_UNLINKING)
data.addLink(dcsHandler->m_reflector, PROTO_DCS, dcsHandler->m_linkState == DCS_LINKED, DIR_INCOMING, true);
} else {
if (dcsHandler->m_linkState != DCS_UNLINKING)
data.addLink(dcsHandler->m_reflector, PROTO_DCS, dcsHandler->m_linkState == DCS_LINKED, dcsHandler->m_direction, false);
}
}
}
}
void CDCSHandler::process(CAMBEData &data)
{
in_addr yourAddress = data.getYourAddress();
unsigned int yourPort = data.getYourPort();
unsigned int myPort = data.getMyPort();
for (auto it=m_DCSHandlers.begin(); it!=m_DCSHandlers.end(); it++) {
CDCSHandler *dcsHandler = *it;
if ( dcsHandler->m_yourAddress.s_addr == yourAddress.s_addr &&
dcsHandler->m_yourPort == yourPort &&
dcsHandler->m_myPort == myPort) {
dcsHandler->processInt(data);
return;
}
}
}
void CDCSHandler::process(CPollData &poll)
{
std::string dcsHandler = poll.getData1();
std::string repeater = poll.getData2();
in_addr yourAddress = poll.getYourAddress();
unsigned int yourPort = poll.getYourPort();
unsigned int myPort = poll.getMyPort();
unsigned int length = poll.getLength();
// Check to see if we already have a link
for (auto it=m_DCSHandlers.begin(); it!=m_DCSHandlers.end(); it++) {
CDCSHandler *handler = *it;
if ( 0==handler->m_reflector.compare(dcsHandler) &&
0==handler->m_repeater.compare(repeater) &&
handler->m_yourAddress.s_addr == yourAddress.s_addr &&
handler->m_yourPort == yourPort &&
handler->m_myPort == myPort &&
handler->m_direction == DIR_OUTGOING &&
handler->m_linkState == DCS_LINKED &&
length == 22U) {
handler->m_pollInactivityTimer.start();
CPollData reply(handler->m_repeater, handler->m_reflector, handler->m_direction, handler->m_yourAddress, handler->m_yourPort);
handler->m_handler->writePoll(reply);
return;
} else if (0==handler->m_reflector.compare(0, LONG_CALLSIGN_LENGTH - 1U, dcsHandler, 0, LONG_CALLSIGN_LENGTH - 1U) &&
handler->m_yourAddress.s_addr == yourAddress.s_addr &&
handler->m_yourPort == yourPort &&
handler->m_myPort == myPort &&
handler->m_direction == DIR_INCOMING &&
handler->m_linkState == DCS_LINKED &&
length == 17U) {
handler->m_pollInactivityTimer.start();
return;
}
}
printf("Unknown incoming DCS poll from %s\n", dcsHandler.c_str());
}
void CDCSHandler::process(CConnectData &connect)
{
CD_TYPE type = connect.getType();
if (type == CT_ACK || type == CT_NAK || type == CT_UNLINK) {
for (auto it=m_DCSHandlers.begin(); it!=m_DCSHandlers.end(); ) {
CDCSHandler *dcsHandler = *it;
bool res = dcsHandler->processInt(connect, type);
if (res) {
delete dcsHandler;
it = m_DCSHandlers.erase(it);
} else
it++;
}
return;
}
// else if type == CT_LINK1 or type == CT_LINK2
// someone tried to link directly to a Smart Group!
printf("CDCSHandler::process(CConnectData) type=CT_LINK%c, from repeater=%s\n", (type==CT_LINK1) ? '1' : '2', connect.getRepeater().c_str());
}
void CDCSHandler::link(IReflectorCallback *handler, const std::string &repeater, const std::string &gateway, const in_addr &address)
{
// if the handler is currently unlinking, quit!
for (auto it=m_DCSHandlers.begin(); it!=m_DCSHandlers.end(); it++) {
CDCSHandler *dcsHandler = *it;
if (dcsHandler->m_direction == DIR_OUTGOING && dcsHandler->m_destination == handler && dcsHandler->m_linkState != DCS_UNLINKING)
return;
}
CDCSProtocolHandler *protoHandler = m_pool->getHandler();
if (protoHandler == NULL)
return;
CDCSHandler *dcs = new CDCSHandler(handler, gateway, repeater, protoHandler, address, DCS_PORT, DIR_OUTGOING);
if (dcs) {
m_DCSHandlers.push_back(dcs);
CConnectData reply(m_gatewayType, repeater, gateway, CT_LINK1, address, DCS_PORT);
protoHandler->writeConnect(reply);
}
}
void CDCSHandler::unlink(IReflectorCallback *handler, const std::string &callsign, bool exclude)
{
for (auto it=m_DCSHandlers.begin(); it!=m_DCSHandlers.end(); it++) {
CDCSHandler *dcsHandler = *it;
if (dcsHandler != NULL) {
bool found = false;
if (exclude) {
if (dcsHandler->m_direction == DIR_OUTGOING && dcsHandler->m_destination == handler && dcsHandler->m_reflector.compare(callsign)) {
printf("Removing outgoing DCS link %s, %s\n", dcsHandler->m_repeater.c_str(), dcsHandler->m_reflector.c_str());
if (dcsHandler->m_linkState == DCS_LINKING || dcsHandler->m_linkState == DCS_LINKED) {
CConnectData connect(dcsHandler->m_repeater, dcsHandler->m_reflector, CT_UNLINK, dcsHandler->m_yourAddress, dcsHandler->m_yourPort);
dcsHandler->m_handler->writeConnect(connect);
dcsHandler->m_linkState = DCS_UNLINKING;
dcsHandler->m_tryTimer.start(1U);
dcsHandler->m_tryCount = 0U;
}
found = true;
}
} else {
if (dcsHandler->m_destination == handler && 0==dcsHandler->m_reflector.compare(callsign)) {
printf("Removing DCS link %s, %s\n", dcsHandler->m_repeater.c_str(), dcsHandler->m_reflector.c_str());
if (dcsHandler->m_linkState == DCS_LINKING || dcsHandler->m_linkState == DCS_LINKED) {
CConnectData connect(dcsHandler->m_repeater, dcsHandler->m_reflector, CT_UNLINK, dcsHandler->m_yourAddress, dcsHandler->m_yourPort);
dcsHandler->m_handler->writeConnect(connect);
dcsHandler->m_linkState = DCS_UNLINKING;
dcsHandler->m_tryTimer.start(1U);
dcsHandler->m_tryCount = 0U;
}
found = true;
}
}
// If an active link with incoming traffic, send an EOT to the repeater
if (found) {
if (dcsHandler->m_dcsId != 0x00U) {
unsigned int seq = dcsHandler->m_dcsSeq + 1U;
if (seq == 21U)
seq = 0U;
CAMBEData data;
data.setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES);
data.setSeq(seq);
data.setEnd(true);
data.setId(dcsHandler->m_dcsId);
dcsHandler->m_destination->process(data, dcsHandler->m_direction, AS_DCS);
}
m_stateChange = true;
}
}
}
}
void CDCSHandler::unlink(CDCSHandler *dcsHandler)
{
if (dcsHandler != NULL) {
if (dcsHandler->m_repeater.size()) {
printf("Unlinking from DCS dcsHandler %s\n", dcsHandler->m_reflector.c_str());
CConnectData connect(dcsHandler->m_repeater, dcsHandler->m_reflector, CT_UNLINK, dcsHandler->m_yourAddress, dcsHandler->m_yourPort);
dcsHandler->m_handler->writeConnect(connect);
dcsHandler->m_linkState = DCS_UNLINKING;
dcsHandler->m_tryTimer.start(1U);
dcsHandler->m_tryCount = 0U;
}
}
}
void CDCSHandler::unlink()
{
for (auto it=m_DCSHandlers.begin(); it!=m_DCSHandlers.end(); it++) {
CDCSHandler* dcsHandler = *it;
CDCSHandler::unlink(dcsHandler);
}
}
void CDCSHandler::writeHeader(IReflectorCallback *handler, CHeaderData &header, DIRECTION direction)
{
for (auto it=m_DCSHandlers.begin(); it!=m_DCSHandlers.end(); it++) {
CDCSHandler *dcsHandler = *it;
dcsHandler->writeHeaderInt(handler, header, direction);
}
}
void CDCSHandler::writeAMBE(IReflectorCallback *handler, CAMBEData &data, DIRECTION direction)
{
for (auto it=m_DCSHandlers.begin(); it!=m_DCSHandlers.end(); it++) {
CDCSHandler *dcsHandler = *it;
dcsHandler->writeAMBEInt(handler, data, direction);
}
}
void CDCSHandler::gatewayUpdate(const std::string &dcsHandler, const std::string &address)
{
std::string gateway = dcsHandler;
gateway.resize(LONG_CALLSIGN_LENGTH - 1U, ' ');
for (auto it=m_DCSHandlers.begin(); it!=m_DCSHandlers.end(); it++) {
CDCSHandler *dcsHandler = *it;
if (0 == dcsHandler->m_reflector.compare(0, LONG_CALLSIGN_LENGTH - 1U, gateway)) {
if (address.size()) {
// A new address, change the value
printf("Changing IP address of DCS gateway or dcsHandler %s to %s\n", dcsHandler->m_reflector.c_str(), address.c_str());
dcsHandler->m_yourAddress.s_addr = ::inet_addr(address.c_str());
} else {
printf("IP address for DCS gateway or dcsHandler %s has been removed\n", dcsHandler->m_reflector.c_str());
// No address, this probably shouldn't happen....
if (dcsHandler->m_direction == DIR_OUTGOING && dcsHandler->m_destination != NULL)
dcsHandler->m_destination->linkFailed(DP_DCS, dcsHandler->m_reflector, false);
m_stateChange = true;
delete dcsHandler;
it = m_DCSHandlers.erase(it);
it--;
}
}
}
}
void CDCSHandler::clock(unsigned int ms)
{
for (auto it=m_DCSHandlers.begin(); it!=m_DCSHandlers.end(); ) {
CDCSHandler *handler = *it;
bool ret = handler->clockInt(ms);
if (ret) {
delete handler;
it = m_DCSHandlers.erase(it);
} else
it++;
}
}
void CDCSHandler::finalise()
{
for (auto it=m_DCSHandlers.begin(); it!=m_DCSHandlers.end(); ) {
CDCSHandler *handler = *it;
delete handler;
it = m_DCSHandlers.erase(it);
}
}
void CDCSHandler::processInt(CAMBEData &data)
{
// Make a copy of the AMBE data so that any changes made here don't modify the original
CAMBEData temp(data);
unsigned int id = temp.getId();
CHeaderData& header = temp.getHeader();
unsigned int seqNo = temp.getSeq();
std::string my = header.getMyCall1();
std::string rpt2 = header.getRptCall2();
if (m_whiteList != NULL) {
bool res = m_whiteList->isInList(my);
if (!res) {
printf("%s rejected from DCS as not found in the white list\n", my.c_str());
m_dcsId = 0x00U;
return;
}
}
if (m_blackList != NULL) {
bool res = m_blackList->isInList(my);
if (res) {
printf("%s rejected from DCS as found in the black list\n", my.c_str());
m_dcsId = 0x00U;
return;
}
}
if (m_linkState != DCS_LINKED)
return;
switch (m_direction) {
case DIR_OUTGOING:
if (m_reflector.compare(rpt2))
return;
if (m_dcsId == 0x00U && seqNo != 0U)
return;
if (m_dcsId == 0x00U) { // && seqNo == 0U) {
m_dcsId = id;
m_dcsSeq = 0x00U;
m_inactivityTimer.start();
header.setCQCQCQ();
header.setFlags(0x00U, 0x00U, 0x00U);
m_destination->process(header, m_direction, AS_DCS);
}
if (id == m_dcsId) {
m_pollInactivityTimer.start();
m_inactivityTimer.start();
m_dcsSeq = seqNo;
if (m_dcsSeq == 0U) {
// Send the header every 21 frames
header.setCQCQCQ();
header.setFlags(0x00U, 0x00U, 0x00U);
m_destination->process(header, m_direction, AS_DUP);
}
m_destination->process(temp, m_direction, AS_DCS);
if (temp.isEnd()) {
m_dcsId = 0x00U;
m_dcsSeq = 0x00U;
m_inactivityTimer.stop();
}
}
break;
case DIR_INCOMING:
if (m_repeater.compare(rpt2))
return;
if (m_dcsId == 0x00U && seqNo != 0U)
return;
if (m_dcsId == 0x00U) { // && seqNo == 0U) {
m_dcsId = id;
m_dcsSeq = 0x00U;
m_inactivityTimer.start();
header.setCQCQCQ();
header.setFlags(0x00U, 0x00U, 0x00U);
m_destination->process(header, m_direction, AS_DCS);
}
if (id == m_dcsId) {
m_pollInactivityTimer.start();
m_inactivityTimer.start();
m_dcsSeq = seqNo;
if (m_dcsSeq == 0U) {
// Send the header every 21 frames
header.setCQCQCQ();
header.setFlags(0x00U, 0x00U, 0x00U);
m_destination->process(header, m_direction, AS_DUP);
}
m_destination->process(temp, m_direction, AS_DCS);
if (temp.isEnd()) {
m_dcsId = 0x00U;
m_dcsSeq = 0x00U;
m_inactivityTimer.stop();
}
}
break;
}
}
bool CDCSHandler::processInt(CConnectData &connect, CD_TYPE type)
{
in_addr yourAddress = connect.getYourAddress();
unsigned int yourPort = connect.getYourPort();
unsigned int myPort = connect.getMyPort();
std::string repeater = connect.getRepeater();
if (m_yourAddress.s_addr != yourAddress.s_addr || m_yourPort != yourPort || m_myPort != myPort)
return false;
switch (type) {
case CT_ACK:
if (m_repeater.compare(repeater))
return false;
if (m_linkState == DCS_LINKING) {
printf("DCS ACK message received from %s\n", m_reflector.c_str());
if (m_direction == DIR_OUTGOING && m_destination != NULL)
m_destination->linkUp(DP_DCS, m_reflector);
m_tryTimer.stop();
m_stateChange = true;
m_linkState = DCS_LINKED;
}
return false;
case CT_NAK:
if (m_repeater.compare(repeater))
return false;
if (m_linkState == DCS_LINKING) {
printf("DCS NAK message received from %s\n", m_reflector.c_str());
if (m_direction == DIR_OUTGOING && m_destination != NULL)
m_destination->linkRefused(DP_DCS, m_reflector);
return true;
}
if (m_linkState == DCS_UNLINKING) {
printf("DCS NAK message received from %s\n", m_reflector.c_str());
if (m_direction == DIR_OUTGOING && m_destination != NULL)
m_destination->linkFailed(DP_DCS, m_reflector, false);
return true;
}
return false;
case CT_UNLINK:
if (m_reflector.compare(repeater))
return false;
if (m_linkState == DCS_LINKED) {
printf("DCS disconnect message received from %s\n", m_reflector.c_str());
if (m_direction == DIR_OUTGOING && m_destination != NULL)
m_destination->linkFailed(DP_DCS, m_reflector, false);
m_stateChange = true;
}
return true;
default:
return false;
}
}
bool CDCSHandler::clockInt(unsigned int ms)
{
m_pollInactivityTimer.clock(ms);
m_inactivityTimer.clock(ms);
m_pollTimer.clock(ms);
m_tryTimer.clock(ms);
if (m_pollInactivityTimer.isRunning() && m_pollInactivityTimer.hasExpired()) {
m_pollInactivityTimer.start();
m_stateChange = true;
m_dcsId = 0x00U;
m_dcsSeq = 0x00U;
switch (m_linkState) {
case DCS_LINKING:
printf("DCS link to %s has failed to connect\n", m_reflector.c_str());
break;
case DCS_LINKED:
printf("DCS link to %s has failed (poll inactivity)\n", m_reflector.c_str());
break;
case DCS_UNLINKING:
printf("DCS link to %s has failed to disconnect cleanly\n", m_reflector.c_str());
break;
default:
break;
}
if (m_direction == DIR_OUTGOING) {
bool reconnect = m_destination->linkFailed(DP_DCS, m_reflector, true);
if (reconnect) {
CConnectData reply(m_gatewayType, m_repeater, m_reflector, CT_LINK1, m_yourAddress, m_yourPort);
m_handler->writeConnect(reply);
m_linkState = DCS_LINKING;
m_tryTimer.start(1U);
m_tryCount = 0U;
return false;
}
}
return true;
}
if (m_inactivityTimer.isRunning() && m_inactivityTimer.hasExpired()) {
m_dcsId = 0x00U;
m_dcsSeq = 0x00U;
m_inactivityTimer.stop();
}
if (m_pollTimer.isRunning() && m_pollTimer.hasExpired()) {
m_pollTimer.start();
CPollData poll(m_repeater, m_reflector, m_direction, m_yourAddress, m_yourPort);
m_handler->writePoll(poll);
}
if (m_linkState == DCS_LINKING) {
if (m_tryTimer.isRunning() && m_tryTimer.hasExpired()) {
CConnectData reply(m_gatewayType, m_repeater, m_reflector, CT_LINK1, m_yourAddress, m_yourPort);
m_handler->writeConnect(reply);
unsigned int timeout = calcBackoff();
m_tryTimer.start(timeout);
}
}
if (m_linkState == DCS_UNLINKING) {
if (m_tryTimer.isRunning() && m_tryTimer.hasExpired()) {
CConnectData connect(m_repeater, m_reflector, CT_UNLINK, m_yourAddress, m_yourPort);
m_handler->writeConnect(connect);
unsigned int timeout = calcBackoff();
m_tryTimer.start(timeout);
}
}
return false;
}
void CDCSHandler::writeHeaderInt(IReflectorCallback *handler, CHeaderData& header, DIRECTION direction)
{
if (m_linkState != DCS_LINKED)
return;
// Is it link in the right direction
if (m_direction != direction)
return;
if (m_destination != handler)
return;
// Already in use?
if (m_dcsId != 0x00)
return;
m_seqNo = 0U;
m_myCall1 = header.getMyCall1();
m_myCall2 = header.getMyCall2();
m_yourCall = header.getYourCall();
m_rptCall1 = header.getRptCall1();
m_rptCall2 = header.getRptCall2();
}
void CDCSHandler::writeAMBEInt(IReflectorCallback *handler, CAMBEData &data, DIRECTION direction)
{
if (m_linkState != DCS_LINKED)
return;
// Is it link in the right direction
if (m_direction != direction)
return;
if (m_destination != handler)
return;
// Already in use?
if (m_dcsId != 0x00)
return;
CHeaderData& header = data.getHeader();
header.setMyCall1(m_myCall1);
header.setMyCall2(m_myCall2);
header.setRptCall1(m_rptCall1);
header.setRptCall2(m_rptCall2);
header.setCQCQCQ();
data.setRptSeq(m_seqNo++);
data.setDestination(m_yourAddress, m_yourPort);
m_handler->writeData(data);
}
bool CDCSHandler::stateChange()
{
bool stateChange = m_stateChange;
m_stateChange = false;
return stateChange;
}
void CDCSHandler::writeStatus(FILE *file)
{
for (auto it=m_DCSHandlers.begin(); it!=m_DCSHandlers.end(); it++) {
CDCSHandler *dcsHandler = *it;
struct tm *tm = ::gmtime(&dcsHandler->m_time);
switch (dcsHandler->m_direction) {
case DIR_OUTGOING:
if (dcsHandler->m_linkState == DCS_LINKED) {
fprintf(file, "%04d-%02d-%02d %02d:%02d:%02d: DCS link - Type: Repeater Rptr: %s Refl: %s Dir: Outgoing\n",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
dcsHandler->m_repeater.c_str(), dcsHandler->m_reflector.c_str());
}
break;
case DIR_INCOMING:
if (dcsHandler->m_linkState == DCS_LINKED) {
fprintf(file, "%04d-%02d-%02d %02d:%02d:%02d: DCS link - Type: Repeater Rptr: %s Refl: %s Dir: Incoming\n",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
dcsHandler->m_repeater.c_str(), dcsHandler->m_reflector.c_str());
}
break;
}
}
}
unsigned int CDCSHandler::calcBackoff()
{
if (m_tryCount >= 7U) {
m_tryCount++;
return 60U;
}
unsigned int timeout = 1U;
for (unsigned int i = 0U; i < m_tryCount; i++)
timeout *= 2U;
m_tryCount++;
if (timeout > 60U)
return 60U;
else
return timeout;
}

@ -0,0 +1,129 @@
/*
* Copyright (C) 2012,2013,2015 by Jonathan Naylor G4KLX
* Copyright (c) 2017-2018 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 <netinet/in.h>
#include <string>
#include <cstdio>
#include <list>
#include "DCSProtocolHandlerPool.h"
#include "RemoteRepeaterData.h"
#include "ReflectorCallback.h"
#include "DStarDefines.h"
#include "CallsignList.h"
#include "ConnectData.h"
#include "AMBEData.h"
#include "PollData.h"
#include "Timer.h"
#include "Defs.h"
enum DCS_STATE {
DCS_LINKING,
DCS_LINKED,
DCS_UNLINKING
};
class CDCSHandler {
public:
static void setDCSProtocolHandlerPool(CDCSProtocolHandlerPool *pool);
static void setDCSProtocolIncoming(CDCSProtocolHandler *handler);
static void setGatewayType(GATEWAY_TYPE type);
static void link(IReflectorCallback *handler, const std::string &repeater, const std::string &reflector, const in_addr &address);
static void unlink(IReflectorCallback *handler, const std::string &reflector = std::string(""), bool exclude = true);
static void unlink(CDCSHandler *reflector);
static void unlink();
static void writeHeader(IReflectorCallback *handler, CHeaderData &header, DIRECTION direction);
static void writeAMBE(IReflectorCallback *handler, CAMBEData &data, DIRECTION direction);
static void process(CAMBEData &header);
static void process(CPollData &data);
static void process(CConnectData &connect);
static void gatewayUpdate(const std::string &reflector, const std::string &address);
static void clock(unsigned int ms);
static bool stateChange();
static void writeStatus(FILE *file);
static void setWhiteList(CCallsignList *list);
static void setBlackList(CCallsignList *list);
static void finalise();
static void getInfo(IReflectorCallback *handler, CRemoteRepeaterData &data);
static std::string getIncoming(const std::string &callsign);
protected:
CDCSHandler(IReflectorCallback *handler, const std::string &reflector, const std::string &repeater, CDCSProtocolHandler *protoHandler, const in_addr &address, unsigned int port, DIRECTION direction);
~CDCSHandler();
void processInt(CAMBEData &data);
bool processInt(CConnectData &connect, CD_TYPE type);
void writeHeaderInt(IReflectorCallback *handler, CHeaderData &header, DIRECTION direction);
void writeAMBEInt(IReflectorCallback *handler, CAMBEData &data, DIRECTION direction);
bool clockInt(unsigned int ms);
private:
static std::list<CDCSHandler *> m_DCSHandlers;
static CDCSProtocolHandlerPool *m_pool;
static CDCSProtocolHandler *m_incoming;
static bool m_stateChange;
static GATEWAY_TYPE m_gatewayType;
static CCallsignList *m_whiteList;
static CCallsignList *m_blackList;
std::string m_reflector;
std::string m_repeater;
CDCSProtocolHandler *m_handler;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
DIRECTION m_direction;
DCS_STATE m_linkState;
IReflectorCallback *m_destination;
time_t m_time;
CTimer m_pollTimer;
CTimer m_pollInactivityTimer;
CTimer m_tryTimer;
unsigned int m_tryCount;
unsigned int m_dcsId;
unsigned int m_dcsSeq;
unsigned int m_seqNo;
CTimer m_inactivityTimer;
// Header data
std::string m_yourCall;
std::string m_myCall1;
std::string m_myCall2;
std::string m_rptCall1;
std::string m_rptCall2;
unsigned int calcBackoff();
};

@ -0,0 +1,195 @@
/*
* Copyright (C) 2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 "DCSProtocolHandler.h"
#include "Utils.h"
// #define DUMP_TX
const unsigned int BUFFER_LENGTH = 2000U;
CDCSProtocolHandler::CDCSProtocolHandler(unsigned int port, const std::string& addr) :
m_socket(addr, port),
m_type(DC_NONE),
m_buffer(NULL),
m_length(0U),
m_yourAddress(),
m_yourPort(0U),
m_myPort(port)
{
m_buffer = new unsigned char[BUFFER_LENGTH];
}
CDCSProtocolHandler::~CDCSProtocolHandler()
{
delete[] m_buffer;
}
bool CDCSProtocolHandler::open()
{
return m_socket.open();
}
unsigned int CDCSProtocolHandler::getPort() const
{
return m_myPort;
}
bool CDCSProtocolHandler::writeData(const CAMBEData& data)
{
unsigned char buffer[100U];
unsigned int length = data.getDCSData(buffer, 100U);
#if defined(DUMP_TX)
CUtils::dump("Sending Data", buffer, length);
#endif
return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort());
}
bool CDCSProtocolHandler::writePoll(const CPollData& poll)
{
unsigned char buffer[25U];
unsigned int length = poll.getDCSData(buffer, 25U);
#if defined(DUMP_TX)
CUtils::dump("Sending Poll", buffer, length);
#endif
return m_socket.write(buffer, length, poll.getYourAddress(), poll.getYourPort());
}
bool CDCSProtocolHandler::writeConnect(const CConnectData& connect)
{
unsigned char buffer[520U];
unsigned int length = connect.getDCSData(buffer, 520U);
#if defined(DUMP_TX)
CUtils::dump("Sending Connect", buffer, length);
#endif
return m_socket.write(buffer, length, connect.getYourAddress(), connect.getYourPort());
}
DCS_TYPE CDCSProtocolHandler::read()
{
bool res = true;
// Loop until we have no more data from the socket or we have data for the higher layers
while (res)
res = readPackets();
return m_type;
}
bool CDCSProtocolHandler::readPackets()
{
m_type = DC_NONE;
// No more data?
int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_yourAddress, m_yourPort);
if (length <= 0)
return false;
m_length = length;
if (m_buffer[0] == '0' && m_buffer[1] == '0' && m_buffer[2] == '0' && m_buffer[3] == '1') {
if (m_length == 100U) {
m_type = DC_DATA;
return false;
}
} else if (m_buffer[0] == 'E' && m_buffer[1] == 'E' && m_buffer[2] == 'E' && m_buffer[3] == 'E') {
// CUtils::dump("Status data", m_buffer, m_length);
return true;
} else {
switch (m_length) {
case 17U:
case 22U:
m_type = DC_POLL;
return false;
case 14U:
case 19U:
case 519U:
m_type = DC_CONNECT;
return false;
case 35U:
// CUtils::dump("Status data", m_buffer, m_length);
return true;
default:
break;
}
}
// An unknown type
// CUtils::dump("Unknown packet type from DCS", m_buffer, m_length);
return true;
}
CAMBEData* CDCSProtocolHandler::readData()
{
if (m_type != DC_DATA)
return NULL;
CAMBEData* data = new CAMBEData;
bool res = data->setDCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete data;
return NULL;
}
return data;
}
CPollData* CDCSProtocolHandler::readPoll()
{
if (m_type != DC_POLL)
return NULL;
CPollData* poll = new CPollData;
bool res = poll->setDCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete poll;
return NULL;
}
return poll;
}
CConnectData* CDCSProtocolHandler::readConnect()
{
if (m_type != DC_CONNECT)
return NULL;
CConnectData* connect = new CConnectData;
bool res = connect->setDCSData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete connect;
return NULL;
}
return connect;
}
void CDCSProtocolHandler::close()
{
m_socket.close();
}

@ -0,0 +1,69 @@
/*
* Copyright (C) 2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <netinet/in.h>
#include <string>
#include "UDPReaderWriter.h"
#include "DStarDefines.h"
#include "ConnectData.h"
#include "AMBEData.h"
#include "PollData.h"
enum DCS_TYPE {
DC_NONE,
DC_DATA,
DC_POLL,
DC_CONNECT
};
class CDCSProtocolHandler {
public:
CDCSProtocolHandler(unsigned int port, const std::string& addr = std::string(""));
~CDCSProtocolHandler();
bool open();
unsigned int getPort() const;
bool writeData(const CAMBEData& data);
bool writeConnect(const CConnectData& connect);
bool writePoll(const CPollData& poll);
DCS_TYPE read();
CAMBEData* readData();
CPollData* readPoll();
CConnectData* readConnect();
void close();
private:
CUDPReaderWriter m_socket;
DCS_TYPE m_type;
unsigned char* m_buffer;
unsigned int m_length;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
bool readPackets();
};

@ -0,0 +1,111 @@
/*
* Copyright (C) 2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017-2018 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 <cassert>
#include "DCSProtocolHandlerPool.h"
#include "Utils.h"
CDCSProtocolHandlerPool::CDCSProtocolHandlerPool(const unsigned int port, const std::string &addr) :
m_basePort(port),
m_address(addr)
{
assert(port > 0U);
m_index = m_pool.end();
printf("DCS UDP port base = %u\n", port);
}
CDCSProtocolHandlerPool::~CDCSProtocolHandlerPool()
{
while (m_pool.end() != m_pool.begin()) {
auto it = m_pool.begin();
delete it->second;
m_pool.erase(it);
}
}
CDCSProtocolHandler *CDCSProtocolHandlerPool::getHandler()
{
unsigned int port = m_basePort;
while (m_pool.end() != m_pool.find(port))
port++; // find an unused port
CDCSProtocolHandler *proto = new CDCSProtocolHandler(port, m_address);
if (proto) {
if (proto->open()) {
m_pool[port] = proto;
printf("New CDCSProtocolHandler now on port %u.\n", port);
} else {
delete proto;
proto = NULL;
printf("ERROR: Can't open new DCS UDP port %u!\n", port);
}
} else
printf("ERROR: Can't allocate new CDCSProtocolHandler at port %u\n", port);
return proto;
}
void CDCSProtocolHandlerPool::release(CDCSProtocolHandler *handler)
{
assert(handler != NULL);
for (auto it=m_pool.begin(); it!=m_pool.end(); it++) {
if (it->second == handler) {
it->second->close();
delete it->second;
printf("Releasing CDCSProtocolHandler on port %u.\n", it->first);
m_pool.erase(it);
return;
}
}
// we should never get here!
printf("ERROR: could not find CDCSProtocolHander (port=%u) to release!\n", handler->getPort());
}
DCS_TYPE CDCSProtocolHandlerPool::read()
{
if (m_index == m_pool.end())
m_index = m_pool.begin();
while (m_index != m_pool.end()) {
DCS_TYPE type = m_index->second->read();
if (type != DC_NONE)
return type;
m_index++;
}
return DC_NONE;
}
CAMBEData *CDCSProtocolHandlerPool::readData()
{
return m_index->second->readData();
}
CPollData *CDCSProtocolHandlerPool::readPoll()
{
return m_index->second->readPoll();
}
CConnectData *CDCSProtocolHandlerPool::readConnect()
{
return m_index->second->readConnect();
}
void CDCSProtocolHandlerPool::close()
{
for (auto it=m_pool.begin(); it!=m_pool.end(); it++)
it->second->close();
}

@ -0,0 +1,48 @@
/*
* Copyright (C) 2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017-2018 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 <string>
#include <map>
#include "DCSProtocolHandler.h"
class CDCSProtocolHandlerPool {
public:
CDCSProtocolHandlerPool(const unsigned int port, const std::string &addr = std::string(""));
~CDCSProtocolHandlerPool();
CDCSProtocolHandler *getHandler();
void release(CDCSProtocolHandler *handler);
DCS_TYPE read();
CAMBEData *readData();
CPollData *readPoll();
CConnectData *readConnect();
void close();
private:
std::map<int,CDCSProtocolHandler *> m_pool;
std::map<int,CDCSProtocolHandler *>::iterator m_index;
unsigned int m_basePort;
std::string m_address;
};

@ -0,0 +1,317 @@
/*
* Copyright (C) 2011,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <cassert>
#include <cstring>
#include "DStarDefines.h"
#include "DDData.h"
#include "Utils.h"
unsigned int ETHERNET_ADDRESS_LENGTH = 6U;
unsigned int BUFFER_LENGTH = 2500U;
CDDData::CDDData() :
m_header(),
m_length(0U),
m_frame(NULL)
{
m_frame = new unsigned char[BUFFER_LENGTH];
}
CDDData::CDDData(const CDDData& data) :
m_header(data.m_header),
m_length(data.m_length),
m_frame(NULL)
{
m_frame = new unsigned char[BUFFER_LENGTH];
::memcpy(m_frame, data.m_frame, data.m_length);
}
CDDData::~CDDData()
{
delete[] m_frame;
}
bool CDDData::setIcomRepeaterData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort)
{
assert(data != NULL);
assert(length >= 29U);
bool ret = m_header.setIcomRepeaterData(data, length, true, yourAddress, yourPort);
if (!ret)
return false;
m_length = data[59] * 256U + data[58];
if (m_length > BUFFER_LENGTH)
m_length = BUFFER_LENGTH;
::memcpy(m_frame, data + 60U, m_length);
return true;
}
bool CDDData::setHBRepeaterData(const unsigned char *data, unsigned int length, const in_addr& /*yourAddress*/, unsigned int /*yourPort*/)
{
assert(data != NULL);
assert(length >= 60U);
m_header.setData(data, length, false);
m_length = length - 44U;
if (m_length > BUFFER_LENGTH)
m_length = BUFFER_LENGTH;
::memcpy(m_frame, data + 44U, m_length);
return true;
}
unsigned int CDDData::getIcomRepeaterData(unsigned char *data, unsigned int length)
{
assert(data != NULL);
assert(length >= 32U);
// This section is used when it's normal data (i.e. not an ack) for an Icom controller
data[0] = 'D';
data[1] = 'S';
data[2] = 'T';
data[3] = 'R';
data[4] = m_header.getRptSeq() / 256U; // Packet sequence number
data[5] = m_header.getRptSeq() % 256U;
data[6] = 0x73; // Not a response
data[7] = 0x11; // DD Data type
unsigned int dataLength = m_length + 50U;
data[8] = dataLength / 256U; // Length
data[9] = dataLength % 256U;
data[10] = 0x40; // DD Data
data[11] = 0xFF;
data[12] = 0xFF;
data[13] = 0xFF;
data[14] = 0x00; // Dummy ID
data[15] = 0x00;
data[16] = 0xC0; // DD Data
m_header.getData(data + 17U, RADIO_HEADER_LENGTH_BYTES, true);
// Another length field
data[58] = m_length % 256U;
data[59] = m_length / 256U;
// Now copy the payload
::memcpy(data + 60U, m_frame, m_length);
return 60U + m_length;
}
unsigned int CDDData::getHBRepeaterData(unsigned char *data, unsigned int length)
{
assert(data != NULL);
assert(length >= 1600U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'R';
data[3] = 'P';
data[4] = 0x24U;
m_header.getData(data + 5U, RADIO_HEADER_LENGTH_BYTES, false);
// Now copy the payload
::memcpy(data + 44U, m_frame, m_length);
return 44U + m_length;
}
unsigned int CDDData::getRptSeq() const
{
return m_header.getRptSeq();
}
void CDDData::setRptSeq(unsigned int seqNo)
{
m_header.setRptSeq(seqNo);
}
unsigned char CDDData::getBand1() const
{
return m_header.getBand1();
}
unsigned char CDDData::getBand2() const
{
return m_header.getBand2();
}
unsigned char CDDData::getBand3() const
{
return m_header.getBand3();
}
void CDDData::setBand1(unsigned char band)
{
m_header.setBand1(band);
}
void CDDData::setBand2(unsigned char band)
{
m_header.setBand2(band);
}
void CDDData::setBand3(unsigned char band)
{
m_header.setBand3(band);
}
unsigned char CDDData::getFlag1() const
{
return m_header.getFlag1();
}
unsigned char CDDData::getFlag2() const
{
return m_header.getFlag2();
}
unsigned char CDDData::getFlag3() const
{
return m_header.getFlag3();
}
void CDDData::setFlags(unsigned char flag1, unsigned char flag2, unsigned char flag3)
{
m_header.setFlags(flag1, flag2, flag3);
}
std::string CDDData::getMyCall1() const
{
return m_header.getMyCall1();
}
std::string CDDData::getMyCall2() const
{
return m_header.getMyCall2();
}
std::string CDDData::getYourCall() const
{
return m_header.getYourCall();
}
std::string CDDData::getRptCall1() const
{
return m_header.getRptCall1();
}
std::string CDDData::getRptCall2() const
{
return m_header.getRptCall2();
}
void CDDData::setMyCall1(const std::string& callsign)
{
m_header.setMyCall1(callsign);
}
void CDDData::setMyCall2(const std::string& callsign)
{
m_header.setMyCall2(callsign);
}
void CDDData::setYourCall(const std::string& callsign)
{
m_header.setYourCall(callsign);
}
void CDDData::setRptCall1(const std::string& callsign)
{
m_header.setRptCall1(callsign);
}
void CDDData::setRptCall2(const std::string& callsign)
{
m_header.setRptCall2(callsign);
}
void CDDData::setRepeaters(const std::string& rpt1, const std::string& rpt2)
{
m_header.setRepeaters(rpt1, rpt2);
}
void CDDData::setDestination(const in_addr& address, unsigned int port)
{
m_header.setDestination(address, port);
}
in_addr CDDData::getYourAddress() const
{
return m_header.getYourAddress();
}
unsigned int CDDData::getYourPort() const
{
return m_header.getYourPort();
}
void CDDData::setEthernetFrame(const unsigned char *frame, unsigned int length)
{
assert(frame != NULL);
assert(length > 0U);
m_length = length;
if (m_length > BUFFER_LENGTH)
m_length = BUFFER_LENGTH;
::memcpy(m_frame, frame, m_length);
}
unsigned int CDDData::getEthernetFrame(unsigned char *frame, unsigned int length) const
{
assert(frame != NULL);
assert(length > 0U);
if (length > m_length)
length = m_length;
::memcpy(frame, m_frame, length);
return length;
}
unsigned char* CDDData::getSourceAddress() const
{
return m_frame + ETHERNET_ADDRESS_LENGTH;
}
unsigned char* CDDData::getDestinationAddress() const
{
return m_frame + 0U;
}

@ -0,0 +1,83 @@
/*
* Copyright (C) 2011,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <netinet/in.h>
#include "HeaderData.h"
class CDDData {
public:
CDDData();
CDDData(const CDDData& data);
~CDDData();
bool setIcomRepeaterData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort);
bool setHBRepeaterData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort);
unsigned int getIcomRepeaterData(unsigned char* data, unsigned int length);
unsigned int getHBRepeaterData(unsigned char* data, unsigned int length);
unsigned char getBand1() const;
unsigned char getBand2() const;
unsigned char getBand3() const;
void setBand1(unsigned char band);
void setBand2(unsigned char band);
void setBand3(unsigned char band);
unsigned char getFlag1() const;
unsigned char getFlag2() const;
unsigned char getFlag3() const;
void setFlags(unsigned char flag1, unsigned char flag2, unsigned char flag3);
std::string getMyCall1() const;
std::string getMyCall2() const;
std::string getYourCall() const;
std::string getRptCall1() const;
std::string getRptCall2() const;
void setMyCall1(const std::string& callsign);
void setMyCall2(const std::string& callsign);
void setYourCall(const std::string& callsign);
void setRptCall1(const std::string& callsign);
void setRptCall2(const std::string& callsign);
unsigned int getRptSeq() const;
void setRptSeq(unsigned int seqNo);
void setEthernetFrame(const unsigned char* frame, unsigned int length);
unsigned int getEthernetFrame(unsigned char* frame, unsigned int length) const;
unsigned char* getSourceAddress() const;
unsigned char* getDestinationAddress() const;
void setRepeaters(const std::string& rpt1, const std::string& rpt2);
void setDestination(const in_addr& address, unsigned int port);
in_addr getYourAddress() const;
unsigned int getYourPort() const;
private:
CHeaderData m_header;
unsigned int m_length;
unsigned char* m_frame;
};

@ -0,0 +1,766 @@
/*
* Copyright (C) 2010-2015 by Jonathan Naylor G4KLX
* Copyright (c) 2017-2018 by Thomas A. Early
*
* 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 <cassert>
#include "DExtraHandler.h"
#include "Utils.h"
std::list<CDExtraHandler *> CDExtraHandler::m_DExtraHandlers;
std::string CDExtraHandler::m_callsign;
CDExtraProtocolHandlerPool *CDExtraHandler::m_pool = NULL;
bool CDExtraHandler::m_stateChange = false;
CCallsignList *CDExtraHandler::m_whiteList = NULL;
CCallsignList *CDExtraHandler::m_blackList = NULL;
CDExtraHandler::CDExtraHandler(IReflectorCallback *handler, const std::string &dextraHandler, const std::string &repeater, CDExtraProtocolHandler *protoHandler, const in_addr &address, unsigned int port, DIRECTION direction) :
m_reflector(dextraHandler),
m_repeater(repeater),
m_handler(protoHandler),
m_yourAddress(address),
m_yourPort(port),
m_direction(direction),
m_linkState(DEXTRA_LINKING),
m_destination(handler),
m_time(),
m_pollTimer(1000U, 10U),
m_pollInactivityTimer(1000U, 60U),
m_tryTimer(1000U, 1U),
m_tryCount(0U),
m_dExtraId(0x00U),
m_dExtraSeq(0x00U),
m_inactivityTimer(1000U, NETWORK_TIMEOUT),
m_header(NULL)
{
assert(protoHandler != NULL);
assert(handler != NULL);
assert(port > 0U);
m_pollInactivityTimer.start();
m_time = ::time(NULL);
if (direction == DIR_INCOMING) {
m_pollTimer.start();
m_stateChange = true;
m_linkState = DEXTRA_LINKED;
} else {
m_linkState = DEXTRA_LINKING;
m_tryTimer.start();
}
}
CDExtraHandler::~CDExtraHandler()
{
if (m_direction == DIR_OUTGOING)
m_pool->release(m_handler);
delete m_header;
}
void CDExtraHandler::setCallsign(const std::string& callsign)
{
m_callsign.assign(callsign);
m_callsign.resize(LONG_CALLSIGN_LENGTH, ' ');
m_callsign[LONG_CALLSIGN_LENGTH - 1U] = ' ';
}
void CDExtraHandler::setDExtraProtocolHandlerPool(CDExtraProtocolHandlerPool *pool)
{
assert(pool != NULL);
m_pool = pool;
}
void CDExtraHandler::setWhiteList(CCallsignList *list)
{
assert(list != NULL);
m_whiteList = list;
}
void CDExtraHandler::setBlackList(CCallsignList *list)
{
assert(list != NULL);
m_blackList = list;
}
std::string CDExtraHandler::getIncoming(const std::string &callsign)
{
std::string incoming;
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); it++) {
CDExtraHandler *dextraHandler = *it;
if (dextraHandler->m_direction==DIR_INCOMING && 0==dextraHandler->m_repeater.compare(callsign)) {
incoming.append(dextraHandler->m_reflector);
incoming.append(" ");
}
}
return incoming;
}
void CDExtraHandler::getInfo(IReflectorCallback *handler, CRemoteRepeaterData &data)
{
assert(handler != NULL);
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); it++) {
CDExtraHandler *dextraHandler = *it;
if (dextraHandler->m_destination == handler) {
if (dextraHandler->m_direction == DIR_INCOMING && 0 == dextraHandler->m_repeater.size()) {
if (dextraHandler->m_linkState != DEXTRA_UNLINKING)
data.addLink(dextraHandler->m_reflector, PROTO_DEXTRA, dextraHandler->m_linkState == DEXTRA_LINKED, DIR_INCOMING, true);
} else {
if (dextraHandler->m_linkState != DEXTRA_UNLINKING)
data.addLink(dextraHandler->m_reflector, PROTO_DEXTRA, dextraHandler->m_linkState == DEXTRA_LINKED, dextraHandler->m_direction, false);
}
}
}
}
std::string CDExtraHandler::getDongles()
{
std::string dongles;
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); it++) {
CDExtraHandler *dextraHandler = *it;
if (dextraHandler->m_direction==DIR_INCOMING && 0==dextraHandler->m_repeater.size()) {
dongles.append("X:");
dongles.append(dextraHandler->m_reflector);
dongles.append(" ");
}
}
return dongles;
}
void CDExtraHandler::process(CHeaderData &header)
{
in_addr yourAddress = header.getYourAddress();
unsigned int yourPort = header.getYourPort();
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); it++) {
CDExtraHandler *dextraHandler = *it;
if (dextraHandler->m_yourAddress.s_addr==yourAddress.s_addr && dextraHandler->m_yourPort==yourPort)
dextraHandler->processInt(header);
}
}
void CDExtraHandler::process(CAMBEData &data)
{
in_addr yourAddress = data.getYourAddress();
unsigned int yourPort = data.getYourPort();
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); it++) {
CDExtraHandler *dextraHandler = *it;
if (yourAddress.s_addr==dextraHandler->m_yourAddress.s_addr && yourPort==dextraHandler->m_yourPort)
dextraHandler->processInt(data);
}
}
void CDExtraHandler::process(const CPollData &poll)
{
std::string reflector = poll.getData1();
in_addr yourAddress = poll.getYourAddress();
unsigned int yourPort = poll.getYourPort();
// reset all inactivity times from this reflector
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); it++) {
CDExtraHandler *handler = *it;
if ( 0==handler->m_reflector.compare(0, LONG_CALLSIGN_LENGTH-1, reflector, 0, LONG_CALLSIGN_LENGTH-1) &&
handler->m_yourAddress.s_addr == yourAddress.s_addr &&
handler->m_yourPort == yourPort &&
handler->m_linkState == DEXTRA_LINKED) {
handler->m_pollInactivityTimer.start();
}
}
}
void CDExtraHandler::process(CConnectData &connect)
{
CD_TYPE type = connect.getType();
if (type == CT_ACK || type == CT_NAK || type == CT_UNLINK) {
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); ) {
CDExtraHandler *dextraHandler = *it;
bool res = dextraHandler->processInt(connect, type);
if (res) {
delete dextraHandler;
it = m_DExtraHandlers.erase(it);
} else
it++;
}
return;
}
// else if type == CT_LINK1 or type == CT_LINK2
// someone tried to link directly to a Smart Group!
printf("CDExtraHandler::process(CConnectData) type=CT_LINK%c, SGSchannel=%s, from repeater=%s\n", (type==CT_LINK1) ? '1' : '2', m_callsign.c_str(), connect.getRepeater().c_str());
}
void CDExtraHandler::link(IReflectorCallback *handler, const std::string &repeater, const std::string &gateway, const in_addr &address)
{
CDExtraProtocolHandler *protoHandler = m_pool->getHandler();
if (protoHandler == NULL)
return;
CDExtraHandler *dextra = new CDExtraHandler(handler, gateway, repeater, protoHandler, address, DEXTRA_PORT, DIR_OUTGOING);
if (dextra) {
m_DExtraHandlers.push_back(dextra);
CConnectData reply(repeater, gateway, CT_LINK1, address, DEXTRA_PORT);
protoHandler->writeConnect(reply);
}
}
void CDExtraHandler::unlink(IReflectorCallback *handler, const std::string &callsign, bool exclude)
{
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); it++) {
CDExtraHandler *dextraHandler = *it;
bool found = false;
if (exclude) {
if (dextraHandler->m_direction == DIR_OUTGOING && dextraHandler->m_destination == handler && dextraHandler->m_reflector.compare(callsign)) {
printf("Removing outgoing DExtra link %s, %s\n", dextraHandler->m_repeater.c_str(), dextraHandler->m_reflector.c_str());
if (dextraHandler->m_linkState == DEXTRA_LINKING || dextraHandler->m_linkState == DEXTRA_LINKED) {
CConnectData connect(dextraHandler->m_repeater, dextraHandler->m_yourAddress, dextraHandler->m_yourPort);
dextraHandler->m_handler->writeConnect(connect);
dextraHandler->m_linkState = DEXTRA_UNLINKING;
dextraHandler->m_destination->linkFailed(DP_DEXTRA, dextraHandler->m_reflector, false);
}
found = true;
}
} else {
if (dextraHandler->m_destination == handler && 0==dextraHandler->m_reflector.compare(callsign)) {
printf("Removing DExtra link %s, %s\n", dextraHandler->m_repeater.c_str(), dextraHandler->m_reflector.c_str());
if (dextraHandler->m_linkState == DEXTRA_LINKING || dextraHandler->m_linkState == DEXTRA_LINKED) {
CConnectData connect(dextraHandler->m_repeater, dextraHandler->m_yourAddress, dextraHandler->m_yourPort);
dextraHandler->m_handler->writeConnect(connect);
dextraHandler->m_linkState = DEXTRA_UNLINKING;
dextraHandler->m_destination->linkFailed(DP_DEXTRA, dextraHandler->m_reflector, false);
}
found = true;
}
}
// If an active link with incoming traffic, send an EOT to the repeater
if (found) {
if (dextraHandler->m_dExtraId != 0x00U) {
unsigned int seq = dextraHandler->m_dExtraSeq + 1U;
if (seq == 21U)
seq = 0U;
CAMBEData data;
data.setData(END_PATTERN_BYTES, DV_FRAME_LENGTH_BYTES);
data.setSeq(seq);
data.setEnd(true);
data.setId(dextraHandler->m_dExtraId);
dextraHandler->m_destination->process(data, dextraHandler->m_direction, AS_DEXTRA);
}
m_stateChange = true;
delete dextraHandler;
it = m_DExtraHandlers.erase(it);
it--;
}
}
}
void CDExtraHandler::unlink(CDExtraHandler *dextraHandler)
{
if (dextraHandler != NULL) {
if (dextraHandler->m_repeater.size()) {
printf("Unlinking from DExtra dextraHandler %s\n", dextraHandler->m_reflector.c_str());
CConnectData connect(dextraHandler->m_repeater, dextraHandler->m_yourAddress, dextraHandler->m_yourPort);
dextraHandler->m_handler->writeConnect(connect);
dextraHandler->m_linkState = DEXTRA_UNLINKING;
}
}
}
void CDExtraHandler::unlink()
{
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); it++) {
CDExtraHandler *dextraHandler = *it;
CDExtraHandler::unlink(dextraHandler);
}
}
void CDExtraHandler::writeHeader(IReflectorCallback *handler, CHeaderData &header, DIRECTION direction)
{
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); it++) {
CDExtraHandler *dextraHandler = *it;
dextraHandler->writeHeaderInt(handler, header, direction);
}
}
void CDExtraHandler::writeAMBE(IReflectorCallback *handler, CAMBEData &data, DIRECTION direction)
{
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); it++) {
CDExtraHandler *dextraHandler = *it;
dextraHandler->writeAMBEInt(handler, data, direction);
}
}
void CDExtraHandler::gatewayUpdate(const std::string &dextraHandler, const std::string &address)
{
std::string gateway = dextraHandler;
gateway.resize(LONG_CALLSIGN_LENGTH - 1U, ' ');
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); it++) {
CDExtraHandler *dextraHandler = *it;
if (0==dextraHandler->m_reflector.compare(0, LONG_CALLSIGN_LENGTH-1, gateway)) {
if (address.size()) {
// A new address, change the value
printf("Changing IP address of DExtra gateway or dextraHandler %s to %s\n", dextraHandler->m_reflector.c_str(), address.c_str());
dextraHandler->m_yourAddress.s_addr = ::inet_addr(address.c_str());
} else {
printf("IP address for DExtra gateway or dextraHandler %s has been removed\n", dextraHandler->m_reflector.c_str());
// No address, this probably shouldn't happen....
if (dextraHandler->m_direction == DIR_OUTGOING && dextraHandler->m_destination != NULL)
dextraHandler->m_destination->linkFailed(DP_DEXTRA, dextraHandler->m_reflector, false);
m_stateChange = true;
delete dextraHandler;
it = m_DExtraHandlers.erase(it);
it--;
}
}
}
}
void CDExtraHandler::clock(unsigned int ms)
{
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); ) {
CDExtraHandler *dextraHandler = *it;
bool ret = dextraHandler->clockInt(ms);
if (ret) {
delete dextraHandler;
it = m_DExtraHandlers.erase(it);
} else
it++;
}
}
void CDExtraHandler::finalise()
{
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); ) {
CDExtraHandler *handler = *it;
delete handler;
it = m_DExtraHandlers.erase(it);
}
}
void CDExtraHandler::processInt(CHeaderData& header)
{
std::string my = header.getMyCall1();
std::string rpt1 = header.getRptCall1();
std::string rpt2 = header.getRptCall2();
unsigned int id = header.getId();
if (m_whiteList != NULL) {
bool res = m_whiteList->isInList(my);
if (!res) {
printf("%s rejected from DExtra as not found in the white list\n", my.c_str());
m_dExtraId = 0x00U;
return;
}
}
if (m_blackList != NULL) {
bool res = m_blackList->isInList(my);
if (res) {
printf("%s rejected from DExtra as found in the black list\n", my.c_str());
m_dExtraId = 0x00U;
return;
}
}
if (m_linkState != DEXTRA_LINKED)
return;
switch (m_direction) {
case DIR_OUTGOING: {
// Always a repeater connection
if (m_reflector.compare(rpt2) && m_reflector.compare(rpt1))
return;
// If we're already processing, ignore the new header
if (m_dExtraId != 0x00U)
return;
m_dExtraId = id;
m_dExtraSeq = 0x00U;
m_inactivityTimer.start();
delete m_header;
m_header = new CHeaderData(header);
m_header->setCQCQCQ();
m_header->setFlags(0x00U, 0x00U, 0x00U);
m_destination->process(*m_header, m_direction, AS_DEXTRA);
}
break;
case DIR_INCOMING:
if (m_repeater.size()) {
// A repeater connection
if (m_repeater.compare(rpt2) && m_repeater.compare(rpt1))
return;
// If we're already processing, ignore the new header
if (m_dExtraId != 0x00U)
return;
m_dExtraId = id;
m_dExtraSeq = 0x00U;
m_inactivityTimer.start();
delete m_header;
m_header = new CHeaderData(header);
m_header->setCQCQCQ();
m_header->setFlags(0x00U, 0x00U, 0x00U);
m_destination->process(*m_header, m_direction, AS_DEXTRA);
}
break;
}
}
void CDExtraHandler::processInt(CAMBEData &data)
{
if (m_linkState != DEXTRA_LINKED)
return;
if (m_dExtraId != data.getId())
return;
m_pollInactivityTimer.start();
m_inactivityTimer.start();
m_dExtraSeq = data.getSeq();
// Send the header every 21 frames, if we have it
if (m_dExtraSeq == 0U && m_header != NULL)
m_destination->process(*m_header, m_direction, AS_DUP);
// Copy the data to ensure it remains unchanged
CAMBEData temp(data);
m_destination->process(temp, m_direction, AS_DEXTRA);
if (temp.isEnd()) {
delete m_header;
m_header = NULL;
m_dExtraId = 0x00U;
m_dExtraSeq = 0x00U;
m_inactivityTimer.stop();
}
}
bool CDExtraHandler::processInt(CConnectData &connect, CD_TYPE type)
{
in_addr yourAddress = connect.getYourAddress();
unsigned int yourPort = connect.getYourPort();
std::string repeater = connect.getRepeater();
if (m_yourAddress.s_addr != yourAddress.s_addr || m_yourPort != yourPort)
return false;
switch (type) {
case CT_ACK:
if (m_repeater.compare(repeater))
return false;
if (m_linkState == DEXTRA_LINKING) {
printf("DExtra ACK message received from %s\n", m_reflector.c_str());
if (m_direction == DIR_OUTGOING && m_destination != NULL)
m_destination->linkUp(DP_DEXTRA, m_reflector);
m_tryTimer.stop();
m_pollTimer.start();
m_stateChange = true;
m_linkState = DEXTRA_LINKED;
}
return false;
case CT_NAK:
if (m_repeater.compare(repeater))
return false;
if (m_linkState == DEXTRA_LINKING) {
printf("DExtra NAK message received from %s\n", m_reflector.c_str());
if (m_direction == DIR_OUTGOING && m_destination != NULL)
m_destination->linkRefused(DP_DEXTRA, m_reflector);
return true;
}
return false;
case CT_UNLINK:
if (m_reflector.compare(repeater))
return false;
if (m_linkState == DEXTRA_LINKED) {
printf("DExtra disconnect message received from %s\n", m_reflector.c_str());
if (m_direction == DIR_OUTGOING && m_destination != NULL)
m_destination->linkFailed(DP_DEXTRA, m_reflector, false);
m_stateChange = true;
}
return true;
default:
return false;
}
}
bool CDExtraHandler::clockInt(unsigned int ms)
{
m_tryTimer.clock(ms);
m_pollTimer.clock(ms);
m_inactivityTimer.clock(ms);
m_pollInactivityTimer.clock(ms);
if (m_pollInactivityTimer.isRunning() && m_pollInactivityTimer.hasExpired()) {
m_pollInactivityTimer.start();
delete m_header;
m_header = NULL;
m_stateChange = true;
m_dExtraId = 0x00U;
m_dExtraSeq = 0x00U;
switch (m_linkState) {
case DEXTRA_LINKING:
printf("DExtra link to %s has failed to connect\n", m_reflector.c_str());
break;
case DEXTRA_LINKED:
printf("DExtra link to %s has failed (poll inactivity)\n", m_reflector.c_str());
break;
case DEXTRA_UNLINKING:
printf("DExtra link to %s has failed to disconnect cleanly\n", m_reflector.c_str());
break;
default:
break;
}
if (m_direction == DIR_OUTGOING) {
bool reconnect = m_destination->linkFailed(DP_DEXTRA, m_reflector, true);
if (reconnect) {
CConnectData reply(m_repeater, m_reflector, CT_LINK1, m_yourAddress, m_yourPort);
m_handler->writeConnect(reply);
m_linkState = DEXTRA_LINKING;
m_tryTimer.start(1U);
m_tryCount = 0U;
return false;
}
}
return true;
}
if (m_pollTimer.isRunning() && m_pollTimer.hasExpired()) {
if (m_linkState == DEXTRA_LINKED) {
if (m_repeater.size()) {
std::string callsign = m_repeater;
callsign[LONG_CALLSIGN_LENGTH - 1U] =' ';
CPollData poll(callsign, m_yourAddress, m_yourPort);
m_handler->writePoll(poll);
} else {
CPollData poll(m_callsign, m_yourAddress, m_yourPort);
m_handler->writePoll(poll);
}
}
m_pollTimer.start();
}
if (m_inactivityTimer.isRunning() && m_inactivityTimer.hasExpired()) {
delete m_header;
m_header = NULL;
m_dExtraId = 0x00U;
m_dExtraSeq = 0x00U;
m_inactivityTimer.stop();
}
if (m_linkState == DEXTRA_LINKING) {
if (m_tryTimer.isRunning() && m_tryTimer.hasExpired()) {
CConnectData reply(m_repeater, m_reflector, CT_LINK1, m_yourAddress, m_yourPort);
m_handler->writeConnect(reply);
unsigned int timeout = calcBackoff();
m_tryTimer.start(timeout);
}
}
return false;
}
void CDExtraHandler::writeHeaderInt(IReflectorCallback *handler, CHeaderData &header, DIRECTION direction)
{
if (m_linkState != DEXTRA_LINKED)
return;
// Is it link in the right direction
if (m_direction != direction)
return;
// Already in use?
if (m_dExtraId != 0x00)
return;
switch (m_direction) {
case DIR_OUTGOING:
if (m_destination == handler) {
header.setDestination(m_yourAddress, m_yourPort);
m_handler->writeHeader(header);
}
break;
case DIR_INCOMING:
if (0==m_repeater.size() || m_destination == handler) {
header.setDestination(m_yourAddress, m_yourPort);
m_handler->writeHeader(header);
}
break;
}
}
void CDExtraHandler::writeAMBEInt(IReflectorCallback *handler, CAMBEData &data, DIRECTION direction)
{
if (m_linkState != DEXTRA_LINKED)
return;
// Is it link in the right direction
if (m_direction != direction)
return;
// Already in use?
if (m_dExtraId != 0x00)
return;
switch (m_direction) {
case DIR_OUTGOING:
if (m_destination == handler) {
data.setDestination(m_yourAddress, m_yourPort);
m_handler->writeAMBE(data);
}
break;
case DIR_INCOMING:
if (0==m_repeater.size() || m_destination == handler) {
data.setDestination(m_yourAddress, m_yourPort);
m_handler->writeAMBE(data);
}
break;
}
}
bool CDExtraHandler::stateChange()
{
bool stateChange = m_stateChange;
m_stateChange = false;
return stateChange;
}
void CDExtraHandler::writeStatus(FILE *file)
{
for (auto it=m_DExtraHandlers.begin(); it!=m_DExtraHandlers.end(); it++) {
CDExtraHandler *dextraHandler = *it;
struct tm *tm = ::gmtime(&dextraHandler->m_time);
switch (dextraHandler->m_direction) {
case DIR_OUTGOING:
if (dextraHandler->m_linkState == DEXTRA_LINKED) {
fprintf(file, "%04d-%02d-%02d %02d:%02d:%02d: DExtra link - Type: Repeater Rptr: %s Refl: %s Dir: Outgoing\n",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
dextraHandler->m_repeater.c_str(), dextraHandler->m_reflector.c_str());
}
break;
case DIR_INCOMING:
if (dextraHandler->m_linkState == DEXTRA_LINKED) {
if (0==dextraHandler->m_repeater.size())
fprintf(file, "%04d-%02d-%02d %02d:%02d:%02d: DExtra link - Type: Dongle User: %s Dir: Incoming\n",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
dextraHandler->m_reflector.c_str());
else
fprintf(file, "%04d-%02d-%02d %02d:%02d:%02d: DExtra link - Type: Repeater Rptr: %s Refl: %s Dir: Incoming\n",
tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
dextraHandler->m_repeater.c_str(), dextraHandler->m_reflector.c_str());
}
break;
}
}
}
unsigned int CDExtraHandler::calcBackoff()
{
if (m_tryCount >= 7U) {
m_tryCount++;
return 60U;
}
unsigned int timeout = 1U;
for (unsigned int i = 0U; i < m_tryCount; i++)
timeout *= 2U;
m_tryCount++;
if (timeout > 60U)
return 60U;
else
return timeout;
}

@ -0,0 +1,121 @@
/*
* Copyright (C) 2010-2013,2015 by Jonathan Naylor G4KLX
* Copyright (c) 2017-2018 by Thomas A. Early
*
* 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 <netinet/in.h>
#include <string>
#include <list>
#include "DExtraProtocolHandlerPool.h"
#include "RemoteRepeaterData.h"
#include "ReflectorCallback.h"
#include "DStarDefines.h"
#include "CallsignList.h"
#include "ConnectData.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "PollData.h"
#include "Timer.h"
#include "Defs.h"
enum DEXTRA_STATE {
DEXTRA_LINKING,
DEXTRA_LINKED,
DEXTRA_UNLINKING
};
class CDExtraHandler {
public:
static void setCallsign(const std::string &callsign);
static void setDExtraProtocolHandlerPool(CDExtraProtocolHandlerPool *pool);
static void link(IReflectorCallback *handler, const std::string &repeater, const std::string &reflector, const in_addr &address);
static void unlink(IReflectorCallback *handler, const std::string &reflector = std::string(""), bool exclude = true);
static void unlink(CDExtraHandler *reflector);
static void unlink();
static void writeHeader(IReflectorCallback *handler, CHeaderData &header, DIRECTION direction);
static void writeAMBE(IReflectorCallback *handler, CAMBEData &data, DIRECTION direction);
static void process(CHeaderData &header);
static void process(CAMBEData &data);
static void process(const CPollData &poll);
static void process(CConnectData &connect);
static void gatewayUpdate(const std::string &reflector, const std::string &address);
static void clock(unsigned int ms);
static bool stateChange();
static void writeStatus(FILE *file);
static void setWhiteList(CCallsignList *list);
static void setBlackList(CCallsignList *list);
static void finalise();
static void getInfo(IReflectorCallback *handler, CRemoteRepeaterData &data);
static std::string getIncoming(const std::string &callsign);
static std::string getDongles();
protected:
CDExtraHandler(IReflectorCallback *handler, const std::string &reflector, const std::string &repeater, CDExtraProtocolHandler *protoHandler, const in_addr &address, unsigned int port, DIRECTION direction);
~CDExtraHandler();
void processInt(CHeaderData &header);
void processInt(CAMBEData &data);
bool processInt(CConnectData &connect, CD_TYPE type);
void writeHeaderInt(IReflectorCallback *handler, CHeaderData &header, DIRECTION direction);
void writeAMBEInt(IReflectorCallback *handler, CAMBEData &data, DIRECTION direction);
bool clockInt(unsigned int ms);
private:
static std::list<CDExtraHandler *> m_DExtraHandlers;
static std::string m_callsign;
static CDExtraProtocolHandlerPool *m_pool;
static bool m_stateChange;
static CCallsignList *m_whiteList;
static CCallsignList *m_blackList;
std::string m_reflector;
std::string m_repeater;
CDExtraProtocolHandler *m_handler;
in_addr m_yourAddress;
unsigned int m_yourPort;
DIRECTION m_direction;
DEXTRA_STATE m_linkState;
IReflectorCallback *m_destination;
time_t m_time;
CTimer m_pollTimer;
CTimer m_pollInactivityTimer;
CTimer m_tryTimer;
unsigned int m_tryCount;
unsigned int m_dExtraId;
unsigned int m_dExtraSeq;
CTimer m_inactivityTimer;
CHeaderData *m_header;
unsigned int calcBackoff();
};

@ -0,0 +1,228 @@
/*
* Copyright (C) 2010-2013 by Jonathan Naylor G4KLX
* Copyright (C) 2017 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 "DExtraProtocolHandler.h"
#include "Utils.h"
// #define DUMP_TX
const unsigned int BUFFER_LENGTH = 1000U;
CDExtraProtocolHandler::CDExtraProtocolHandler(unsigned int port, const std::string& addr) :
m_socket(addr, port),
m_type(DE_NONE),
m_buffer(NULL),
m_length(0U),
m_yourAddress(),
m_yourPort(0U),
m_myPort(port)
{
m_buffer = new unsigned char[BUFFER_LENGTH];
}
CDExtraProtocolHandler::~CDExtraProtocolHandler()
{
delete[] m_buffer;
}
bool CDExtraProtocolHandler::open()
{
return m_socket.open();
}
unsigned int CDExtraProtocolHandler::getPort() const
{
return m_myPort;
}
bool CDExtraProtocolHandler::writeHeader(const CHeaderData& header)
{
unsigned char buffer[60U];
unsigned int length = header.getDExtraData(buffer, 60U, true);
#if defined(DUMP_TX)
CUtils::dump("Sending Header", buffer, length);
#endif
for (unsigned int i = 0U; i < 5U; i++) {
bool res = m_socket.write(buffer, length, header.getYourAddress(), header.getYourPort());
if (!res)
return false;
}
return true;
}
bool CDExtraProtocolHandler::writeAMBE(const CAMBEData& data)
{
unsigned char buffer[40U];
unsigned int length = data.getDExtraData(buffer, 40U);
#if defined(DUMP_TX)
CUtils::dump("Sending Data", buffer, length);
#endif
return m_socket.write(buffer, length, data.getYourAddress(), data.getYourPort());
}
bool CDExtraProtocolHandler::writePoll(const CPollData& poll)
{
unsigned char buffer[20U];
unsigned int length = poll.getDExtraData(buffer, 20U);
#if defined(DUMP_TX)
CUtils::dump("Sending Poll", buffer, length);
#endif
return m_socket.write(buffer, length, poll.getYourAddress(), poll.getYourPort());
}
bool CDExtraProtocolHandler::writeConnect(const CConnectData& connect)
{
unsigned char buffer[20U];
unsigned int length = connect.getDExtraData(buffer, 20U);
#if defined(DUMP_TX)
CUtils::dump("Sending Connect", buffer, length);
#endif
for (unsigned int i = 0U; i < 2U; i++) {
bool res = m_socket.write(buffer, length, connect.getYourAddress(), connect.getYourPort());
if (!res)
return false;
}
return true;
}
DEXTRA_TYPE CDExtraProtocolHandler::read()
{
bool res = true;
// Loop until we have no more data from the socket or we have data for the higher layers
while (res)
res = readPackets();
return m_type;
}
bool CDExtraProtocolHandler::readPackets()
{
m_type = DE_NONE;
// No more data?
int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_yourAddress, m_yourPort);
if (length <= 0)
return false;
m_length = length;
if (m_buffer[0] != 'D' || m_buffer[1] != 'S' || m_buffer[2] != 'V' || m_buffer[3] != 'T') {
switch (m_length) {
case 9U:
m_type = DE_POLL;
return false;
case 11U:
case 14U:
m_type = DE_CONNECT;
return false;
default:
return true;
}
} else {
// Header or data packet type?
if (m_buffer[14] == 0x80)
m_type = DE_HEADER;
else
m_type = DE_AMBE;
return false;
}
}
CHeaderData* CDExtraProtocolHandler::newHeader()
{
if (m_type != DE_HEADER)
return NULL;
CHeaderData* header = new CHeaderData;
// DExtra checksums are unreliable
bool res = header->setDExtraData(m_buffer, m_length, false, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete header;
return NULL;
}
return header;
}
CAMBEData* CDExtraProtocolHandler::newAMBE()
{
if (m_type != DE_AMBE)
return NULL;
CAMBEData* data = new CAMBEData;
bool res = data->setDExtraData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete data;
return NULL;
}
return data;
}
CPollData* CDExtraProtocolHandler::newPoll()
{
if (m_type != DE_POLL)
return NULL;
CPollData* poll = new CPollData;
bool res = poll->setDExtraData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete poll;
return NULL;
}
return poll;
}
CConnectData* CDExtraProtocolHandler::newConnect()
{
if (m_type != DE_CONNECT)
return NULL;
CConnectData* connect = new CConnectData;
bool res = connect->setDExtraData(m_buffer, m_length, m_yourAddress, m_yourPort, m_myPort);
if (!res) {
delete connect;
return NULL;
}
return connect;
}
void CDExtraProtocolHandler::close()
{
m_socket.close();
}

@ -0,0 +1,72 @@
/*
* Copyright (C) 2010-2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <netinet/in.h>
#include "UDPReaderWriter.h"
#include "DStarDefines.h"
#include "ConnectData.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "PollData.h"
enum DEXTRA_TYPE {
DE_NONE,
DE_HEADER,
DE_AMBE,
DE_POLL,
DE_CONNECT
};
class CDExtraProtocolHandler {
public:
CDExtraProtocolHandler(unsigned int port, const std::string& addr = std::string(""));
~CDExtraProtocolHandler();
bool open();
unsigned int getPort() const;
bool writeHeader(const CHeaderData& header);
bool writeAMBE(const CAMBEData& data);
bool writeConnect(const CConnectData& connect);
bool writePoll(const CPollData& poll);
DEXTRA_TYPE read();
CHeaderData* newHeader();
CAMBEData* newAMBE();
CPollData* newPoll();
CConnectData* newConnect();
void close();
private:
CUDPReaderWriter m_socket;
DEXTRA_TYPE m_type;
unsigned char* m_buffer;
unsigned int m_length;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
bool readPackets();
};

@ -0,0 +1,116 @@
/*
* Copyright (C) 2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017-2018 by Thomas A. Early
*
* 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 <cassert>
#include "DExtraProtocolHandlerPool.h"
#include "Utils.h"
CDExtraProtocolHandlerPool::CDExtraProtocolHandlerPool(const unsigned int port, const std::string &addr) :
m_basePort(port),
m_address(addr)
{
assert(port > 0U);
m_index = m_pool.end();
printf("DExtra UDP port base = %u\n", port);
}
CDExtraProtocolHandlerPool::~CDExtraProtocolHandlerPool()
{
while (m_pool.end() != m_pool.begin()) {
auto it = m_pool.begin();
delete it->second;
m_pool.erase(it);
}
}
CDExtraProtocolHandler* CDExtraProtocolHandlerPool::getHandler()
{
unsigned int port = m_basePort;
while (m_pool.end() != m_pool.find(port))
port++; // find an unused port
CDExtraProtocolHandler *proto = new CDExtraProtocolHandler(port, m_address);
if (proto) {
if (proto->open()) {
m_pool[port] = proto;
printf("New CDExtraProtocolHandler now on UDP port %u.\n", port);
} else {
delete proto;
proto = NULL;
printf("ERROR: Can't open new DExtra UDP port %u!\n", port);
}
} else
printf("ERROR: Can't allocate new CDExtraProtocolHandler at port %u\n", port);
return proto;
}
void CDExtraProtocolHandlerPool::release(CDExtraProtocolHandler *handler)
{
assert(handler != NULL);
for (auto it=m_pool.begin(); it!=m_pool.end(); it++) {
if (it->second == handler) {
it->second->close();
delete it->second;
printf("Releasing CDExtraProtocolHandler on port %u.\n", it->first);
m_pool.erase(it);
return;
}
}
// we should never get here!
printf("ERROR: could not find CDExtraProtocolHander (port=%u) to release!\n", handler->getPort());
}
DEXTRA_TYPE CDExtraProtocolHandlerPool::read()
{
if (m_index == m_pool.end())
m_index = m_pool.begin();
while (m_index != m_pool.end()) {
DEXTRA_TYPE type = m_index->second->read();
if (type != DE_NONE)
return type;
m_index++;
}
return DE_NONE;
}
CHeaderData *CDExtraProtocolHandlerPool::newHeader()
{
return m_index->second->newHeader();
}
CAMBEData *CDExtraProtocolHandlerPool::newAMBE()
{
return m_index->second->newAMBE();
}
CPollData *CDExtraProtocolHandlerPool::newPoll()
{
return m_index->second->newPoll();
}
CConnectData *CDExtraProtocolHandlerPool::newConnect()
{
return m_index->second->newConnect();
}
void CDExtraProtocolHandlerPool::close()
{
for (auto it=m_pool.begin(); it!=m_pool.end(); it++)
it->second->close();
}

@ -0,0 +1,49 @@
/*
* Copyright (C) 2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017-2018 by Thomas A. Early
*
* 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 <string>
#include <map>
#include "DExtraProtocolHandler.h"
class CDExtraProtocolHandlerPool {
public:
CDExtraProtocolHandlerPool(const unsigned int port, const std::string &addr = std::string(""));
~CDExtraProtocolHandlerPool();
CDExtraProtocolHandler *getHandler();
void release(CDExtraProtocolHandler *handler);
DEXTRA_TYPE read();
CHeaderData *newHeader();
CAMBEData *newAMBE();
CPollData *newPoll();
CConnectData *newConnect();
void close();
private:
std::map<unsigned int, CDExtraProtocolHandler *> m_pool;
std::map<unsigned int, CDExtraProtocolHandler *>::iterator m_index;
unsigned int m_basePort;
std::string m_address;
};

@ -0,0 +1,179 @@
/*
* Copyright (C) 2009-2015 by Jonathan Naylor, G4KLX
* Copyright (c) 2017,2018 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; version 2 of the License.
*
* 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.
*/
#pragma once
const unsigned int DSTAR_GMSK_SYMBOL_RATE = 4800U;
const float DSTAR_GMSK_BT = 0.5F;
const bool BIT_SYNC_BITS[] = {true, false, true, false};
const unsigned int BIT_SYNC_LENGTH_BITS = 4U;
const bool FRAME_SYNC_BITS[] = {true, true, true, false, true, true, false, false,
true, false, true, false, false, false, false};
const unsigned int FRAME_SYNC_LENGTH_BITS = 15U;
const unsigned char DATA_SYNC_BYTES[] = {0x55, 0x2D, 0x16};
const bool DATA_SYNC_BITS[] = {true, false, true, false, true, false, true, false,
true, false, true, true, false, true, false, false,
false, true, true, false, true, false, false, false};
const unsigned char END_PATTERN_BYTES[] = {0x55, 0x55, 0x55, 0x55, 0xC8, 0x7A,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
const bool END_PATTERN_BITS[] = {true, false, true, false, true, false, true, false,
true, false, true, false, true, false, true, false,
true, false, true, false, true, false, true, false,
true, false, true, false, true, false, true, false,
false, false, false, true, false, false, true, true,
false, true, false, true, true, true, true, false};
const unsigned int END_PATTERN_LENGTH_BITS = 48U;
const unsigned int END_PATTERN_LENGTH_BYTES = END_PATTERN_LENGTH_BITS / 8U;
const unsigned char NULL_AMBE_DATA_BYTES[] = {0x9E, 0x8D, 0x32, 0x88, 0x26, 0x1A, 0x3F, 0x61, 0xE8};
const bool NULL_AMBE_DATA_BITS[] = {false, true, true, true, true, false, false, true,
true, false, true, true, false, false, false, true,
false, true, false, false, true, true, false, false,
false, false, false, true, false, false, false, true,
false, true, true, false, false, true, false, false,
false, true, false, true, true, false, false, false,
true, true, true, true, true, true, false, false,
true, false, false, false, false, true, true, false,
false, false, false, true, false, true, true, true};
// Note that these are already scrambled, 0x66 0x66 0x66 otherwise
const unsigned char NULL_SLOW_DATA_BYTES[] = {0x16, 0x29, 0xF5};
const bool NULL_SLOW_DATA_BITS[] = {false, true, true, false, true, false, false, false,
true, false, false, true, false, true, false, false,
true, false, true, false, true, true, true, true};
const unsigned int VOICE_FRAME_LENGTH_BITS = 72U;
const unsigned int VOICE_FRAME_LENGTH_BYTES = VOICE_FRAME_LENGTH_BITS / 8U;
const unsigned int DATA_FRAME_LENGTH_BITS = 24U;
const unsigned int DATA_FRAME_LENGTH_BYTES = DATA_FRAME_LENGTH_BITS / 8U;
const unsigned int DV_FRAME_LENGTH_BITS = VOICE_FRAME_LENGTH_BITS + DATA_FRAME_LENGTH_BITS;
const unsigned int DV_FRAME_LENGTH_BYTES = VOICE_FRAME_LENGTH_BYTES + DATA_FRAME_LENGTH_BYTES;
// The length of the end frame, three bytes extra
const unsigned int DV_FRAME_MAX_LENGTH_BITS = DV_FRAME_LENGTH_BITS + 24U;
const unsigned int DV_FRAME_MAX_LENGTH_BYTES = DV_FRAME_MAX_LENGTH_BITS / 8U;
const unsigned int FEC_SECTION_LENGTH_BITS = 660U;
const unsigned int RADIO_HEADER_LENGTH_BITS = 330U;
const unsigned int RADIO_HEADER_LENGTH_BYTES = 41U;
const unsigned int DATA_BLOCK_SIZE_BITS = 21U * DV_FRAME_LENGTH_BITS;
const unsigned int DATA_BLOCK_SIZE_BYTES = 21U * DV_FRAME_LENGTH_BYTES;
const unsigned int LONG_CALLSIGN_LENGTH = 8U;
const unsigned int SHORT_CALLSIGN_LENGTH = 4U;
const unsigned char SLOW_DATA_TYPE_MASK = 0xF0U;
const unsigned char SLOW_DATA_TYPE_GPS = 0x30U;
const unsigned char SLOW_DATA_TYPE_TEXT = 0x40U;
const unsigned char SLOW_DATA_TYPE_HEADER = 0x50U;
const unsigned char DATA_MASK = 0x80U;
const unsigned char REPEATER_MASK = 0x40U;
const unsigned char INTERRUPTED_MASK = 0x20U;
const unsigned char CONTROL_SIGNAL_MASK = 0x10U;
const unsigned char URGENT_MASK = 0x08U;
const unsigned char REPEATER_CONTROL_MASK = 0x07U;
const unsigned char REPEATER_CONTROL = 0x07U;
const unsigned char AUTO_REPLY = 0x06U;
const unsigned char RESEND_REQUESTED = 0x04U;
const unsigned char ACK_FLAG = 0x03U;
const unsigned char NO_RESPONSE = 0x02U;
const unsigned char RELAY_UNAVAILABLE = 0x01U;
const unsigned int DSTAR_FRAME_TIME_MS = 20U;
const unsigned int DSTAR_FRAMES_PER_SEC = 50U;
const unsigned char SCRAMBLER_BYTE1 = 0x70U;
const unsigned char SCRAMBLER_BYTE2 = 0x4FU;
const unsigned char SCRAMBLER_BYTE3 = 0x93U;
const unsigned int DPLUS_PORT = 20001U;
const unsigned int DEXTRA_PORT = 30001U;
const unsigned int DCS_PORT = 30051U;
const unsigned int CCS_PORT = 30062U; // Port for CCS7
const unsigned int G2_DV_PORT = 40000U;
const unsigned int G2_DD_PORT = 40001U;
const unsigned int NETWORK_TIMEOUT = 2U; // Network timeout for G2, CCS, DCS, DExtra, and D-Plus
const unsigned int REPEATER_TIMEOUT = 2U; // Repeater timeout
const unsigned int REPLY_TIME = 2U; // The turnaround time for version, echo, audio prompts
enum DSTAR_LINKTYPE {
LT_NONE,
LT_DEXTRA,
LT_DCS
};
enum DSTAR_PROTOCOL {
DP_UNKNOWN,
DP_LOOPBACK,
DP_DEXTRA,
DP_DPLUS,
DP_DCS
};
enum AUDIO_SOURCE {
AS_G2,
AS_ECHO,
AS_INFO,
AS_XBAND,
AS_DRATS,
AS_DPLUS,
AS_DEXTRA,
AS_DCS,
AS_DUP,
AS_VERSION,
AS_CCS
};
enum DSTAR_RX_STATE {
DSRXS_LISTENING,
DSRXS_PROCESS_HEADER,
DSRXS_PROCESS_DATA,
DSRXS_PROCESS_SLOW_DATA
};
enum DSTAR_RPT_STATE {
DSRS_SHUTDOWN,
DSRS_LISTENING,
DSRS_VALID,
DSRS_VALID_WAIT,
DSRS_INVALID,
DSRS_INVALID_WAIT,
DSRS_TIMEOUT,
DSRS_TIMEOUT_WAIT,
DSRS_NETWORK
};
enum NETWORK_TYPE {
NETWORK_NONE,
NETWORK_HEADER,
NETWORK_DATA,
NETWORK_TEXT
};
enum DSTAR_MODE {
MODE_DUPLEX,
MODE_SIMPLEX,
MODE_GATEWAY
};

@ -0,0 +1,308 @@
/*
* Copyright (C) 2012,2013,2015 by Jonathan Naylor G4KLX
* Copyright (C) 2011 by DV Developer Group. DJ0ABR
* Copyright (c) 2017 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 <cstdio>
#include "DTMF.h"
const unsigned char DTMF_MASK[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x82U, 0x00U, 0x00U};
const unsigned char DTMF_SIG[] = {0x82U, 0x08U, 0x20U, 0x82U, 0x00U, 0x00U, 0x00U, 0x00U, 0x00U};
const unsigned char DTMF_SYM_MASK[] = {0x10U, 0x40U, 0x08U, 0x20U};
const unsigned char DTMF_SYM0[] = {0x00U, 0x40U, 0x08U, 0x20U};
const unsigned char DTMF_SYM1[] = {0x00U, 0x00U, 0x00U, 0x00U};
const unsigned char DTMF_SYM2[] = {0x00U, 0x40U, 0x00U, 0x00U};
const unsigned char DTMF_SYM3[] = {0x10U, 0x00U, 0x00U, 0x00U};
const unsigned char DTMF_SYM4[] = {0x00U, 0x00U, 0x00U, 0x20U};
const unsigned char DTMF_SYM5[] = {0x00U, 0x40U, 0x00U, 0x20U};
const unsigned char DTMF_SYM6[] = {0x10U, 0x00U, 0x00U, 0x20U};
const unsigned char DTMF_SYM7[] = {0x00U, 0x00U, 0x08U, 0x00U};
const unsigned char DTMF_SYM8[] = {0x00U, 0x40U, 0x08U, 0x00U};
const unsigned char DTMF_SYM9[] = {0x10U, 0x00U, 0x08U, 0x00U};
const unsigned char DTMF_SYMA[] = {0x10U, 0x40U, 0x00U, 0x00U};
const unsigned char DTMF_SYMB[] = {0x10U, 0x40U, 0x00U, 0x20U};
const unsigned char DTMF_SYMC[] = {0x10U, 0x40U, 0x08U, 0x00U};
const unsigned char DTMF_SYMD[] = {0x10U, 0x40U, 0x08U, 0x20U};
const unsigned char DTMF_SYMS[] = {0x00U, 0x00U, 0x08U, 0x20U};
const unsigned char DTMF_SYMH[] = {0x10U, 0x00U, 0x08U, 0x20U};
CDTMF::CDTMF() :
m_data(),
m_command(),
m_pressed(false),
m_releaseCount(0U),
m_pressCount(0U),
m_lastChar(' ')
{
}
CDTMF::~CDTMF()
{
}
bool CDTMF::decode(const unsigned char* ambe, bool end)
{
// DTMF begins with these byte values
if (!end && (ambe[0] & DTMF_MASK[0]) == DTMF_SIG[0] && (ambe[1] & DTMF_MASK[1]) == DTMF_SIG[1] &&
(ambe[2] & DTMF_MASK[2]) == DTMF_SIG[2] && (ambe[3] & DTMF_MASK[3]) == DTMF_SIG[3] &&
(ambe[4] & DTMF_MASK[4]) == DTMF_SIG[4] && (ambe[5] & DTMF_MASK[5]) == DTMF_SIG[5] &&
(ambe[6] & DTMF_MASK[6]) == DTMF_SIG[6] && (ambe[7] & DTMF_MASK[7]) == DTMF_SIG[7] &&
(ambe[8] & DTMF_MASK[8]) == DTMF_SIG[8]) {
unsigned char sym0 = ambe[4] & DTMF_SYM_MASK[0];
unsigned char sym1 = ambe[5] & DTMF_SYM_MASK[1];
unsigned char sym2 = ambe[7] & DTMF_SYM_MASK[2];
unsigned char sym3 = ambe[8] & DTMF_SYM_MASK[3];
char c = ' ';
if (sym0 == DTMF_SYM0[0] && sym1 == DTMF_SYM0[1] && sym2 == DTMF_SYM0[2] && sym3 == DTMF_SYM0[3])
c = '0';
else if (sym0 == DTMF_SYM1[0] && sym1 == DTMF_SYM1[1] && sym2 == DTMF_SYM1[2] && sym3 == DTMF_SYM1[3])
c = '1';
else if (sym0 == DTMF_SYM2[0] && sym1 == DTMF_SYM2[1] && sym2 == DTMF_SYM2[2] && sym3 == DTMF_SYM2[3])
c = '2';
else if (sym0 == DTMF_SYM3[0] && sym1 == DTMF_SYM3[1] && sym2 == DTMF_SYM3[2] && sym3 == DTMF_SYM3[3])
c = '3';
else if (sym0 == DTMF_SYM4[0] && sym1 == DTMF_SYM4[1] && sym2 == DTMF_SYM4[2] && sym3 == DTMF_SYM4[3])
c = '4';
else if (sym0 == DTMF_SYM5[0] && sym1 == DTMF_SYM5[1] && sym2 == DTMF_SYM5[2] && sym3 == DTMF_SYM5[3])
c = '5';
else if (sym0 == DTMF_SYM6[0] && sym1 == DTMF_SYM6[1] && sym2 == DTMF_SYM6[2] && sym3 == DTMF_SYM6[3])
c = '6';
else if (sym0 == DTMF_SYM7[0] && sym1 == DTMF_SYM7[1] && sym2 == DTMF_SYM7[2] && sym3 == DTMF_SYM7[3])
c = '7';
else if (sym0 == DTMF_SYM8[0] && sym1 == DTMF_SYM8[1] && sym2 == DTMF_SYM8[2] && sym3 == DTMF_SYM8[3])
c = '8';
else if (sym0 == DTMF_SYM9[0] && sym1 == DTMF_SYM9[1] && sym2 == DTMF_SYM9[2] && sym3 == DTMF_SYM9[3])
c = '9';
else if (sym0 == DTMF_SYMA[0] && sym1 == DTMF_SYMA[1] && sym2 == DTMF_SYMA[2] && sym3 == DTMF_SYMA[3])
c = 'A';
else if (sym0 == DTMF_SYMB[0] && sym1 == DTMF_SYMB[1] && sym2 == DTMF_SYMB[2] && sym3 == DTMF_SYMB[3])
c = 'B';
else if (sym0 == DTMF_SYMC[0] && sym1 == DTMF_SYMC[1] && sym2 == DTMF_SYMC[2] && sym3 == DTMF_SYMC[3])
c = 'C';
else if (sym0 == DTMF_SYMD[0] && sym1 == DTMF_SYMD[1] && sym2 == DTMF_SYMD[2] && sym3 == DTMF_SYMD[3])
c = 'D';
else if (sym0 == DTMF_SYMS[0] && sym1 == DTMF_SYMS[1] && sym2 == DTMF_SYMS[2] && sym3 == DTMF_SYMS[3])
c = '*';
else if (sym0 == DTMF_SYMH[0] && sym1 == DTMF_SYMH[1] && sym2 == DTMF_SYMH[2] && sym3 == DTMF_SYMH[3])
c = '#';
if (c == m_lastChar) {
m_pressCount++;
} else {
m_lastChar = c;
m_pressCount = 0U;
}
if (c != ' ' && !m_pressed && m_pressCount >= 3U) {
m_data.push_back(c);
m_releaseCount = 0U;
m_pressed = true;
}
return c != ' ';
} else {
// If it is not a DTMF Code
if ((end || m_releaseCount >= 100U) && m_data.size() > 0U) {
m_command = m_data;
m_data.clear();
m_releaseCount = 0U;
}
m_pressed = false;
m_releaseCount++;
m_pressCount = 0U;
m_lastChar = ' ';
return false;
}
}
bool CDTMF::hasCommand() const
{
return m_command.size() > 0;
}
// DTMF to YOUR call command
std::string CDTMF::translate()
{
std::string command = m_command;
m_command.clear();
if (0 == command.size())
return std::string("");
if (0 == command.compare("#"))
return " U";
if (0 == command.compare("0"))
return " I";
if (0 == command.compare("A"))
return "CA ";
if (0 == command.compare("00"))
return " I";
if (0 == command.compare("**"))
return " L";
if (command.at(0) == '*')
return processReflector("REF", command.substr(1));
else if (command.at(0) == 'B')
return processReflector("XRF", command.substr(1));
else if (command.at(0) == 'D')
return processReflector("DCS", command.substr(1));
else
return processCCS(command);
}
void CDTMF::reset()
{
m_data.clear();
m_command.clear();
m_pressed = false;
m_pressCount = 0U;
m_releaseCount = 0U;
m_lastChar = ' ';
}
std::string CDTMF::processReflector(const std::string& prefix, const std::string& command) const
{
unsigned int len = command.size();
char c = command.at(len - 1U);
if (c == 'A' || c == 'B' || c == 'C' || c == 'D') {
if (len < 2U || len > 4U)
return std::string("");
unsigned long n = std::stoul(command.substr(0, len-1U));
if (n == 0UL)
return std::string("");
char ostr[32];
snprintf(ostr, 32, "%s%03lu%cL", prefix.c_str(), n, c);
return std::string(ostr);
} else {
if (len < 3U || len > 5U)
return std::string("");
unsigned long n1 = std::stoul(command.substr(0,len-2U));
if (n1 == 0UL)
return std::string("");
unsigned long n2 = std::stoul(command.substr(2));
if (n2 == 0UL || n2 > 26UL)
return std::string("");
c = 'A' + n2 - 1UL;
char ostr[32];
snprintf(ostr, 32, "%s%03lu%cL", prefix.c_str(), n1, c);
return std::string(ostr);
}
}
std::string CDTMF::processCCS(const std::string& command) const
{
unsigned int len = command.size();
std::string out("");
char ostr[32];
switch (len) {
case 3U: {
// CCS7 for local repeater without band
unsigned long n = std::stoul(command);
if (n == 0UL)
return out;
snprintf(ostr, 32, "C%03lu ", n);
}
break;
case 4U: {
char c = command.at(3U);
if (c == 'A' || c == 'B' || c == 'C' || c == 'D') {
// CCS7 for local repeater with band
unsigned long n = std::stoul(command.substr(0, 3));
if (n == 0UL)
return out;
snprintf(ostr, 32, "C%03lu%c ", n, c);
} else {
// CCS7 for local user
unsigned long n = std::stoul(command);
if (n == 0UL)
return out;
snprintf(ostr, 32, "C%04lu ", n);
}
}
break;
case 5U: {
char c = command.at(4U);
if (c == 'A' || c == 'B' || c == 'C' || c == 'D') {
// CCS7 for local hostspot with band
unsigned long n = std::stoul(command.substr(0, 4));
if (n == 0UL)
return out;
snprintf(ostr, 32, "C%04lu%c ", n, c);
}
}
break;
case 6U: {
// CCS7 for full repeater without band
unsigned long n = std::stoul(command);
if (n == 0UL)
return out;
snprintf(ostr, 32, "C%06lu ", n);
}
break;
case 7U: {
char c = command.at(6U);
if (c == 'A' || c == 'B' || c == 'C' || c == 'D') {
// CCS7 for full repeater with band
unsigned long n = std::stoul(command.substr(0, 6));
if (n == 0UL)
return out;
snprintf(ostr, 32, "C%06lu%c", n, c);
} else {
// CCS7 for full user or CCS7 for full hostpot without band
unsigned long n = std::stoul(command);
if (n == 0UL)
return out;
snprintf(ostr, 32, "C%07lu", n);
}
}
break;
case 8U: {
char c = command.at(7U);
if (c == 'A' || c == 'B' || c == 'C' || c == 'D') {
// CCS7 for full hotspot with band
unsigned long n = std::stoul(command.substr(0, 7));
if (n == 0UL)
return out;
snprintf(ostr, 32, "C%07lu%c", n, c);
}
}
break;
default:
break;
}
out = ostr;
return out;
}

@ -0,0 +1,47 @@
/*
* Copyright (C) 2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 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 <string>
class CDTMF {
public:
CDTMF();
~CDTMF();
bool decode(const unsigned char* ambe, bool end);
bool hasCommand() const;
std::string translate();
void reset();
private:
std::string m_data;
std::string m_command;
bool m_pressed;
unsigned int m_releaseCount;
unsigned int m_pressCount;
char m_lastChar;
std::string processReflector(const std::string& prefix, const std::string& command) const;
std::string processCCS(const std::string& command) const;
};

@ -0,0 +1,183 @@
/*
* Copyright (C) 2009,2013,2014 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <cstring>
#include <arpa/inet.h>
#include "DVTOOLFileReader.h"
#include "DStarDefines.h"
static const char DVTOOL_SIGNATURE[] = "DVTOOL";
static const unsigned int DVTOOL_SIGNATURE_LENGTH = 6U;
static const char DSVT_SIGNATURE[] = "DSVT";
static const unsigned int DSVT_SIGNATURE_LENGTH = 4U;
static const unsigned int FIXED_DATA_LENGTH = 9U;
static const unsigned char HEADER_FLAG = 0x10;
static const unsigned char DATA_FLAG = 0x20;
static const unsigned char HEADER_MASK = 0x80;
static const unsigned char TRAILER_MASK = 0x40;
const unsigned int BUFFER_LENGTH = 255U;
CDVTOOLFileReader::CDVTOOLFileReader() :
m_fileName(),
m_file(NULL),
m_records(0U),
m_type(DVTFR_NONE),
m_buffer(NULL),
m_length(0U),
m_seqNo(0U)
{
m_buffer = new unsigned char[BUFFER_LENGTH];
}
CDVTOOLFileReader::~CDVTOOLFileReader()
{
delete[] m_buffer;
}
std::string CDVTOOLFileReader::getFileName() const
{
return m_fileName;
}
unsigned int CDVTOOLFileReader::getRecords() const
{
return m_records;
}
bool CDVTOOLFileReader::open(const std::string& fileName)
{
m_fileName = fileName;
m_file = fopen(fileName.c_str(), "rb");
if (NULL == m_file)
return false;
unsigned char buffer[DVTOOL_SIGNATURE_LENGTH];
size_t n = fread(buffer, 1, DVTOOL_SIGNATURE_LENGTH, m_file);
if (n != DVTOOL_SIGNATURE_LENGTH) {
fclose(m_file);
return false;
}
if (::memcmp(buffer, DVTOOL_SIGNATURE, DVTOOL_SIGNATURE_LENGTH) != 0) {
fclose(m_file);
return false;
}
uint32_t uint32;
n = fread(&uint32, sizeof(uint32_t), 1, m_file);
if (n != 1) {
fclose(m_file);
return false;
}
m_records = ntohl(uint32);
m_seqNo = 0U;
return true;
}
DVTFR_TYPE CDVTOOLFileReader::read()
{
uint16_t uint16;
size_t n = fread(&uint16, sizeof(uint16_t), 1, m_file);
if (n != 1)
return DVTFR_NONE;
m_length = htons(uint16) - 15U;
unsigned char bytes[FIXED_DATA_LENGTH];
n = fread(bytes, 1, DSVT_SIGNATURE_LENGTH, m_file);
if (n != DSVT_SIGNATURE_LENGTH)
return DVTFR_NONE;
if (::memcmp(bytes, DSVT_SIGNATURE, DSVT_SIGNATURE_LENGTH) != 0)
return DVTFR_NONE;
char flag;
n = fread(&flag, 1, 1, m_file);
if (n != 1)
return DVTFR_NONE;
m_type = (flag == HEADER_FLAG) ? DVTFR_HEADER : DVTFR_DATA;
n = fread(bytes, 1, FIXED_DATA_LENGTH, m_file);
if (n != FIXED_DATA_LENGTH)
return DVTFR_NONE;
n = fread(&flag, 1, 1, m_file);
if (n != 1)
return DVTFR_NONE;
if (m_type == DVTFR_DATA)
m_seqNo = flag;
n = fread(m_buffer, 1, m_length, m_file);
if (n != m_length)
return DVTFR_NONE;
return m_type;
}
CHeaderData* CDVTOOLFileReader::readHeader()
{
if (m_type != DVTFR_HEADER)
return NULL;
CHeaderData* header = new CHeaderData;
if (m_buffer[39U] == 0xFFU && m_buffer[40U] == 0xFFU) {
header->setDVTOOLData(m_buffer, RADIO_HEADER_LENGTH_BYTES, false);
return header;
}
// Header checksum testing is enabled
bool valid = header->setDVTOOLData(m_buffer, RADIO_HEADER_LENGTH_BYTES, true);
if (!valid) {
delete header;
return NULL;
}
return header;
}
CAMBEData* CDVTOOLFileReader::readData()
{
if (m_type != DVTFR_DATA)
return NULL;
CAMBEData* data = new CAMBEData;
data->setData(m_buffer, m_length);
data->setSeq(m_seqNo);
return data;
}
void CDVTOOLFileReader::close()
{
fclose(m_file);
}

@ -0,0 +1,60 @@
/*
* Copyright (C) 2009,2014 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <cstdint>
#include <cstdio>
#include "HeaderData.h"
#include "AMBEData.h"
enum DVTFR_TYPE {
DVTFR_NONE,
DVTFR_HEADER,
DVTFR_DATA
};
class CDVTOOLFileReader {
public:
CDVTOOLFileReader();
~CDVTOOLFileReader();
std::string getFileName() const;
unsigned int getRecords() const;
bool open(const std::string& fileName);
DVTFR_TYPE read();
CHeaderData* readHeader();
CAMBEData* readData();
void close();
private:
std::string m_fileName;
FILE *m_file;
uint32_t m_records;
DVTFR_TYPE m_type;
unsigned char* m_buffer;
unsigned int m_length;
unsigned char m_seqNo;
};

125
Defs.h

@ -0,0 +1,125 @@
/*
* Copyright (C) 2010-2015 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
const std::string DEXTRA_HOSTS_FILE_NAME("DExtra_Hosts.txt");
const std::string DCS_HOSTS_FILE_NAME("DCS_Hosts.txt");
enum RECONNECT {
RECONNECT_NEVER,
RECONNECT_FIXED,
RECONNECT_5MINS,
RECONNECT_10MINS,
RECONNECT_15MINS,
RECONNECT_20MINS,
RECONNECT_25MINS,
RECONNECT_30MINS,
RECONNECT_60MINS,
RECONNECT_90MINS,
RECONNECT_120MINS,
RECONNECT_180MINS
};
enum DIRECTION {
DIR_INCOMING,
DIR_OUTGOING
};
enum PROTOCOL {
PROTO_DEXTRA,
PROTO_DCS,
PROTO_CCS
};
enum HW_TYPE {
HW_HOMEBREW,
HW_ICOM,
HW_DUMMY
};
enum TEXT_LANG {
TL_ENGLISH_UK,
TL_DEUTSCH,
TL_DANSK,
TL_FRANCAIS,
TL_ITALIANO,
TL_POLSKI,
TL_ENGLISH_US,
TL_ESPANOL,
TL_SVENSKA,
TL_NEDERLANDS_NL,
TL_NEDERLANDS_BE,
TL_NORSK,
TL_PORTUGUES
};
enum IRCDDB_STATUS {
IS_DISABLED,
IS_DISCONNECTED,
IS_CONNECTING,
IS_CONNECTED
};
enum G2_STATUS {
G2_NONE,
G2_LOCAL,
G2_USER,
G2_REPEATER,
G2_OK,
G2_XBAND,
G2_ECHO,
G2_VERSION,
G2_SMARTGROUP
};
enum LINK_STATUS {
LS_NONE,
LS_PENDING_IRCDDB,
LS_LINKING_LOOPBACK,
LS_LINKING_DEXTRA,
LS_LINKING_DCS,
LS_LINKING_CCS,
LS_LINKED_LOOPBACK,
LS_LINKED_DEXTRA,
LS_LINKED_DCS,
LS_LINKED_CCS,
LS_INIT
};
enum SLOWDATA_STATE {
SS_FIRST,
SS_SECOND
};
enum CALLSIGN_SWITCH {
SCS_GROUP_CALLSIGN,
SCS_USER_CALLSIGN
};
enum GATEWAY_TYPE {
GT_REPEATER,
GT_HOTSPOT,
GT_DONGLE,
GT_SMARTGROUP
};
const unsigned int TIME_PER_TIC_MS = 5U;

@ -0,0 +1,160 @@
/*
* Copyright (C) 2011-2014 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <cassert>
#include "DStarDefines.h"
#include "EchoUnit.h"
#include "Defs.h"
#include "Utils.h"
const unsigned int MAX_FRAMES = 60U * DSTAR_FRAMES_PER_SEC;
CEchoUnit::CEchoUnit(IRepeaterCallback* handler, const std::string& callsign) :
m_handler(handler),
m_callsign(callsign),
m_status(ES_IDLE),
m_timer(1000U, REPLY_TIME),
m_header(NULL),
m_data(NULL),
m_in(0U),
m_out(0U),
m_time()
{
assert(handler != NULL);
m_data = new CAMBEData*[MAX_FRAMES];
for (unsigned int i = 0U; i < MAX_FRAMES; i++)
m_data[i] = NULL;
}
CEchoUnit::~CEchoUnit()
{
delete[] m_data;
}
void CEchoUnit::writeHeader(const CHeaderData& header)
{
if (m_status != ES_IDLE)
return;
m_header = new CHeaderData(header);
m_in = 0U;
m_status = ES_RECEIVE;
}
void CEchoUnit::writeData(const CAMBEData& data)
{
if (m_status != ES_RECEIVE)
return;
if (m_in < MAX_FRAMES) {
m_data[m_in] = new CAMBEData(data);
m_in++;
}
if (data.isEnd()) {
printf("Received %.1f secs of audio from %s for echoing\n", float(m_in) / float(DSTAR_FRAMES_PER_SEC), m_header->getMyCall1().c_str());
m_timer.start();
m_status = ES_WAIT;
}
}
void CEchoUnit::end()
{
if (m_status != ES_RECEIVE)
return;
printf("Received %.1f secs of audio from %s for echoing\n", float(m_in) / float(DSTAR_FRAMES_PER_SEC), m_header->getMyCall1().c_str());
m_timer.start();
m_status = ES_WAIT;
}
void CEchoUnit::clock(unsigned int ms)
{
m_timer.clock(ms);
if (m_status == ES_WAIT && m_timer.hasExpired()) {
m_timer.stop();
// RPT1 and RPT2 will be filled in later
m_header->setMyCall1(m_callsign);
m_header->setMyCall2("ECHO");
m_header->setYourCall("CQCQCQ ");
m_handler->process(*m_header, DIR_INCOMING, AS_ECHO);
delete m_header;
m_out = 0U;
m_status = ES_TRANSMIT;
m_time = std::chrono::high_resolution_clock::now();
return;
}
if (m_status == ES_TRANSMIT) {
auto now = std::chrono::high_resolution_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::milliseconds>(now - m_time);
unsigned int needed = elapsed.count() / DSTAR_FRAME_TIME_MS;
while (m_out < needed) {
CAMBEData* data = m_data[m_out];
m_data[m_out] = NULL;
m_out++;
if (m_in == m_out)
data->setEnd(true);
m_handler->process(*data, DIR_INCOMING, AS_ECHO);
delete data;
if (m_in == m_out) {
m_in = 0U;
m_out = 0U;
m_status = ES_IDLE;
return;
}
}
return;
}
}
void CEchoUnit::cancel()
{
for (unsigned int i = 0U; i < MAX_FRAMES; i++) {
if (m_data[i] != NULL) {
delete m_data[i];
m_data[i] = NULL;
}
}
m_status = ES_IDLE;
m_out = 0U;
m_in = 0U;
m_timer.stop();
}

@ -0,0 +1,63 @@
/*
* Copyright (C) 2011,2012 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <chrono>
#include "RepeaterCallback.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "Timer.h"
enum ECHO_STATUS {
ES_IDLE,
ES_RECEIVE,
ES_WAIT,
ES_TRANSMIT
};
class CEchoUnit {
public:
CEchoUnit(IRepeaterCallback* handler, const std::string& callsign);
~CEchoUnit();
void writeHeader(const CHeaderData& header);
void writeData(const CAMBEData& data);
void end();
void cancel();
void clock(unsigned int ms);
private:
IRepeaterCallback* m_handler;
std::string m_callsign;
ECHO_STATUS m_status;
CTimer m_timer;
CHeaderData* m_header;
CAMBEData** m_data;
unsigned int m_in;
unsigned int m_out;
std::chrono::high_resolution_clock::time_point m_time;
};

@ -0,0 +1,123 @@
/*
* Copyright (C) 2010-2014 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <cassert>
#include "GroupHandler.h"
#include "G2Handler.h"
#include "Utils.h"
#include "Defs.h"
unsigned int CG2Handler::m_maxRoutes = 0U;
CG2Handler** CG2Handler::m_routes = NULL;
CG2ProtocolHandler* CG2Handler::m_handler = NULL;
CG2Handler::CG2Handler(const in_addr& address, unsigned int id) :
m_address(address),
m_id(id),
m_inactivityTimer(1000U, NETWORK_TIMEOUT)
{
m_inactivityTimer.start();
}
CG2Handler::~CG2Handler()
{
}
void CG2Handler::initialise(unsigned int maxRoutes)
{
m_maxRoutes = maxRoutes;
if (maxRoutes == 0U)
return;
m_routes = new CG2Handler*[m_maxRoutes];
for (unsigned int i = 0U; i < m_maxRoutes; i++)
m_routes[i] = NULL;
}
void CG2Handler::setG2ProtocolHandler(CG2ProtocolHandler* handler)
{
assert(handler != NULL);
m_handler = handler;
}
void CG2Handler::process(CHeaderData& header)
{
// Is this a busy reply?
unsigned char flag1 = header.getFlag1();
if (flag1 == 0x01) {
// Don't check the incoming stream
// printf("G2 busy message received\n"));
return;
}
// Check to see if this is for Smart Group
CGroupHandler* handler = CGroupHandler::findGroup(header);
if (handler != NULL) {
handler->process(header);
return;
}
}
void CG2Handler::process(CAMBEData& data)
{
// Check to see if this is for Smart Group
CGroupHandler* handler = CGroupHandler::findGroup(data);
if (handler != NULL) {
handler->process(data);
return;
}
}
void CG2Handler::clock(unsigned int ms)
{
for (unsigned int i = 0U; i < m_maxRoutes; i++) {
CG2Handler* route = m_routes[i];
if (route != NULL) {
bool ret = route->clockInt(ms);
if (ret) {
delete route;
m_routes[i] = NULL;
}
}
}
}
void CG2Handler::finalise()
{
for (unsigned int i = 0U; i < m_maxRoutes; i++)
delete m_routes[i];
delete[] m_routes;
}
bool CG2Handler::clockInt(unsigned int ms)
{
m_inactivityTimer.clock(ms);
if (m_inactivityTimer.isRunning() && m_inactivityTimer.hasExpired()) {
printf("Inactivity timeout for a G2 route has expired\n");
return true;
}
return false;
}

@ -0,0 +1,58 @@
/*
* Copyright (C) 2010,2012 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <netinet/in.h>
#include "G2ProtocolHandler.h"
#include "DStarDefines.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "Timer.h"
class CG2Handler {
public:
static void initialise(unsigned int maxRoutes);
static void setG2ProtocolHandler(CG2ProtocolHandler* handler);
static void process(CHeaderData& header);
static void process(CAMBEData& header);
static void clock(unsigned int ms);
static void finalise();
protected:
CG2Handler(const in_addr& address, unsigned int id);
~CG2Handler();
bool clockInt(unsigned int ms);
private:
static unsigned int m_maxRoutes;
static CG2Handler** m_routes;
static CG2ProtocolHandler* m_handler;
in_addr m_address;
unsigned int m_id;
CTimer m_inactivityTimer;
};

@ -0,0 +1,170 @@
/*
* Copyright (C) 2010,2011,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017-2018 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>
#include "G2ProtocolHandler.h"
#include "Utils.h"
// #define DUMP_TX
const unsigned int BUFFER_LENGTH = 255U;
CG2ProtocolHandler::CG2ProtocolHandler(unsigned int port, const std::string& addr) :
m_socket(addr, port),
m_type(GT_NONE),
m_buffer(NULL),
m_length(0U),
m_address(),
m_port(0U)
{
m_buffer = new unsigned char[BUFFER_LENGTH];
}
CG2ProtocolHandler::~CG2ProtocolHandler()
{
delete[] m_buffer;
portmap.clear();
}
bool CG2ProtocolHandler::open()
{
return m_socket.open();
}
bool CG2ProtocolHandler::writeHeader(const CHeaderData& header)
{
unsigned char buffer[60U];
unsigned int length = header.getG2Data(buffer, 60U, true);
#if defined(DUMP_TX)
CUtils::dump("Sending Header", buffer, length);
#endif
in_addr addr = header.getYourAddress();
auto found = portmap.find(addr.s_addr);
unsigned int port = (portmap.end()==found) ? header.getYourPort() : found->second;
for (unsigned int i = 0U; i < 5U; i++) {
bool res = m_socket.write(buffer, length, addr, port);
if (!res)
return false;
}
return true;
}
bool CG2ProtocolHandler::writeAMBE(const CAMBEData& data)
{
unsigned char buffer[40U];
unsigned int length = data.getG2Data(buffer, 40U);
#if defined(DUMP_TX)
CUtils::dump("Sending Data", buffer, length);
#endif
in_addr addr = data.getYourAddress();
auto found = portmap.find(addr.s_addr);
unsigned int port = (portmap.end()==found) ? data.getYourPort() : found->second;
return m_socket.write(buffer, length, addr, port);
}
G2_TYPE CG2ProtocolHandler::read()
{
bool res = true;
// Loop until we have no more data from the socket or we have data for the higher layers
while (res)
res = readPackets();
return m_type;
}
bool CG2ProtocolHandler::readPackets()
{
m_type = GT_NONE;
// No more data?
int length = m_socket.read(m_buffer, BUFFER_LENGTH, m_address, m_port);
if (length <= 0)
return false;
m_length = length;
// save the incoming port (this is to enable mobile hotspots)
if (portmap.end() == portmap.find(m_address.s_addr)) {
printf("new address %s on port %u\n", inet_ntoa(m_address), m_port);
portmap[m_address.s_addr] = m_port;
} else {
if (portmap[m_address.s_addr] != m_port) {
printf("new port for %s is %u, was %u\n", inet_ntoa(m_address), m_port, portmap[m_address.s_addr]);
portmap[m_address.s_addr] = m_port;
}
}
if (m_buffer[0] != 'D' || m_buffer[1] != 'S' || m_buffer[2] != 'V' || m_buffer[3] != 'T') {
return true;
} else {
// Header or data packet type?
if ((m_buffer[14] & 0x80) == 0x80)
m_type = GT_HEADER;
else
m_type = GT_AMBE;
return false;
}
}
CHeaderData* CG2ProtocolHandler::readHeader()
{
if (m_type != GT_HEADER)
return NULL;
CHeaderData* header = new CHeaderData;
// G2 checksums are unreliable
bool res = header->setG2Data(m_buffer, m_length, false, m_address, m_port);
if (!res) {
delete header;
return NULL;
}
return header;
}
CAMBEData* CG2ProtocolHandler::readAMBE()
{
if (m_type != GT_AMBE)
return NULL;
CAMBEData* data = new CAMBEData;
bool res = data->setG2Data(m_buffer, m_length, m_address, m_port);
if (!res) {
delete data;
return NULL;
}
return data;
}
void CG2ProtocolHandler::close()
{
m_socket.close();
}

@ -0,0 +1,62 @@
/*
* Copyright (C) 2010,2011 by Jonathan Naylor G4KLX
* Copyright (c) 2017-2018 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 <unordered_map>
#include "UDPReaderWriter.h"
#include "DStarDefines.h"
#include "HeaderData.h"
#include "AMBEData.h"
enum G2_TYPE {
GT_NONE,
GT_HEADER,
GT_AMBE
};
class CG2ProtocolHandler {
public:
CG2ProtocolHandler(unsigned int port, const std::string& addr = std::string(""));
~CG2ProtocolHandler();
bool open();
bool writeHeader(const CHeaderData& header);
bool writeAMBE(const CAMBEData& data);
G2_TYPE read();
CHeaderData* readHeader();
CAMBEData* readAMBE();
void close();
private:
std::unordered_map<uint32_t, unsigned int> portmap;
CUDPReaderWriter m_socket;
G2_TYPE m_type;
unsigned char* m_buffer;
unsigned int m_length;
in_addr m_address;
unsigned int m_port;
bool readPackets();
};

@ -0,0 +1,55 @@
/*
* Copyright (C) 2010,2011,2012 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 "GatewayCache.h"
CGatewayCache::CGatewayCache()
{
}
CGatewayCache::~CGatewayCache()
{
for (std::unordered_map<std::string, CGatewayRecord *>::iterator it = m_cache.begin(); it != m_cache.end(); ++it)
delete it->second;
}
CGatewayRecord* CGatewayCache::find(const std::string& gateway)
{
return m_cache[gateway];
}
void CGatewayCache::update(const std::string& gateway, const std::string& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock)
{
CGatewayRecord* rec = m_cache[gateway];
in_addr addr_in;
addr_in.s_addr = ::inet_addr(address.c_str());
if (rec == NULL)
// A brand new record is needed
m_cache[gateway] = new CGatewayRecord(gateway, addr_in, protocol, addrLock, protoLock);
else
// Update an existing record
rec->setData(addr_in, protocol, addrLock, protoLock);
}
unsigned int CGatewayCache::getCount() const
{
return m_cache.size();
}

@ -0,0 +1,97 @@
/*
* Copyright (C) 2010,2011,2012 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <unordered_map>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "DStarDefines.h"
#include "Defs.h"
class CGatewayRecord {
public:
CGatewayRecord(const std::string& gateway, in_addr address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock) :
m_gateway(gateway),
m_address(address),
m_protocol(DP_UNKNOWN),
m_addrLock(addrLock),
m_protoLock(false)
{
if (protocol != DP_UNKNOWN) {
m_protocol = protocol;
m_protoLock = protoLock;
}
}
std::string getGateway() const
{
return m_gateway;
}
in_addr getAddress() const
{
return m_address;
}
DSTAR_PROTOCOL getProtocol() const
{
return m_protocol;
}
void setData(in_addr address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock)
{
if (!m_addrLock) {
m_address = address;
m_addrLock = addrLock;
}
if (!m_protoLock) {
if (protocol != DP_UNKNOWN) {
m_protocol = protocol;
m_protoLock = protoLock;
}
}
}
private:
std::string m_gateway;
in_addr m_address;
DSTAR_PROTOCOL m_protocol;
bool m_addrLock;
bool m_protoLock;
};
class CGatewayCache {
public:
CGatewayCache();
~CGatewayCache();
CGatewayRecord* find(const std::string& gateway);
void update(const std::string& gateway, const std::string& address, DSTAR_PROTOCOL protocol, bool addrLock, bool protoLock);
unsigned int getCount() const;
private:
std::unordered_map<std::string, CGatewayRecord *> m_cache;
};

File diff suppressed because it is too large Load Diff

@ -0,0 +1,196 @@
/*
* Copyright (C) 2011-2014 by Jonathan Naylor G4KLX
* Copyright (c) 2017,2018 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 <netinet/in.h>
#include <string>
#include <map>
#include <list>
#include <set>
#include "RemoteGroup.h"
#include "G2ProtocolHandler.h"
#include "ReflectorCallback.h" // DEXTRA_LINK || DCS_LINK
#include "RepeaterCallback.h"
#include "TextCollector.h"
#include "CacheManager.h"
#include "DStarDefines.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "IRCDDB.h"
#include "Timer.h"
enum LOGUSER {
LU_ON,
LU_OFF
};
class CSGSUser {
public:
CSGSUser(const std::string& callsign, unsigned int timeout);
~CSGSUser();
void reset();
bool clock(unsigned int ms);
bool hasExpired();
std::string getCallsign() const;
CTimer getTimer() const;
private:
std::string m_callsign;
CTimer m_timer;
};
class CSGSId {
public:
CSGSId(unsigned int id, unsigned int timeout, CSGSUser* user);
~CSGSId();
unsigned int getId() const;
void reset();
void setLogin();
void setInfo();
void setLogoff();
void setEnd();
bool clock(unsigned int ms);
bool hasExpired();
bool isLogin() const;
bool isInfo() const;
bool isLogoff() const;
bool isEnd() const;
CSGSUser* getUser() const;
CTextCollector& getTextCollector();
private:
unsigned int m_id;
CTimer m_timer;
bool m_login;
bool m_info;
bool m_logoff;
bool m_end;
CSGSUser *m_user;
CTextCollector m_textCollector;
};
class CSGSRepeater {
public:
std::string m_destination;
std::string m_repeater;
std::string m_gateway;
in_addr m_address;
};
class CGroupHandler : public IReflectorCallback {
public:
static void add(const std::string &callsign, const std::string &logoff, const std::string &repeater, const std::string &infoText, const std::string &permanent,
unsigned int userTimeout, CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const std::string & eflector);
static void setG2Handler(CG2ProtocolHandler *handler);
static void setIRC(CIRCDDB *irc);
static void setCache(CCacheManager *cache);
static void setGateway(const std::string &gateway);
static void link();
static std::list<std::string> listGroups();
static CGroupHandler *findGroup(const std::string &callsign);
static CGroupHandler *findGroup(const CHeaderData &header);
static CGroupHandler *findGroup(const CAMBEData &data);
static void finalise();
static void clock(unsigned int ms);
void process(CHeaderData &header);
void process(CAMBEData &data);
bool remoteLink(const std::string &reflector);
void updateReflectorInfo();
DSTAR_LINKTYPE getLinkType();
void setLinkType(DSTAR_LINKTYPE linkType);
void clearReflector();
CRemoteGroup *getInfo() const;
bool logoff(const std::string& callsign);
virtual bool process(CHeaderData &header, DIRECTION direction, AUDIO_SOURCE source);
virtual bool process(CAMBEData &data, DIRECTION direction, AUDIO_SOURCE source);
virtual void linkUp(DSTAR_PROTOCOL protocol, const std::string &callsign);
virtual void linkRefused(DSTAR_PROTOCOL protocol, const std::string &callsign);
virtual bool linkFailed(DSTAR_PROTOCOL protocol, const std::string &callsign, bool isRecoverable);
virtual bool singleHeader();
protected:
CGroupHandler(const std::string &callsign, const std::string &logoff, const std::string &repeater, const std::string &infoText, const std::string &permanent,
unsigned int userTimeout, CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const std::string &reflector);
virtual ~CGroupHandler();
bool linkInt();
void clockInt(unsigned int ms);
private:
static std::list<CGroupHandler *> m_Groups;
static CG2ProtocolHandler *m_g2Handler;
static CIRCDDB *m_irc;
static CCacheManager *m_cache;
static std::string m_gateway;
static std::string m_name;
// Group info
std::string m_groupCallsign;
std::string m_offCallsign;
std::string m_shortCallsign;
std::string m_repeater;
std::string m_infoText;
std::set<std::string> m_permanent;
std::string m_linkReflector;
std::string m_linkGateway;
LINK_STATUS m_linkStatus;
LINK_STATUS m_oldlinkStatus;
CTimer m_linkTimer;
DSTAR_LINKTYPE m_linkType;
unsigned int m_id;
CTimer m_announceTimer;
unsigned int m_userTimeout;
CALLSIGN_SWITCH m_callsignSwitch;
bool m_txMsgSwitch;
std::map<unsigned int, CSGSId *> m_ids;
std::map<std::string, CSGSUser *> m_users;
std::map<std::string, CSGSRepeater *> m_repeaters;
void sendFromText(const std::string &text) const;
void sendToRepeaters(CHeaderData &header) const;
void sendToRepeaters(CAMBEData &data) const;
void sendAck(const CUserData &user, const std::string &text) const;
void logUser(LOGUSER lu, const std::string channel, const std::string user);
};

@ -0,0 +1,953 @@
/*
* Copyright (C) 2010-2014 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <ctime>
#include <cstdlib>
#include <cassert>
#include <cstring>
#include "HeaderData.h"
#include "CCITTChecksum.h"
#include "DStarDefines.h"
#include "Utils.h"
void CHeaderData::initialise()
{
srand(time(NULL));
}
void CHeaderData::finalise()
{
}
unsigned int CHeaderData::createId()
{
return (::rand() % 65535U) + 1U;
}
CHeaderData::CHeaderData() :
m_rptSeq(0U),
m_id(0U),
m_band1(0x00U),
m_band2(0x02U),
m_band3(0x01U),
m_flag1(0U),
m_flag2(0U),
m_flag3(0U),
m_myCall1(NULL),
m_myCall2(NULL),
m_yourCall(NULL),
m_rptCall1(NULL),
m_rptCall2(NULL),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U),
m_errors(0U)
{
m_myCall1 = new unsigned char[LONG_CALLSIGN_LENGTH];
m_myCall2 = new unsigned char[SHORT_CALLSIGN_LENGTH];
m_yourCall = new unsigned char[LONG_CALLSIGN_LENGTH];
m_rptCall1 = new unsigned char[LONG_CALLSIGN_LENGTH];
m_rptCall2 = new unsigned char[LONG_CALLSIGN_LENGTH];
::memset(m_rptCall1, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_rptCall2, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_yourCall, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_myCall1, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_myCall2, ' ', SHORT_CALLSIGN_LENGTH);
}
CHeaderData::CHeaderData(const CHeaderData& header) :
m_rptSeq(header.m_rptSeq),
m_id(header.m_id),
m_band1(header.m_band1),
m_band2(header.m_band2),
m_band3(header.m_band3),
m_flag1(header.m_flag1),
m_flag2(header.m_flag2),
m_flag3(header.m_flag3),
m_myCall1(NULL),
m_myCall2(NULL),
m_yourCall(NULL),
m_rptCall1(NULL),
m_rptCall2(NULL),
m_yourAddress(header.m_yourAddress),
m_yourPort(header.m_yourPort),
m_myPort(header.m_myPort),
m_errors(header.m_errors)
{
m_myCall1 = new unsigned char[LONG_CALLSIGN_LENGTH];
m_myCall2 = new unsigned char[SHORT_CALLSIGN_LENGTH];
m_yourCall = new unsigned char[LONG_CALLSIGN_LENGTH];
m_rptCall1 = new unsigned char[LONG_CALLSIGN_LENGTH];
m_rptCall2 = new unsigned char[LONG_CALLSIGN_LENGTH];
::memcpy(m_myCall1, header.m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, header.m_myCall2, SHORT_CALLSIGN_LENGTH);
::memcpy(m_yourCall, header.m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, header.m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall2, header.m_rptCall2, LONG_CALLSIGN_LENGTH);
}
CHeaderData::CHeaderData(const std::string& myCall1, const std::string& myCall2, const std::string& yourCall,
const std::string& rptCall1, const std::string& rptCall2, unsigned char flag1,
unsigned char flag2, unsigned char flag3) :
m_rptSeq(0U),
m_id(0U),
m_band1(0U),
m_band2(0U),
m_band3(0U),
m_flag1(flag1),
m_flag2(flag2),
m_flag3(flag3),
m_myCall1(NULL),
m_myCall2(NULL),
m_yourCall(NULL),
m_rptCall1(NULL),
m_rptCall2(NULL),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U),
m_errors(0U)
{
m_myCall1 = new unsigned char[LONG_CALLSIGN_LENGTH];
m_myCall2 = new unsigned char[SHORT_CALLSIGN_LENGTH];
m_yourCall = new unsigned char[LONG_CALLSIGN_LENGTH];
m_rptCall1 = new unsigned char[LONG_CALLSIGN_LENGTH];
m_rptCall2 = new unsigned char[LONG_CALLSIGN_LENGTH];
::memset(m_myCall1, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_myCall2, ' ', SHORT_CALLSIGN_LENGTH);
::memset(m_yourCall, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_rptCall1, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_rptCall2, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < myCall1.size() && i < LONG_CALLSIGN_LENGTH; i++)
m_myCall1[i] = myCall1[i];
for (unsigned int i = 0U; i < myCall2.size() && i < SHORT_CALLSIGN_LENGTH; i++)
m_myCall2[i] = myCall2[i];
for (unsigned int i = 0U; i < yourCall.size() && i < LONG_CALLSIGN_LENGTH; i++)
m_yourCall[i] = yourCall[i];
for (unsigned int i = 0U; i < rptCall1.size() && i < LONG_CALLSIGN_LENGTH; i++)
m_rptCall1[i] = rptCall1[i];
for (unsigned int i = 0U; i < rptCall2.size() && i < LONG_CALLSIGN_LENGTH; i++)
m_rptCall2[i] = rptCall2[i];
}
CHeaderData::~CHeaderData()
{
delete[] m_myCall1;
delete[] m_myCall2;
delete[] m_yourCall;
delete[] m_rptCall1;
delete[] m_rptCall2;
}
bool CHeaderData::setIcomRepeaterData(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort)
{
assert(data != NULL);
assert(length >= 58U);
m_rptSeq = data[4] * 256U + data[5];
m_band1 = data[11];
m_band2 = data[12];
m_band3 = data[13];
m_id = data[14] * 256U + data[15];
m_flag1 = data[17U];
m_flag2 = data[18U];
m_flag3 = data[19U];
::memcpy(m_rptCall2, data + 20U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 28U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 36U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 44U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 52U, SHORT_CALLSIGN_LENGTH);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
if (check) {
CCCITTChecksum cksum;
cksum.update(data + 17U, RADIO_HEADER_LENGTH_BYTES - 2U);
bool valid = cksum.check(data + 17U + RADIO_HEADER_LENGTH_BYTES - 2U);
if (!valid)
CUtils::dump("Header checksum failure from the repeater", data + 17U, RADIO_HEADER_LENGTH_BYTES);
return valid;
} else {
return true;
}
}
bool CHeaderData::setHBRepeaterData(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort)
{
assert(data != NULL);
assert(length >= 49U);
m_id = data[5U] * 256U + data[6U];
m_errors = data[7U];
m_flag1 = data[8U];
m_flag2 = data[9U];
m_flag3 = data[10U];
::memcpy(m_rptCall2, data + 11U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 19U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 27U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 35U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 43U, SHORT_CALLSIGN_LENGTH);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
if (check) {
CCCITTChecksum cksum;
cksum.update(data + 8U, RADIO_HEADER_LENGTH_BYTES - 2U);
bool valid = cksum.check(data + 8U + RADIO_HEADER_LENGTH_BYTES - 2U);
if (!valid)
CUtils::dump("Header checksum failure from the repeater", data + 8U, RADIO_HEADER_LENGTH_BYTES);
return valid;
} else {
return true;
}
}
void CHeaderData::setDCSData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(length >= 100U);
m_id = data[44U] * 256U + data[43U];
m_flag1 = data[4U];
m_flag2 = data[5U];
m_flag3 = data[6U];
::memcpy(m_rptCall2, data + 7U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 15U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 23U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 31U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 39U, SHORT_CALLSIGN_LENGTH);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
}
void CHeaderData::setCCSData(const unsigned char *data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(length >= 100U);
m_id = data[44U] * 256U + data[43U];
::memcpy(m_rptCall2, data + 7U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 15U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 23U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 31U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 39U, SHORT_CALLSIGN_LENGTH);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
}
bool CHeaderData::setG2Data(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort)
{
assert(data != NULL);
assert(length >= 56U);
m_band1 = data[9];
m_band2 = data[10];
m_band3 = data[11];
m_id = data[12] * 256U + data[13];
m_flag1 = data[15U];
m_flag2 = data[16U];
m_flag3 = data[17U];
::memcpy(m_rptCall2, data + 18U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 26U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 34U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 42U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 50U, SHORT_CALLSIGN_LENGTH);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
if (check) {
CCCITTChecksum cksum;
cksum.update(data + 15U, RADIO_HEADER_LENGTH_BYTES - 2U);
bool valid = cksum.check(data + 15U + RADIO_HEADER_LENGTH_BYTES - 2U);
if (!valid)
CUtils::dump("Header checksum failure from G2", data + 15U, RADIO_HEADER_LENGTH_BYTES);
return valid;
} else {
return true;
}
}
bool CHeaderData::setDExtraData(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(length >= 56U);
m_band1 = data[9];
m_band2 = data[10];
m_band3 = data[11];
m_id = data[12] * 256U + data[13];
m_flag1 = data[15U];
m_flag2 = data[16U];
m_flag3 = data[17U];
::memcpy(m_rptCall2, data + 18U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 26U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 34U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 42U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 50U, SHORT_CALLSIGN_LENGTH);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
if (check) {
CCCITTChecksum cksum;
cksum.update(data + 15U, RADIO_HEADER_LENGTH_BYTES - 2U);
bool valid = cksum.check(data + 15U + RADIO_HEADER_LENGTH_BYTES - 2U);
if (!valid)
CUtils::dump("Header checksum failure from DExtra", data + 15U, RADIO_HEADER_LENGTH_BYTES);
return valid;
} else {
return true;
}
}
bool CHeaderData::setDPlusData(const unsigned char *data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(length >= 58U);
if (data[0] != 0x3A || data[1] != 0x80) {
CUtils::dump("Invalid header length from D-Plus", data, length);
return false;
}
m_band1 = data[11];
m_band2 = data[12];
m_band3 = data[13];
m_id = data[14] * 256U + data[15];
m_flag1 = data[17U];
m_flag2 = data[18U];
m_flag3 = data[19U];
::memcpy(m_rptCall2, data + 20U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 28U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 36U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 44U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 52U, SHORT_CALLSIGN_LENGTH);
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
if (check) {
CCCITTChecksum cksum;
cksum.update(data + 17U, RADIO_HEADER_LENGTH_BYTES - 2U);
bool valid = cksum.check(data + 17U + RADIO_HEADER_LENGTH_BYTES - 2U);
if (!valid)
CUtils::dump("Header checksum failure from D-Plus", data + 17U, RADIO_HEADER_LENGTH_BYTES);
return valid;
} else {
return true;
}
}
bool CHeaderData::setDVTOOLData(const unsigned char* data, unsigned int length, bool check)
{
assert(data != NULL);
assert(length >= RADIO_HEADER_LENGTH_BYTES);
m_flag1 = data[0U];
m_flag2 = data[1U];
m_flag3 = data[2U];
::memcpy(m_rptCall2, data + 3U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 11U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 19U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 27U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 35U, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum cksum;
cksum.update(data, RADIO_HEADER_LENGTH_BYTES - 2U);
bool valid = cksum.check(data + RADIO_HEADER_LENGTH_BYTES - 2U);
if (!valid)
CUtils::dump("Header checksum failure from DVTOOL", data, RADIO_HEADER_LENGTH_BYTES);
return valid;
} else {
return true;
}
}
unsigned int CHeaderData::getIcomRepeaterData(unsigned char *data, unsigned int length, bool check) const
{
assert(data != NULL);
assert(length >= 58U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'T';
data[3] = 'R';
data[4] = m_rptSeq / 256U; // Packet sequence number
data[5] = m_rptSeq % 256U;
data[6] = 0x73; // Not a response
data[7] = 0x12; // Data type
data[8] = 0x00; // Length of 48 bytes following
data[9] = 0x30;
data[10] = 0x20; // AMBE plus Slow Data following
data[11] = m_band1;
data[12] = m_band2;
data[13] = m_band3;
data[14] = m_id / 256U; // Unique session id
data[15] = m_id % 256U;
data[16] = 0x80;
data[17] = m_flag1; // Flags 1, 2, and 3
data[18] = m_flag2;
data[19] = m_flag3;
::memcpy(data + 20U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 28U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 36U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 44U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 52U, m_myCall2, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum csum;
csum.update(data + 17, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
csum.result(data + 56);
} else {
data[56] = 0xFF;
data[57] = 0xFF;
}
return 58U;
}
unsigned int CHeaderData::getHBRepeaterData(unsigned char *data, unsigned int length, bool check) const
{
assert(data != NULL);
assert(length >= 49U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'R';
data[3] = 'P';
data[4] = 0x20U;
data[5] = m_id / 256U; // Unique session id
data[6] = m_id % 256U;
data[7] = 0U;
data[8] = m_flag1; // Flags 1, 2, and 3
data[9] = m_flag2;
data[10] = m_flag3;
::memcpy(data + 11U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 19U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 27U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 35U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 43U, m_myCall2, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum csum;
csum.update(data + 8U, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
csum.result(data + 47U);
} else {
data[47] = 0xFF;
data[48] = 0xFF;
}
return 49U;
}
void CHeaderData::getDCSData(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 100U);
data[4] = m_flag1; // Flags 1, 2, and 3
data[5] = m_flag2;
data[6] = m_flag3;
::memcpy(data + 7U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 15U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 23U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 31U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 39U, m_myCall2, SHORT_CALLSIGN_LENGTH);
}
void CHeaderData::getCCSData(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 100U);
::memcpy(data + 7U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 15U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 23U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 31U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 39U, m_myCall2, SHORT_CALLSIGN_LENGTH);
}
unsigned int CHeaderData::getG2Data(unsigned char *data, unsigned int length, bool check) const
{
assert(data != NULL);
assert(length >= 56U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'V';
data[3] = 'T';
data[4] = 0x10;
data[5] = 0x00;
data[6] = 0x15;
data[7] = 0x09;
data[8] = 0x20;
data[9] = m_band1;
data[10] = m_band2;
data[11] = m_band3;
data[12] = m_id / 256U; // Unique session id
data[13] = m_id % 256U;
data[14] = 0x80;
data[15] = m_flag1; // Flags 1, 2, and 3
data[16] = m_flag2;
data[17] = m_flag3;
::memcpy(data + 18U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 26U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 34U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 42U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 50U, m_myCall2, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum csum;
csum.update(data + 15, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
csum.result(data + 54);
} else {
data[54] = 0xFF;
data[55] = 0xFF;
}
return 56U;
}
unsigned int CHeaderData::getDExtraData(unsigned char* data, unsigned int length, bool check) const
{
assert(data != NULL);
assert(length >= 56U);
data[0] = 'D';
data[1] = 'S';
data[2] = 'V';
data[3] = 'T';
data[4] = 0x10;
data[5] = 0x00;
data[6] = 0x00;
data[7] = 0x00;
data[8] = 0x20;
data[9] = m_band1;
data[10] = m_band2;
data[11] = m_band3;
data[12] = m_id % 256U; // Unique session id
data[13] = m_id / 256U;
data[14] = 0x80;
data[15] = 0x00; // Flags 1, 2, and 3
data[16] = 0x00;
data[17] = 0x00;
::memcpy(data + 18U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 26U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 34U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 42U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 50U, m_myCall2, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum csum;
csum.update(data + 15, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
csum.result(data + 54);
} else {
data[54] = 0xFF;
data[55] = 0xFF;
}
return 56U;
}
unsigned int CHeaderData::getDPlusData(unsigned char* data, unsigned int length, bool check) const
{
assert(data != NULL);
assert(length >= 58U);
data[0] = 0x3A;
data[1] = 0x80;
data[2] = 'D';
data[3] = 'S';
data[4] = 'V';
data[5] = 'T';
data[6] = 0x10;
data[7] = 0x00;
data[8] = 0x00;
data[9] = 0x00;
data[10] = 0x20;
data[11] = m_band1;
data[12] = m_band2;
data[13] = m_band3;
data[14] = m_id % 256U; // Unique session id
data[15] = m_id / 256U;
data[16] = 0x80;
data[17] = 0x00; // Flags 1, 2, and 3
data[18] = 0x00;
data[19] = 0x00;
::memcpy(data + 20U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 28U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 36U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 44U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 52U, m_myCall2, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum csum;
csum.update(data + 17, 4U * LONG_CALLSIGN_LENGTH + SHORT_CALLSIGN_LENGTH + 3U);
csum.result(data + 56);
} else {
data[56] = 0xFF;
data[57] = 0xFF;
}
return 58U;
}
unsigned int CHeaderData::getId() const
{
return m_id;
}
void CHeaderData::setId(unsigned int id)
{
m_id = id;
}
unsigned char CHeaderData::getBand1() const
{
return m_band1;
}
unsigned char CHeaderData::getBand2() const
{
return m_band2;
}
unsigned char CHeaderData::getBand3() const
{
return m_band3;
}
void CHeaderData::setBand1(unsigned char band)
{
m_band1 = band;
}
void CHeaderData::setBand2(unsigned char band)
{
m_band2 = band;
}
void CHeaderData::setBand3(unsigned char band)
{
m_band3 = band;
}
unsigned int CHeaderData::getRptSeq() const
{
return m_rptSeq;
}
void CHeaderData::setRptSeq(unsigned int seqNo)
{
m_rptSeq = seqNo;
}
unsigned char CHeaderData::getFlag1() const
{
return m_flag1;
}
unsigned char CHeaderData::getFlag2() const
{
return m_flag2;
}
unsigned char CHeaderData::getFlag3() const
{
return m_flag3;
}
void CHeaderData::setFlags(unsigned char flag1, unsigned char flag2, unsigned char flag3)
{
m_flag1 = flag1;
m_flag2 = flag2;
m_flag3 = flag3;
}
std::string CHeaderData::getMyCall1() const
{
return std::string((const char*)m_myCall1, LONG_CALLSIGN_LENGTH);
}
std::string CHeaderData::getMyCall2() const
{
return std::string((const char*)m_myCall2, SHORT_CALLSIGN_LENGTH);
}
std::string CHeaderData::getYourCall() const
{
return std::string((const char*)m_yourCall, LONG_CALLSIGN_LENGTH);
}
std::string CHeaderData::getRptCall1() const
{
return std::string((const char*)m_rptCall1, LONG_CALLSIGN_LENGTH);
}
std::string CHeaderData::getRptCall2() const
{
return std::string((const char*)m_rptCall2, LONG_CALLSIGN_LENGTH);
}
void CHeaderData::setFlag1(unsigned char flag)
{
m_flag1 = flag;
}
void CHeaderData::setFlag2(unsigned char flag)
{
m_flag2 = flag;
}
void CHeaderData::setFlag3(unsigned char flag)
{
m_flag3 = flag;
}
void CHeaderData::setMyCall1(const std::string& my1)
{
::memset(m_myCall1, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < my1.size(); i++)
m_myCall1[i] = my1[i];
}
void CHeaderData::setMyCall2(const std::string& my2)
{
::memset(m_myCall2, ' ', SHORT_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < my2.size(); i++)
m_myCall2[i] = my2[i];
}
void CHeaderData::setYourCall(const std::string& your)
{
::memset(m_yourCall, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < your.size(); i++)
m_yourCall[i] = your[i];
}
void CHeaderData::setRptCall1(const std::string& rpt1)
{
::memset(m_rptCall1, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < rpt1.size(); i++)
m_rptCall1[i] = rpt1[i];
}
void CHeaderData::setRptCall2(const std::string& rpt2)
{
::memset(m_rptCall2, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < rpt2.size(); i++)
m_rptCall2[i] = rpt2[i];
}
void CHeaderData::setRepeaters(const std::string& rpt1, const std::string& rpt2)
{
::memset(m_rptCall1, ' ', LONG_CALLSIGN_LENGTH);
::memset(m_rptCall2, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < rpt1.size(); i++)
m_rptCall1[i] = rpt1[i];
for (unsigned int i = 0U; i < rpt2.size(); i++)
m_rptCall2[i] = rpt2[i];
}
void CHeaderData::setCQCQCQ()
{
::memcpy(m_yourCall, "CQCQCQ ", LONG_CALLSIGN_LENGTH);
}
bool CHeaderData::setData(const unsigned char *data, unsigned int length, bool check)
{
assert(data != NULL);
assert(length >= RADIO_HEADER_LENGTH_BYTES);
m_flag1 = data[0U];
m_flag2 = data[1U];
m_flag3 = data[2U];
::memcpy(m_rptCall2, data + 3U, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, data + 11U, LONG_CALLSIGN_LENGTH);
::memcpy(m_yourCall, data + 19U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall1, data + 27U, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, data + 35U, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum cksum;
cksum.update(data, RADIO_HEADER_LENGTH_BYTES - 2U);
return cksum.check(data + RADIO_HEADER_LENGTH_BYTES - 2U);
} else {
return true;
}
}
unsigned int CHeaderData::getData(unsigned char *data, unsigned int length, bool check) const
{
assert(data != NULL);
assert(length >= RADIO_HEADER_LENGTH_BYTES);
data[0] = m_flag1; // Flags 1, 2, and 3
data[1] = m_flag2;
data[2] = m_flag3;
::memcpy(data + 3U, m_rptCall2, LONG_CALLSIGN_LENGTH);
::memcpy(data + 11U, m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 19U, m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(data + 27U, m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(data + 35U, m_myCall2, SHORT_CALLSIGN_LENGTH);
if (check) {
CCCITTChecksum csum;
csum.update(data, RADIO_HEADER_LENGTH_BYTES - 2U);
csum.result(data + RADIO_HEADER_LENGTH_BYTES - 2U);
return RADIO_HEADER_LENGTH_BYTES;
} else {
return RADIO_HEADER_LENGTH_BYTES - 2U;
}
}
void CHeaderData::setDestination(const in_addr& address, unsigned int port)
{
m_yourAddress = address;
m_yourPort = port;
}
in_addr CHeaderData::getYourAddress() const
{
return m_yourAddress;
}
unsigned int CHeaderData::getYourPort() const
{
return m_yourPort;
}
unsigned int CHeaderData::getMyPort() const
{
return m_myPort;
}
CHeaderData& CHeaderData::operator =(const CHeaderData& header)
{
if (&header != this) {
m_rptSeq = header.m_rptSeq;
m_id = header.m_id;
m_band1 = header.m_band1;
m_band2 = header.m_band2;
m_band3 = header.m_band3;
m_flag1 = header.m_flag1;
m_flag2 = header.m_flag2;
m_flag3 = header.m_flag3;
m_yourAddress = header.m_yourAddress;
m_yourPort = header.m_yourPort;
m_myPort = header.m_myPort;
m_errors = header.m_errors;
::memcpy(m_myCall1, header.m_myCall1, LONG_CALLSIGN_LENGTH);
::memcpy(m_myCall2, header.m_myCall2, SHORT_CALLSIGN_LENGTH);
::memcpy(m_yourCall, header.m_yourCall, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall1, header.m_rptCall1, LONG_CALLSIGN_LENGTH);
::memcpy(m_rptCall2, header.m_rptCall2, LONG_CALLSIGN_LENGTH);
}
return *this;
}

@ -0,0 +1,124 @@
/*
* Copyright (C) 2010-2014 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <netinet/in.h>
class CHeaderData {
public:
CHeaderData();
CHeaderData(const CHeaderData& header);
CHeaderData(const std::string& myCall1, const std::string& myCall2, const std::string& yourCall,
const std::string& rptCall1, const std::string& rptCall2, unsigned char flag1 = 0x00,
unsigned char flag2 = 0x00, unsigned char flag3 = 0x00);
~CHeaderData();
bool setIcomRepeaterData(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort);
bool setHBRepeaterData(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort);
bool setG2Data(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort);
bool setDExtraData(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setDPlusData(const unsigned char* data, unsigned int length, bool check, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
void setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
void setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
unsigned int getIcomRepeaterData(unsigned char* data, unsigned int length, bool check) const;
unsigned int getHBRepeaterData(unsigned char* data, unsigned int length, bool check) const;
unsigned int getDExtraData(unsigned char* data, unsigned int length, bool check) const;
unsigned int getDPlusData(unsigned char* data, unsigned int length, bool check) const;
unsigned int getG2Data(unsigned char* data, unsigned int length, bool check) const;
void getDCSData(unsigned char* data, unsigned int length) const;
void getCCSData(unsigned char* data, unsigned int length) const;
bool setDVTOOLData(const unsigned char* data, unsigned int length, bool check);
unsigned int getId() const;
void setId(unsigned int id);
unsigned char getBand1() const;
unsigned char getBand2() const;
unsigned char getBand3() const;
void setBand1(unsigned char band);
void setBand2(unsigned char band);
void setBand3(unsigned char band);
unsigned int getRptSeq() const;
void setRptSeq(unsigned int seqNo);
unsigned char getFlag1() const;
unsigned char getFlag2() const;
unsigned char getFlag3() const;
std::string getMyCall1() const;
std::string getMyCall2() const;
std::string getYourCall() const;
std::string getRptCall1() const;
std::string getRptCall2() const;
void setFlag1(unsigned char flag);
void setFlag2(unsigned char flag);
void setFlag3(unsigned char flag);
void setFlags(unsigned char flag1, unsigned char flag2, unsigned char flag3);
void setMyCall1(const std::string& callsign);
void setMyCall2(const std::string& callsign);
void setYourCall(const std::string& callsign);
void setRptCall1(const std::string& callsign);
void setRptCall2(const std::string& callsign);
void setCQCQCQ();
void setRepeaters(const std::string& rpt1, const std::string& rpt2);
void setDestination(const in_addr& address, unsigned int port);
bool setData(const unsigned char* data, unsigned int length, bool check);
unsigned int getData(unsigned char* data, unsigned int length, bool check) const;
in_addr getYourAddress() const;
unsigned int getYourPort() const;
unsigned int getMyPort() const;
unsigned int getErrors() const;
static void initialise();
static void finalise();
static unsigned int createId();
CHeaderData& operator=(const CHeaderData& header);
private:
unsigned int m_rptSeq;
unsigned int m_id;
unsigned char m_band1;
unsigned char m_band2;
unsigned char m_band3;
unsigned char m_flag1;
unsigned char m_flag2;
unsigned char m_flag3;
unsigned char* m_myCall1;
unsigned char* m_myCall2;
unsigned char* m_yourCall;
unsigned char* m_rptCall1;
unsigned char* m_rptCall2;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
unsigned int m_errors;
};

@ -0,0 +1,140 @@
/*
* Copyright (C) 2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <cassert>
#include <cstring>
#include "HeardData.h"
CHeardData::CHeardData() :
m_reflector(),
m_repeater(),
m_user(),
m_ext(),
m_address(),
m_port(0U)
{
}
CHeardData::CHeardData(const CHeardData& data) :
m_reflector(data.m_reflector),
m_repeater(data.m_repeater),
m_user(data.m_user),
m_ext(data.m_ext),
m_address(data.m_address),
m_port(data.m_port)
{
}
CHeardData::CHeardData(const CHeaderData& data, const std::string& repeater, const std::string& reflector) :
m_reflector(reflector),
m_repeater(repeater),
m_user(),
m_ext(),
m_address(),
m_port()
{
m_user = data.getMyCall1();
m_ext = data.getMyCall2();
}
CHeardData::~CHeardData()
{
}
bool CHeardData::setIcomRepeaterData(const unsigned char *data, unsigned int length, const in_addr& address, unsigned int port)
{
assert(data != NULL);
assert(length >= 26U);
std::string sdata((const char *)data);
m_user = sdata.substr(10, LONG_CALLSIGN_LENGTH);
m_repeater = sdata.substr(18, LONG_CALLSIGN_LENGTH);
m_address = address;
m_port = port;
return true;
}
unsigned int CHeardData::getCCSData(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 100U);
::memset(data, 0x00U, 100U);
data[0U] = '0';
data[1U] = '0';
data[2U] = '0';
data[3U] = '1';
::memset(data + 7U, ' ', 36U);
for (unsigned int i = 0U; i < m_reflector.size(); i++)
data[i + 7U] = m_reflector.at(i);
for (unsigned int i = 0U; i < m_repeater.size(); i++)
data[i + 15U] = m_repeater.at(i);
::memcpy(data + 23U, "CQCQCQ ", LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < m_user.size(); i++)
data[i + 31U] = m_user.at(i);
for (unsigned int i = 0U; i < m_ext.size(); i++)
data[i + 39U] = m_ext.at(i);
data[61U] = 0x01U;
data[63U] = 0x21U;
::memset(data + 64U, ' ', 20U);
data[93U] = 0x36U;
return 100U;
}
std::string CHeardData::getRepeater() const
{
return m_repeater;
}
std::string CHeardData::getUser() const
{
return m_user;
}
void CHeardData::setDestination(const in_addr& address, unsigned int port)
{
m_address = address;
m_port = port;
}
in_addr CHeardData::getAddress() const
{
return m_address;
}
unsigned int CHeardData::getPort() const
{
return m_port;
}

@ -0,0 +1,52 @@
/*
* Copyright (C) 2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <netinet/in.h>
#include "DStarDefines.h"
#include "HeaderData.h"
class CHeardData {
public:
CHeardData();
CHeardData(const CHeardData& data);
CHeardData(const CHeaderData& data, const std::string& repeater, const std::string& reflector);
~CHeardData();
bool setIcomRepeaterData(const unsigned char* data, unsigned int length, const in_addr& address, unsigned int port);
unsigned int getCCSData(unsigned char* data, unsigned int length) const;
std::string getRepeater() const;
std::string getUser() const;
void setDestination(const in_addr& address, unsigned int port);
in_addr getAddress() const;
unsigned int getPort() const;
private:
std::string m_reflector;
std::string m_repeater;
std::string m_user;
std::string m_ext;
in_addr m_address;
unsigned int m_port;
};

@ -0,0 +1,45 @@
/*
CIRCDDB - ircDDB client library in C++
Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (c) 2017 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "IRCMessageQueue.h"
class IRCApplication
{
public:
virtual void userJoin(const std::string& nick, const std::string& name, const std::string& host) = 0;
virtual void userLeave(const std::string& nick) = 0;
virtual void userChanOp(const std::string& nick, bool op) = 0;
virtual void userListReset(void) = 0;
virtual void msgChannel(IRCMessage *m) = 0;
virtual void msgQuery(IRCMessage *m) = 0;
virtual void setCurrentNick(const std::string& nick) = 0;
virtual void setTopic(const std::string& topic) = 0;
virtual void setBestServer(const std::string& ircUser) = 0;
virtual void setSendQ(IRCMessageQueue *s) = 0;
virtual IRCMessageQueue *getSendQ(void) = 0;
};

@ -0,0 +1,335 @@
/*
CIRCDDB - ircDDB client library in C++
Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (C) 2012 Jonathan Naylor, G4KLX
Copyright (c) 2017 by Thomas A. Early
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, see <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <cstring>
#include <chrono>
#include <thread>
#include "IRCClient.h"
#include "Utils.h"
IRCClient::IRCClient(IRCApplication *app, const std::string& update_channel, const std::string& hostName, unsigned int port, const std::string& callsign,
const std::string& password, const std::string& versionInfo, const std::string& localAddr)
{
CUtils::safeStringCopy(m_host_name, hostName.c_str(), sizeof m_host_name);
m_callsign = callsign;
CUtils::ToLower(m_callsign);
m_port = port;
m_password = password;
m_app = app;
if (0 == localAddr.size())
CUtils::safeStringCopy(m_local_addr, "0.0.0.0", sizeof m_local_addr);
else
CUtils::safeStringCopy(m_local_addr, localAddr.c_str(), sizeof m_local_addr);
m_proto = new IRCProtocol(app, m_callsign, m_password, update_channel, versionInfo);
m_recvQ = NULL;
m_sendQ = NULL;
m_recv = NULL;
}
IRCClient::~IRCClient()
{
delete m_proto;
}
void IRCClient::startWork()
{
m_terminateThread = false;
m_future = std::async(std::launch::async, &IRCClient::Entry, this);
}
void IRCClient::stopWork()
{
m_terminateThread = true;
m_future.get();
}
void IRCClient::Entry()
{
const unsigned int MAXIPV4ADDR = 10;
struct sockaddr_in addr[MAXIPV4ADDR];
struct sockaddr_in myaddr;
unsigned int numAddr = 0;
int state = 0;
int timer = 0;
int sock = 0;
unsigned int currentAddr = 0;
int result = CUtils::getAllIPV4Addresses(m_local_addr, 0, &numAddr, &myaddr, 1);
if (result || 1!=numAddr) {
printf("IRCClient::Entry: local address not parseable, using 0.0.0.0\n");
memset(&myaddr, 0, sizeof(struct sockaddr_in));
}
while (true) {
if (timer > 0)
timer--;
switch (state) {
case 0:
if (m_terminateThread) {
printf("IRCClient::Entry: thread terminated at state=%d\n", state);
return;
}
if (timer == 0) {
timer = 30;
if (0 == CUtils::getAllIPV4Addresses(m_host_name, m_port, &numAddr, addr, MAXIPV4ADDR)) {
printf("IRCClient::Entry: number of DNS entries %d\n", numAddr);
if (numAddr > 0) {
currentAddr = 0;
state = 1;
timer = 0;
}
}
}
break;
case 1:
if (m_terminateThread) {
printf("IRCClient::Entry: thread terminated at state=%d\n", state);
return;
}
if (timer == 0) {
sock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (sock < 0) {
printf("IRCClient::Entry: socket\n");
timer = 30;
state = 0;
} else {
if (fcntl(sock, F_SETFL, O_NONBLOCK) < 0) {
printf("IRCClient::Entry: fcntl\n");
close(sock);
timer = 30;
state = 0;
} else {
unsigned char * h = (unsigned char *) &(myaddr.sin_addr);
if (h[0] || h[1] || h[2] || h[3])
printf("IRCClient::Entry: bind: local address %d.%d.%d.%d\n", h[0], h[1], h[2], h[3]);
int res = bind(sock, (struct sockaddr *)&myaddr, sizeof (struct sockaddr_in));
if (res) {
printf("IRCClient::Entry: bind\n");
close(sock);
state = 0;
timer = 30;
break;
}
h = (unsigned char *) &(addr[currentAddr].sin_addr);
printf("IRCClient::Entry: trying to connect to %d.%d.%d.%d\n", h[0], h[1], h[2], h[3]);
res = connect(sock, (struct sockaddr *)(addr + currentAddr), sizeof (struct sockaddr_in));
if (res == 0) {
printf("IRCClient::Entry: connected\n");
state = 4;
} else {
if (errno == EINPROGRESS) {
printf("IRCClient::Entry: connect in progress\n");
state = 3;
timer = 10; // 5 second timeout
} else {
printf("IRCClient::Entry: connect\n");
close(sock);
currentAddr++;
if (currentAddr >= numAddr) {
state = 0;
timer = 30;
} else {
state = 1;
timer = 4;
}
}
}
} // connect
}
}
break;
case 3:
{
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = 0;
fd_set myset;
FD_ZERO(&myset);
FD_SET(sock, &myset);
int res = select(sock+1, NULL, &myset, NULL, &tv);
if (res < 0) {
printf("IRCClient::Entry: select\n");
close(sock);
state = 0;
timer = 30;
}
else if (res > 0) // connect is finished
{
socklen_t val_len;
int value;
val_len = sizeof value;
if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &value, &val_len) < 0) {
printf("IRCClient::Entry: getsockopt\n");
close(sock);
state = 0;
timer = 30;
} else {
if (value) {
printf("IRCClient::Entry: SO_ERROR=%d\n", value);
close(sock);
currentAddr ++;
if (currentAddr >= numAddr) {
state = 0;
timer = 30;
} else {
state = 1;
timer = 2;
}
} else {
printf("IRCClient::Entry: connected2\n");
state = 4;
}
}
}
else if (timer == 0) { // select timeout and timer timeout
printf("IRCClient::Entry: connect timeout\n");
close(sock);
currentAddr ++;
if (currentAddr >= numAddr) {
state = 0;
timer = 30;
} else {
state = 1; // open new socket
timer = 2;
}
}
}
break;
case 4:
{
m_recvQ = new IRCMessageQueue();
m_sendQ = new IRCMessageQueue();
m_recv = new IRCReceiver(sock, m_recvQ);
m_recv->startWork();
m_proto->setNetworkReady(true);
state = 5;
timer = 0;
}
break;
case 5:
if (m_terminateThread)
state = 6;
else {
if (m_recvQ->isEOF()) {
timer = 0;
state = 6;
} else if (m_proto->processQueues(m_recvQ, m_sendQ) == false) {
timer = 0;
state = 6;
}
while (5==state && m_sendQ->messageAvailable()) {
IRCMessage *m = m_sendQ->getMessage();
std::string out;
m->composeMessage(out);
char buf[200];
CUtils::safeStringCopy(buf, out.c_str(), sizeof buf);
int len = strlen(buf);
if (buf[len - 1] == 10) { // is there a NL char at the end?
int r = send(sock, buf, len, 0);
if (r != len) {
printf("IRCClient::Entry: short write %d < %d\n", r, len);
timer = 0;
state = 6;
}
} else {
printf("IRCClient::Entry: no NL at end, len=%d\n", len);
timer = 0;
state = 6;
}
delete m;
}
}
break;
case 6:
{
if (m_app) {
m_app->setSendQ(NULL);
m_app->userListReset();
}
m_proto->setNetworkReady(false);
m_recv->stopWork();
std::this_thread::sleep_for(std::chrono::seconds(2));
delete m_recv;
delete m_recvQ;
delete m_sendQ;
close(sock);
if (m_terminateThread) { // request to end the thread
printf("IRCClient::Entry: thread terminated at state=%d\n", state);
return;
}
timer = 30;
state = 0; // reconnect to IRC server
}
break;
}
std::this_thread::sleep_for(std::chrono::milliseconds(500));
}
return;
}

@ -0,0 +1,59 @@
/*
CIRCDDB - ircDDB client library in C++
Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (C) 2011,2012 Jonathan Naylor, G4KLX
Copyright (c) 2017 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <future>
#include "IRCReceiver.h"
#include "IRCMessageQueue.h"
#include "IRCProtocol.h"
#include "IRCApplication.h"
class IRCClient
{
public:
IRCClient( IRCApplication *app, const std::string& update_channel, const std::string& hostName, unsigned int port, const std::string& callsign,
const std::string& password, const std::string& versionInfo, const std::string& localAddr);
~IRCClient();
void startWork();
void stopWork();
protected:
void Entry();
private:
char m_host_name[100];
char m_local_addr[100];
unsigned int m_port;
std::string m_callsign;
std::string m_password;
bool m_terminateThread;
IRCReceiver *m_recv;
IRCMessageQueue *m_recvQ;
IRCMessageQueue *m_sendQ;
IRCProtocol *m_proto;
IRCApplication *m_app;
std::future<void> m_future;
};

@ -0,0 +1,32 @@
/*
CIRCDDBClient - ircDDB client library in C++
Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (C) 2011,2012 Jonathan Naylor, G4KLX
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, see <http://www.gnu.org/licenses/>.
*/
#include "IRCDDB.h"
CIRCDDB::CIRCDDB()
{
}
CIRCDDB::~CIRCDDB()
{
}

@ -0,0 +1,139 @@
/*
CIRCDDB - ircDDB client library in C++
Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (C) 2011,2012 Jonathan Naylor, G4KLX
Copyright (c) 2017 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <vector>
enum IRCDDB_RESPONSE_TYPE {
IDRT_NONE,
IDRT_USER,
IDRT_GATEWAY,
IDRT_REPEATER
};
class CIRCDDB
{
public:
CIRCDDB();
virtual ~CIRCDDB();
// A false return implies a network error, or unable to log in
virtual bool open() = 0;
// rptrQTH can be called multiple times if necessary
// callsign The callsign of the repeater
// latitude WGS84 position of antenna in degrees, positive value -> NORTH
// longitude WGS84 position of antenna in degrees, positive value -> EAST
// desc1, desc2 20-character description of QTH
// infoURL URL of a web page with information about the repeater
virtual void rptrQTH(const std::string& callsign, double latitude, double longitude, const std::string& desc1, const std::string& desc2, const std::string& infoURL) = 0;
// rptrQRG can be called multiple times if necessary
// callsign callsign of the repeater
// txFrequency repeater TX frequency in MHz
// duplexShift duplex shift in MHz (positive or negative value): RX_freq = txFrequency + duplexShift
// range range of the repeater in meters (meters = miles * 1609.344)
// agl height of the antenna above ground in meters (meters = feet * 0.3048)
virtual void rptrQRG(const std::string& callsign, double txFrequency, double duplexShift, double range, double agl) = 0;
// If you call this method once, watchdog messages will be sent to the
// to the ircDDB network every 15 minutes. Invoke this method every 1-2 minutes to indicate
// that the gateway is working properly. After activating the watchdog, a red LED will be displayed
// on the ircDDB web page if this method is not called within a period of about 30 minutes.
// The string wdInfo should contain information about the source of the alive messages, e.g.,
// version of the RF decoding software. For example, the ircDDB java software sets this
// to "rpm_ircddbmhd-x.z-z". The string wdInfo must contain at least one non-space character.
virtual void kickWatchdog(const std::string& callsign, const std::string& wdInfo) = 0;
// get internal network status
virtual int getConnectionState() = 0;
// one of these values is returned:
// 0 = not (yet) connected to the IRC server
// 1-6 = a new connection was established, download of repeater info etc. is
// in progress
// 7 = the ircDDB connection is fully operational
// 10 = some network error occured, next state is "0" (new connection attempt)
// Send heard data, a false return implies a network error
virtual bool sendHeard(const std::string& myCall, const std::string& myCallExt, const std::string& yourCall, const std::string& rpt1, const std::string& rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3) = 0;
// same as sendHeard with two new fields:
// network_destination: empty string or 8-char call sign of the repeater
// or reflector, where this transmission is relayed to.
// tx_message: 20-char TX message or empty string, if the user did not
// send a TX message
virtual bool sendHeardWithTXMsg(const std::string& myCall, const std::string& myCallExt, const std::string& yourCall, const std::string& rpt1, const std::string& rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, const std::string& network_destination, const std::string& tx_message) = 0;
// this method should be called at the end of a transmission
// num_dv_frames: number of DV frames sent out (96 bit frames, 20ms)
// num_dv_silent_frames: number of DV silence frames sent out in the
// last transmission, or -1 if the information is not available
// num_bit_errors: number of bit errors of the received data. This should
// be the derived from the first Golay block of the voice data. This
// error correction code only looks at 24 bits of the 96 bit frame.
// So, the overall bit error rate is calculated like this:
// BER = num_bit_errors / (num_dv_frames * 24)
// Set num_bit_errors = -1, if the error information is not available.
virtual bool sendHeardWithTXStats(const std::string& myCall, const std::string& myCallExt, const std::string& yourCall, const std::string& rpt1, const std::string& rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, int num_dv_frames, int num_dv_silent_frames, int num_bit_errors) = 0;
// The following three functions don't block waiting for a reply, they just send the data
// Send query for a gateway/reflector, a false return implies a network error
virtual bool findGateway(const std::string& gatewayCallsign) = 0;
// Send query for a repeater module, a false return implies a network error
virtual bool findRepeater(const std::string& repeaterCallsign) = 0;
// Send query for a user, a false return implies a network error
virtual bool findUser(const std::string& userCallsign) = 0;
// Support for the Smart Group Server
virtual void sendSGSInfo(const std::string subcommand, const std::vector<std::string> parms) = 0;
// The following functions are for processing received messages
// Get the waiting message type
virtual IRCDDB_RESPONSE_TYPE getMessageType() = 0;
// Get a gateway message, as a result of IDRT_REPEATER returned from getMessageType()
// A false return implies a network error
virtual bool receiveRepeater(std::string& repeaterCallsign, std::string& gatewayCallsign, std::string& address) = 0;
// Get a gateway message, as a result of IDRT_GATEWAY returned from getMessageType()
// A false return implies a network error
virtual bool receiveGateway(std::string& gatewayCallsign, std::string& address) = 0;
// Get a user message, as a result of IDRT_USER returned from getMessageType()
// A false return implies a network error
virtual bool receiveUser(std::string& userCallsign, std::string& repeaterCallsign, std::string& gatewayCallsign, std::string& address) = 0;
virtual bool receiveUser(std::string& userCallsign, std::string& repeaterCallsign, std::string& gatewayCallsign, std::string& address, std::string& timeStamp) = 0;
virtual void close() = 0; // Implictely kills any threads in the IRC code
};
typedef std::vector<CIRCDDB*> CIRCDDB_Array;

File diff suppressed because it is too large Load Diff

@ -0,0 +1,97 @@
/*
CIRCDDB - ircDDB client library in C++
Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (C) 2012 Jonathan Naylor, G4KLX
Copyright (c) 2017 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "IRCDDB.h"
#include "IRCApplication.h"
#include <string>
#include <future>
#include <ctime>
#include <vector>
class IRCDDBAppPrivate;
class IRCDDBApp : public IRCApplication
{
public:
IRCDDBApp(const std::string& update_channel);
virtual ~IRCDDBApp();
virtual void userJoin(const std::string& nick, const std::string& name, const std::string& host);
virtual void userLeave(const std::string& nick);
virtual void userChanOp(const std::string& nick, bool op);
virtual void userListReset();
virtual void msgChannel(IRCMessage *m);
virtual void msgQuery(IRCMessage *m);
virtual void setCurrentNick(const std::string& nick);
virtual void setTopic(const std::string& topic);
virtual void setBestServer(const std::string& ircUser);
virtual void setSendQ(IRCMessageQueue *s);
virtual IRCMessageQueue *getSendQ();
void startWork();
void stopWork();
IRCDDB_RESPONSE_TYPE getReplyMessageType();
IRCMessage *getReplyMessage();
bool findUser(const std::string& s);
bool findRepeater(const std::string& s);
bool findGateway(const std::string& s);
bool sendHeard(const std::string& myCall, const std::string& myCallExt, const std::string& yourCall, const std::string& rpt1, const std::string& rpt2, unsigned char flag1,
unsigned char flag2, unsigned char flag3, const std::string& destination, const std::string& tx_msg, const std::string& tx_stats);
void sendSGSInfo(const std::string &subcommand, const std::vector<std::string> &pars);
int getConnectionState();
void rptrQRG(const std::string& callsign, double txFrequency, double duplexShift, double range, double agl);
void rptrQTH(const std::string& callsign, double latitude, double longitude, const std::string& desc1, const std::string& desc2, const std::string& infoURL);
void kickWatchdog(const std::string& callsign, const std::string& wdInfo);
protected:
void Entry();
private:
void doUpdate(std::string& msg);
void doNotFound(std::string& msg, std::string& retval);
std::string getIPAddress(std::string& zonerp_cs);
bool findServerUser();
unsigned int calculateUsn(const std::string& nick);
std::string getLastEntryTime(int tableID);
IRCDDBAppPrivate *d;
time_t m_maxTime;
std::future<void> m_future;
};

@ -0,0 +1,409 @@
/*
CIRCDDBClient - ircDDB client library in C++
Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (C) 2011,2012 Jonathan Naylor, G4KLX
Copyright (c) 2017 by Thomas A. Early
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, see <http://www.gnu.org/licenses/>.
*/
#include "IRCDDBClient.h"
#include "IRCClient.h"
#include "IRCDDBApp.h"
#include "Utils.h"
struct CIRCDDBClientPrivate
{
IRCClient *client;
IRCDDBApp *app;
};
CIRCDDBClient::CIRCDDBClient(const std::string& hostName, unsigned int port, const std::string& callsign, const std::string& password, const std::string& versionInfo, const std::string& localAddr, bool isQuadNet ) :
d(new CIRCDDBClientPrivate),
m_isQuadNet(isQuadNet)
{
std::string update_channel("#dstar");
d->app = new IRCDDBApp(update_channel);
d->client = new IRCClient(d->app, update_channel, hostName, port, callsign, password, versionInfo, localAddr);
}
CIRCDDBClient::~CIRCDDBClient()
{
delete d->client;
delete d->app;
delete d;
}
// A false return implies a network error, or unable to log in
bool CIRCDDBClient::open()
{
printf("start client and app\n");
d->client->startWork();
d->app->startWork();
return true;
}
int CIRCDDBClient::getConnectionState()
{
return d->app->getConnectionState();
}
void CIRCDDBClient::rptrQTH(const std::string& callsign, double latitude, double longitude, const std::string& desc1, const std::string& desc2, const std::string& infoURL)
{
d->app->rptrQTH(callsign, latitude, longitude, desc1, desc2, infoURL);
}
void CIRCDDBClient::rptrQRG(const std::string& callsign, double txFrequency, double duplexShift, double range, double agl)
{
d->app->rptrQRG(callsign, txFrequency, duplexShift, range, agl);
}
void CIRCDDBClient::kickWatchdog(const std::string& callsign, const std::string& wdInfo)
{
d->app->kickWatchdog(callsign, wdInfo);
}
// Send heard data, a false return implies a network error
bool CIRCDDBClient::sendHeard( const std::string& myCall, const std::string& myCallExt, const std::string& yourCall, const std::string& rpt1,
const std::string& rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3 )
{
if (myCall.size() != 8) {
printf("CIRCDDBClient::sendHeard:myCall='%s' len != 8\n", myCall.c_str());
return false;
}
if (myCallExt.size() != 4) {
printf("CIRCDDBClient::sendHeard:myCallExt='%s' len != 4\n", myCallExt.c_str());
return false;
}
if (yourCall.size() != 8) {
printf("CIRCDDBClient::sendHeard:yourCall='%s' len != 8\n", yourCall.c_str());
return false;
}
if (rpt1.size() != 8) {
printf("CIRCDDBClient::sendHeard:rpt1='%s' len != 8\n", rpt1.c_str());
return false;
}
if (rpt2.size() != 8) {
printf("CIRCDDBClient::sendHeard:rpt2='%s' len != 8\n", rpt2.c_str());
return false;
}
return d->app->sendHeard(myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, std::string(" "), std::string(""), std::string(""));
}
void CIRCDDBClient::sendSGSInfo(const std::string subcommand, const std::vector<std::string> parms)
{
printf("CIRCDDBClient::sendSGSInfo subcommand %s parms", subcommand.c_str());
for(unsigned int i=0; i < parms.size();i++)
printf(" %s", parms[i].c_str());
printf("\n");
if(m_isQuadNet) {
d->app->sendSGSInfo(subcommand, parms);
}
}
// Send heard data, a false return implies a network error
bool CIRCDDBClient::sendHeardWithTXMsg(const std::string& myCall, const std::string& myCallExt, const std::string& yourCall, const std::string& rpt1,
const std::string& rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, const std::string& network_destination, const std::string& tx_message)
{
if (myCall.size() != 8) {
printf("CIRCDDBClient::sendHeardWithTXMsg:myCall='%s' len != 8\n", myCall.c_str());
return false;
}
if (myCallExt.size() != 4) {
printf("CIRCDDBClient::sendHeardWithTXMsg:myCallExt='%s' len != 4\n", myCallExt.c_str());
return false;
}
if (yourCall.size() != 8) {
printf("CIRCDDBClient::sendHeardWithTXMsg:yourCall='%s' len != 8\n", yourCall.c_str());
return false;
}
if (rpt1.size() != 8) {
printf("CIRCDDBClient::sendHeardWithTXMsg:rpt1='%s' len != 8\n", rpt1.c_str());
return false;
}
if (rpt2.size() != 8) {
printf("CIRCDDBClient::sendHeardWithTXMsg:rpt2='%s' len != 8\n", rpt2.c_str());
return false;
}
std::string dest(network_destination);
if (0 == dest.size())
dest = std::string(" ");
if (8 != dest.size()) {
printf("CIRCDDBClient::sendHeardWithTXMsg:network_destination='%s' len != 8\n", dest.c_str());
return false;
}
std::string msg;
if (20 == tx_message.size()) {
for (unsigned int i=0; i < tx_message.size(); i++) {
char ch = tx_message.at(i);
if ((ch > 32) && (ch < 127))
msg.push_back(ch);
else
msg.push_back('_');
}
}
return d->app->sendHeard(myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, dest, msg, std::string(""));
}
bool CIRCDDBClient::sendHeardWithTXStats( const std::string& myCall, const std::string& myCallExt, const std::string& yourCall, const std::string& rpt1,
const std::string& rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, int num_dv_frames, int num_dv_silent_frames, int num_bit_errors)
{
if ((num_dv_frames <= 0) || (num_dv_frames > 65535)) {
printf("CIRCDDBClient::sendHeardWithTXStats:num_dv_frames=%d not in range 1-65535\n", num_dv_frames);
return false;
}
if (num_dv_silent_frames > num_dv_frames) {
printf("CIRCDDBClient::sendHeardWithTXStats:num_dv_silent_frames=%d > num_dv_frames=%d\n", num_dv_silent_frames, num_dv_frames);
return false;
}
if (num_bit_errors > (4*num_dv_frames)) { // max 4 bit errors per frame
printf("CIRCDDBClient::sendHeardWithTXStats:num_bit_errors > (4*num_dv_frames), %d > 4*%d\n", num_bit_errors, num_dv_frames);
return false;
}
if (myCall.size() != 8) {
printf("CIRCDDBClient::sendHeardWithTXStats:myCall='%s' len != 8\n", myCall.c_str());
return false;
}
if (myCallExt.size() != 4) {
printf("CIRCDDBClient::sendHeardWithTXStats:myCallExt='%s' len != 4\n", myCallExt.c_str());
return false;
}
if (yourCall.size() != 8) {
printf("CIRCDDBClient::sendHeardWithTXStats:yourCall='%s' len != 8\n", yourCall.c_str());
return false;
}
if (rpt1.size() != 8) {
printf("CIRCDDBClient::sendHeardWithTXStats:rpt1='%s' len != 8\n", rpt1.c_str());
return false;
}
if (rpt2.size() != 8) {
printf("CIRCDDBClient::sendHeardWithTXStats:rpt2='%s' len != 8\n", rpt2.c_str());
return false;
}
char str[10];
snprintf(str, 10, "%04x", num_dv_frames);
std::string stats(str);
if (num_dv_silent_frames >= 0) {
snprintf(str, 10, "%02x", (num_dv_silent_frames * 100) / num_dv_frames);
stats.append(str);
if (num_bit_errors >= 0) {
snprintf(str, 10, "%02x", (num_bit_errors * 125) / (num_dv_frames * 3));
stats.append(str);
}
}
stats.resize(20, '_');
return d->app->sendHeard(myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, std::string(" "), std::string(""), stats);
}
// Send query for a gateway/reflector, a false return implies a network error
bool CIRCDDBClient::findGateway(const std::string& gatewayCallsign)
{
if (8 != gatewayCallsign.size()) {
printf("CIRCDDBClient::findGateway:gatewayCallsign='%s' len != 8\n", gatewayCallsign.c_str());
return false;
}
std::string gw(gatewayCallsign);
CUtils::ToUpper(gw);
return d->app->findGateway(gw);
}
bool CIRCDDBClient::findRepeater(const std::string& repeaterCallsign)
{
if (8 != repeaterCallsign.size()) {
printf("CIRCDDBClient::findRepeater:repeaterCallsign='%s' len != 8\n", repeaterCallsign.c_str());
return false;
}
std::string rptr(repeaterCallsign);
CUtils::ToUpper(rptr);
return d->app->findRepeater(rptr);
}
// Send query for a user, a false return implies a network error
bool CIRCDDBClient::findUser(const std::string& userCallsign)
{
if (8 != userCallsign.size()) {
printf("CIRCDDBClient::findUser:userCall='%s' len != 8\n", userCallsign.c_str());
return false;
}
std::string usr(userCallsign);
CUtils::ToUpper(usr);
return d->app->findUser(usr);
}
// The following functions are for processing received messages
// Get the waiting message type
IRCDDB_RESPONSE_TYPE CIRCDDBClient::getMessageType()
{
return d->app->getReplyMessageType();
}
// Get a gateway message, as a result of IDRT_REPEATER returned from getMessageType()
// A false return implies a network error
bool CIRCDDBClient::receiveRepeater(std::string& repeaterCallsign, std::string& gatewayCallsign, std::string& address)
{
IRCDDB_RESPONSE_TYPE rt = d->app->getReplyMessageType();
if (rt != IDRT_REPEATER) {
printf("CIRCDDBClient::receiveRepeater: unexpected response type=%d\n", rt);
return false;
}
IRCMessage *m = d->app->getReplyMessage();
if (m == NULL) {
printf("CIRCDDBClient::receiveRepeater: no message\n");
return false;
}
if (m->getCommand().compare("IDRT_REPEATER")) {
printf("CIRCDDBClient::receiveRepeater: wrong message type, expected 'IDRT_REPEATER, got '%s'\n", m->getCommand().c_str());
delete m;
return false;
}
if (3 != m->getParamCount()) {
printf("CIRCDDBClient::receiveRepeater: unexpected number of message parameters, expected 3, got %d\n", m->getParamCount());
delete m;
return false;
}
repeaterCallsign = m->getParam(0);
gatewayCallsign = m->getParam(1);
address = m->getParam(2);
delete m;
return true;
}
// Get a gateway message, as a result of IDRT_GATEWAY returned from getMessageType()
// A false return implies a network error
bool CIRCDDBClient::receiveGateway(std::string& gatewayCallsign, std::string& address)
{
IRCDDB_RESPONSE_TYPE rt = d->app->getReplyMessageType();
if (rt != IDRT_GATEWAY) {
printf("CIRCDDBClient::receiveGateway: unexpected response type=%d\n", rt);
return false;
}
IRCMessage *m = d->app->getReplyMessage();
if (m == NULL) {
printf("CIRCDDBClient::receiveGateway: no message\n");
return false;
}
if (m->getCommand().compare("IDRT_GATEWAY")) {
printf("CIRCDDBClient::receiveGateway: wrong message type, expected 'IDRT_GATEWAY' got '%s'\n", m->getCommand().c_str());
delete m;
return false;
}
if (2 != m->getParamCount()) {
printf("CIRCDDBClient::receiveGateway: unexpected number of message parameters, expected 2, got %d\n", m->getParamCount());
delete m;
return false;
}
gatewayCallsign = m->getParam(0);
address = m->getParam(1);
delete m;
return true;
}
// Get a user message, as a result of IDRT_USER returned from getMessageType()
// A false return implies a network error
bool CIRCDDBClient::receiveUser(std::string& userCallsign, std::string& repeaterCallsign, std::string& gatewayCallsign, std::string& address)
{
std::string dummy("");
return receiveUser(userCallsign, repeaterCallsign, gatewayCallsign, address, dummy);
}
bool CIRCDDBClient::receiveUser(std::string& userCallsign, std::string& repeaterCallsign, std::string& gatewayCallsign, std::string& address, std::string& timeStamp)
{
IRCDDB_RESPONSE_TYPE rt = d->app->getReplyMessageType();
if (rt != IDRT_USER) {
printf("CIRCDDBClient::receiveUser: unexpected response type=%d\n", rt);
return false;
}
IRCMessage * m = d->app->getReplyMessage();
if (m == NULL) {
printf("CIRCDDBClient::receiveUser: no message\n");
return false;
}
if (m->getCommand().compare("IDRT_USER")) {
printf("CIRCDDBClient::receiveUser: wrong message type, expected 'IDRT_USER', got '%s'\n", m->getCommand().c_str());
delete m;
return false;
}
if (5 != m->getParamCount()) {
printf("CIRCDDBClient::receiveUser: unexpected number of message parameters, expected 5, got %d\n", m->getParamCount());
delete m;
return false;
}
userCallsign = m->getParam(0);
repeaterCallsign = m->getParam(1);
gatewayCallsign = m->getParam(2);
address = m->getParam(3);
timeStamp = m->getParam(4);
delete m;
return true;
}
void CIRCDDBClient::close() // Implictely kills any threads in the IRC code
{
d->client -> stopWork();
d->app -> stopWork();
}

@ -0,0 +1,138 @@
/*
CIRCDDB - ircDDB client library in C++
Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (C) 2011,2012 Jonathan Naylor, G4KLX
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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <vector>
#include "IRCDDB.h"
struct CIRCDDBPrivate;
class CIRCDDBClient : public CIRCDDB{
public:
CIRCDDBClient(const std::string& hostName, unsigned int port, const std::string& callsign, const std::string& password, const std::string& versionInfo,
const std::string& localAddr = std::string(""), bool isQuadNet = false);
~CIRCDDBClient();
// A false return implies a network error, or unable to log in
bool open();
// rptrQTH can be called multiple times if necessary
// callsign The callsign of the repeater
// latitude WGS84 position of antenna in degrees, positive value -> NORTH
// longitude WGS84 position of antenna in degrees, positive value -> EAST
// desc1, desc2 20-character description of QTH
// infoURL URL of a web page with information about the repeater
void rptrQTH( const std::string& callsign, double latitude, double longitude, const std::string& desc1, const std::string& desc2, const std::string& infoURL);
// rptrQRG can be called multiple times if necessary
// callsign callsign of the repeater
// txFrequency repeater TX frequency in MHz
// duplexShift duplex shift in MHz (positive or negative value): RX_freq = txFrequency + duplexShift
// range range of the repeater in meters (meters = miles * 1609.344)
// agl height of the antenna above ground in meters (meters = feet * 0.3048)
void rptrQRG( const std::string& callsign, double txFrequency, double duplexShift, double range, double agl );
// If you call this method once, watchdog messages will be sent to the
// to the ircDDB network every 15 minutes. Invoke this method every 1-2 minutes to indicate
// that the gateway is working properly. After activating the watchdog, a red LED will be displayed
// on the ircDDB web page if this method is not called within a period of about 30 minutes.
// The string wdInfo should contain information about the source of the alive messages, e.g.,
// version of the RF decoding software. For example, the ircDDB java software sets this
// to "rpm_ircddbmhd-x.z-z". The string wdInfo must contain at least one non-space character.
void kickWatchdog(const std::string& callsign, const std::string& wdInfo);
// get internal network status
int getConnectionState();
// one of these values is returned:
// 0 = not (yet) connected to the IRC server
// 1-6 = a new connection was established, download of repeater info etc. is
// in progress
// 7 = the ircDDB connection is fully operational
// 10 = some network error occured, next state is "0" (new connection attempt)
// Send heard data, a false return implies a network error
bool sendHeard(const std::string& myCall, const std::string& myCallExt, const std::string& yourCall, const std::string& rpt1, const std::string& rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3);
// same as sendHeard with two new fields:
// network_destination: empty string or 8-char call sign of the repeater
// or reflector, where this transmission is relayed to.
// tx_message: 20-char TX message or empty string, if the user did not
// send a TX message
bool sendHeardWithTXMsg(const std::string& myCall, const std::string& myCallExt, const std::string& yourCall, const std::string& rpt1, const std::string& rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, const std::string& network_destination, const std::string& tx_message);
// this method should be called at the end of a transmission
// num_dv_frames: number of DV frames sent out (96 bit frames, 20ms)
// num_dv_silent_frames: number of DV silence frames sent out in the
// last transmission, or -1 if the information is not available
// num_bit_errors: number of bit errors of the received data. This should
// be the derived from the first Golay block of the voice data. This
// error correction code only looks at 24 bits of the 96 bit frame.
// So, the overall bit error rate is calculated like this:
// BER = num_bit_errors / (num_dv_frames * 24)
// Set num_bit_errors = -1, if the error information is not available.
bool sendHeardWithTXStats(const std::string& myCall, const std::string& myCallExt, const std::string& yourCall, const std::string& rpt1, const std::string& rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, int num_dv_frames, int num_dv_silent_frames, int num_bit_errors);
// The following three functions don't block waiting for a reply, they just send the data
// Send query for a gateway/reflector, a false return implies a network error
bool findGateway(const std::string& gatewayCallsign);
// Send query for a repeater module, a false return implies a network error
bool findRepeater(const std::string& repeaterCallsign);
// Send query for a user, a false return implies a network error
bool findUser(const std::string& userCallsign);
// Support for the Smart Group Server
void sendSGSInfo(const std::string subcommand, const std::vector<std::string> parms);
// The following functions are for processing received messages
// Get the waiting message type
IRCDDB_RESPONSE_TYPE getMessageType();
// Get a gateway message, as a result of IDRT_REPEATER returned from getMessageType()
// A false return implies a network error
bool receiveRepeater(std::string& repeaterCallsign, std::string& gatewayCallsign, std::string& address);
// Get a gateway message, as a result of IDRT_GATEWAY returned from getMessageType()
// A false return implies a network error
bool receiveGateway(std::string& gatewayCallsign, std::string& address);
// Get a user message, as a result of IDRT_USER returned from getMessageType()
// A false return implies a network error
bool receiveUser(std::string& userCallsign, std::string& repeaterCallsign, std::string& gatewayCallsign, std::string& address);
bool receiveUser(std::string& userCallsign, std::string& repeaterCallsign, std::string& gatewayCallsign, std::string& address, std::string& timeStamp);
void close(); // Implictely kills any threads in the IRC code
private:
struct CIRCDDBClientPrivate * const d;
bool m_isQuadNet;
};

@ -0,0 +1,370 @@
/*
CIRCDDBClient - ircDDB client library in C++
Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (C) 2011,2012 Jonathan Naylor, G4KLX
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, see <http://www.gnu.org/licenses/>.
*/
#include "IRCDDBMultiClient.h"
#include <stdio.h>
CIRCDDBMultiClient::CIRCDDBMultiClient(const CIRCDDB_Array& clients) :
m_clients(),
m_queriesLock(),
m_responseQueueLock()
{
for (unsigned int i = 0; i < clients.size(); i++) {
if (clients[i] != NULL)
m_clients.push_back(clients[i]);
}
m_clients.shrink_to_fit();
}
CIRCDDBMultiClient::~CIRCDDBMultiClient()
{
for (unsigned int i = 0; i < m_clients.size(); i++) {
delete m_clients[i];
}
while (m_responseQueue.size() > 0) {
delete m_responseQueue[0];
m_responseQueue.erase(m_responseQueue.begin());
}
for (CIRCDDBMultiClientQuery_HashMap::iterator it = m_userQueries.begin(); it != m_userQueries.end(); it++)
delete it->second;
m_userQueries.clear();
for (CIRCDDBMultiClientQuery_HashMap::iterator it = m_repeaterQueries.begin(); it != m_repeaterQueries.end(); it++)
delete it->second;
m_repeaterQueries.clear();
for (CIRCDDBMultiClientQuery_HashMap::iterator it = m_gatewayQueries.begin(); it != m_gatewayQueries.end(); it++)
delete it->second;
m_gatewayQueries.clear();
}
bool CIRCDDBMultiClient::open()
{
bool result = true;
for (unsigned int i = 0; i < m_clients.size(); i++) {
result = m_clients[i]->open() && result;
}
if (!result) close();
return result;
}
void CIRCDDBMultiClient::rptrQTH(const std::string & callsign, double latitude, double longitude, const std::string & desc1, const std::string & desc2, const std::string & infoURL)
{
for (unsigned int i = 0; i < m_clients.size(); i++) {
m_clients[i]->rptrQTH(callsign, latitude, longitude, desc1, desc2, infoURL);
}
}
void CIRCDDBMultiClient::rptrQRG(const std::string & callsign, double txFrequency, double duplexShift, double range, double agl)
{
for (unsigned int i = 0; i < m_clients.size(); i++) {
m_clients[i]->rptrQRG(callsign, txFrequency, duplexShift, range, agl);
}
}
void CIRCDDBMultiClient::kickWatchdog(const std::string & callsign, const std::string & wdInfo)
{
for (unsigned int i = 0; i < m_clients.size(); i++) {
m_clients[i]->kickWatchdog(callsign, wdInfo);
}
}
int CIRCDDBMultiClient::getConnectionState()
{
for (unsigned int i = 0; i < m_clients.size(); i++) {
int state = m_clients[i]->getConnectionState();
if (state != 7)
return state;
}
return 7;
}
bool CIRCDDBMultiClient::sendHeard(const std::string & myCall, const std::string & myCallExt, const std::string & yourCall, const std::string & rpt1, const std::string & rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3)
{
bool result = true;
for (unsigned int i = 0; i < m_clients.size(); i++) {
result = m_clients[i]->sendHeard(myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3) && result;
}
return result;
}
bool CIRCDDBMultiClient::sendHeardWithTXMsg(const std::string & myCall, const std::string & myCallExt, const std::string & yourCall, const std::string & rpt1, const std::string & rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, const std::string & network_destination, const std::string & tx_message)
{
bool result = true;
for (unsigned int i = 0; i < m_clients.size(); i++) {
result = m_clients[i]->sendHeardWithTXMsg(myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, network_destination, tx_message) && result;
}
return result;
}
bool CIRCDDBMultiClient::sendHeardWithTXStats(const std::string & myCall, const std::string & myCallExt, const std::string & yourCall, const std::string & rpt1, const std::string & rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, int num_dv_frames, int num_dv_silent_frames, int num_bit_errors)
{
bool result = true;
for (unsigned int i = 0; i < m_clients.size(); i++) {
result = m_clients[i]->sendHeardWithTXStats(myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, num_dv_frames, num_dv_silent_frames, num_bit_errors) && result;
}
return result;
}
void CIRCDDBMultiClient::sendSGSInfo(const std::string subcommand, const std::vector<std::string> parms)
{
for (unsigned int i = 0; i < m_clients.size(); i++) {
m_clients[i]->sendSGSInfo(subcommand, parms);
}
}
bool CIRCDDBMultiClient::findGateway(const std::string & gatewayCallsign)
{
pushQuery(IDRT_GATEWAY, gatewayCallsign, new CIRCDDBMultiClientQuery("", "", gatewayCallsign, "", "", IDRT_GATEWAY));
bool result = true;
for (unsigned int i = 0; i < m_clients.size(); i++) {
result = m_clients[i]->findGateway(gatewayCallsign) && result;
}
return result;
}
bool CIRCDDBMultiClient::findRepeater(const std::string & repeaterCallsign)
{
pushQuery(IDRT_REPEATER, repeaterCallsign, new CIRCDDBMultiClientQuery("", repeaterCallsign, "", "", "", IDRT_REPEATER));
bool result = true;
for (unsigned int i = 0; i < m_clients.size(); i++) {
result = m_clients[i]->findRepeater(repeaterCallsign) && result;
}
return result;
}
bool CIRCDDBMultiClient::findUser(const std::string & userCallsign)
{
pushQuery(IDRT_USER, userCallsign, new CIRCDDBMultiClientQuery(userCallsign, "", "", "", "", IDRT_USER));
bool result = true;
for (unsigned int i = 0; i < m_clients.size(); i++) {
result = m_clients[i]->findUser(userCallsign) && result;
}
return result;
}
IRCDDB_RESPONSE_TYPE CIRCDDBMultiClient::getMessageType()
{
//procees the inner clients at each call
for (unsigned int i = 0; i < m_clients.size(); i++) {
std::string user = "", repeater = "", gateway = "", address = "", timestamp = "", key = "";
IRCDDB_RESPONSE_TYPE type = m_clients[i]->getMessageType();
switch (type) {
case IDRT_USER: {
if (!m_clients[i]->receiveUser(user, repeater, gateway, address, timestamp))
type = IDRT_NONE;
key = user;
break;
}
case IDRT_GATEWAY: {
if (!m_clients[i]->receiveGateway(gateway, address))
type = IDRT_NONE;
key = gateway;
break;
}
case IDRT_REPEATER: {
if (!m_clients[i]->receiveRepeater(repeater, gateway, address))
type = IDRT_NONE;
key = repeater;
break;
}
case IDRT_NONE: {
default:
break;
}
}
if (type != IDRT_NONE)
{
m_queriesLock.lock();
bool canAddToQueue = false;
bool wasQuery = false;
CIRCDDBMultiClientQuery * item = popQuery(type, key);
if (item != NULL) {//is this a response to a query we've sent ?
item->Update(user, repeater, gateway, address, timestamp);//update item (if needed)
canAddToQueue = (item->incrementResponseCount() >= m_clients.size()); //did all the clients respond or did we have an answer ?
wasQuery = true;
}
else {
item = new CIRCDDBMultiClientQuery(user, repeater, gateway, address, timestamp, type);
canAddToQueue = true;
}
if (canAddToQueue) {
m_responseQueueLock.lock();
m_responseQueue.push_back(item);
m_responseQueueLock.unlock();
}
else if (wasQuery)
pushQuery(type, key, item);
m_queriesLock.unlock();
}
}
IRCDDB_RESPONSE_TYPE result = IDRT_NONE;
m_responseQueueLock.lock();
if (m_responseQueue.size() != 0) result = m_responseQueue[0]->getType();
m_responseQueueLock.unlock();
return result;
}
bool CIRCDDBMultiClient::receiveRepeater(std::string & repeaterCallsign, std::string & gatewayCallsign, std::string & address)
{
CIRCDDBMultiClientQuery * item = checkAndGetNextResponse(IDRT_REPEATER, "CIRCDDBMultiClient::receiveRepeater: unexpected response type");
if (item == NULL)
return false;
repeaterCallsign = item->getRepeater();
gatewayCallsign = item->getGateway();
address = item->getAddress();
delete item;
return true;
}
bool CIRCDDBMultiClient::receiveGateway(std::string & gatewayCallsign, std::string & address)
{
CIRCDDBMultiClientQuery * item = checkAndGetNextResponse(IDRT_GATEWAY, "CIRCDDBMultiClient::receiveGateway: unexpected response type");
if (item == NULL)
return false;
gatewayCallsign = item->getGateway();
address = item->getAddress();
delete item;
return true;
}
bool CIRCDDBMultiClient::receiveUser(std::string & userCallsign, std::string & repeaterCallsign, std::string & gatewayCallsign, std::string & address)
{
std::string dummy;
return receiveUser(userCallsign, repeaterCallsign, gatewayCallsign, address, dummy);
}
bool CIRCDDBMultiClient::receiveUser(std::string & userCallsign, std::string & repeaterCallsign, std::string & gatewayCallsign, std::string & address, std::string & timeStamp)
{
CIRCDDBMultiClientQuery * item = checkAndGetNextResponse(IDRT_USER, "CIRCDDBMultiClient::receiveUser: unexpected response type");
if (item == NULL) {
//wxLogMessage(wxT("CIRCDDBMultiClient::receiveUser NO USER IN QUEUE"));
return false;
}
//wxLogMessage(wxT("CIRCDDBMultiClient::receiveUser : %s"), item->toString());
userCallsign = item->getUser();
repeaterCallsign = item->getRepeater();
gatewayCallsign = item->getGateway();
address = item->getAddress();
timeStamp = item->getTimestamp();
delete item;
return true;
}
void CIRCDDBMultiClient::close()
{
for (unsigned int i = 0; i < m_clients.size(); i++) {
m_clients[i]->close();
}
}
CIRCDDBMultiClientQuery * CIRCDDBMultiClient::checkAndGetNextResponse(IRCDDB_RESPONSE_TYPE expectedType, std::string errorMessage)
{
CIRCDDBMultiClientQuery * item = NULL;
m_responseQueueLock.lock();
if (m_responseQueue.size() == 0 || m_responseQueue[0]->getType() != expectedType) {
printf(errorMessage.c_str());
}
else {
item = m_responseQueue[0];
m_responseQueue.erase(m_responseQueue.begin());
}
m_responseQueueLock.unlock();
return item;
}
void CIRCDDBMultiClient::pushQuery(IRCDDB_RESPONSE_TYPE type, const std::string& key, CIRCDDBMultiClientQuery * query)
{
CIRCDDBMultiClientQuery_HashMap * queries = getQueriesHashMap(type);
m_queriesLock.lock();
if (queries != NULL && (*queries)[key] == NULL)
(*queries)[key] = query;
else
delete query;
m_queriesLock.unlock();
}
CIRCDDBMultiClientQuery * CIRCDDBMultiClient::popQuery(IRCDDB_RESPONSE_TYPE type, const std::string & key)
{
CIRCDDBMultiClientQuery_HashMap * queries = getQueriesHashMap(type);
m_queriesLock.lock();
CIRCDDBMultiClientQuery * item = NULL;
if (queries != NULL && queries->count(key) != 0) {
item = queries->at(key);
queries->erase(key);
}
m_queriesLock.unlock();
return item;
}
CIRCDDBMultiClientQuery_HashMap * CIRCDDBMultiClient::getQueriesHashMap(IRCDDB_RESPONSE_TYPE type)
{
switch (type)
{
case IDRT_USER:
return &m_userQueries;
case IDRT_GATEWAY:
return &m_gatewayQueries;
case IDRT_REPEATER:
return &m_repeaterQueries;
case IDRT_NONE:
default:
return NULL;
}
}

@ -0,0 +1,177 @@
/*
CIRCDDB - ircDDB client library in C++
Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (C) 2011,2012 Jonathan Naylor, G4KLX
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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include "IRCDDB.h"
#include <string>
#include <vector>
#include <map>
#include <sstream>
#include <mutex>
//Small data container to keep track of queries with sent to the inner clients
class CIRCDDBMultiClientQuery
{
public:
CIRCDDBMultiClientQuery(const std::string& user,
const std::string& repeater,
const std::string& gateway,
const std::string& address,
const std::string& timestamp,
IRCDDB_RESPONSE_TYPE type) :
m_user(user),
m_repeater(repeater),
m_gateway(gateway),
m_address(address),
m_timestamp(timestamp),
m_type(type),
m_responseCount(0)
{
}
std::string getUser() const
{
return m_user;
}
std::string getRepeater() const
{
return m_repeater;
}
std::string getGateway() const
{
return m_gateway;
}
std::string getAddress() const
{
return m_address;
}
std::string getTimestamp() const
{
return m_timestamp;
}
unsigned int getResponseCount()
{
return m_responseCount;
}
unsigned int incrementResponseCount()
{
++m_responseCount;
//wxLogMessage(wxT("Resp Count : %s %d"), toString(), m_responseCount);
return m_responseCount;
}
/*
Updates the entry, but only if the timestamp is newer. if an address was already specified it is kept.
*/
void Update(const std::string& user, const std::string& repeater, const std::string& gateway, const std::string& address, const std::string& timestamp)
{
//wxLogMessage(wxT("Before : %s"), toString());
if (timestamp.empty() || timestamp.compare(m_timestamp) >= 0) {
m_user = user;
m_repeater = repeater;
m_gateway = gateway;
m_timestamp = timestamp;
if(m_address.empty() && !address.empty())
m_address = address;
}
//wxLogMessage(wxT("After : %s"), toString());
}
IRCDDB_RESPONSE_TYPE getType()
{
return m_type;
}
std::string toString()
{
std::stringstream strStream;
strStream << m_user << " " << m_repeater << " " << m_gateway << " " << m_address << " " << m_timestamp;
return strStream.str();
}
private:
std::string m_user;
std::string m_repeater;
std::string m_gateway;
std::string m_address;
std::string m_timestamp;
IRCDDB_RESPONSE_TYPE m_type;
unsigned int m_responseCount;
};
typedef std::map<std::string, CIRCDDBMultiClientQuery*> CIRCDDBMultiClientQuery_HashMap;
typedef std::vector<CIRCDDBMultiClientQuery*> CIRCDDBMultiClientQuery_Array;
class CIRCDDBMultiClient : public CIRCDDB
{
public:
CIRCDDBMultiClient(const CIRCDDB_Array& clients);
~CIRCDDBMultiClient();
// Inherited via CIRCDDB
virtual bool open();
virtual void rptrQTH(const std::string & callsign, double latitude, double longitude, const std::string & desc1, const std::string & desc2, const std::string & infoURL);
virtual void rptrQRG(const std::string & callsign, double txFrequency, double duplexShift, double range, double agl);
virtual void kickWatchdog(const std::string & callsign, const std::string & wdInfo);
virtual int getConnectionState() ;
virtual bool sendHeard(const std::string & myCall, const std::string & myCallExt, const std::string & yourCall, const std::string & rpt1, const std::string & rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3);
virtual bool sendHeardWithTXMsg(const std::string & myCall, const std::string & myCallExt, const std::string & yourCall, const std::string & rpt1, const std::string & rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, const std::string & network_destination, const std::string & tx_message);
virtual bool sendHeardWithTXStats(const std::string & myCall, const std::string & myCallExt, const std::string & yourCall, const std::string & rpt1, const std::string & rpt2, unsigned char flag1, unsigned char flag2, unsigned char flag3, int num_dv_frames, int num_dv_silent_frames, int num_bit_errors);
virtual bool findGateway(const std::string & gatewayCallsign);
virtual bool findRepeater(const std::string & repeaterCallsign);
virtual bool findUser(const std::string & userCallsign);
virtual IRCDDB_RESPONSE_TYPE getMessageType();
virtual bool receiveRepeater(std::string & repeaterCallsign, std::string & gatewayCallsign, std::string & address);
virtual bool receiveGateway(std::string & gatewayCallsign, std::string & address);
virtual bool receiveUser(std::string & userCallsign, std::string & repeaterCallsign, std::string & gatewayCallsign, std::string & address);
virtual bool receiveUser(std::string & userCallsign, std::string & repeaterCallsign, std::string & gatewayCallsign, std::string & address, std::string & timeStamp);
virtual void sendSGSInfo(const std::string subcommand, const std::vector<std::string> parms);
virtual void close();
//
private :
CIRCDDB_Array m_clients;
std::recursive_mutex m_queriesLock;
std::recursive_mutex m_responseQueueLock;
CIRCDDBMultiClientQuery_HashMap m_userQueries;
CIRCDDBMultiClientQuery_HashMap m_repeaterQueries;
CIRCDDBMultiClientQuery_HashMap m_gatewayQueries;
CIRCDDBMultiClientQuery_Array m_responseQueue;
CIRCDDBMultiClientQuery * checkAndGetNextResponse(IRCDDB_RESPONSE_TYPE expectedType, std::string errorMessage);
void pushQuery(IRCDDB_RESPONSE_TYPE type, const std::string& key, CIRCDDBMultiClientQuery * query);
CIRCDDBMultiClientQuery * popQuery(IRCDDB_RESPONSE_TYPE type, const std::string& key);
CIRCDDBMultiClientQuery_HashMap * getQueriesHashMap(IRCDDB_RESPONSE_TYPE type);
};

@ -0,0 +1,131 @@
/*
CIRCDDB - ircDDB client library in C++
Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (c) 2017 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, see <http://www.gnu.org/licenses/>.
*/
#include "IRCMessage.h"
#include "Utils.h"
IRCMessage::IRCMessage()
{
numParams = 0;
prefixParsed = false;
}
IRCMessage::IRCMessage(const std::string& toNick, const std::string& msg)
{
command.assign("PRIVMSG");
numParams = 2;
params.push_back(toNick);
params.push_back(msg);
prefixParsed = false;
}
IRCMessage::IRCMessage(const std::string& cmd)
{
command = cmd;
numParams = 0;
prefixParsed = false;
}
IRCMessage::~IRCMessage()
{
}
void IRCMessage::addParam(const std::string& p)
{
params.push_back(p);
numParams = params.size();
}
int IRCMessage::getParamCount()
{
return params.size();
}
std::string IRCMessage::getParam(int pos)
{
return params[pos];
}
std::string IRCMessage::getCommand()
{
return command;
}
bool IRCMessage::parsePrefix()
{
std::string::size_type p1 = prefix.find('!');
if (std::string::npos == p1)
return false;
std::string::size_type p2 = prefix.find('@');
if (std::string::npos == p2)
return false;
prefixComponents.push_back(prefix.substr(0, p1));
prefixComponents.push_back(prefix.substr(p1+1, p2-p1-1));
prefixComponents.push_back(prefix.substr(p2 + 1));
return true;
}
std::string& IRCMessage::getPrefixNick()
{
if (!prefixParsed)
prefixParsed = parsePrefix();
return prefixParsed ? prefixComponents[0] : prefix;
}
std::string& IRCMessage::getPrefixName()
{
if (!prefixParsed)
prefixParsed = parsePrefix();
return prefixParsed ? prefixComponents[1] : prefix;
}
std::string& IRCMessage::getPrefixHost()
{
if (!prefixParsed)
prefixParsed = parsePrefix();
return prefixParsed ? prefixComponents[2] : prefix;
}
void IRCMessage::composeMessage(std::string& output)
{
std::string o;
if (prefix.size() > 0)
o = std::string(":") + prefix + std::string(" ");
o.append(command);
for (int i=0; i < numParams; i++) {
if (i == (numParams - 1))
o.append(std::string(" :") + params[i]);
else
o.append(std::string(" ") + params[i]);
}
o.append(std::string("\r\n"));
output = o;
}

@ -0,0 +1,53 @@
/*
CIRCDDB - ircDDB client library in C++
Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (c) 2017 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <vector>
class IRCMessage
{
public:
IRCMessage();
IRCMessage(const std::string& toNick, const std::string& msg);
IRCMessage(const std::string& command);
~IRCMessage();
std::string prefix;
std::string command;
std::vector<std::string> params;
int numParams;
std::string& getPrefixNick();
std::string& getPrefixName();
std::string& getPrefixHost();
void composeMessage(std::string& output);
void addParam(const std::string& p);
std::string getCommand();
std::string getParam(int pos);
int getParamCount();
private:
bool parsePrefix();
std::vector<std::string> prefixComponents;
bool prefixParsed;
};

@ -0,0 +1,86 @@
/*
CIRCDDB - ircDDB client library in C++
Based on code by:
Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Completely rewritten by:
Copyright (c) 2017 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, see <http://www.gnu.org/licenses/>.
*/
#include "IRCMessageQueue.h"
IRCMessageQueue::IRCMessageQueue()
{
m_eof = false;
}
IRCMessageQueue::~IRCMessageQueue()
{
accessMutex.lock();
while (! m_queue.empty()) {
delete m_queue.front();
m_queue.pop();
}
accessMutex.unlock();
}
bool IRCMessageQueue::isEOF()
{
return m_eof;
}
void IRCMessageQueue::signalEOF()
{
m_eof = true;
}
bool IRCMessageQueue::messageAvailable()
{
accessMutex.lock();
bool retv = ! m_queue.empty();
accessMutex.unlock();
return retv;
}
IRCMessage *IRCMessageQueue::peekFirst()
{
accessMutex.lock();
IRCMessage *msg = m_queue.empty() ? NULL : m_queue.front();
accessMutex.unlock();
return msg;
}
IRCMessage *IRCMessageQueue::getMessage()
{
accessMutex.lock();
IRCMessage *msg = m_queue.empty() ? NULL : m_queue.front();
if (msg)
m_queue.pop();
accessMutex.unlock();
return msg;
}
void IRCMessageQueue::putMessage(IRCMessage *m)
{
accessMutex.lock();
m_queue.push(m);
accessMutex.unlock();
}

@ -0,0 +1,49 @@
/*
CIRCDDB - ircDDB client library in C++
Based on original code by:
Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Completely rewritten by:
Copyright (c) 2017 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <mutex>
#include <queue>
#include "IRCMessage.h"
class IRCMessageQueue
{
public:
IRCMessageQueue();
~IRCMessageQueue();
bool isEOF();
void signalEOF();
bool messageAvailable();
IRCMessage *getMessage();
IRCMessage *peekFirst();
void putMessage(IRCMessage *m);
private:
bool m_eof;
std::mutex accessMutex;
std::queue<IRCMessage *> m_queue;
};

@ -0,0 +1,314 @@
/*
CIRCDDB - ircDDB client library in C++
Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (c) 2017 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, see <http://www.gnu.org/licenses/>.
*/
#include <regex>
#include "IRCProtocol.h"
#include "Utils.h"
#define CIRCDDB_VERSION "2.0.0"
IRCProtocol::IRCProtocol(IRCApplication *app, const std::string& callsign, const std::string& password, const std::string& channel, const std::string& versionInfo)
{
m_password = password;
m_channel = channel;
m_app = app;
m_versionInfo.assign("CIRCDDB:");
m_versionInfo.append(CIRCDDB_VERSION);
if (versionInfo.size()) {
m_versionInfo.push_back(' ');
m_versionInfo.append(versionInfo);
}
std::string::size_type hyphenPos = callsign.find('-');
if (hyphenPos == std::string::npos) {
m_nicks[0] = callsign + std::string("-1");
m_nicks[1] = callsign + std::string("-2");
m_nicks[2] = callsign + std::string("-3");
m_nicks[3] = callsign + std::string("-4");
} else {
m_nicks[0] = callsign;
m_nicks[1] = callsign;
m_nicks[2] = callsign;
m_nicks[3] = callsign;
}
m_name = callsign;
m_pingTimer = 60; // 30 seconds
m_state = 0;
m_timer = 0;
chooseNewNick();
}
IRCProtocol::~IRCProtocol()
{
}
void IRCProtocol::chooseNewNick()
{
int r = rand() % 4;
m_currentNick = m_nicks[r];
}
void IRCProtocol::setNetworkReady(bool b)
{
if (b == true) {
if (0 != m_state)
printf("IRCProtocol::setNetworkReady: unexpected state\n");
m_state = 1;
chooseNewNick();
} else
m_state = 0;
}
bool IRCProtocol::processQueues(IRCMessageQueue *recvQ, IRCMessageQueue *sendQ)
{
if (m_timer > 0)
m_timer--;
while (recvQ->messageAvailable()) {
IRCMessage *m = recvQ->getMessage();
if (0 == m->command.compare("004")) {
if (4 == m_state) {
if (m->params.size() > 1) {
std::regex serverNamePattern("^grp[1-9]s[1-9].ircDDB$");
if (std::regex_match(m->params[1], serverNamePattern))
m_app->setBestServer(std::string("s-") + m->params[1].substr(0,6));
}
m_state = 5; // next: JOIN
m_app->setCurrentNick(m_currentNick);
}
} else if (0 == m->command.compare("PING")) {
IRCMessage *m2 = new IRCMessage();
m2->command = std::string("PONG");
if (m->params.size() > 0) {
m2->numParams = 1;
m2->params.push_back(m->params[0]);
}
sendQ -> putMessage(m2);
} else if (0 == m->command.compare("JOIN")) {
if (m->numParams>=1 && 0==m->params[0].compare(m_channel)) {
if (0==m->getPrefixNick().compare(m_currentNick) && 6==m_state) {
if (m_debugChannel.size())
m_state = 7; // next: join debug_channel
else
m_state = 10; // next: WHO *
} else if (m_app)
m_app->userJoin(m->getPrefixNick(), m->getPrefixName(), m->getPrefixHost());
}
if (m->numParams>=1 && 0==m->params[0].compare(m_debugChannel)) {
if (0==m->getPrefixNick().compare(m_currentNick) && 8==m_state)
m_state = 10; // next: WHO *
}
} else if (0 == m->command.compare("PONG")) {
if (12 == m_state) {
m_timer = m_pingTimer;
m_state = 11;
}
} else if (0 == m->command.compare("PART")) {
if (m->numParams>=1 && 0==m->params[0].compare(m_channel)) {
if (m_app != NULL)
m_app->userLeave(m->getPrefixNick());
}
} else if (0 == m->command.compare("KICK")) {
if (m->numParams>=2 && 0==m->params[0].compare(m_channel)) {
if (0 == m->params[1].compare(m_currentNick)) {
// i was kicked!!
delete m;
return false;
} else if (m_app)
m_app->userLeave(m->params[1]);
}
} else if (0 == m->command.compare("QUIT")) {
if (m_app)
m_app->userLeave(m->getPrefixNick());
} else if (0 == m->command.compare("MODE")) {
if (m->numParams>=3 && 0==m->params[0].compare(m_channel)) {
if (m_app) {
std::string mode = m->params[1];
for (size_t i=1; i<mode.size() && (size_t)m->numParams>=i+2; i++) {
if ('o' == mode[i]) {
if ('+' == mode[0])
m_app->userChanOp(m->params[i+1], true);
else if ('-' == mode[0])
m_app->userChanOp(m->params[i+1], false);
}
} // for
}
}
} else if (0 == m->command.compare("PRIVMSG")) {
if (m->numParams==2 && m_app) {
if (0 == m->params[0].compare(m_channel) && m_app)
m_app->msgChannel(m);
else if (0 == m->params[0].compare(m_currentNick) && m_app)
m_app->msgQuery(m);
}
} else if (0 == m->command.compare("352")) { // WHO list
if (m->numParams>=7 && 0==m->params[0].compare(m_currentNick) && 0==m->params[1].compare(m_channel)) {
if (m_app) {
m_app->userJoin(m->params[5], m->params[2], m->params[3]);
m_app->userChanOp(m->params[5], 0==m->params[6].compare("H@"));
}
}
} else if (0 == m->command.compare("433")) { // nick collision
if (2 == m_state) {
m_state = 3; // nick collision, choose new nick
m_timer = 10; // wait 5 seconds..
}
} else if (0==m->command.compare("332") || 0==m->command.compare("TOPIC")) { // topic
if (2==m->numParams && m_app && 0==m->params[0].compare(m_channel))
m_app->setTopic(m->params[1]);
}
delete m;
}
IRCMessage *m;
switch (m_state) {
case 1:
m = new IRCMessage();
m->command = std::string("PASS");
m->numParams = 1;
m->params.push_back(m_password);
sendQ->putMessage(m);
m = new IRCMessage();
m->command = std::string("NICK");
m->numParams = 1;
m->params.push_back(m_currentNick);
sendQ->putMessage(m);
m_timer = 10; // wait for possible nick collision message
m_state = 2;
break;
case 2:
if (0 == m_timer) {
m = new IRCMessage();
m->command = std::string("USER");
m->numParams = 4;
m->params.push_back(m_name);
m->params.push_back(std::string("0"));
m->params.push_back(std::string("*"));
m->params.push_back(m_versionInfo);
sendQ->putMessage(m);
m_timer = 30;
m_state = 4; // wait for login message
}
break;
case 3:
if (0 == m_timer) {
chooseNewNick();
m = new IRCMessage();
m->command = std::string("NICK");
m->numParams = 1;
m->params.push_back(m_currentNick);
sendQ->putMessage(m);
m_timer = 10; // wait for possible nick collision message
m_state = 2;
}
break;
case 4:
if (0 == m_timer) // no login message received -> disconnect
return false;
break;
case 5:
m = new IRCMessage();
m->command = std::string("JOIN");
m->numParams = 1;
m->params.push_back(m_channel);
sendQ->putMessage(m);
m_timer = 30;
m_state = 6; // wait for join message
break;
case 6:
if (0 == m_timer) // no join message received -> disconnect
return false;
break;
case 7:
if (0 == m_debugChannel.size())
return false; // this state cannot be processed if there is no debug_channel
m = new IRCMessage();
m->command = std::string("JOIN");
m->numParams = 1;
m->params.push_back(m_debugChannel);
sendQ->putMessage(m);
m_timer = 30;
m_state = 8; // wait for join message
break;
case 8:
if (0 == m_timer) // no join message received -> disconnect
return false;
break;
case 10:
m = new IRCMessage();
m->command = std::string("WHO");
m->numParams = 2;
m->params.push_back(m_channel);
m->params.push_back(std::string("*"));
sendQ->putMessage(m);
m_timer = m_pingTimer;
m_state = 11; // wait for timer and then send ping
if (m_app)
m_app->setSendQ(sendQ); // this switches the application on
break;
case 11:
if (0 == m_timer) {
m = new IRCMessage();
m->command = std::string("PING");
m->numParams = 1;
m->params.push_back(m_currentNick);
sendQ->putMessage(m);
m_timer = m_pingTimer;
m_state = 12; // wait for pong
}
break;
case 12:
if (0 == m_timer) // no pong message received -> disconnect
return false;
break;
}
return true;
}

@ -0,0 +1,56 @@
/*
CIRCDDB - ircDDB client library in C++
Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (c) 2017 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <string>
#include <array>
#include "IRCMessageQueue.h"
#include "IRCApplication.h"
class IRCProtocol
{
public:
IRCProtocol (IRCApplication * app, const std::string& callsign, const std::string& password, const std::string& channel, const std::string& versionInfo);
~IRCProtocol();
void setNetworkReady(bool state);
bool processQueues(IRCMessageQueue *recvQ, IRCMessageQueue *sendQ);
private:
void chooseNewNick();
std::array<std::string, 4> m_nicks;
std::string m_password;
std::string m_channel;
std::string m_name;
std::string m_currentNick;
std::string m_versionInfo;
std::string m_debugChannel;
int m_state;
int m_timer;
int m_pingTimer;
IRCApplication *m_app;
};

@ -0,0 +1,160 @@
/*
CIRCDDB - ircDDB client library in C++
Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (c) 2017 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, see <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <sys/socket.h>
#include "IRCReceiver.h"
#include "Utils.h"
IRCReceiver::IRCReceiver(int sock, IRCMessageQueue *q)
{
m_sock = sock;
m_recvQ = q;
}
IRCReceiver::~IRCReceiver()
{
}
void IRCReceiver::startWork()
{
m_terminateThread = false;
m_future = std::async(std::launch::async, &IRCReceiver::Entry, this);
}
void IRCReceiver::stopWork()
{
m_terminateThread = true;
m_future.get();
}
static int doRead(int sock, char *buf, int buf_size)
{
struct timeval tv;
tv.tv_sec = 1;
tv.tv_usec = 0;
fd_set rdset;
fd_set errset;
FD_ZERO(&rdset);
FD_ZERO(&errset);
FD_SET(sock, &rdset);
FD_SET(sock, &errset);
int res = select(sock+1, &rdset, NULL, &errset, &tv);
if (res < 0) {
printf("IRCReceiver::doRead: select\n");
return -1;
} else if (res > 0) {
if (FD_ISSET(sock, &errset)) {
printf("IRCReceiver::doRead: select (FD_ISSET(sock, exceptfds))\n");
return -1;
}
if (FD_ISSET(sock, &rdset)) {
res = recv(sock, buf, buf_size, 0);
if (res < 0) {
printf("IRCReceiver::doRead: read\n");
return -1;
} else if (res == 0) {
printf("IRCReceiver::doRead: EOF read==0\n");
return -1;
} else
return res;
}
}
return 0;
}
void IRCReceiver::Entry()
{
IRCMessage *m = new IRCMessage();
int state = 0;
while (! m_terminateThread) {
char buf[200];
int r = doRead(m_sock, buf, sizeof buf);
if (r < 0) {
m_recvQ->signalEOF();
delete m; // delete unfinished IRCMessage
break;
}
for (int i=0; i < r; i++) {
char b = buf[i];
if (b > 0) {
if (b == '\n') {
m_recvQ->putMessage(m);
m = new IRCMessage();
state = 0;
}
else if (b != '\r') {
switch (state) {
case 0: // command
if (b == ':')
state = 1; // prefix
else if (b != ' ') {
m->command.push_back(b);
state = 2; // command
}
break;
case 1: // prefix
if (b == ' ')
state = 2; // command is next
else
m->prefix.push_back(b);
break;
case 2:
if (b == ' ') {
state = 3; // params are next
m->numParams = 1;
m->params.push_back(std::string(""));
} else
m->command.push_back(b);
break;
case 3:
if (b == ' ') {
m->numParams++;
if (m->numParams >= 15)
state = 5; // ignore the rest
m->params.push_back(std::string(""));
} else if (b==':' && m->params[m->numParams-1].size()==0)
state = 4; // rest of line is this param
else
m->params[m->numParams-1].push_back(b);
break;
case 4:
m->params[m->numParams-1].push_back(b);
break;
} // switch
}
} // if
} // for
} // while
return;
}

@ -0,0 +1,45 @@
/*
CIRCDDB - ircDDB client library in C++
Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de)
Copyright (C) 2012 Jonathan Naylor, G4KLX
Copyright (c) 2017 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, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <future>
#include "IRCMessageQueue.h"
class IRCReceiver
{
public:
IRCReceiver(int sock, IRCMessageQueue *q);
~IRCReceiver();
void startWork();
void stopWork();
protected:
void Entry();
private:
bool m_terminateThread;
int m_sock;
IRCMessageQueue *m_recvQ;
std::future<void> m_future;
};

@ -0,0 +1,59 @@
# Copyright (c) 2017 by Thomas A. Early N7TAE
# if you change these locations, make sure the sgs.service file is updated!
BINDIR=/usr/local/bin
CFGDIR=/usr/local/etc
# choose this if you want debugging help
#CPPFLAGS=-g -ggdb -W -Wall -std=c++11 -DCFG_DIR=\"$(CFGDIR)\"
# or, you can choose this for a much smaller executable without debugging help
CPPFLAGS=-W -Wall -std=c++11 -DCFG_DIR=\"$(CFGDIR)\"
SRCS = $(wildcard *.cpp)
OBJS = $(SRCS:.cpp=.o)
DEPS = $(SRCS:.cpp=.d)
sgs : GitVersion.h $(OBJS)
g++ $(CPPFLAGS) -o sgs $(OBJS) -lconfig++ -pthread
%.o : %.cpp
g++ $(CPPFLAGS) -MMD -MD -c $< -o $@
.PHONY: clean
clean:
$(RM) GitVersion.h $(OBJS) $(DEPS) sgs
-include $(DEPS)
# install, uninstall and removehostfiles need root priviledges
newhostfiles :
/usr/bin/wget http://www.pistar.uk/downloads/DExtra_Hosts.txt && sudo /bin/mv -f DExtra_Hosts.txt $(CFGDIR)
/usr/bin/wget http://www.pistar.uk/downloads/DCS_Hosts.txt && sudo /bin/mv -f DCS_Hosts.txt $(CFGDIR)
install : sgs
/bin/cp -f sgs.cfg $(CFGDIR)
/bin/cp -f sgs $(BINDIR)
/bin/cp -f sgs.service /lib/systemd/system
systemctl enable sgs.service
systemctl daemon-reload
systemctl start sgs.service
uninstall :
systemctl stop sgs.service
systemctl disable sgs.service
/bin/rm -f /lib/systemd/system/sgs.service
systemctl daemon-reload
/bin/rm -f $(BINDIR)/sgs
/bin/rm -f $(CFGDIR)/sgs.cfg
removehostfiles :
/bin/rm -f $(CFGDIR)/DExtra_Hosts.txt
/bin/rm -f $(CFGDIR)/DCS_Hosts.txt
GitVersion.h:
ifneq ("$(wildcard .git/index)","")
echo "const char *gitversion = \"$(shell git rev-parse HEAD)\";" > $@
else
echo "const char *gitversion = \"0000000000000000000000000000000000000000\";" > $@
endif

@ -0,0 +1,290 @@
/*
* Copyright (C) 2010,2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <cassert>
#include <cstring>
#include "PollData.h"
#include "DStarDefines.h"
#include "Utils.h"
CPollData::CPollData(const std::string& data1, const std::string& data2, DIRECTION direction, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) :
m_data1(data1),
m_data2(data2),
m_direction(direction),
m_dongle(false),
m_length(0U),
m_yourAddress(yourAddress),
m_yourPort(yourPort),
m_myPort(myPort)
{
assert(yourPort > 0U);
}
CPollData::CPollData(const std::string& data, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) :
m_data1(data),
m_data2(),
m_direction(DIR_OUTGOING),
m_dongle(false),
m_length(0U),
m_yourAddress(yourAddress),
m_yourPort(yourPort),
m_myPort(myPort)
{
assert(yourPort > 0U);
}
CPollData::CPollData(const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort) :
m_data1(),
m_data2(),
m_direction(DIR_OUTGOING),
m_dongle(false),
m_length(0U),
m_yourAddress(yourAddress),
m_yourPort(yourPort),
m_myPort(myPort)
{
assert(yourPort > 0U);
}
CPollData::CPollData() :
m_data1(),
m_data2(),
m_direction(DIR_OUTGOING),
m_dongle(false),
m_length(0U),
m_yourAddress(),
m_yourPort(0U),
m_myPort(0U)
{
}
CPollData::~CPollData()
{
}
bool CPollData::setDExtraData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(length >= 9U);
assert(yourPort > 0U);
m_data1 = std::string((const char*)data);
m_data1.resize(LONG_CALLSIGN_LENGTH, ' ');
m_dongle = data[LONG_CALLSIGN_LENGTH] != 0x00;
m_length = length;
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
bool CPollData::setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(yourPort > 0U);
std::string sdata((const char *)data);
switch (length) {
case 17U:
m_data1 = sdata.substr(0, LONG_CALLSIGN_LENGTH);
m_data2 = sdata.substr(9, LONG_CALLSIGN_LENGTH);
m_length = length;
m_direction = DIR_INCOMING;
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
break;
case 22U:
m_data1 = sdata.substr(0, LONG_CALLSIGN_LENGTH);
m_data2 = sdata.substr(9, LONG_CALLSIGN_LENGTH - 1U);
m_data2.push_back(sdata[17]);
m_length = length;
m_direction = DIR_OUTGOING;
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
break;
}
return true;
}
bool CPollData::setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(data != NULL);
assert(length >= 25U);
assert(yourPort > 0U);
m_data1 = std::string((const char*)data);
m_data1.resize(25);
m_length = length;
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
bool CPollData::setDPlusData(const unsigned char* /*data*/, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort)
{
assert(yourPort > 0U);
m_length = length;
m_yourAddress = yourAddress;
m_yourPort = yourPort;
m_myPort = myPort;
return true;
}
unsigned int CPollData::getDExtraData(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 9U);
::memset(data, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < m_data1.size() && i < LONG_CALLSIGN_LENGTH; i++)
data[i] = m_data1.at(i);
data[LONG_CALLSIGN_LENGTH] = 0x00;
return 9U;
}
unsigned int CPollData::getDCSData(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 22U);
if (m_direction == DIR_OUTGOING) {
::memset(data, ' ', 17U);
for (unsigned int i = 0U; i < m_data1.size() && i < LONG_CALLSIGN_LENGTH; i++)
data[i + 0U] = m_data1.at(i);
data[8U] = 0x00U;
for (unsigned int i = 0U; i < m_data2.size() && i < LONG_CALLSIGN_LENGTH; i++)
data[i + 9U] = m_data2.at(i);
return 17U;
} else {
::memset(data, ' ', 22U);
for (unsigned int i = 0U; i < m_data1.size() && i < LONG_CALLSIGN_LENGTH; i++)
data[i + 0U] = m_data1.at(i);
for (unsigned int i = 0U; i < m_data2.size() && i < (LONG_CALLSIGN_LENGTH - 1U); i++)
data[i + 9U] = m_data2.at(i);
if (m_data2.size() >= LONG_CALLSIGN_LENGTH)
data[17U] = m_data2.at(LONG_CALLSIGN_LENGTH - 1U);
data[18U] = 0x0AU;
data[19U] = 0x00U;
return 22U;
}
}
unsigned int CPollData::getCCSData(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 25U);
::memset(data, ' ', 25U);
for (unsigned int i = 0U; i < m_data1.size() && i < LONG_CALLSIGN_LENGTH; i++)
data[i + 0U] = m_data1.at(i);
if (m_data2.size()) {
for (unsigned int i = 0U; i < m_data2.size() && i < LONG_CALLSIGN_LENGTH; i++)
data[i + 8U] = m_data2.at(i);
}
return 25U;
}
unsigned int CPollData::getDPlusData(unsigned char *data, unsigned int length) const
{
assert(data != NULL);
assert(length >= 3U);
data[0U] = 0x03;
data[1U] = 0x60;
data[2U] = 0x00;
return 3U;
}
std::string CPollData::getData1() const
{
return m_data1;
}
void CPollData::setData1(const std::string& data)
{
m_data1 = data;
}
std::string CPollData::getData2() const
{
return m_data2;
}
void CPollData::setData2(const std::string& data)
{
m_data2 = data;
}
bool CPollData::isDongle() const
{
return m_dongle;
}
in_addr CPollData::getYourAddress() const
{
return m_yourAddress;
}
unsigned int CPollData::getYourPort() const
{
return m_yourPort;
}
unsigned int CPollData::getMyPort() const
{
return m_myPort;
}
DIRECTION CPollData::getDirection() const
{
return m_direction;
}
unsigned int CPollData::getLength() const
{
return m_length;
}

@ -0,0 +1,70 @@
/*
* Copyright (C) 2010,2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <netinet/in.h>
#include "Defs.h"
class CPollData {
public:
CPollData(const std::string& data1, const std::string& data2, DIRECTION direction, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U);
CPollData(const std::string& data, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U);
CPollData(const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort = 0U);
CPollData();
~CPollData();
bool setDExtraData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setDPlusData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setDCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
bool setCCSData(const unsigned char* data, unsigned int length, const in_addr& yourAddress, unsigned int yourPort, unsigned int myPort);
unsigned int getDExtraData(unsigned char* data, unsigned int length) const;
unsigned int getDPlusData(unsigned char* data, unsigned int length) const;
unsigned int getDCSData(unsigned char* data, unsigned int length) const;
unsigned int getCCSData(unsigned char* data, unsigned int length) const;
std::string getData1() const;
void setData1(const std::string& data);
std::string getData2() const;
void setData2(const std::string& data);
bool isDongle() const;
in_addr getYourAddress() const;
unsigned int getYourPort() const;
unsigned int getMyPort() const;
DIRECTION getDirection() const;
unsigned int getLength() const;
private:
std::string m_data1;
std::string m_data2;
DIRECTION m_direction;
bool m_dongle;
unsigned int m_length;
in_addr m_yourAddress;
unsigned int m_yourPort;
unsigned int m_myPort;
};

@ -0,0 +1,74 @@
smart-group-server
==================
## Introduction
This smart-group-server is based on an original idea by John Hays K7VE for a routing group server he called **STARnet Digital**. This idea was first coded by Jonathan G4KLX and he called the resulting program **StarNetServer**. The smart-group-server is derrived from Jonathan's code and still contains his original copyrights and GPLV#2 license. This new implementation of a group routing server has many improvements and new features compared to its predecessor. The main features for the end-user is that Smart Groups allow a user to "listen first" before transmitting and also be able to see the status of the Smart Groups and users. The smart-group-server can now also handle connections from mobile clients (hotspots that get their internet connection from a cellphone). The most useful feature for provider is that a single smart-group-server can serve both DCS- **and** DExtra-linked groups and only the required UDP ports are created. In addtion, by using the remote control application, Smart Groups can be unlinked and linked dynamically, freeing and reallocating resources as required. It was designed expressly for QuadNet. The smart-group-server interact with QuadNet using new IRC messages to provide additional information that will typically be display on the ROUTING GROUPS web page at openquad.net. The smart-group-server may not function proplerly on other IRCDDB networks.
### What's New
* **V# 180407** A bug has been fixed where if you use the "LOGOFF in the text field" method of logging off a smart-group-server, the ROUTING GROUPS page was not being updated properly.
* **V# 180401** In some situations, the smart-group-server does not "following" a user if he switches repeaters. It was clear that the *last repeater used* cache was not being updated properly. This has been fixed by cleaning up how the user cache is used in the CGroupHandler class.
* **V# 180322** The smart-group-server is now compatible with mobile hotspots! You should be able to route to any Smart Group from a smart-phone-tethered hotspot. Thanks goes to Colby Ross, W1BSB for helping with this very important new capability!
* **V# 180218** The CRepeaterHandler class has been removed from the project, along with the CDDDataHandler and DCCSHandler classes. A crash bug, where someone would try to link to a Smart Group module, has been fixed.
* **V# 180205** There was a benign bug causing some linked groups not to properly receive polls from X-reflectors, causing these groups to do an unnecessary re-link at the end of each poll inactivity timer. This has been fixed. I introduced this bug when I did major modifications to the C-Handler, C-ProtocolHandler and C-ProtcolHandlerPool classes. The C-ProtocolHandlerPool classes now use a std::list to keep the C-ProtcolHandler instances, instead of a static array. I consider this release the first public release compareable to what some would call *Version 1.0.0*.
* **V# 180203** A buffer overflow error has been fixed in CConnectData. I introduced this bug when replacing wxWidgets. (I wish the standard library had a format or sprint class method for std::string! It's probably the only thing that wxWidgets has over the C++11 standard library.) Now it's fixed.
* **V# 180118** Smart Groups can now be linked and unlinked by the sgsremote program. See the README of my sgs-remote git repository. You need to unlink before you link and once you unlink a Smart Group you can link it to either an XRF or a DCS reflector. Also **the format of the configuration file has changed**. The callsign and address parameters have been moved from the ircddb section to a new section called gateway. See the example.cfg file for more information. Finally, the install section of the Makefile has been separated into two pieces, one to get the latest Host*.txt files and another to install the smart-group-server program and configuration files, see below.
* **V# 180103** The smart-group-server now supports linking both DExtra and DCS reflectors to different channels *in the same server instance*. The compile time switches for DEXTRA_LINK and DCS_LINK are gone. If you need an unlinked channel, don't define a *reflector* parameter in the configuration.
* **V# 180101** There is no hard limit on how many channels you can have running on a single smart-group-server. There is a practical limit. For instance, you could run out of ports for DExtra or DCS linking. There is also a performance limit when there are so many channels, servicing a single time slice takes longer than a D-Star frame. I don't know when this will happen. Resource allocation is much more efficient. DExtra and DCS resource are only allocated for the channels defined in the configuration file.
* **Original Version** The underlying IRCDDB version has been upgraded to 2.0.0 and supports new IRC Messages that the smart-group-server uses to communicate the channel states to the Quadnet Servers. The dependancy on wxWidgets is gone!
## Server OS Requirements
The smart-group-server requires a modern OS to compile and run. At least Debian 8 or Ubuntu 16.10, or equivilent. The command
```
g++ --version
```
must return at least Version 4.9. The latest Debian and Ubuntu will be far above this. Unlike the StarNetServer, smart-group-server does not use wxWidgits. Modern C++ calls to the standard library (c++11 standard) are used instead of wxWidgets: std::string replaces wxString, std::future replaces wxThreads and standard std::map, std::list, std::queue and std::vector replace the older wx containers. The only external library used is libconfig++. The smart-group-server is significantly improved regarding resource utiliztion compared to the ancestral StarNetServer. The smart-group-server only creates resources for the channel you define in your configuration file. Also, there is no theoretical limit to the number of channels you can create. Of course there is a practical limit based on the underlying hardware.
The smart-group-server is installed as a systemd service. If you want to run this on a system without systemd, you are on your own. I am done dealing with init.d scripts in SysVInit!
## Adminstrative Requirements
This Smart Group Server should have a unique IP address when it logs into QuadNet. That means you probably won't be able to run it from your home if you also have an ircddb gateway running from home. You probably shouldn't run it from your home anyway. The computer your Smart Group Server is running on should have reliable, 24/7 internet access and reliable, 24/7 power. It should also be properly protected from hackers. There are plenty of companies that provide virtual severs that easily fulfill these requirements for verly little money. (You don't need much horse-power for a typical Smart Group Server. For example, a $5/month server on Amazon Lightsail works fine.)
Also the Smart Group Server needs to have a unique callsign in QuadNet, one that will not be used by another client on QuadNet. Ideally, you should use a Club callsign, see the Configuring section below.
## Building
These instructions are for a Debian-based OS. Begin by downloading this git repository:
```
git clone git://github.com/n7tae/smart-group-server.git
```
Install the only needed development library:
```
sudo apt-get install libconfig++-dev
```
Change to the smart-group-server directory and type `make`. This should make the executable, `sgs` without errors or warnings. By default, you will have a group server that can link groups to X-Reflectors or DCS-Reflectors. Of course you can declare an unlinked channel by simply not defining a *reflector* parameter for that channel.
## Configuring
Before you install the group server, you need to create a configuration file called `sgs.cfg`. There is an example configuration file: `example.cfg`. The smart-group-server supports an unlimited number of channels. However there will be a practical limit based on you hardware capability. Also remember that a unique port is created for each DExtra or DCS link on a running smart-group-server. At some point you system will simply run out of connections. Be sure you look and the "StarNet Groups" tab on the openquad.net web page to be sure your new channel callsigns and logoff callsigns are not already in use! Each channel you define requires a band letter. Bands can be shared between channels. Choose any uppercase letter from 'A' to 'Z'. Each channel will have a group logon callsign and a group logoff callsign. The logon and logoff will differ only in the last letter of the callsign. PLEASE DON'T CHOOSE a channel callsign beginning in "REF", "XRF", "XLX", "DCS" or "CCS". While it is possible, it's really confusing for new-comers on QuadNet. Also, avoid subscribe and unsubscribe callsigns that end in "U". Jonathan's ircddbgateway will interpret this as an unlink command and never send it to the smart-group-server.
Your callsign parameter in the ircddb section of your configuration file is the callsign that will be used for logging into QuadNet. THIS NEEDS TO BE A UNIQUE CALLSIGN on QuadNet. Don't use your callsign if you are already using it for a repeater or a hot-spot. Ideally, you should use a Club callsign. Check with your club to see if you can use your club's callsign. Of course, don't do this if your club hosts a D-Star repeater with this callsign. If your club callsign is not available, either apply to be a trustee for a new callsign from you club, or get together with three of your friends and start a club. All the information you need is at arrl.org or w5yi.org. It's not difficult to do, and once you file your application, you'll get your new Club Callsign very quickly.
## Installing and Uninstalling
To install and start the smart-group-server, first type `make newhostfiles`. This will download the latest DCS and DExtra host files and install them. (This command downloads the files to the build directory and then moves them to /usr/local/etc with `sudo`, so it may prompt you for your password.) Then type `sudo make install`. This will put all the executable and the sgs.cfg configuration file the in /usr/local and then start the server. See the Makefile for more information. A very useful way to start it is:
```
sudo make install && sudo journalctl -u sgs.service -f
```
This will allow you to view the smart-group-server log file while it's booting up. When you are satisfied it's running okay you can Control-C to end the journalctl session. To uninstall it, type `sudo make uninstall` and `sudo make removehostfiles`. This will stop the server and remove all files from /usr/local. You can then delete the build directory to remove every trace of the smart-group-server.
73
Tom
n7tae (at) arrl (dot) net

@ -0,0 +1,40 @@
/*
* Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 "DStarDefines.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "Defs.h"
class IReflectorCallback {
public:
virtual bool process(CHeaderData& header, DIRECTION direction, AUDIO_SOURCE source) = 0;
virtual bool process(CAMBEData& data, DIRECTION direction, AUDIO_SOURCE source) = 0;
virtual bool linkFailed(DSTAR_PROTOCOL protocol, const std::string& callsign, bool isRecoverable) = 0;
virtual void linkRefused(DSTAR_PROTOCOL protocol, const std::string& callsign) = 0;
virtual void linkUp(DSTAR_PROTOCOL protocol, const std::string& callsign) = 0;
private:
};

@ -0,0 +1,95 @@
/*
* Copyright (C) 2011 by Jonathan Naylor G4KLX
* Copyright (c) 2017,2018 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 "RemoteGroup.h"
CRemoteGroup::CRemoteGroup(const std::string& callsign, const std::string& logoff, const std::string &repeater, const std::string &infoText,
const std::string &linkReflector, LINK_STATUS linkStatus, unsigned int userTimeout) :
m_callsign(callsign),
m_logoff(logoff),
m_repeater(repeater),
m_infoText(infoText),
m_linkReflector(linkReflector),
m_linkStatus(linkStatus),
m_userTimeout(userTimeout),
m_users()
{
if (logoff.compare(" "))
logoff.empty();
}
CRemoteGroup::~CRemoteGroup()
{
while (m_users.size()) {
delete m_users.back();
m_users.pop_back();
}
}
void CRemoteGroup::addUser(const std::string& callsign, uint32_t timer, uint32_t timeout)
{
CRemoteUser *user = new CRemoteUser(callsign, timer, timeout);
m_users.push_back(user);
}
std::string CRemoteGroup::getCallsign() const
{
return m_callsign;
}
std::string CRemoteGroup::getLogoff() const
{
return m_logoff;
}
std::string CRemoteGroup::getRepeater() const
{
return m_repeater;
}
std::string CRemoteGroup::getInfoText() const
{
return m_infoText;
}
std::string CRemoteGroup::getReflector() const
{
return m_linkReflector;
}
LINK_STATUS CRemoteGroup::getLinkStatus() const
{
return m_linkStatus;
}
unsigned int CRemoteGroup::getUserTimeout() const
{
return m_userTimeout;
}
uint32_t CRemoteGroup::getUserCount() const
{
return m_users.size();
}
CRemoteUser *CRemoteGroup::getUser(uint32_t n) const
{
return m_users[n];
}

@ -0,0 +1,56 @@
/*
* Copyright (C) 2011 by Jonathan Naylor G4KLX
* Copyright (c) 2017,2018 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 <string>
#include <vector>
#include "Defs.h"
#include "RemoteUser.h"
class CRemoteGroup {
public:
CRemoteGroup(const std::string& callsign, const std::string& logoff, const std::string &repeater, const std::string &infoText, const std::string &linkReflector,
LINK_STATUS linkStatus, unsigned int userTimeout);
~CRemoteGroup();
void addUser(const std::string& callsign, uint32_t timer, uint32_t timeout);
std::string getCallsign() const;
std::string getLogoff() const;
std::string getRepeater() const;
std::string getInfoText() const;
std::string getReflector() const;
LINK_STATUS getLinkStatus() const;
unsigned int getUserTimeout() const;
uint32_t getUserCount() const;
CRemoteUser *getUser(uint32_t n) const;
private:
std::string m_callsign;
std::string m_logoff;
std::string m_repeater;
std::string m_infoText;
std::string m_linkReflector;
LINK_STATUS m_linkStatus;
unsigned int m_userTimeout;
std::vector<CRemoteUser *> m_users;
};

@ -0,0 +1,184 @@
/*
* Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017-2018 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 <cassert>
#include <cstdlib>
#include <list>
#include "GroupHandler.h"
#include "RemoteHandler.h"
#include "DExtraHandler.h"
#include "DStarDefines.h"
#include "DCSHandler.h"
#include "Utils.h"
CRemoteHandler::CRemoteHandler(const std::string &password, unsigned int port, const std::string &address) :
m_password(password),
m_handler(port, address),
m_random(0U)
{
assert(port > 0U);
assert(password.size());
}
CRemoteHandler::~CRemoteHandler()
{
}
bool CRemoteHandler::open()
{
return m_handler.open();
}
void CRemoteHandler::process()
{
RPH_TYPE type = m_handler.readType();
switch (type) {
case RPHT_LOGOUT:
m_handler.setLoggedIn(false);
printf("Remote control user has logged out\n");
break;
case RPHT_LOGIN:
m_random = (uint32_t)rand();
m_handler.sendRandom(m_random);
break;
case RPHT_HASH: {
bool valid = m_handler.readHash(m_password, m_random);
if (valid) {
printf("Remote control user has logged in\n");
m_handler.setLoggedIn(true);
m_handler.sendACK();
} else {
printf("Remote control user has failed login authentication\n");
m_handler.setLoggedIn(false);
m_handler.sendNAK("Invalid password");
}
}
break;
case RPHT_SMARTGROUP: {
std::string callsign = m_handler.readGroup();
sendGroup(callsign);
}
break;
case RPHT_LINK: {
std::string callsign, reflector;
m_handler.readLink(callsign, reflector);
printf("Remote control user has linked \"%s\" to \"%s\"\n", callsign.c_str(), reflector.c_str());
link(callsign, reflector);
}
break;
case RPHT_UNLINK: {
std::string callsign;
m_handler.readUnlink(callsign);
printf("Remote control user has unlinked \"%s\"\n", callsign.c_str());
unlink(callsign);
}
break;
case RPHT_LOGOFF: {
std::string callsign, user;
m_handler.readLogoff(callsign, user);
printf("Remote control user has logged off \"%s\" from \"%s\"\n", user.c_str(), callsign.c_str());
logoff(callsign, user);
}
break;
default:
break;
}
}
void CRemoteHandler::close()
{
m_handler.close();
}
void CRemoteHandler::sendGroup(const std::string &callsign)
{
CGroupHandler *group = CGroupHandler::findGroup(callsign);
if (group == NULL) {
m_handler.sendNAK("Invalid Smart Group callsign");
return;
}
CRemoteGroup *data = group->getInfo();
if (data != NULL)
m_handler.sendGroup(*data);
delete data;
}
void CRemoteHandler::link(const std::string &callsign, const std::string &reflector)
{
CGroupHandler *smartGroup = CGroupHandler::findGroup(callsign);
if (NULL == smartGroup) {
m_handler.sendNAK(std::string("Invalid Smart Group subscribe call ") + callsign);
return;
}
if (smartGroup->remoteLink(reflector))
m_handler.sendACK();
else
m_handler.sendNAK("link failed");
}
void CRemoteHandler::unlink(const std::string &callsign)
{
CGroupHandler *smartGroup = CGroupHandler::findGroup(callsign);
if (NULL == smartGroup) {
m_handler.sendNAK(std::string("Invalid Smart Group subscribe call ") + callsign);
return;
}
CRemoteGroup *data = smartGroup->getInfo();
if (data) {
switch (smartGroup->getLinkType()) {
case LT_DEXTRA:
CDExtraHandler::unlink(smartGroup, data->getReflector(), false);
break;
case LT_DCS:
CDCSHandler::unlink(smartGroup, data->getReflector(), false);
break;
default:
delete data;
m_handler.sendNAK("alread unlinked");
return;
}
delete data;
} else {
m_handler.sendNAK("could not get Smart Group info");
return;
}
smartGroup->setLinkType(LT_NONE);
smartGroup->clearReflector();
m_handler.sendACK();
}
void CRemoteHandler::logoff(const std::string &callsign, const std::string &user)
{
CGroupHandler *pGroup = CGroupHandler::findGroup(callsign);
if (pGroup == NULL) {
m_handler.sendNAK("Invalid Smart Group callsign");
return;
}
bool res = pGroup->logoff(user);
if (!res)
m_handler.sendNAK("Invalid Smart Group user callsign");
else
m_handler.sendACK();
}

@ -0,0 +1,48 @@
/*
* Copyright (C) 2011,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017-2018 by Thomas A. Early
*
* 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 <string>
#include <cstdint>
#include "RemoteProtocolHandler.h"
#include "Timer.h"
class CRemoteHandler {
public:
CRemoteHandler(const std::string &password, unsigned int port, const std::string &address = std::string(""));
~CRemoteHandler();
bool open();
void process();
void close();
private:
std::string m_password;
CRemoteProtocolHandler m_handler;
uint32_t m_random;
void sendGroup(const std::string &callsign);
void link(const std::string &callsign, const std::string &reflector);
void unlink(const std::string &callsign);
void logoff(const std::string &callsign, const std::string &user);
};

@ -0,0 +1,58 @@
/*
* Copyright (C) 2011 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 "RemoteLinkData.h"
CRemoteLinkData::CRemoteLinkData(const std::string& callsign, PROTOCOL protocol, bool linked, DIRECTION direction, bool dongle) :
m_callsign(callsign),
m_protocol(protocol),
m_linked(linked),
m_direction(direction),
m_dongle(dongle)
{
}
CRemoteLinkData::~CRemoteLinkData()
{
}
std::string CRemoteLinkData::getCallsign() const
{
return m_callsign;
}
int32_t CRemoteLinkData::getProtocol() const
{
return int32_t(m_protocol);
}
int32_t CRemoteLinkData::isLinked() const
{
return m_linked ? 1 : 0;
}
int32_t CRemoteLinkData::getDirection() const
{
return int32_t(m_direction);
}
int32_t CRemoteLinkData::isDongle() const
{
return m_dongle ? 1 : 0;
}

@ -0,0 +1,45 @@
/*
* Copyright (C) 2011 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <cstdint>
#include "Defs.h"
class CRemoteLinkData {
public:
CRemoteLinkData(const std::string& callsign, PROTOCOL protocol, bool linked, DIRECTION direction, bool dongle);
~CRemoteLinkData();
std::string getCallsign() const;
int32_t getProtocol() const;
int32_t isLinked() const;
int32_t getDirection() const;
int32_t isDongle() const;
private:
std::string m_callsign;
PROTOCOL m_protocol;
bool m_linked;
DIRECTION m_direction;
bool m_dongle;
};

@ -0,0 +1,431 @@
/*
* Copyright (C) 2011,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017,2018 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 <cassert>
#include <cstring>
#include "RemoteProtocolHandler.h"
#include "DStarDefines.h"
#include "SHA256.h"
#include "Utils.h"
#define wxINT32_SWAP_ON_BE(x) x
#define wxUINT32_SWAP_ON_BE(x) x
const unsigned int BUFFER_LENGTH = 2000U;
CRemoteProtocolHandler::CRemoteProtocolHandler(unsigned int port, const std::string &address) :
m_socket(address, port),
m_address(),
m_port(0U),
m_loggedIn(false),
m_type(RPHT_NONE),
m_inBuffer(NULL),
m_inLength(0U),
m_outBuffer(NULL)
{
assert(port > 0U);
m_inBuffer = new unsigned char[BUFFER_LENGTH];
m_outBuffer = new unsigned char[BUFFER_LENGTH];
}
CRemoteProtocolHandler::~CRemoteProtocolHandler()
{
delete[] m_outBuffer;
delete[] m_inBuffer;
}
bool CRemoteProtocolHandler::open()
{
return m_socket.open();
}
RPH_TYPE CRemoteProtocolHandler::readType()
{
m_type = RPHT_NONE;
in_addr address;
unsigned int port;
int length = m_socket.read(m_inBuffer, BUFFER_LENGTH, address, port);
if (length <= 0)
return m_type;
// CUtils::dump("Incoming", m_inBuffer, length);
if (memcmp(m_inBuffer, "LIN", 3U) == 0) {
m_loggedIn = false;
m_address = address;
m_port = port;
m_type = RPHT_LOGIN;
return m_type;
}
if (address.s_addr == inet_addr("127.0.0.1")) {
if (memcmp(m_inBuffer, "LKS", 3U) == 0) {
m_inLength = length;
m_type = RPHT_LINKSCR;
return m_type;
}
}
if (m_loggedIn) {
if (address.s_addr != m_address.s_addr || port != m_port) {
sendNAK("You are not logged in");
return m_type;
}
}
m_inLength = length;
if (memcmp(m_inBuffer, "SHA", 3U) == 0) {
if (m_loggedIn) {
sendNAK("Someone is already logged in");
return m_type;
}
m_type = RPHT_HASH;
return m_type;
} else if (memcmp(m_inBuffer, "GCS", 3U) == 0) {
if (!m_loggedIn) {
sendNAK("You are not logged in");
return m_type;
}
m_type = RPHT_CALLSIGNS;
return m_type;
} else if (memcmp(m_inBuffer, "GRP", 3U) == 0) {
if (!m_loggedIn) {
sendNAK("You are not logged in");
return m_type;
}
m_type = RPHT_REPEATER;
return m_type;
} else if (memcmp(m_inBuffer, "GSN", 3U) == 0) {
if (!m_loggedIn) {
sendNAK("You are not logged in");
return m_type;
}
m_type = RPHT_SMARTGROUP;
return m_type;
} else if (memcmp(m_inBuffer, "LNK", 3U) == 0) {
if (!m_loggedIn) {
sendNAK("You are not logged in");
return m_type;
}
m_type = RPHT_LINK;
return m_type;
} else if (memcmp(m_inBuffer, "UNL", 3U) == 0) {
if (!m_loggedIn) {
sendNAK("You are not logged in");
return m_type;
}
m_type = RPHT_UNLINK;
return m_type;
} else if (memcmp(m_inBuffer, "LGO", 3U) == 0) {
if (!m_loggedIn) {
sendNAK("You are not logged in");
return m_type;
}
m_type = RPHT_LOGOFF;
return m_type;
} else if (memcmp(m_inBuffer, "LOG", 3U) == 0) {
if (!m_loggedIn)
return m_type;
m_type = RPHT_LOGOUT;
return m_type;
} else {
if (!m_loggedIn) {
sendNAK("You are not logged in");
return m_type;
}
m_type = RPHT_UNKNOWN;
return m_type;
}
}
bool CRemoteProtocolHandler::readHash(const std::string &password, uint32_t random)
{
if (m_type != RPHT_HASH)
return false;
unsigned char *hash = m_inBuffer + 3U;
unsigned int len = password.size() + sizeof(uint32_t);
unsigned char *in = new unsigned char[len];
unsigned char *out = new unsigned char[32U];
memcpy(in, &random, sizeof(uint32_t));
for (unsigned int i = 0U; i < password.size(); i++)
in[i + sizeof(unsigned int)] = password.at(i);
CSHA256 sha256;
sha256.buffer(in, len, out);
bool res = memcmp(out, hash, 32U) == 0;
delete[] in;
delete[] out;
return res;
}
std::string CRemoteProtocolHandler::readRepeater()
{
if (m_type != RPHT_REPEATER)
return std::string("");
std::string callsign((char *)(m_inBuffer + 3U), LONG_CALLSIGN_LENGTH);
return callsign;
}
std::string CRemoteProtocolHandler::readGroup()
{
if (m_type != RPHT_SMARTGROUP)
return std::string("");
std::string callsign((char *)(m_inBuffer + 3U), LONG_CALLSIGN_LENGTH);
return callsign;
}
bool CRemoteProtocolHandler::readLogoff(std::string &callsign, std::string &user)
{
if (m_type != RPHT_LOGOFF)
return false;
callsign = std::string((char *)(m_inBuffer + 3U), LONG_CALLSIGN_LENGTH);
user = std::string((char *)(m_inBuffer + 3U + LONG_CALLSIGN_LENGTH), LONG_CALLSIGN_LENGTH);
return true;
}
bool CRemoteProtocolHandler::readLink(std::string &callsign, std::string &reflector)
{
if (m_type != RPHT_LINK)
return false;
callsign = std::string((char *)(m_inBuffer + 3U), LONG_CALLSIGN_LENGTH);
reflector = std::string((char *)(m_inBuffer + 3U + LONG_CALLSIGN_LENGTH), LONG_CALLSIGN_LENGTH);
if (0==reflector.compare(" "))
return false;
return true;
}
bool CRemoteProtocolHandler::readUnlink(std::string &callsign)
{
if (m_type != RPHT_UNLINK)
return false;
callsign = std::string((char *)(m_inBuffer + 3U), LONG_CALLSIGN_LENGTH);
return true;
}
bool CRemoteProtocolHandler::sendCallsigns(const std::list<std::string> &repeaters, const std::list<std::string> &groups)
{
unsigned char *p = m_outBuffer;
memcpy(p, "CAL", 3U);
p += 3U;
for (auto it=repeaters.cbegin(); it!=repeaters.cend(); it++) {
*p++ = 'R';
memset(p, ' ' , LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < it->size(); i++)
p[i] = it->at(i);
p += LONG_CALLSIGN_LENGTH;
}
for (auto it=groups.cbegin(); it!=groups.cend(); it++) {
*p++ = 'S';
memset(p, ' ' , LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < it->size(); i++)
p[i] = it->at(i);
p += LONG_CALLSIGN_LENGTH;
}
// CUtils::dump(wxT("Outgoing"), m_outBuffer, p - m_outBuffer);
return m_socket.write(m_outBuffer, p - m_outBuffer, m_address, m_port);
}
bool CRemoteProtocolHandler::sendRepeater(const CRemoteRepeaterData &data)
{
unsigned char *p = m_outBuffer;
memcpy(p, "RPT", 3U);
p += 3U;
memset(p, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < data.getCallsign().size(); i++)
p[i] = data.getCallsign().at(i);
p += LONG_CALLSIGN_LENGTH;
uint32_t reconnect = wxINT32_SWAP_ON_BE(data.getReconnect());
memcpy(p, &reconnect, sizeof(uint32_t));
p += sizeof(uint32_t);
memset(p, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < data.getReflector().size(); i++)
p[i] = data.getReflector().at(i);
p += LONG_CALLSIGN_LENGTH;
for (unsigned int n = 0U; n < data.getLinkCount(); n++) {
CRemoteLinkData *link = data.getLink(n);
memset(p, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < link->getCallsign().size(); i++)
p[i] = link->getCallsign().at(i);
p += LONG_CALLSIGN_LENGTH;
uint32_t protocol = wxINT32_SWAP_ON_BE(link->getProtocol());
memcpy(p, &protocol, sizeof(uint32_t));
p += sizeof(uint32_t);
uint32_t linked = wxINT32_SWAP_ON_BE(link->isLinked());
memcpy(p, &linked, sizeof(uint32_t));
p += sizeof(uint32_t);
uint32_t direction = wxINT32_SWAP_ON_BE(link->getDirection());
memcpy(p, &direction, sizeof(uint32_t));
p += sizeof(uint32_t);
uint32_t dongle = wxINT32_SWAP_ON_BE(link->isDongle());
memcpy(p, &dongle, sizeof(uint32_t));
p += sizeof(uint32_t);
}
// CUtils::dump("Outgoing", m_outBuffer, p - m_outBuffer);
return m_socket.write(m_outBuffer, p - m_outBuffer, m_address, m_port);
}
bool CRemoteProtocolHandler::sendGroup(const CRemoteGroup &data)
{
unsigned char *p = m_outBuffer;
memcpy(p, "SNT", 3U);
p += 3U;
memset(p, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < data.getCallsign().size(); i++)
p[i] = data.getCallsign().at(i);
p += LONG_CALLSIGN_LENGTH;
memset(p, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < data.getLogoff().size(); i++)
p[i] = data.getLogoff().at(i);
p += LONG_CALLSIGN_LENGTH;
memset(p, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < data.getRepeater().size(); i++)
p[i] = data.getRepeater().at(i);
p += LONG_CALLSIGN_LENGTH;
memset(p, ' ', 20);
for (unsigned int i = 0U; i < data.getInfoText().size(); i++)
p[i] = data.getInfoText().at(i);
p += 20;
memset(p, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < data.getReflector().size(); i++)
p[i] = data.getReflector().at(i);
p += LONG_CALLSIGN_LENGTH;
LINK_STATUS ls = data.getLinkStatus();
memcpy(p, &ls, sizeof(enum LINK_STATUS));
p += sizeof(enum LINK_STATUS);
unsigned int ut = data.getUserTimeout();
memcpy(p, &ut, sizeof(unsigned int));
p += sizeof(unsigned int);
for (unsigned int n = 0U; n < data.getUserCount(); n++) {
CRemoteUser *user = data.getUser(n);
memset(p, ' ', LONG_CALLSIGN_LENGTH);
for (unsigned int i = 0U; i < user->getCallsign().size(); i++)
p[i] = user->getCallsign().at(i);
p += LONG_CALLSIGN_LENGTH;
uint32_t timer = wxUINT32_SWAP_ON_BE(user->getTimer());
memcpy(p, &timer, sizeof(uint32_t));
p += sizeof(uint32_t);
uint32_t timeout = wxUINT32_SWAP_ON_BE(user->getTimeout());
memcpy(p, &timeout, sizeof(uint32_t));
p += sizeof(uint32_t);
}
// CUtils::dump("Outgoing", m_outBuffer, p - m_outBuffer);
return m_socket.write(m_outBuffer, p - m_outBuffer, m_address, m_port);
}
void CRemoteProtocolHandler::setLoggedIn(bool set)
{
m_loggedIn = set;
}
void CRemoteProtocolHandler::close()
{
m_socket.close();
}
bool CRemoteProtocolHandler::sendACK()
{
memcpy(m_outBuffer + 0U, "ACK", 3U);
// CUtils::dump("Outgoing", m_outBuffer, 3U);
return m_socket.write(m_outBuffer, 3U, m_address, m_port);
}
bool CRemoteProtocolHandler::sendNAK(const std::string &text)
{
memcpy(m_outBuffer + 0U, "NAK", 3U);
memset(m_outBuffer + 3U, 0x00U, text.size() + 1U);
for (unsigned int i = 0U; i < text.size(); i++)
m_outBuffer[i + 3U] = text.at(i);
// CUtils::dump("Outgoing", m_outBuffer, 3U + text.size() + 1U);
return m_socket.write(m_outBuffer, 3U + text.size() + 1U, m_address, m_port);
}
bool CRemoteProtocolHandler::sendRandom(uint32_t random)
{
memcpy(m_outBuffer + 0U, "RND", 3U);
uint32_t temp = wxUINT32_SWAP_ON_BE(random);
memcpy(m_outBuffer + 3U, &temp, sizeof(uint32_t));
// CUtils::dump("Outgoing", m_outBuffer, 3U + sizeof(uint32_t));
return m_socket.write(m_outBuffer, 3U + sizeof(uint32_t), m_address, m_port);
}

@ -0,0 +1,82 @@
/*
* Copyright (C) 2011,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017,2018 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 <string>
#include <cstdint>
#include <list>
#include "RemoteGroup.h"
#include "RemoteRepeaterData.h"
#include "UDPReaderWriter.h"
#include "Defs.h"
enum RPH_TYPE {
RPHT_NONE,
RPHT_LOGIN,
RPHT_HASH,
RPHT_CALLSIGNS,
RPHT_REPEATER,
RPHT_SMARTGROUP,
RPHT_LINK,
RPHT_UNLINK,
RPHT_LINKSCR,
RPHT_LOGOFF,
RPHT_LOGOUT,
RPHT_UNKNOWN
};
class CRemoteProtocolHandler {
public:
CRemoteProtocolHandler(unsigned int port, const std::string &address = std::string(""));
~CRemoteProtocolHandler();
bool open();
RPH_TYPE readType();
std::string readRepeater();
std::string readGroup();
bool readHash(const std::string &password, uint32_t random);
bool readLink(std::string &callsign, std::string &reflector);
bool readUnlink(std::string &callsign);
bool readLogoff(std::string &callsign, std::string &user);
bool sendACK();
bool sendNAK(const std::string &text);
bool sendRandom(uint32_t random);
bool sendCallsigns(const std::list<std::string> &repeaters, const std::list<std::string> &groups);
bool sendRepeater(const CRemoteRepeaterData &data);
bool sendGroup(const CRemoteGroup &data);
void setLoggedIn(bool set);
void close();
private:
CUDPReaderWriter m_socket;
in_addr m_address;
unsigned int m_port;
bool m_loggedIn;
RPH_TYPE m_type;
unsigned char *m_inBuffer;
unsigned int m_inLength;
unsigned char *m_outBuffer;
};

@ -0,0 +1,67 @@
/*
* Copyright (C) 2011 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 "RemoteRepeaterData.h"
CRemoteRepeaterData::CRemoteRepeaterData(const std::string& callsign, RECONNECT reconnect, const std::string& reflector) :
m_callsign(callsign),
m_reconnect(reconnect),
m_reflector(reflector),
m_links()
{
}
CRemoteRepeaterData::~CRemoteRepeaterData()
{
while (m_links.size()) {
delete m_links.back();
m_links.pop_back();
}
}
void CRemoteRepeaterData::addLink(const std::string& callsign, PROTOCOL protocol, bool linked, DIRECTION direction, bool dongle)
{
CRemoteLinkData *data = new CRemoteLinkData(callsign, protocol, linked, direction, dongle);
m_links.push_back(data);
}
std::string CRemoteRepeaterData::getCallsign() const
{
return m_callsign;
}
int32_t CRemoteRepeaterData::getReconnect() const
{
return int32_t(m_reconnect);
}
std::string CRemoteRepeaterData::getReflector() const
{
return m_reflector;
}
unsigned int CRemoteRepeaterData::getLinkCount() const
{
return m_links.size();
}
CRemoteLinkData *CRemoteRepeaterData::getLink(unsigned int n) const
{
return m_links[n];
}

@ -0,0 +1,47 @@
/*
* Copyright (C) 2011 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <cstdint>
#include <vector>
#include "RemoteLinkData.h"
class CRemoteRepeaterData {
public:
CRemoteRepeaterData(const std::string& callsign, RECONNECT reconnect, const std::string& reflector);
~CRemoteRepeaterData();
void addLink(const std::string& callsign, PROTOCOL protocol, bool linked, DIRECTION direction, bool dongle);
std::string getCallsign() const;
int32_t getReconnect() const;
std::string getReflector() const;
unsigned int getLinkCount() const;
CRemoteLinkData *getLink(unsigned int n) const;
private:
std::string m_callsign;
RECONNECT m_reconnect;
std::string m_reflector;
std::vector<CRemoteLinkData *> m_links;
};

@ -0,0 +1,46 @@
/*
* Copyright (C) 2011 by Jonathan Naylor G4KLX
* Copyright (c) 2017,2018 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 "RemoteUser.h"
CRemoteUser::CRemoteUser(const std::string& callsign, uint32_t timer, uint32_t timeout) :
m_callsign(callsign),
m_timer(timer),
m_timeout(timeout)
{
}
CRemoteUser::~CRemoteUser()
{
}
std::string CRemoteUser::getCallsign() const
{
return m_callsign;
}
uint32_t CRemoteUser::getTimer() const
{
return m_timer;
}
uint32_t CRemoteUser::getTimeout() const
{
return m_timeout;
}

@ -0,0 +1,37 @@
/*
* Copyright (C) 2011 by Jonathan Naylor G4KLX
* Copyright (c) 2017,2018 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 <string>
class CRemoteUser {
public:
CRemoteUser(const std::string& callsign, uint32_t timer, uint32_t timeout);
~CRemoteUser();
std::string getCallsign() const;
uint32_t getTimer() const;
uint32_t getTimeout() const;
private:
std::string m_callsign;
uint32_t m_timer;
uint32_t m_timeout;
};

@ -0,0 +1,52 @@
/*
* Copyright (C) 2010 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 "RepeaterCache.h"
CRepeaterCache::CRepeaterCache()
{
}
CRepeaterCache::~CRepeaterCache()
{
for (std::unordered_map<std::string, CRepeaterRecord *>::iterator it = m_cache.begin(); it != m_cache.end(); ++it)
delete it->second;
}
CRepeaterRecord* CRepeaterCache::find(const std::string& repeater)
{
return m_cache[repeater];
}
void CRepeaterCache::update(const std::string& repeater, const std::string& gateway)
{
CRepeaterRecord* rec = m_cache[repeater];
if (rec == NULL)
// A brand new record is needed
m_cache[repeater] = new CRepeaterRecord(repeater, gateway);
else
// Update an existing record
rec->setGateway(gateway);
}
unsigned int CRepeaterCache::getCount() const
{
return m_cache.size();
}

@ -0,0 +1,66 @@
/*
* Copyright (C) 2010 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <string>
#include <unordered_map>
class CRepeaterRecord {
public:
CRepeaterRecord(const std::string& repeater, const std::string& gateway) :
m_repeater(repeater),
m_gateway(gateway)
{
}
std::string getRepeater() const
{
return m_repeater;
}
std::string getGateway() const
{
return m_gateway;
}
void setGateway(const std::string& gateway)
{
m_gateway = gateway;
}
private:
std::string m_repeater;
std::string m_gateway;
};
class CRepeaterCache {
public:
CRepeaterCache();
~CRepeaterCache();
CRepeaterRecord* find(const std::string& repeater);
void update(const std::string& repeater, const std::string& gateway);
unsigned int getCount() const;
private:
std::unordered_map<std::string, CRepeaterRecord *> m_cache;
};

@ -0,0 +1,33 @@
/*
* Copyright (C) 2011,2012,2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 "DStarDefines.h"
#include "HeaderData.h"
#include "AMBEData.h"
#include "Defs.h"
class IRepeaterCallback {
public:
virtual bool process(CHeaderData& header, DIRECTION direction, AUDIO_SOURCE source) = 0;
virtual bool process(CAMBEData& data, DIRECTION direction, AUDIO_SOURCE source) = 0;
private:
};

@ -0,0 +1,63 @@
/*
* Copyright (C) 2010-2013 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 "HeaderData.h"
#include "StatusData.h"
#include "HeardData.h"
#include "AMBEData.h"
#include "TextData.h"
#include "PollData.h"
#include "DDData.h"
enum REPEATER_TYPE {
RT_NONE,
RT_POLL,
RT_HEARD,
RT_HEADER,
RT_AMBE,
RT_BUSY_HEADER,
RT_BUSY_AMBE,
RT_DD
};
class IRepeaterProtocolHandler {
public:
virtual bool open() = 0;
virtual bool writeHeader(CHeaderData& header) = 0;
virtual bool writeAMBE(CAMBEData& data) = 0;
virtual bool writeDD(CDDData& data) = 0;
virtual bool writeText(CTextData& text) = 0;
virtual bool writeStatus(CStatusData& status) = 0;
virtual REPEATER_TYPE read() = 0;
virtual CPollData* readPoll() = 0;
virtual CHeardData* readHeard() = 0;
virtual CHeaderData* readHeader() = 0;
virtual CAMBEData* readAMBE() = 0;
virtual CDDData* readDD() = 0;
virtual CHeaderData* readBusyHeader() = 0;
virtual CAMBEData* readBusyAMBE() = 0;
virtual void close() = 0;
private:
};

@ -0,0 +1,150 @@
/*
* Copyright (C) 2010,2011 by Jonathan Naylor G4KLX
* Copyright (c) 2017,2018 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/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string>
#include <iostream>
#include "SGSConfig.h"
#include "SGSApp.h"
#include "Version.h"
#include "IRCDDBMultiClient.h"
#include "IRCDDBClient.h"
#include "Utils.h"
#include "GitVersion.h"
int main(int argc, char *argv[])
{
setbuf(stdout, NULL);
if (2 != argc) {
printf("usage: %s path_to_config_file\n", argv[0]);
printf(" %s --version\n", argv[0]);
return 1;
}
if ('-' == argv[1][0]) {
printf("\nSmart Group Server Version %s (GitID #%.7s) Copyright (C) %s\n", VERSION.c_str(), gitversion, VENDOR_NAME.c_str());
printf("Smart Group Server comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n");
printf("This is free software, and you are welcome to distribute it\nunder certain conditions that are discussed in the LICENSE file.\n\n");
return 0;
}
std::string cfgFile(argv[1]);
CSGSApp gateway(cfgFile);
if (!gateway.init()) {
return 1;
}
gateway.run();
return 0;
}
CSGSApp::CSGSApp(const std::string &configFile) : m_configFile(configFile), m_thread(NULL)
{
}
CSGSApp::~CSGSApp()
{
}
bool CSGSApp::init()
{
return createThread();
}
void CSGSApp::run()
{
m_thread->run();
printf("exiting\n");
}
bool CSGSApp::createThread()
{
printf("\nSmart Group Server Version %s (GitID #%.7s) Copyright (C) %s\n", VERSION.c_str(), gitversion, VENDOR_NAME.c_str());
printf("Smart Group Server comes with ABSOLUTELY NO WARRANTY; see the LICENSE for details.\n");
printf("This is free software, and you are welcome to distribute it\nunder certain conditions that are discussed in the LICENSE file.\n\n");
CSGSConfig config(m_configFile);
m_thread = new CSGSThread(config.getLinkCount("XRF"), config.getLinkCount("DCS"));
std::string CallSign, address;
config.getGateway(CallSign, address);
CallSign.resize(7, ' ');
CallSign.push_back('G');
printf("Gateway callsign set to %s, local address set to %s\n", CallSign.c_str(), address.c_str());
CIRCDDB_Array clients;
for(unsigned int i=0; i < config.getIrcDDBCount(); i++) {
std::string hostname, username, password;
bool isQuadNet;
config.getIrcDDB(i, hostname, username, password, isQuadNet);
std::cout << "ircDDB " << i + 1 << " set to " << hostname << " username set to " << username << " QuadNet " << isQuadNet << std::endl;
CIRCDDB *ircDDB = new CIRCDDBClient(hostname, 9007U, username, password, std::string("linux_SmartGroupServer") + std::string("-") + VERSION, address, isQuadNet);
clients.push_back(ircDDB);
}
CIRCDDBMultiClient* multiClient = new CIRCDDBMultiClient(clients);
bool res = multiClient->open();
if (!res) {
printf("Cannot initialise the ircDDB protocol handler\n");
return false;
}
m_thread->setIRC(multiClient);
for (unsigned int i=0; i<config.getModCount(); i++) {
std::string band, callsign, logoff, info, permanent, reflector;
unsigned int usertimeout;
CALLSIGN_SWITCH callsignswitch;
bool txmsgswitch;
config.getGroup(i, band, callsign, logoff, info, permanent, usertimeout, callsignswitch, txmsgswitch, reflector);
if (callsign.size() && isalnum(callsign[0])) {
std::string repeater(CallSign);
repeater.resize(7, ' ');
repeater.push_back(band[0]);
m_thread->addGroup(callsign, logoff, repeater, info, permanent, usertimeout, callsignswitch, txmsgswitch, reflector);
printf("Group %d: %s/%s using %s, \"%s\", perm: %s, timeout: %u mins, c/s switch: %s, msg switch: %s, Linked: %s\n",
i, callsign.c_str(), logoff.c_str(), repeater.c_str(), info.c_str(), permanent.c_str(), usertimeout,
SCS_GROUP_CALLSIGN==callsignswitch ? "Group" : "User", txmsgswitch ? "true" : "false", reflector.c_str());
}
}
bool remoteEnabled;
std::string remotePassword;
unsigned int remotePort;
config.getRemote(remoteEnabled, remotePassword, remotePort);
printf("Remote enabled set to %d, port set to %u\n", int(remoteEnabled), remotePort);
m_thread->setRemote(remoteEnabled, remotePassword, remotePort);
m_thread->setAddress(address);
m_thread->setCallsign(CallSign);
return true;
}

@ -0,0 +1,37 @@
/*
* Copyright (C) 2010,2011 by Jonathan Naylor G4KLX
* Copyright (c) 2017,2018 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 "SGSThread.h"
class CSGSApp
{
private:
std::string m_configFile;
CSGSThread *m_thread;
bool createThread();
public:
CSGSApp(const std::string &configFile);
~CSGSApp();
bool init();
void run();
};

@ -0,0 +1,314 @@
/*
* Copyright (C) 2010,2011,2012 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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>
#include <sstream>
#include <iostream>
#include "Utils.h"
#include "SGSConfig.h"
CSGSConfig::CSGSConfig(const std::string &pathname)
{
if (pathname.size() < 1) {
printf("Configuration filename too short!\n");
return;
}
Config cfg;
try {
cfg.readFile(pathname.c_str());
}
catch(const FileIOException &fioex) {
printf("Can't read %s\n", pathname.c_str());
return;
}
catch(const ParseException &pex) {
printf("Parse error at %s:%d - %s\n", pex.getFile(), pex.getLine(), pex.getError());
return;
}
if (! get_value(cfg, "gateway.callsign", m_callsign, 3, 8, ""))
return;
if (0 == m_callsign.size())
return;
CUtils::ToUpper(m_callsign);
get_value(cfg, "gateway.address", m_address, 0, 20, "");
printf("GATEWAY: callsign='%s' address='%s'\n", m_callsign.c_str(), m_address.c_str());
//ircDDB Networks
for(int i = 0; i < cfg.lookup("ircddb").getLength(); i++) {
SircDDB * ircddb = new SircDDB();
std::stringstream key;
key << "ircddb.[" << i << "].hostname";
if(! get_value(cfg, key.str(), ircddb->hostname, 5, 30, "") || ircddb->hostname == "") {//do not allow hostname to be empty
delete ircddb;
continue;
}
key.str("");key.clear();
key << "ircddb.[" << i << "].username";
if (! get_value(cfg, key.str(), ircddb->username, 3, 8, m_callsign)) {//default user name to callsign
delete ircddb;
continue;
}
CUtils::ToUpper(ircddb->username);
key.str("");key.clear();
key << "ircddb.[" << i << "].password";
if(!get_value(cfg, key.str(), ircddb->password, 0, 30, "")) {
delete ircddb;
continue;
}
ircddb->isQuadNet = ircddb->hostname.find("openquad.net") != std::string::npos;
this->m_ircDDB.push_back(ircddb);
std::cout << "IRCDDB: host=" << ircddb->hostname << " user=" << ircddb->username << " password=" << ircddb->password << "\n";
}
if(this->m_ircDDB.size() == 0) {//no ircddb network specified? Default to openquad!
SircDDB * ircddb = new SircDDB();
ircddb->hostname = "rr.openquad.net";
ircddb->password = "";
ircddb->username = m_callsign;
ircddb->isQuadNet = true;
this->m_ircDDB.push_back(ircddb);
std::cout << "No ircDDB networks configure'd, defaulting to IRCDDB: host=" << ircddb->hostname << " user=" << ircddb->username << " password=" << ircddb->password << "\n";
}
// module parameters
for (int i=0; i<cfg.lookup("module").getLength(); i++) {
std::stringstream key;
std::string basename, subscribe, unsubscribe, band;
key << "module.[" << i << "].basename";
if (get_value(cfg, key.str(), basename, 1, 7, "")) {
bool isokay = true;
for (std::string::iterator it=basename.begin(); it!=basename.end(); it++) {
if (! isalnum(*it)) {
isokay = false;
break;
}
}
if (isokay)
CUtils::ToUpper(basename);
else {
printf("Malformed basename for module %d: '%s'\n", i, basename.c_str());
basename.empty();
}
}
key.str("");key.clear();
key << "module.[" << i << "].band";
get_value(cfg, key.str(), band, 1, 1, "A");
CUtils::ToUpper(band);
if (! isalpha(band[0])) {
printf("Module %d band is not a letter\n", i);
basename.empty();
}
key.str("");key.clear();
key << "module.[" << i << "].subscribe";
get_value(cfg, key.str(), subscribe, 1, 1, "A");
CUtils::ToUpper(subscribe);
if (subscribe[0] != ' ' && ('A' > subscribe[0] || subscribe[0] > 'Z')) {
printf("subscribe suffix not space or letter\n");
basename.empty();
}
key.str("");key.clear();
key << "module.[" << i << "].unsubscribe";
get_value(cfg, key.str(), unsubscribe, 1, 1, "T");
CUtils::ToUpper(unsubscribe);
if ('A' > unsubscribe[0] || unsubscribe[0] > 'Z') {
printf("unsubscribe suffix not a letter\n");
basename.empty();
}
if (! subscribe.compare(unsubscribe)) {
// subscribe and unsubscribe suffix needs to be different
printf("subscribe and unsubscribe for %s are identical\n", basename.c_str());
basename.empty();
}
// skip to the next module definition
if (0 == basename.size())
continue;
struct Smodule *pmod = new struct Smodule;
// pad basename with spaces
basename.resize(7, ' ');
pmod->callsign = basename + subscribe;
pmod->logoff = basename + unsubscribe;
pmod->band = band;
key.str("");key.clear();
key << "module.[" << i << "].info";
get_value(cfg, key.str(), pmod->info, 0, 20, "Smart Group Server");
if (pmod->info.size())
pmod->info.resize(20, ' ');
key.str("");key.clear();
key << "module.[" << i << "].permanent";
get_value(cfg, key.str(), pmod->permanent, 0, 120, "");
CUtils::ToUpper(pmod->permanent);
int ivalue;
key.str("");key.clear();
key << "module.[" << i << "].usertimeout";
get_value(cfg, key.str(), ivalue, 0, 300, 300);
pmod->usertimeout = (unsigned int)ivalue;
bool bvalue;
key.str("");key.clear();
key << "module.[" << i << "].callsignswitch";
get_value(cfg, key.str(), bvalue, false);
pmod->callsignswitch = bvalue ? SCS_GROUP_CALLSIGN : SCS_USER_CALLSIGN;
key.str("");key.clear();
key << "module.[" << i << "].txmsgswitch";
get_value(cfg, key.str(), pmod->txmsgswitch, true);
key.str("");key.clear();
key << "module.[" << i << "].reflector";
if (! get_value(cfg, key.str(), basename, 8, 8, "")) {
printf("reflector %d must be undefined or exactly 8 chars!\n", i);
basename.empty();
}
pmod->reflector.empty();
if (basename.size()) {
CUtils::ToUpper(basename);
if ( (0==basename.compare(0,3,"XRF") || 0==basename.compare(0,3,"DCS")) && isdigit(basename[3]) && isdigit(basename[4]) && isdigit(basename[5]) && ' '==basename[6] && isalpha(basename[7]) )
pmod->reflector = basename;
}
printf("Module %d: callsign='%s' unsubscribe='%s' info='%s' permanent='%s' usertimeout=%d callsignswitch=%s, txmsgswitch=%s reflector='%s'\n",
i, pmod->callsign.c_str(), pmod->logoff.c_str(), pmod->info.c_str(), pmod->permanent.c_str(), pmod->usertimeout,
SCS_GROUP_CALLSIGN==pmod->callsignswitch ? "Group" : "User", pmod->txmsgswitch ? "true" : "false", pmod->reflector.c_str());
m_module.push_back(pmod);
}
// remote control
get_value(cfg, "remote.enabled", m_remoteEnabled, false);
if (m_remoteEnabled) {
get_value(cfg, "remote.password", m_remotePassword, 6, 30, "");
int ivalue;
get_value(cfg, "remote.port", ivalue, 1000, 65000, 39999);
m_remotePort = (unsigned int)ivalue;
printf("Remote enabled: password='%s', port=%d\n", m_remotePassword.c_str(), m_remotePort);
} else {
m_remotePort = 0U;
m_remotePassword.empty();
printf("Remote disabled\n");
}
}
CSGSConfig::~CSGSConfig()
{
while (m_module.size()) {
delete m_module.back();
m_module.pop_back();
}
while(m_ircDDB.size()) {
delete m_ircDDB.back();
m_ircDDB.pop_back();
}
}
unsigned int CSGSConfig::getModCount()
{
return m_module.size();
}
unsigned int CSGSConfig::getIrcDDBCount()
{
return m_ircDDB.size();
}
unsigned int CSGSConfig::getLinkCount(const char *type)
{
unsigned int count = 0;
for (unsigned int i=0; i<getModCount(); i++)
if (0 == m_module[i]->reflector.compare(0, 3, type))
count++;
return count;
}
bool CSGSConfig::get_value(const Config &cfg, const std::string &path, int &value, int min, int max, int default_value)
{
if (cfg.lookupValue(path, value)) {
if (value < min || value > max)
value = default_value;
} else
value = default_value;
return true;
}
bool CSGSConfig::get_value(const Config &cfg, const std::string &path, bool &value, bool default_value)
{
if (! cfg.lookupValue(path, value))
value = default_value;
return true;
}
bool CSGSConfig::get_value(const Config &cfg, const std::string &path, std::string &value, int min, int max, const std::string &default_value)
{
if (cfg.lookupValue(path, value)) {
int l = value.length();
if (l<min || l>max) {
std::cout << path << "=" << value << " has an inalid length, must be between " << min << " and " << max << " actual " << l << "\n";
return false;
}
} else
value = default_value;
return true;
}
void CSGSConfig::getGateway(std::string& callsign, std::string& address) const
{
callsign = m_callsign;
address = m_address;
}
void CSGSConfig::getIrcDDB(unsigned int ircddb, std::string& hostname, std::string& username, std::string& password, bool &isQuadNet) const
{
hostname = m_ircDDB[ircddb]->hostname;
username = m_ircDDB[ircddb]->username;
password = m_ircDDB[ircddb]->password;
isQuadNet = m_ircDDB[ircddb]->isQuadNet;
}
void CSGSConfig::getGroup(unsigned int mod, std::string& band, std::string& callsign, std::string& logoff, std::string& info, std::string& permanent, unsigned int& userTimeout, CALLSIGN_SWITCH& callsignSwitch, bool& txMsgSwitch, std::string& reflector) const
{
band = m_module[mod]->band;
callsign = m_module[mod]->callsign;
logoff = m_module[mod]->logoff;
info = m_module[mod]->info;
permanent = m_module[mod]->permanent;
userTimeout = m_module[mod]->usertimeout;
callsignSwitch = m_module[mod]->callsignswitch;
txMsgSwitch = m_module[mod]->txmsgswitch;
reflector = m_module[mod]->reflector;
}
void CSGSConfig::getRemote(bool& enabled, std::string& password, unsigned int& port) const
{
enabled = m_remoteEnabled;
password = m_remotePassword;
port = m_remotePort;
}

@ -0,0 +1,78 @@
/*
* Copyright (C) 2010,2011,2012,2014 by Jonathan Naylor G4KLX
* Copyright (c) 2017,2018 by Thomas A. Early
*
* 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>
#include <vector>
#include <libconfig.h++>
#include "Defs.h"
using namespace libconfig;
struct Smodule {
std::string band;
std::string callsign;
std::string logoff;
std::string info;
std::string permanent;
std::string reflector;
bool txmsgswitch;
unsigned int usertimeout;
CALLSIGN_SWITCH callsignswitch;
};
struct SircDDB {
std::string hostname;
std::string username;
std::string password;
bool isQuadNet;
};
class CSGSConfig {
public:
CSGSConfig(const std::string &pathname);
~CSGSConfig();
void getGateway(std::string &callsign, std::string &address) const;
void getIrcDDB(unsigned int ircddb, std::string &hostname, std::string &username, std::string &password, bool &isQuadNet) const;
void getGroup(unsigned int mod, std::string &band, std::string &callsign, std::string &logoff, std::string &info, std::string &permanent, unsigned int &userTimeout, CALLSIGN_SWITCH &callsignSwitch, bool &txMsgSwitch, std::string &reflector) const;
void getRemote(bool &enabled, std::string &password, unsigned int &port) const;
unsigned int getModCount();
unsigned int getLinkCount(const char *type);
unsigned int getIrcDDBCount();
private:
bool get_value(const Config &cfg, const std::string &path, int &value, int min, int max, int default_value);
bool get_value(const Config &cfg, const std::string &path, bool &value, bool default_value);
bool get_value(const Config &cfg, const std::string &path, std::string &value, int min, int max, const std::string &default_value);
std::string m_fileName;
std::string m_callsign;
std::string m_address;
std::vector<struct Smodule *> m_module;
std::vector<struct SircDDB *> m_ircDDB;
bool m_remoteEnabled;
std::string m_remotePassword;
unsigned int m_remotePort;
}
;

@ -0,0 +1,480 @@
/*
* Copyright (C) 2010-2013,2015 by Jonathan Naylor G4KLX
* Copyright (c) 2017-2018 by Thomas 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 <thread>
#include <chrono>
#include <sys/types.h>
#include <sys/stat.h>
#include <netdb.h>
#include <pwd.h>
#include <ctime>
#include <fstream>
#include <cstring>
#include <cassert>
#include "SGSThread.h"
#include "GroupHandler.h"
#include "DExtraHandler.h" // DEXTRA LINK
#include "DCSHandler.h" // DCS LINK
#include "HeaderData.h"
#include "G2Handler.h"
#include "AMBEData.h"
#include "Utils.h"
const unsigned int REMOTE_DUMMY_PORT = 65015U;
CSGSThread::CSGSThread(unsigned int countDExtra, unsigned int countDCS) :
m_countDExtra(countDExtra),
m_countDCS(countDCS),
m_killed(false),
m_stopped(true),
m_callsign(),
m_address(),
m_g2Handler(NULL),
m_irc(NULL),
m_cache(),
m_logEnabled(false),
m_statusTimer(1000U, 1U), // 1 second
m_lastStatus(IS_DISCONNECTED),
m_remoteEnabled(false),
m_remotePassword(),
m_remotePort(0U),
m_remote(NULL)
{
CHeaderData::initialise();
CG2Handler::initialise(0);
printf("SGSThread created. DExtra channels: %d, DCS Channels: %d\n", countDExtra, countDCS);
}
CSGSThread::~CSGSThread()
{
CHeaderData::finalise();
CG2Handler::finalise();
CGroupHandler::finalise();
CDExtraHandler::finalise();
CDCSHandler::finalise();
printf("SGSThread destroyed\n");
}
void CSGSThread::run()
{
m_g2Handler = new CG2ProtocolHandler(G2_DV_PORT, m_address);
bool ret = m_g2Handler->open();
if (!ret) {
printf("Could not open the G2 protocol handler\n");
delete m_g2Handler;
m_g2Handler = NULL;
}
// Wait here until we have the essentials to run
while (!m_killed && (m_g2Handler == NULL || m_irc == NULL || 0==m_callsign.size()))
std::this_thread::sleep_for(std::chrono::milliseconds(500));
if (m_killed)
return;
m_stopped = false;
printf("Starting the Smart Group Server thread\n");
loadReflectors(DEXTRA_HOSTS_FILE_NAME, DP_DEXTRA);
loadReflectors(DCS_HOSTS_FILE_NAME, DP_DCS);
CDExtraProtocolHandlerPool dextraPool(DEXTRA_PORT, m_address);
CDCSProtocolHandlerPool dcsPool(DCS_PORT, m_address);
CG2Handler::setG2ProtocolHandler(m_g2Handler);
CDExtraHandler::setCallsign(m_callsign);
CDExtraHandler::setDExtraProtocolHandlerPool(&dextraPool);
CDCSHandler::setDCSProtocolHandlerPool(&dcsPool);
CDCSHandler::setGatewayType(GT_SMARTGROUP);
CGroupHandler::setCache(&m_cache);
CGroupHandler::setGateway(m_callsign);
CGroupHandler::setG2Handler(m_g2Handler);
CGroupHandler::setIRC(m_irc);
if (m_countDExtra || m_countDCS)
CGroupHandler::link();
if (m_remoteEnabled && m_remotePassword.size() && m_remotePort > 0U) {
m_remote = new CRemoteHandler(m_remotePassword, m_remotePort);
bool res = m_remote->open();
if (!res) {
delete m_remote;
m_remote = NULL;
}
}
time_t start;
time(&start);
m_statusTimer.start();
try {
while (!m_killed) {
processIrcDDB();
processG2();
processDExtra(&dextraPool);
processDCS(&dcsPool);
if (m_remote != NULL)
m_remote->process();
time_t now;
time(&now);
unsigned long ms = (unsigned long)(1000.0 * difftime(now, start));
time(&start);
m_statusTimer.clock(ms);
CG2Handler::clock(ms);
CGroupHandler::clock(ms);
CDExtraHandler::clock(ms);
CDCSHandler::clock(ms);
std::this_thread::sleep_for(std::chrono::milliseconds(TIME_PER_TIC_MS));
}
}
catch (std::exception& e) {
printf("Exception raised - \"%s\"\n", e.what());
}
catch (...) {
printf("Unknown exception raised\n");
}
printf("Stopping the Smart Group Server thread\n");
// Unlink from all reflectors
CDExtraHandler::unlink();
dextraPool.close();
// Unlink from all reflectors
CDCSHandler::unlink();
dcsPool.close();
m_g2Handler->close();
delete m_g2Handler;
m_irc->close();
delete m_irc;
if (m_remote != NULL) {
m_remote->close();
delete m_remote;
}
}
void CSGSThread::kill()
{
m_killed = true;
}
void CSGSThread::setCallsign(const std::string& callsign)
{
if (!m_stopped)
return;
m_callsign = callsign;
}
void CSGSThread::setAddress(const std::string& address)
{
m_address = address;
}
void CSGSThread::addGroup(const std::string& callsign, const std::string& logoff, const std::string& repeater, const std::string& infoText, const std::string& permanent, unsigned int userTimeout, CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const std::string& reflector)
{
CGroupHandler::add(callsign, logoff, repeater, infoText, permanent, userTimeout, callsignSwitch, txMsgSwitch, reflector);
}
void CSGSThread::setIRC(CIRCDDB* irc)
{
assert(irc != NULL);
m_irc = irc;
}
void CSGSThread::setRemote(bool enabled, const std::string& password, unsigned int port)
{
if (enabled) {
m_remoteEnabled = true;
m_remotePassword = password;
m_remotePort = port;
} else {
m_remoteEnabled = false;
m_remotePassword = password;
m_remotePort = REMOTE_DUMMY_PORT;
}
}
void CSGSThread::processIrcDDB()
{
// Once per second
if (m_statusTimer.hasExpired()) {
int status = m_irc->getConnectionState();
switch (status) {
case 0:
case 10:
if (m_lastStatus != IS_DISCONNECTED) {
printf("Disconnected from ircDDB\n");
m_lastStatus = IS_DISCONNECTED;
}
break;
case 7:
if (m_lastStatus != IS_CONNECTED) {
printf("Connected to ircDDB\n");
m_lastStatus = IS_CONNECTED;
}
break;
default:
if (m_lastStatus != IS_CONNECTING) {
printf("Connecting to ircDDB\n");
m_lastStatus = IS_CONNECTING;
}
break;
}
m_statusTimer.start();
}
// Process all incoming ircDDB messages, updating the caches
for (;;) {
IRCDDB_RESPONSE_TYPE type = m_irc->getMessageType();
switch (type) {
case IDRT_NONE:
return;
case IDRT_USER: {
std::string user, repeater, gateway, address, timestamp;
bool res = m_irc->receiveUser(user, repeater, gateway, address, timestamp);
if (!res)
break;
if (address.size()) {
//printf("USER: %s %s %s %s\n", user.c_str(), repeater.c_str(), gateway.c_str(), address.c_str());
m_cache.updateUser(user, repeater, gateway, address, timestamp, DP_DEXTRA, false, false);
//} else {
// printf("USER: %s has no IP address!\n", user.c_str());
}
}
break;
case IDRT_REPEATER: {
std::string repeater, gateway, address;
bool res = m_irc->receiveRepeater(repeater, gateway, address);
if (!res)
break;
if (address.size()) {
// printf("REPEATER: %s %s %s\n", repeater.c_str(), gateway.c_str(), address.c_str());
m_cache.updateRepeater(repeater, gateway, address, DP_DEXTRA, false, false);
// } else {
// printf("REPEATER: %s NOT FOUND\n", repeater.c_str());
}
}
break;
case IDRT_GATEWAY: {
std::string gateway, address;
bool res = m_irc->receiveGateway(gateway, address);
if (!res)
break;
CDExtraHandler::gatewayUpdate(gateway, address);
CDCSHandler::gatewayUpdate(gateway, address);
if (0 == address.size()) {
// printf("GATEWAY: %s %s\n", gateway.c_str(), address.c_str());
m_cache.updateGateway(gateway, address, DP_DEXTRA, false, false);
// } else {
// printf("GATEWAY: %s NOT FOUND\n", gateway.c_str());
}
}
break;
}
}
}
void CSGSThread::processDExtra(CDExtraProtocolHandlerPool *dextraPool)
{
for (;;) {
DEXTRA_TYPE type = dextraPool->read();
switch (type) {
case DE_NONE:
return;
case DE_POLL: {
CPollData* poll = dextraPool->newPoll();
if (poll != NULL) {
CDExtraHandler::process(*poll);
delete poll;
}
}
break;
case DE_CONNECT: {
CConnectData* connect = dextraPool->newConnect();
if (connect != NULL) {
CDExtraHandler::process(*connect);
delete connect;
}
}
break;
case DE_HEADER: {
CHeaderData* header = dextraPool->newHeader();
if (header != NULL) {
// printf("DExtra header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s\n", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str());
CDExtraHandler::process(*header);
delete header;
}
}
break;
case DE_AMBE: {
CAMBEData* data = dextraPool->newAMBE();
if (data != NULL) {
CDExtraHandler::process(*data);
delete data;
}
}
break;
}
}
}
void CSGSThread::processDCS(CDCSProtocolHandlerPool *dcsPool)
{
for (;;) {
DCS_TYPE type = dcsPool->read();
switch (type) {
case DC_NONE:
return;
case DC_POLL: {
CPollData* poll = dcsPool->readPoll();
if (poll != NULL) {
CDCSHandler::process(*poll);
delete poll;
}
}
break;
case DC_CONNECT: {
CConnectData* connect = dcsPool->readConnect();
if (connect != NULL) {
CDCSHandler::process(*connect);
delete connect;
}
}
break;
case DC_DATA: {
CAMBEData* data = dcsPool->readData();
if (data != NULL) {
// printf("DCS header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s\n", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str());
CDCSHandler::process(*data);
delete data;
}
}
break;
}
}
}
void CSGSThread::processG2()
{
for (;;) {
G2_TYPE type = m_g2Handler->read();
switch (type) {
case GT_NONE:
return;
case GT_HEADER: {
CHeaderData* header = m_g2Handler->readHeader();
if (header != NULL) {
//printf("G2 header - My: %s/%s Your: %s Rpt1: %s Rpt2: %s Flags: %02X %02X %02X\n", header->getMyCall1().c_str(), header->getMyCall2().c_str(), header->getYourCall().c_str(), header->getRptCall1().c_str(), header->getRptCall2().c_str(), header->getFlag1(), header->getFlag2(), header->getFlag3());
CG2Handler::process(*header);
delete header;
}
}
break;
case GT_AMBE: {
CAMBEData* data = m_g2Handler->readAMBE();
if (data != NULL) {
CG2Handler::process(*data);
delete data;
}
}
break;
}
}
}
void CSGSThread::loadReflectors(const std::string fname, DSTAR_PROTOCOL dstarProtocol)
{
std::string filepath(CFG_DIR);
filepath += std::string("/") + fname;
struct stat sbuf;
if (stat(filepath.c_str(), &sbuf)) {
printf("%s doesn't exist!\n", filepath.c_str());
return;
}
std::ifstream hostfile;
hostfile.open(filepath, std::ifstream::in);
char line[256];
hostfile.getline(line, 256);
int count=0, tries=0;
while (hostfile.good()) {
const char *space = " \t\r";
char *first = strtok(line, space);
if (first) {
if ('#' != first[0]) {
tries++;
char *second = strtok(NULL, space);
if (second) {
char *third = strtok(NULL, space);
if (third && '#'==third[0])
third = NULL;
std::string name(first);
name.resize(7, ' ');
name.push_back('G');
struct hostent *he = gethostbyname(second);
if (he) {
count++;
std::string address(inet_ntoa(*(struct in_addr*)(he->h_addr_list[0])));
m_cache.updateGateway(name, address, dstarProtocol, third?1:0, true);
// printf("reflector:%s, address:%s lock:%s\n", name.c_str(), address.c_str(), third?"true":"false");
}
}
}
}
hostfile.getline(line, 256);
}
printf("Loaded %u of %u %s reflectors\n", count, tries, DP_DEXTRA==dstarProtocol?"DExtra":"DCS");
}

@ -0,0 +1,76 @@
/*
* Copyright (C) 2010-2015 by Jonathan Naylor G4KLX
* Copyright (c) 2017,2018 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 <string>
#include "DExtraProtocolHandlerPool.h" // DEXTRA_LINK
#include "DCSProtocolHandlerPool.h" // DCS_LINK
#include "G2ProtocolHandler.h"
#include "RemoteHandler.h"
#include "CacheManager.h"
#include "IRCDDB.h"
#include "Timer.h"
#include "Defs.h"
class CSGSThread {
public:
CSGSThread(unsigned int countDExtra, unsigned int countDCS);
virtual ~CSGSThread();
virtual void setCallsign(const std::string& callsign);
virtual void setAddress(const std::string& address);
virtual void addGroup(const std::string& callsign, const std::string& logoff, const std::string& repeater, const std::string& infoText, const std::string& permanent,
unsigned int userTimeout, CALLSIGN_SWITCH callsignSwitch, bool txMsgSwitch, const std::string& reflector);
virtual void setRemote(bool enabled, const std::string& password, unsigned int port);
virtual void setIRC(CIRCDDB* irc);
virtual void run();
virtual void kill();
private:
unsigned int m_countDExtra;
unsigned int m_countDCS;
bool m_killed;
bool m_stopped;
std::string m_callsign;
std::string m_address;
CG2ProtocolHandler *m_g2Handler;
CIRCDDB *m_irc;
CCacheManager m_cache;
bool m_logEnabled;
CTimer m_statusTimer;
IRCDDB_STATUS m_lastStatus;
bool m_remoteEnabled;
std::string m_remotePassword;
unsigned int m_remotePort;
CRemoteHandler *m_remote;
void processIrcDDB();
void processG2();
void loadReflectors(const std::string fname, DSTAR_PROTOCOL dstarProtocol);
void processDExtra(CDExtraProtocolHandlerPool *dextraPool);
void processDCS(CDCSProtocolHandlerPool *dcsPool);
};

@ -0,0 +1,373 @@
/*
* Copyright (C) 2005, 2006, 2008 Free Software Foundation, Inc.
* Copyright (C) 2011 by Jonathan Naylor G4KLX
* Copyright (c) 2017 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 <cassert>
#include <cstring>
#include "SHA256.h"
#ifdef WORDS_BIGENDIAN
# define SWAP(n) (n)
#else
# define SWAP(n) \
(((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24))
#endif
#define BLOCKSIZE 4096
#if BLOCKSIZE % 64 != 0
# error "invalid BLOCKSIZE"
#endif
/* This array contains the bytes used to pad the buffer to the next
64-byte boundary. */
static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ };
/*
Takes a pointer to a 256 bit block of data (eight 32 bit ints) and
intializes it to the start constants of the SHA256 algorithm. This
must be called before using hash in the call to sha256_hash
*/
CSHA256::CSHA256() :
m_state(NULL),
m_total(NULL),
m_buflen(0U),
m_buffer(NULL)
{
m_state = new uint32_t[8U];
m_total = new uint32_t[2U];
m_buffer = new uint32_t[32U];
init();
}
CSHA256::~CSHA256()
{
delete[] m_state;
delete[] m_total;
delete[] m_buffer;
}
void CSHA256::init()
{
m_state[0] = 0x6a09e667UL;
m_state[1] = 0xbb67ae85UL;
m_state[2] = 0x3c6ef372UL;
m_state[3] = 0xa54ff53aUL;
m_state[4] = 0x510e527fUL;
m_state[5] = 0x9b05688cUL;
m_state[6] = 0x1f83d9abUL;
m_state[7] = 0x5be0cd19UL;
m_total[0] = m_total[1] = 0;
m_buflen = 0;
}
/* Copy the value from v into the memory location pointed to by *cp,
If your architecture allows unaligned access this is equivalent to
* (uint32_t *) cp = v */
static inline void set_uint32(unsigned char* cp, uint32_t v)
{
assert(cp != NULL);
memcpy(cp, &v, sizeof v);
}
/* Put result from CTX in first 32 bytes following RESBUF. The result
must be in little endian byte order. */
unsigned char* CSHA256::read(unsigned char* resbuf)
{
assert(resbuf != NULL);
for (unsigned int i = 0U; i < 8U; i++)
set_uint32(resbuf + i * sizeof(m_state[0]), SWAP(m_state[i]));
return resbuf;
}
/* Process the remaining bytes in the internal buffer and the usual
prolog according to the standard and write the result to RESBUF. */
void CSHA256::conclude()
{
/* Take yet unprocessed bytes into account. */
unsigned int bytes = m_buflen;
unsigned int size = (bytes < 56) ? 64 / 4 : 64 * 2 / 4;
/* Now count remaining bytes. */
m_total[0] += bytes;
if (m_total[0] < bytes)
++m_total[1];
/* Put the 64-bit file length in *bits* at the end of the buffer.
Use set_uint32 rather than a simple assignment, to avoid risk of
unaligned access. */
set_uint32((unsigned char*)&m_buffer[size - 2], SWAP((m_total[1] << 3) | (m_total[0] >> 29)));
set_uint32((unsigned char*)&m_buffer[size - 1], SWAP(m_total[0] << 3));
memcpy(&((char*)m_buffer)[bytes], fillbuf, (size - 2) * 4 - bytes);
/* Process last bytes. */
processBlock((unsigned char*)m_buffer, size * 4);
}
unsigned char* CSHA256::finish(unsigned char* resbuf)
{
assert(resbuf != NULL);
conclude();
return read(resbuf);
}
/* Compute SHA256 message digest for LEN bytes beginning at BUFFER. The
result is always in little endian byte order, so that a byte-wise
output yields to the wanted ASCII representation of the message
digest. */
unsigned char* CSHA256::buffer(const unsigned char* buffer, unsigned int len, unsigned char* resblock)
{
assert(buffer != NULL);
assert(resblock != NULL);
/* Initialize the computation context. */
init();
/* Process whole buffer but last len % 64 bytes. */
processBytes(buffer, len);
/* Put result in desired memory area. */
return finish(resblock);
}
void CSHA256::processBytes(const unsigned char* buffer, unsigned int len)
{
assert(buffer != NULL);
/* When we already have some bits in our internal buffer concatenate
both inputs first. */
if (m_buflen != 0U) {
unsigned int left_over = m_buflen;
unsigned int add = 128U - left_over > len ? len : 128U - left_over;
memcpy(&((char*)m_buffer)[left_over], buffer, add);
m_buflen += add;
if (m_buflen > 64U) {
processBlock((unsigned char*)m_buffer, m_buflen & ~63U);
m_buflen &= 63U;
/* The regions in the following copy operation cannot overlap. */
memcpy(m_buffer, &((char*)m_buffer)[(left_over + add) & ~63U], m_buflen);
}
buffer += add;
len -= add;
}
/* Process available complete blocks. */
if (len >= 64U) {
//#if !_STRING_ARCH_unaligned
//# define alignof(type) offsetof (struct { char c; type x; }, x)
//# define UNALIGNED_P(p) (((unsigned int) p) % alignof (uint32_t) != 0)
// if (UNALIGNED_P (buffer)) {
// while (len > 64U) {
// memcpy(m_buffer, buffer, 64U);
// processBlock((unsigned char*)m_buffer, 64U);
// buffer += 64U;
// len -= 64U;
// }
// } else
//#endif
{
processBlock(buffer, len & ~63U);
buffer += (len & ~63U);
len &= 63U;
}
}
/* Move remaining bytes in internal buffer. */
if (len > 0U) {
unsigned int left_over = m_buflen;
memcpy(&((char*)m_buffer)[left_over], buffer, len);
left_over += len;
if (left_over >= 64U) {
processBlock((unsigned char*)m_buffer, 64U);
left_over -= 64U;
memcpy(m_buffer, &m_buffer[16], left_over);
}
m_buflen = left_over;
}
}
/* --- Code below is the primary difference between sha1.c and sha256.c --- */
/* SHA256 round constants */
#define K(I) roundConstants[I]
static const uint32_t roundConstants[64] = {
0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL,
0x3956c25bUL, 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL,
0xd807aa98UL, 0x12835b01UL, 0x243185beUL, 0x550c7dc3UL,
0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, 0xc19bf174UL,
0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL,
0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL,
0x983e5152UL, 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL,
0xc6e00bf3UL, 0xd5a79147UL, 0x06ca6351UL, 0x14292967UL,
0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, 0x53380d13UL,
0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL,
0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL,
0xd192e819UL, 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL,
0x19a4c116UL, 0x1e376c08UL, 0x2748774cUL, 0x34b0bcb5UL,
0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, 0x682e6ff3UL,
0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL,
0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL,
};
/* Round functions. */
#define F2(A,B,C) ( ( A & B ) | ( C & ( A | B ) ) )
#define F1(E,F,G) ( G ^ ( E & ( F ^ G ) ) )
/* Process LEN bytes of BUFFER, accumulating context into CTX.
It is assumed that LEN % 64 == 0.
Most of this code comes from GnuPG's cipher/sha1.c. */
void CSHA256::processBlock(const unsigned char* buffer, unsigned int len)
{
assert(buffer != NULL);
const uint32_t* words = (uint32_t*)buffer;
unsigned int nwords = len / sizeof(uint32_t);
const uint32_t* endp = words + nwords;
uint32_t x[16];
uint32_t a = m_state[0];
uint32_t b = m_state[1];
uint32_t c = m_state[2];
uint32_t d = m_state[3];
uint32_t e = m_state[4];
uint32_t f = m_state[5];
uint32_t g = m_state[6];
uint32_t h = m_state[7];
/* First increment the byte count. FIPS PUB 180-2 specifies the possible
length of the file up to 2^64 bits. Here we only compute the
number of bytes. Do a double word increment. */
m_total[0] += len;
if (m_total[0] < len)
++m_total[1];
#define rol(x, n) (((x) << (n)) | ((x) >> (32 - (n))))
#define S0(x) (rol(x,25)^rol(x,14)^(x>>3))
#define S1(x) (rol(x,15)^rol(x,13)^(x>>10))
#define SS0(x) (rol(x,30)^rol(x,19)^rol(x,10))
#define SS1(x) (rol(x,26)^rol(x,21)^rol(x,7))
#define M(I) (tm = S1(x[(I-2)&0x0f]) + x[(I-7)&0x0f] + S0(x[(I-15)&0x0f]) + x[I&0x0f], x[I&0x0f] = tm)
#define R(A,B,C,D,E,F,G,H,K,M) do { t0 = SS0(A) + F2(A,B,C); \
t1 = H + SS1(E) + F1(E,F,G) + K + M; \
D += t1; H = t0 + t1; \
} while(0)
while (words < endp) {
uint32_t tm;
uint32_t t0, t1;
/* FIXME: see sha1.c for a better implementation. */
for (unsigned int t = 0U; t < 16U; t++) {
x[t] = SWAP(*words);
words++;
}
R( a, b, c, d, e, f, g, h, K( 0), x[ 0] );
R( h, a, b, c, d, e, f, g, K( 1), x[ 1] );
R( g, h, a, b, c, d, e, f, K( 2), x[ 2] );
R( f, g, h, a, b, c, d, e, K( 3), x[ 3] );
R( e, f, g, h, a, b, c, d, K( 4), x[ 4] );
R( d, e, f, g, h, a, b, c, K( 5), x[ 5] );
R( c, d, e, f, g, h, a, b, K( 6), x[ 6] );
R( b, c, d, e, f, g, h, a, K( 7), x[ 7] );
R( a, b, c, d, e, f, g, h, K( 8), x[ 8] );
R( h, a, b, c, d, e, f, g, K( 9), x[ 9] );
R( g, h, a, b, c, d, e, f, K(10), x[10] );
R( f, g, h, a, b, c, d, e, K(11), x[11] );
R( e, f, g, h, a, b, c, d, K(12), x[12] );
R( d, e, f, g, h, a, b, c, K(13), x[13] );
R( c, d, e, f, g, h, a, b, K(14), x[14] );
R( b, c, d, e, f, g, h, a, K(15), x[15] );
R( a, b, c, d, e, f, g, h, K(16), M(16) );
R( h, a, b, c, d, e, f, g, K(17), M(17) );
R( g, h, a, b, c, d, e, f, K(18), M(18) );
R( f, g, h, a, b, c, d, e, K(19), M(19) );
R( e, f, g, h, a, b, c, d, K(20), M(20) );
R( d, e, f, g, h, a, b, c, K(21), M(21) );
R( c, d, e, f, g, h, a, b, K(22), M(22) );
R( b, c, d, e, f, g, h, a, K(23), M(23) );
R( a, b, c, d, e, f, g, h, K(24), M(24) );
R( h, a, b, c, d, e, f, g, K(25), M(25) );
R( g, h, a, b, c, d, e, f, K(26), M(26) );
R( f, g, h, a, b, c, d, e, K(27), M(27) );
R( e, f, g, h, a, b, c, d, K(28), M(28) );
R( d, e, f, g, h, a, b, c, K(29), M(29) );
R( c, d, e, f, g, h, a, b, K(30), M(30) );
R( b, c, d, e, f, g, h, a, K(31), M(31) );
R( a, b, c, d, e, f, g, h, K(32), M(32) );
R( h, a, b, c, d, e, f, g, K(33), M(33) );
R( g, h, a, b, c, d, e, f, K(34), M(34) );
R( f, g, h, a, b, c, d, e, K(35), M(35) );
R( e, f, g, h, a, b, c, d, K(36), M(36) );
R( d, e, f, g, h, a, b, c, K(37), M(37) );
R( c, d, e, f, g, h, a, b, K(38), M(38) );
R( b, c, d, e, f, g, h, a, K(39), M(39) );
R( a, b, c, d, e, f, g, h, K(40), M(40) );
R( h, a, b, c, d, e, f, g, K(41), M(41) );
R( g, h, a, b, c, d, e, f, K(42), M(42) );
R( f, g, h, a, b, c, d, e, K(43), M(43) );
R( e, f, g, h, a, b, c, d, K(44), M(44) );
R( d, e, f, g, h, a, b, c, K(45), M(45) );
R( c, d, e, f, g, h, a, b, K(46), M(46) );
R( b, c, d, e, f, g, h, a, K(47), M(47) );
R( a, b, c, d, e, f, g, h, K(48), M(48) );
R( h, a, b, c, d, e, f, g, K(49), M(49) );
R( g, h, a, b, c, d, e, f, K(50), M(50) );
R( f, g, h, a, b, c, d, e, K(51), M(51) );
R( e, f, g, h, a, b, c, d, K(52), M(52) );
R( d, e, f, g, h, a, b, c, K(53), M(53) );
R( c, d, e, f, g, h, a, b, K(54), M(54) );
R( b, c, d, e, f, g, h, a, K(55), M(55) );
R( a, b, c, d, e, f, g, h, K(56), M(56) );
R( h, a, b, c, d, e, f, g, K(57), M(57) );
R( g, h, a, b, c, d, e, f, K(58), M(58) );
R( f, g, h, a, b, c, d, e, K(59), M(59) );
R( e, f, g, h, a, b, c, d, K(60), M(60) );
R( d, e, f, g, h, a, b, c, K(61), M(61) );
R( c, d, e, f, g, h, a, b, K(62), M(62) );
R( b, c, d, e, f, g, h, a, K(63), M(63) );
a = m_state[0] += a;
b = m_state[1] += b;
c = m_state[2] += c;
d = m_state[3] += d;
e = m_state[4] += e;
f = m_state[5] += f;
g = m_state[6] += g;
h = m_state[7] += h;
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save

Powered by TurnKey Linux.