/* 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 . */ #include "IRCDDBClient.h" #include "IRCClient.h" #include "IRCDDBApp.h" #include "Utils.h" #include "Log.h" struct CIRCDDBClientPrivate { IRCClient *client; IRCDDBApp *m_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 ) : m_d(new CIRCDDBClientPrivate), m_isQuadNet(isQuadNet) { std::string update_channel("#dstar"); m_d->m_app = new IRCDDBApp(update_channel); m_d->client = new IRCClient(m_d->m_app, update_channel, hostName, port, callsign, password, versionInfo, localAddr); } CIRCDDBClient::~CIRCDDBClient() { delete m_d->client; delete m_d->m_app; delete m_d; } // A false return implies a network error, or unable to log in bool CIRCDDBClient::open() { CLog::logInfo("IRCDDB start client and app\n"); m_d->client->startWork(); m_d->m_app->startWork(); return true; } int CIRCDDBClient::getConnectionState() { return m_d->m_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) { m_d->m_app->rptrQTH(callsign, latitude, longitude, desc1, desc2, infoURL); } void CIRCDDBClient::rptrQRG(const std::string& callsign, double txFrequency, double duplexShift, double range, double agl) { m_d->m_app->rptrQRG(callsign, txFrequency, duplexShift, range, agl); } void CIRCDDBClient::kickWatchdog(const std::string& callsign, const std::string& wdInfo) { m_d->m_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) { CLog::logDebug("CIRCDDBClient::sendHeard:myCall='%s' len != 8\n", myCall.c_str()); return false; } if (myCallExt.size() != 4) { CLog::logDebug("CIRCDDBClient::sendHeard:myCallExt='%s' len != 4\n", myCallExt.c_str()); return false; } if (yourCall.size() != 8) { CLog::logDebug("CIRCDDBClient::sendHeard:yourCall='%s' len != 8\n", yourCall.c_str()); return false; } if (rpt1.size() != 8) { CLog::logDebug("CIRCDDBClient::sendHeard:rpt1='%s' len != 8\n", rpt1.c_str()); return false; } if (rpt2.size() != 8) { CLog::logDebug("CIRCDDBClient::sendHeard:rpt2='%s' len != 8\n", rpt2.c_str()); return false; } return m_d->m_app->sendHeard(myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, std::string(" "), std::string(""), std::string("")); } void CIRCDDBClient::sendDStarGatewayInfo(const std::string subcommand, const std::vector parms) { CLog::logDebug("CIRCDDBClient::sendDStarGatewayInfo subcommand %s parms", subcommand.c_str()); for(unsigned int i=0; i < parms.size();i++) CLog::logInfo(" %s", parms[i].c_str()); CLog::logInfo("\n"); if(m_isQuadNet) { m_d->m_app->sendDStarGatewayInfo(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) { CLog::logDebug("CIRCDDBClient::sendHeardWithTXMsg:myCall='%s' len != 8\n", myCall.c_str()); return false; } if (myCallExt.size() != 4) { CLog::logDebug("CIRCDDBClient::sendHeardWithTXMsg:myCallExt='%s' len != 4\n", myCallExt.c_str()); return false; } if (yourCall.size() != 8) { CLog::logDebug("CIRCDDBClient::sendHeardWithTXMsg:yourCall='%s' len != 8\n", yourCall.c_str()); return false; } if (rpt1.size() != 8) { CLog::logDebug("CIRCDDBClient::sendHeardWithTXMsg:rpt1='%s' len != 8\n", rpt1.c_str()); return false; } if (rpt2.size() != 8) { CLog::logDebug("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()) { CLog::logDebug("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 m_d->m_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)) { CLog::logDebug("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) { CLog::logDebug("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 CLog::logDebug("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) { CLog::logDebug("CIRCDDBClient::sendHeardWithTXStats:myCall='%s' len != 8\n", myCall.c_str()); return false; } if (myCallExt.size() != 4) { CLog::logDebug("CIRCDDBClient::sendHeardWithTXStats:myCallExt='%s' len != 4\n", myCallExt.c_str()); return false; } if (yourCall.size() != 8) { CLog::logDebug("CIRCDDBClient::sendHeardWithTXStats:yourCall='%s' len != 8\n", yourCall.c_str()); return false; } if (rpt1.size() != 8) { CLog::logDebug("CIRCDDBClient::sendHeardWithTXStats:rpt1='%s' len != 8\n", rpt1.c_str()); return false; } if (rpt2.size() != 8) { CLog::logDebug("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 m_d->m_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()) { CLog::logDebug("CIRCDDBClient::findGateway:gatewayCallsign='%s' len != 8\n", gatewayCallsign.c_str()); return false; } std::string gw(gatewayCallsign); CUtils::ToUpper(gw); return m_d->m_app->findGateway(gw); } bool CIRCDDBClient::findRepeater(const std::string& repeaterCallsign) { if (8 != repeaterCallsign.size()) { CLog::logDebug("CIRCDDBClient::findRepeater:repeaterCallsign='%s' len != 8\n", repeaterCallsign.c_str()); return false; } std::string rptr(repeaterCallsign); CUtils::ToUpper(rptr); return m_d->m_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()) { CLog::logDebug("CIRCDDBClient::findUser:userCall='%s' len != 8\n", userCallsign.c_str()); return false; } CLog::logTrace("IRC Find user %s", userCallsign.c_str()); std::string usr(userCallsign); CUtils::ToUpper(usr); return m_d->m_app->findUser(usr); } bool CIRCDDBClient::notifyRepeaterG2NatTraversal(const std::string& repeater) { return m_d->m_app->notifyRepeaterG2NatTraversal(repeater); } bool CIRCDDBClient::notifyRepeaterDextraNatTraversal(const std::string& repeater, unsigned int myPort) { return m_d->m_app->notifyRepeaterDextraNatTraversal(repeater, myPort); } bool CIRCDDBClient::notifyRepeaterDPlusNatTraversal(const std::string& repeater, unsigned int myPort) { return m_d->m_app->notifyRepeaterDPlusNatTraversal(repeater, myPort); } // The following functions are for processing received messages // Get the waiting message type IRCDDB_RESPONSE_TYPE CIRCDDBClient::getMessageType() { return m_d->m_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 = m_d->m_app->getReplyMessageType(); if (rt != IDRT_REPEATER) { CLog::logDebug("CIRCDDBClient::receiveRepeater: unexpected response type=%d\n", rt); return false; } IRCMessage *m = m_d->m_app->getReplyMessage(); if (m == NULL) { CLog::logDebug("CIRCDDBClient::receiveRepeater: no message\n"); return false; } if (m->getCommand().compare("IDRT_REPEATER")) { CLog::logDebug("CIRCDDBClient::receiveRepeater: wrong message type, expected 'IDRT_REPEATER, got '%s'\n", m->getCommand().c_str()); delete m; return false; } if (3 != m->getParamCount()) { CLog::logDebug("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 = m_d->m_app->getReplyMessageType(); if (rt != IDRT_GATEWAY) { CLog::logDebug("CIRCDDBClient::receiveGateway: unexpected response type=%d\n", rt); return false; } IRCMessage *m = m_d->m_app->getReplyMessage(); if (m == NULL) { CLog::logDebug("CIRCDDBClient::receiveGateway: no message\n"); return false; } if (m->getCommand().compare("IDRT_GATEWAY")) { CLog::logDebug("CIRCDDBClient::receiveGateway: wrong message type, expected 'IDRT_GATEWAY' got '%s'\n", m->getCommand().c_str()); delete m; return false; } if (2 != m->getParamCount()) { CLog::logDebug("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 = m_d->m_app->getReplyMessageType(); if (rt != IDRT_USER) { CLog::logDebug("CIRCDDBClient::receiveUser: unexpected response type=%d\n", rt); return false; } IRCMessage * m = m_d->m_app->getReplyMessage(); if (m == NULL) { CLog::logDebug("CIRCDDBClient::receiveUser: no message\n"); return false; } if (m->getCommand().compare("IDRT_USER")) { CLog::logDebug("CIRCDDBClient::receiveUser: wrong message type, expected 'IDRT_USER', got '%s'\n", m->getCommand().c_str()); delete m; return false; } if (5 != m->getParamCount()) { CLog::logDebug("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); //CLog::logTrace("IRC Receive User %s %s %s %s %s", userCallsign.c_str(), repeaterCallsign.c_str(), gatewayCallsign.c_str(), address.c_str(), timeStamp.c_str()); delete m; return true; } bool CIRCDDBClient::receiveNATTraversalG2(std::string& address) { IRCDDB_RESPONSE_TYPE rt = m_d->m_app->getReplyMessageType(); if(rt != IDRT_NATTRAVERSAL_G2) { CLog::logDebug("CIRCDDBClient::receiveNATTraversalG2: unexpected response type=%d\n", rt); return false; } IRCMessage * m = m_d->m_app->getReplyMessage(); if (m == NULL) { CLog::logDebug("CIRCDDBClient::receiveNATTraversalG2: no message\n"); return false; } if (m->getCommand().compare("NATTRAVERSAL_G2")) { CLog::logDebug("CIRCDDBClient::receiveNATTraversalG2: wrong message type, expected 'NATTRAVERSAL_G2', got '%s'\n", m->getCommand().c_str()); delete m; return false; } if (1 != m->getParamCount()) { CLog::logDebug("CIRCDDBClient::receiveNATTraversalG2: unexpected number of message parameters, expected 1, got %d\n", m->getParamCount()); delete m; return false; } address = m->m_params[0]; delete m; return true; } bool CIRCDDBClient::receiveNATTraversalDextra(std::string& address, std::string& remotePort) { IRCDDB_RESPONSE_TYPE rt = m_d->m_app->getReplyMessageType(); if(rt != IDRT_NATTRAVERSAL_DEXTRA) { CLog::logDebug("CIRCDDBClient::receiveNATTraversalDextra: unexpected response type=%d\n", rt); return false; } IRCMessage * m = m_d->m_app->getReplyMessage(); if (m == NULL) { CLog::logDebug("CIRCDDBClient::receiveNATTraversalDextra: no message\n"); return false; } if (m->getCommand().compare("NATTRAVERSAL_DEXTRA")) { CLog::logDebug("CIRCDDBClient::receiveNATTraversalDextra: wrong message type, expected 'NATTRAVERSAL_DEXTRA', got '%s'\n", m->getCommand().c_str()); delete m; return false; } if (2 != m->getParamCount()) { CLog::logDebug("CIRCDDBClient::receiveNATTraversalDextra: unexpected number of message parameters, expected 2, got %d\n", m->getParamCount()); delete m; return false; } address = m->m_params[0]; remotePort = m->m_params[1]; delete m; return true; } bool CIRCDDBClient::receiveNATTraversalDPlus(std::string& address, std::string& remotePort) { IRCDDB_RESPONSE_TYPE rt = m_d->m_app->getReplyMessageType(); if(rt != IDRT_NATTRAVERSAL_DPLUS) { CLog::logDebug("CIRCDDBClient::receiveNATTraversalDPlus: unexpected response type=%d\n", rt); return false; } IRCMessage * m = m_d->m_app->getReplyMessage(); if (m == NULL) { CLog::logDebug("CIRCDDBClient::receiveNATTraversalDPlus: no message\n"); return false; } if (m->getCommand().compare("NATTRAVERSAL_DPLUS")) { CLog::logDebug("CIRCDDBClient::receiveNATTraversalDPlus: wrong message type, expected 'NATTRAVERSAL_DPLUS', got '%s'\n", m->getCommand().c_str()); delete m; return false; } if (2 != m->getParamCount()) { CLog::logDebug("CIRCDDBClient::receiveNATTraversalDPlus: unexpected number of message parameters, expected 2, got %d\n", m->getParamCount()); delete m; return false; } address = m->m_params[0]; remotePort = m->m_params[1]; delete m; return true; } void CIRCDDBClient::close() // Implictely kills any threads in the IRC code { m_d->client -> stopWork(); m_d->m_app -> stopWork(); }