From 3f480a923faa5959330caf0ab04c527067571972 Mon Sep 17 00:00:00 2001 From: ac2ie Date: Sat, 30 Nov 2013 18:32:26 -0500 Subject: [PATCH] initial commit --- BUILDING | 66 + CONFIGURING | 359 +++ COPYING | 340 +++ IRCApplication.h | 54 + IRCClient.cpp | 512 ++++ IRCClient.h | 76 + IRCDDB.cpp | 478 +++ IRCDDB.h | 173 ++ IRCDDBApp.cpp | 1333 +++++++++ IRCDDBApp.h | 98 + IRCMessage.cpp | 177 ++ IRCMessage.h | 71 + IRCMessageQueue.cpp | 137 + IRCMessageQueue.h | 79 + IRCProtocol.cpp | 447 +++ IRCProtocol.h | 65 + IRCReceiver.cpp | 249 ++ IRCReceiver.h | 62 + IRCutils.cpp | 180 ++ IRCutils.h | 31 + Makefile | 153 + announce/already_linked.dat | Bin 0 -> 3287 bytes announce/already_unlinked.dat | Bin 0 -> 3519 bytes announce/failed_linked.dat | Bin 0 -> 3229 bytes announce/id.dat | Bin 0 -> 2997 bytes announce/linked.dat | Bin 0 -> 2707 bytes announce/unlinked.dat | Bin 0 -> 2997 bytes build_wxBase | 33 + dstar_dv.cpp | 253 ++ dstar_dv.h | 48 + dvap_rptr.cfg | 81 + dvap_rptr.cpp | 2395 +++++++++++++++ exec_H.sh | 2 + exec_R.sh | 2 + g2_ircddb.cfg | 177 ++ g2_ircddb.cpp | 5124 ++++++++++++++++++++++++++++++++ g2_link.cfg | 96 + g2_link.cpp | 5142 +++++++++++++++++++++++++++++++++ g2link_test.cpp | 406 +++ g2link_test_audio.cpp | 399 +++ get_gwy_list.sh | 10 + golay23.cpp | 280 ++ golay23.h | 5 + gwys.txt | 99 + proc_g2_ircddb_dtmfs.sh | 150 + service.dvap_rptr | 60 + service.g2_ircddb | 60 + service.g2_link | 61 + service.proc_g2_ircddb_dtmfs | 63 + 49 files changed, 20086 insertions(+) create mode 100644 BUILDING create mode 100644 CONFIGURING create mode 100644 COPYING create mode 100644 IRCApplication.h create mode 100644 IRCClient.cpp create mode 100644 IRCClient.h create mode 100644 IRCDDB.cpp create mode 100644 IRCDDB.h create mode 100644 IRCDDBApp.cpp create mode 100644 IRCDDBApp.h create mode 100644 IRCMessage.cpp create mode 100644 IRCMessage.h create mode 100644 IRCMessageQueue.cpp create mode 100644 IRCMessageQueue.h create mode 100644 IRCProtocol.cpp create mode 100644 IRCProtocol.h create mode 100644 IRCReceiver.cpp create mode 100644 IRCReceiver.h create mode 100644 IRCutils.cpp create mode 100644 IRCutils.h create mode 100644 Makefile create mode 100644 announce/already_linked.dat create mode 100644 announce/already_unlinked.dat create mode 100644 announce/failed_linked.dat create mode 100644 announce/id.dat create mode 100644 announce/linked.dat create mode 100644 announce/unlinked.dat create mode 100755 build_wxBase create mode 100644 dstar_dv.cpp create mode 100644 dstar_dv.h create mode 100644 dvap_rptr.cfg create mode 100644 dvap_rptr.cpp create mode 100755 exec_H.sh create mode 100755 exec_R.sh create mode 100644 g2_ircddb.cfg create mode 100644 g2_ircddb.cpp create mode 100644 g2_link.cfg create mode 100644 g2_link.cpp create mode 100644 g2link_test.cpp create mode 100644 g2link_test_audio.cpp create mode 100755 get_gwy_list.sh create mode 100644 golay23.cpp create mode 100644 golay23.h create mode 100644 gwys.txt create mode 100755 proc_g2_ircddb_dtmfs.sh create mode 100755 service.dvap_rptr create mode 100755 service.g2_ircddb create mode 100755 service.g2_link create mode 100755 service.proc_g2_ircddb_dtmfs diff --git a/BUILDING b/BUILDING new file mode 100644 index 0000000..63fbdde --- /dev/null +++ b/BUILDING @@ -0,0 +1,66 @@ +Creating a DVAP hotspot based on Scott Lawson KI4LKF software is easy. + +I highly recommend doing this with Debian on a Raspberry Pi or a BeagleBone Black, but it should +also work on virtually an hardware platform with any Debian-based OS. UBUNTU should work, but +make no guarantees! + +The first order of business is to get the RasPi or the BBB up and running on a Debian image. +There is plenty of room on the interal 2GB memory of the BBB to install all of what follows. +(Unless you need a GUI, it that case you will need a lot more packages than I list below and +a big enough external uSD card to hold the image.) + +Look to http://raspberrypi.org for the RasPi Raspbian hard float image and to +http://www.armhf.com for the BBB Debian image. + +The RasPi should have all the needed packages but the BBB will not. +On the BBB you will have: +sudo su +apt-get update +apt-get upgrade +agt-get install make g++ unzip + +and maybe a few more. + +The first order of business (after you expand the archvive this file came in) is to build the +wxBase-2.8.12 library. There is a script to do all of this for you called "build_wsBase", +but if you want to do this by hand, cat this script and type the command manually. + +Come back to this directory and type "make" to build all the executables. If you need DTMFS +then also "build g2link_test". + +Now you should edit all the *.cfg files, g2_ircddb.cfg, g2_link.cfg and dvap_rptr.cfg. +These files contain detailed comments. Just read through them and edit accordingly. +Additional information about the configuration as well as other important and useful +features are also in the CONFIGURING file. + +The gwys.txt file is the internet address and port numbers for any gateway you would +like g2_link to be able to connect to. The one delivered with this package is special: +It has only XReflectors and DStar reflectors and the XReflectors are configures +with the 20001 port. This will allow you to connect to everybody without opening any +ports on your router. If you want to set up a repeater or access point, you will need +to port forward several ports to your RasPi or BBB. Of course you will need to lock +down the local address of the device with a static IP or reserving an address from +your router. Executing get_gwys_list.sh will download a HUGE list of reflectors and +repeaters with port address that my need forwarding to your sytem. + +If you plan on using DTMFS, you can also edit proc_g2_ircddb_dtmfs.sh to add new dtmfs +commands. + +Then install everything by typing "sudo make install". If you want DTMFS, type +"sudo make installdtmfs". + +Everything will install in /usr/local. The executables will be in /usr/local/bin and +the *.cfg files will be in /usr/local/etc. If you need to modify a configuration file, +do it in /usr/local/etc. + +At this point, you can either reboot to start the three or four services, or start them +manually with the "service" command. (See the man page for service.) + +You can clean up the intermediate *.o files with "make clean" and remove the intermediate +*.o files and binary executables with "make realclean". + +If you want to uninstall everything return to the build directory and type +"sudo make uninstall" and possibly "sudo make uninstalldtmfs". This will shutdown the +service scripts, remove everything from /usr/local and remove the service scripts. + +Tom Early, ac2ie@arrl.net diff --git a/CONFIGURING b/CONFIGURING new file mode 100644 index 0000000..b8c592a --- /dev/null +++ b/CONFIGURING @@ -0,0 +1,359 @@ +You need to configure three software programs, g2_ircddb, g2_link, dvap_rptr +by setting values in their *.cfg text files. Use your favorite text editor +to do this. Scott's comments within those files are mostly self explainatory. + +Some other features are discussed below in Scott's own edited words. + +Tom Early, ac2ie@arrl.net + +/* + * Copyright (C) 2010, 2011, 2012 by Scott Lawson KI4LKF + * + * 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. + */ + +G2_ircDDB adapted from the OpenG2 + + DESCRIPTION +================= + +g2_ircddb is a Dstar G2 gateway for the Dstar network with ircDDB routing +and USroot routing. It runs on Linux(as a Linux service). +ircddb originated from the OpenG2 with only one change. + Instead of using the local Postgres database server, + we use the remote IRC database server.(group2 or group1) + So, the difference between OpenG2 and ircddb is 0.01% +This new software g2_ircddb has been approved for use on the ircDDB network. + +IRC Gateways such as g2_ircdb (other IRC gateways copied from our OpenG2) +connect to group2 server(USA/Canada) or group1 server(Europe). +What are these group1, group2 servers? Group1 and group2 servers are really +the combination of 2 programs running together: + +These are: +---- An IRC (Internet Relay Chat) program, in this case it is the inspIRCd + program +---- the LDAP (Light Directory Access Protocol) program + +So, ircDDB database servers are NOTHING new, they were designed by groups +of people 20-30 years ago, before even the ICOM Dstar came out. +The German IRC team, copied the above programs (IRC, LDAP) from those groups, +and they called it group1 and group2. + +The reason for that was that an IRC(Internet Relay Chat) server, sends data +immediately, while the ICOM dstar network sends data 5 minutes later after a +user keyed up a Dstar repeater. So, IRC is faster, but it overloads your +home-network. + +Using a PERSONAL callsign to set up an ircDBB gateway +===================================================== +In g2_ircddb.cfg, set OWNER equal to your personal callsign. +In g2_link.cfg, set OWNER equal to your personal callsign. +In your repeater config file, set OWNER equal to your personal callsign. + +Using a REPEATER callsign to set up an ircDBB gateway +===================================================== +In g2_ircddb.cfg, set OWNER equal to a REPEATER callsign (that you received +from the ham authority of your Country). +In g2_link.cfg, set OWNER equal to a REPEATER callsign (that you received +from the ham authority of your Country) In your repeater config file, set +OWNER equal to a REPEATER callsign (that you received from the ham authority +of your Country). + +However, since the IRC database servers(group2 in Canada/USA, group1 in +Europe) do NOT allow personal callsigns to be used as gateways, then you may +be asking what is the point? + +Here is the logic: +In some Countries, it is very difficult to receive a GATEWAY (club/group) +callsign, sometimes it takes years. In cases such as these, hams only have +their own personal callsign to use. So, how do we get around the problem when +the IRC servers group1, group2 require a GATEWAY callsign, but the user only +has a PERSONAL callsign? + +Some groups of hams got together and installed their own IRC database server. +An IRC database server (like group1, group2) is really these 2 basic programs: + +---- inspIRCd server software which is an IRC server (Internet Relay Chat) + which was designed in 1988. One such IRC program is inspIRCd which was + chosen by the German IRC team for use in their group1, group2 installations. + +--- LDAP server software, which is the Light Directory Access Protocol server, + that was written in 1993. + +These 2 programs inspIRCd + LDAP make up the the IRC database servers as we know +them today and were installed together by the IRC team on group1, group2 +installations. + +That is all there is to it. + +When LDAP runs, it checks the Gateway password as listed in g2_ircddb.cfg: +IRC_PASS=... When LDAP does NOT run, then the IRC_PASS is NOT checked. + +So, some groups of hams, have installed the inspIRCd server, without installing +the LDAP server. In such an installation, then the IRC_PASS is NOT checked, so +you can use your personal callsign. + +g2_ircDDB supports the following commands in YRCALL + +Note: In the commands that folow, _ is a SPACE. + +1) +For Echotest/playback. +YRCALL=_ _ _ _ _ _ _E + +2) +For Voice Mail: +YRCALL=_ _ _ _ _ _ S0 +The above command will Store/create voice mail in the dvtool file +x_voicemail.dat. +YRCALL=_ _ _ _ _ _ R0 +The above command will Recall/playback voice mail from the dvtool file +x_voicemail.dat. +YRCALL=_ _ _ _ _ _ C0 +The above command will Clear/delete voice mail. File x_voicemail.dat will be +deleted. In all cases, the letter x in the file name x_voicemail is the +module A,B or C. + +3) +For inquiring the status of the link: +YRCALL=_ _ _ _ _ _ _I + +4) +For unlinking: +YRCALL=_ _ _ _ _ _ _U + +5) +For linking: +YRCALL=XXNYYYML +Where XXNYYY is a friendly gateway, M is the gateways's module and L is the +LINK command. +YRCALL=XRFNNNML +Where XRFNNN is a friendly reflector, M is the reflector's module and L is +the LINK command. + +Note about linking: +After linking succeeds, set YRCALL=CQCQCQ because your audio will go to the +remote reflector ONLY if YRCALL=CQCQCQ. + +6) +For executing scripts: +YRCALL=_ _ _ _ _ _ nX +where n can be from 0-9 or A-Z. +Example: YRCALL=_ _ _ _ _ _1X +Then the script exec_1.sh will be executed. +<> + +7) +Enabling and disabling INCOMING HotSpotNode connections: +To Enable: +YRCALL=_ _ _ _ _ _ D1 +To Disable: +YRCALL=_ _ _ _ _ _ D0 + +Required software to run the g2_ircddb gateway correctly: +--- g2_ircddb: The G2 audio gateway. +--- g2_link: This communicates with g2_ircddb to link the local G2 gateway + to reflectors. Note: g2_link is NOT required if you only make + routing calls or talk locally on the repeater. +--- rptr: This is our dstar repeater software that uses a GMSK adapter/modem. + Instead of rptr, you can use our dvap_rptr dstar repeater software + which uses a DVAP device. Intead of rptr, you can use our dvrptr + dstar repeater software which uses the DV-RPTR modem(dg1ht). Intead + of rptr, you can use our snd_rptr dstar repeater software which + uses a SOUND CARD. You can also use the ICOM's RP2C repeater + hardware box. + +ROUTING methods +=============== +Some Dstar routing examples follow. Please do not use the same data because +KJ4NHF is one of our own Dstar repeaters, and KI4LKF is a personal callsign +in our group. + +Example of ZONE routing: + Lets say that your repeater is KJ4NHF, and you are currently on + your local repeater module B, your callsign is KI4LKF + and you want to reach remote gateway XXNYYY module C + In this case, your radio should be programmed like this: + MYCALL=KI4LKF + YRCALL=/XXNYYYC + RPT1=KJ4NHF B + RPT2=KJ4NHF G + +Example of CALLSIGN routing: + Lets say that your repeater is KJ4NHF, and you are currently on + your local repeater module B, your callsign is KI4LKF + and you want to talk to user XX0XXX + In this case, your radio should be programmed like this: + MYCALL=KI4LKF + YRCALL=XX0XXX + RPT1=KJ4NHF B + RPT2=KJ4NHF G + +Example of Cross-Band routing: + Lets say that your repeater is KJ4NHF, and you are currently on + your local repeater module B, your callsign is KI4LKF + and you want to talk from your local module B to your local module C + In this case, your radio should be programmed like this: + MYCALL=KI4LKF + YRCALL=CQCQCQ + RPT1=KJ4NHF B + RPT2=KJ4NHF C + +DTMF decoding and processing +============================= +Prepare the software to decode and process DTMF tones +----------------------------------------------------- +Edit the Shell script proc_g2_ircddb_dtmfs.sh +Correct the value for G2 to be the local G2 gateway +callsign(same value in g2_ircddb.cfg ----> OWNER) +Edit G2_INT_IP as follows: + If your g2_ircddb.cfg, says "G2_INTERNAL_IP=0.0.0.0" then set + G2_INT_IP=127.0.0.1. + If your g2_ircddb, says G2_INTERNAL_IP= + then set G2_INT_IP equal to the exact value of G2_INTERNAL_IP. +Edit G2_INT_PORT to be equal to G2_INTERNAL_PORT in g2_ircddb.cfg. + +Note: When local RF user has entered dtmf tones on the Dstar HT + and then PTT is released, g2_ircddb will print the whole + sequence in the g2_ircddb.log just before it creates the + dtmf file under /tmp. + +How to enter DTMF tones correctly on your Dstar HT +-------------------------------------------------- +If you want to have perfect DTMF decoding/processing in g2_ircddb, +follow these suggestions: + + 1) Hold down each dtmf key for at least 150 milliseconds. + 2) Leave at least 250 milliseconds of "silence", when you go from + one dtmf key to the next dtmf key. + +If you use a DTMF autodialer on your Dstar radio, you can program +the above numbers easily into the ICOM's radio "auto-dialer". + +Note: MAXIMUM dtmf sequence is up to 32 dtmf tones. + +What dtmf tones are being decoded and processed +----------------------------------------------- +To link to an XRF reflector: Example: #02102 +That says: link to XRF021 module B. So, we use the # dtmf key +to mean that we're interested in linking to an XRF reflector. The +last two digits are the remote module 01 thru 05 (which is +translated to A thru E). + +To link to a DCS reflector: Example: D00126 +That says: link to DCS001 module Z. So, we use the D dtmf key +to mean that we're interested in linking to a DCS reflector. The +last two digits are the remote module 01 thru 26 (which is +translated to A thru Z). + +To link to REF: Example: *01601 +That says: link to REF016 module A. So, we use the * dtmf key +to mean that we're interested in linking to a REF reflector. The +last two digits are the remote module 01 thru 05 (which is +translated to A thru E). + +To unlink (from any reflector xrf or ref): 73 + +To get the "link status": 99 + +Note: +You can extend the shell ascript to do more things. like force +your repeater to ID itself. Any YRCALL command that can be executed +by g2link_test, can be added to the shell script. Basically, the +Linux shell script proc_g2_ircddb_dtmfs.sh converts the decoded +dtmf tones into YRCALL commands using g2link_test program. + +=========== +g2_link is a small program that is used to link a local RF repeater +band to a remote reflector. g2_link software is used by our g2_ircddb +(an IRCddb gateway) and by our g2_ccs (a CCS gateway). + +Before we begin, there are some dat files included in the g2_link +ZIP package. These dat files are: + +already_linked.dat +already_unlinked.dat +failed_linked.dat +linked.dat +unlinked.dat +id.dat + +All of the above dat files are dvtool format files and they are used +when certain events take place. For example, when the link between your +gateway and a reflector is established, then the file "linked.dat" will +be played over RF, so anyone listening on your repeater, will hear the +announcement that the link has been established. You do not have to +change these files, unless you want to have your own personal voice +played over RF. These dat files were created using a computer and they +are NOT anyone's voice. + +The only dat file that you should really change is id.dat. The file +id.dat contains the audio "UNLINKED" and nothing else. When the gateway +is not linked and the RF user sets YRCALL=_______I to request the +status of the link, the file id.dat will be played back over RF. But +you should create your own id.dat file that should identify your own +repeater with extra information if you like. + +For example, you could create your own dvtool audio file that contains +the audio: "This is repeater ...". A simple way to create your own +recorded "repeater identification file" id.dat is to use your Dstar HT +and set YRCALL command: YRCALL=______S0 and key up your repeater. +Start talking, and the gateway will record your audio into the file +x_voicemail.dat where x is one of A.B or C. Now copy that file into +id.dat. + +Open the file g2_link.cfg: + Edit the values for LOGIN_CALL, OWNER and ADMIN. LOGIN_CALL is + always your PERSONAL callsign. It is used to connect to REF + reflectors. (REF reflectors check if the value of LOGIN_CALL is a + user callsign registered with UStrust.) + + OWNER is your GATEWAY/club/Repeater callsign. If you do not have a + GATEWAY/club/Repeater callsign, you can set OWNER equal to your + PERSONAL callsign. + + If you set OWNER to a PERSONAL callsign, then the Gateway config + file(g2_ircddb.cfg, g2_ccs.cfg) must also use the same PERSONAL + callsign. If you set OWNER to your GATEWAY/club/Repeater callsign, + then the Gateway config file(g2_ircddb.cfg, g2_ccs.cfg) must also + use the same GATEWAY/club/Repeater callsign. + + You can add as many lines to gwys.txt, if you know of any other + reflectors that you want to be able to connect to. REF reflectors + use port 20001(for USER connections), DCS reflectors use port + 30051 and XRF reflectors use either 30001(for Repeater linking) + or 20001(for USER connections). + + You can have as many ADMIN lines as you want. ADMIN is user + callsign that can connect to your own gateway(g2_link) using + DVTool, ... + +Note: +The zip file g2_lh conatain a small program that you can run to create +the Dashboard for your gateway. + +Note: +You may be thinking why did we put all this source code inside g2_link +instead of putting it all inside the Gateway software g2_ircddb (or +g2_ccs CCS gateway software). Because it is NOT a good design to burden +the G2 gateway with code that is not DSTAR protocol and reflectors are +NOT part of Dstar and they are not Dstar protocol. + +The only code that should be in a gateway, is G2 protocol code. This way +we keep the gateways (g2_ircddb, g2_ccs) clean of any unneccesary source +code logic and containing only pure G2/dstar logic. diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..3912109 --- /dev/null +++ b/COPYING @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/IRCApplication.h b/IRCApplication.h new file mode 100644 index 0000000..4089ab9 --- /dev/null +++ b/IRCApplication.h @@ -0,0 +1,54 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 . + +*/ + +#if !defined(_IRCAPPLICATION_H) +#define _IRCAPPLICATION_H + +#include + +#include "IRCMessageQueue.h" + +class IRCApplication +{ + public: + + virtual void userJoin (const wxString& nick, const wxString& name, const wxString& host) = 0; + virtual void userLeave (const wxString& nick) = 0; + virtual void userChanOp (const wxString& 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 wxString& nick) = 0; + virtual void setTopic(const wxString& topic) = 0; + + virtual void setBestServer(const wxString& ircUser) = 0; + + virtual void setSendQ( IRCMessageQueue * s ) = 0; + virtual IRCMessageQueue * getSendQ (void) = 0; + + virtual ~IRCApplication() {} + +}; + + +#endif diff --git a/IRCClient.cpp b/IRCClient.cpp new file mode 100644 index 0000000..4e00e88 --- /dev/null +++ b/IRCClient.cpp @@ -0,0 +1,512 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 . + +*/ + + +#if defined(WIN32) + +#define WIN32_LEAN_AND_MEAN + +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "IRCClient.h" +#include "IRCutils.h" + +#include + + + +#include +#include + + + + + + +IRCClient::IRCClient( IRCApplication * app, const wxString& update_channel, + const wxString& hostName, unsigned int port, const wxString& callsign, const wxString& password, + const wxString& versionInfo, const wxString& localAddr ) + : wxThread(wxTHREAD_JOINABLE) +{ + safeStringCopy(host_name, hostName.mb_str(), sizeof host_name); + + this -> callsign = callsign.Lower(); + this -> port = port; + this -> password = password; + + this -> app = app; + + if (localAddr.IsEmpty()) + { + safeStringCopy(local_addr, "0.0.0.0", sizeof local_addr); + } + else + { + safeStringCopy(local_addr, localAddr.mb_str(), sizeof local_addr); + } + + proto = new IRCProtocol ( app, this->callsign, password, update_channel, versionInfo ); + + recvQ = NULL; + sendQ = NULL; + + recv = NULL; +} + +IRCClient::~IRCClient() +{ + delete proto; +} + +bool IRCClient::startWork() +{ + + if (Create() != wxTHREAD_NO_ERROR) + { + wxLogError(wxT("IRCClient::startWork: Could not create the worker thread!")); + return false; + } + + terminateThread = false; + + if (Run() != wxTHREAD_NO_ERROR) + { + wxLogError(wxT("IRCClient::startWork: Could not run the worker thread!")); + return false; + } + + return true; +} + +void IRCClient::stopWork() +{ + terminateThread = true; + + Wait(); +} + +wxThread::ExitCode IRCClient::Entry () +{ + + unsigned int numAddr; + +#define MAXIPV4ADDR 10 + struct sockaddr_in addr[MAXIPV4ADDR]; + + struct sockaddr_in myaddr; + + int state = 0; + int timer = 0; + int sock = 0; + unsigned int currentAddr = 0; + + int result; + + + numAddr = 0; + + result = getAllIPV4Addresses(local_addr, 0, &numAddr, &myaddr, 1); + + if ((result != 0) || (numAddr != 1)) + { + wxLogVerbose(wxT("IRCClient::Entry: local address not parseable, using 0.0.0.0")); + memset(&myaddr, 0x00, sizeof(struct sockaddr_in)); + } + + while (true) + { + + if (timer > 0) + { + timer --; + } + + switch (state) + { + case 0: + if (terminateThread) + { + wxLogVerbose(wxT("IRCClient::Entry: thread terminated at state=%d"), state); + return 0; + } + + if (timer == 0) + { + timer = 30; + + if (getAllIPV4Addresses(host_name, port, &numAddr, addr, MAXIPV4ADDR) == 0) + { + wxLogVerbose(wxT("IRCClient::Entry: number of DNS entries %d"), numAddr); + if (numAddr > 0) + { + currentAddr = 0; + state = 1; + timer = 0; + } + } + } + break; + + case 1: + if (terminateThread) + { + wxLogVerbose(wxT("IRCClient::Entry: thread terminated at state=%d"), state); + return 0; + } + + if (timer == 0) + { + sock = socket( PF_INET, SOCK_STREAM, IPPROTO_TCP); + + if (sock < 0) + { + wxLogSysError(wxT("IRCClient::Entry: socket")); + timer = 30; + state = 0; + } + else + { +#if defined(__WINDOWS__) + u_long nonBlock = 1UL; + if (ioctlsocket( sock, FIONBIO, &nonBlock ) != 0) + { + wxLogSysError(wxT("IRCClient::Entry: ioctlsocket")); + closesocket(sock); + timer = 30; + state = 0; + } +#else + if (fcntl( sock, F_SETFL, O_NONBLOCK ) < 0) + { + wxLogSysError(wxT("IRCClient::Entry: fcntl")); + close(sock); + timer = 30; + state = 0; + } +#endif + else + { + unsigned char * h = (unsigned char *) &(myaddr.sin_addr); + int res; + + if ((h[0] != 0) || (h[1] != 0) || (h[2] != 0) || (h[3] != 0)) + { + wxLogVerbose(wxT("IRCClient::Entry: bind: local address %d.%d.%d.%d"), + h[0], h[1], h[2], h[3]); + } + + res = bind(sock, (struct sockaddr *) &myaddr, sizeof (struct sockaddr_in)); + + if (res != 0) + { + + wxLogSysError(wxT("IRCClient::Entry: bind")); +#if defined(__WINDOWS__) + closesocket(sock); +#else + close(sock); +#endif + state = 0; + timer = 30; + break; + } + + + h = (unsigned char *) &(addr[currentAddr].sin_addr); + wxLogVerbose(wxT("IRCClient::Entry: trying to connect to %d.%d.%d.%d"), + h[0], h[1], h[2], h[3]); + + res = connect(sock, (struct sockaddr *) (addr + currentAddr), sizeof (struct sockaddr_in)); + + if (res == 0) + { + wxLogVerbose(wxT("IRCClient::Entry: connected")); + state = 4; + } + else + { +#if defined(__WINDOWS__) + if (WSAGetLastError() == WSAEWOULDBLOCK) +#else + if (errno == EINPROGRESS) +#endif + { + wxLogVerbose(wxT("IRCClient::Entry: connect in progress")); + state = 3; + timer = 10; // 5 second timeout + } + else + { + wxLogSysError(wxT("IRCClient::Entry: connect")); +#if defined(__WINDOWS__) + closesocket(sock); +#else + close(sock); +#endif + 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; + res = select(sock+1, NULL, &myset, NULL, &tv); + + if (res < 0) + { + wxLogSysError(wxT("IRCClient::Entry: select")); +#if defined(__WINDOWS__) + closesocket(sock); +#else + close(sock); +#endif + state = 0; + timer = 30; + } + else if (res > 0) // connect is finished + { +#if defined(__WINDOWS__) + int val_len; +#else + socklen_t val_len; +#endif + int value; + + val_len = sizeof value; + + if (getsockopt(sock, SOL_SOCKET, SO_ERROR, (char *) &value, &val_len) < 0) + { + wxLogSysError(wxT("IRCClient::Entry: getsockopt")); +#if defined(__WINDOWS__) + closesocket(sock); +#else + close(sock); +#endif + state = 0; + timer = 30; + } + else + { + if (value != 0) + { + wxLogWarning(wxT("IRCClient::Entry: SO_ERROR=%d"), value); +#if defined(__WINDOWS__) + closesocket(sock); +#else + close(sock); +#endif + currentAddr ++; + if (currentAddr >= numAddr) + { + state = 0; + timer = 30; + } + else + { + state = 1; + timer = 2; + } + } + else + { + wxLogVerbose(wxT("IRCClient::Entry: connected2")); + state = 4; + } + } + + } + else if (timer == 0) + { // select timeout and timer timeout + wxLogVerbose(wxT("IRCClient::Entry: connect timeout")); +#if defined(__WINDOWS__) + closesocket(sock); +#else + close(sock); +#endif + currentAddr ++; + if (currentAddr >= numAddr) + { + state = 0; + timer = 30; + } + else + { + state = 1; // open new socket + timer = 2; + } + } + + } + break; + + case 4: + { + recvQ = new IRCMessageQueue(); + sendQ = new IRCMessageQueue(); + + recv = new IRCReceiver(sock, recvQ); + recv->startWork(); + + proto->setNetworkReady(true); + state = 5; + timer = 0; + + } + break; + + + case 5: + if (terminateThread) + { + state = 6; + } + else + { + + if (recvQ -> isEOF()) + { + timer = 0; + state = 6; + } + else if (proto -> processQueues(recvQ, sendQ) == false) + { + timer = 0; + state = 6; + } + + while ((state == 5) && sendQ->messageAvailable()) + { + IRCMessage * m = sendQ -> getMessage(); + + wxString out; + + m -> composeMessage ( out ); + + char buf[200]; + safeStringCopy(buf, out.mb_str(wxConvUTF8), 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) + { + wxLogVerbose(wxT("IRCClient::Entry: short write %d < %d"), r, len); + + timer = 0; + state = 6; + } +/* else + { + wxLogVerbose(wxT("write %d bytes (") + out + wxT(")"), len ); + } */ + } + else + { + wxLogVerbose(wxT("IRCClient::Entry: no NL at end, len=%d"), len); + + timer = 0; + state = 6; + } + + delete m; + } + } + break; + + case 6: + { + if (app != NULL) + { + app->setSendQ(NULL); + app->userListReset(); + } + + proto->setNetworkReady(false); + recv->stopWork(); + + wxThread::Sleep(2000); + + delete recv; + delete recvQ; + delete sendQ; + +#if defined(__WINDOWS__) + closesocket(sock); +#else + close(sock); +#endif + + if (terminateThread) // request to end the thread + { + wxLogVerbose(wxT("IRCClient::Entry: thread terminated at state=%d"), state); + return 0; + } + + timer = 30; + state = 0; // reconnect to IRC server + } + break; + + } + + wxThread::Sleep(500); + + } + + return 0; +} + + + + + diff --git a/IRCClient.h b/IRCClient.h new file mode 100644 index 0000000..c8d215c --- /dev/null +++ b/IRCClient.h @@ -0,0 +1,76 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 . + +*/ + + +#if !defined(_IRCCLIENT_H) +#define _IRCCLIENT_H + +#include "IRCReceiver.h" +#include "IRCMessageQueue.h" +#include "IRCProtocol.h" +#include "IRCApplication.h" + +#include + + +class IRCClient : public wxThread +{ + public: + + IRCClient( IRCApplication * app, const wxString& update_channel, + const wxString& hostName, unsigned int port, const wxString& callsign, const wxString& password, + const wxString& versionInfo, const wxString& localAddr ); + + ~IRCClient(); + + + bool startWork(); + + void stopWork(); + + + protected: + + virtual wxThread::ExitCode Entry(); + + + + private: + + char host_name[100]; + char local_addr[100]; + unsigned int port; + wxString callsign; + wxString password; + + bool terminateThread; + + IRCReceiver * recv; + IRCMessageQueue * recvQ; + IRCMessageQueue * sendQ; + IRCProtocol * proto; + + IRCApplication * app; + +}; + + +#endif diff --git a/IRCDDB.cpp b/IRCDDB.cpp new file mode 100644 index 0000000..45ef3ed --- /dev/null +++ b/IRCDDB.cpp @@ -0,0 +1,478 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 "IRCDDB.h" + +#include "IRCClient.h" +#include "IRCDDBApp.h" + +#include + + +struct CIRCDDBPrivate +{ + IRCClient * client; + IRCDDBApp * app; +}; + + +CIRCDDB::CIRCDDB(const wxString& hostName, unsigned int port, + const wxString& callsign, const wxString& password, + const wxString& versionInfo, const wxString& localAddr ) : d( new CIRCDDBPrivate ) + +{ + wxString update_channel = wxT("#dstar"); + + d->app = new IRCDDBApp(update_channel); + + d->client = new IRCClient( d->app, update_channel, hostName, port, callsign, + password, versionInfo, localAddr ); +} + +CIRCDDB::~CIRCDDB() +{ + delete d->client; + delete d->app; + delete d; +} + + + // A false return implies a network error, or unable to log in +bool CIRCDDB::open() +{ + wxLogVerbose(wxT("start")); + return d->client -> startWork() && d->app->startWork(); +} + + +int CIRCDDB::getConnectionState() +{ + return d->app->getConnectionState(); +} + + +void CIRCDDB::rptrQTH( double latitude, double longitude, const wxString& desc1, + const wxString& desc2, const wxString& infoURL ) +{ + d->app->rptrQTH(latitude, longitude, desc1, desc2, infoURL); +} + + +void CIRCDDB::rptrQRG( const wxString& module, double txFrequency, double duplexShift, + double range, double agl ) +{ + d->app->rptrQRG(module, txFrequency, duplexShift, range, agl); +} + + +void CIRCDDB::kickWatchdog ( const wxString& wdInfo ) +{ + d->app->kickWatchdog( wdInfo ); +} + + + +// Send heard data, a false return implies a network error +bool CIRCDDB::sendHeard( const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3 ) +{ + if (myCall.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:myCall: len != 8")); + return false; + } + + if (myCallExt.Len() != 4) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:myCallExt: len != 4")); + return false; + } + + if (yourCall.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:yourCall: len != 8")); + return false; + } + + if (rpt1.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:rpt1: len != 8")); + return false; + } + + if (rpt2.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:rpt2: len != 8")); + return false; + } + + return d->app->sendHeard( myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, + wxT(" "), wxT(""), wxT("")); +} + + +// Send heard data, a false return implies a network error +bool CIRCDDB::sendHeardWithTXMsg( const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3, + const wxString& network_destination, + const wxString& tx_message ) +{ + if (myCall.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:myCall: len != 8")); + return false; + } + + if (myCallExt.Len() != 4) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:myCallExt: len != 4")); + return false; + } + + if (yourCall.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:yourCall: len != 8")); + return false; + } + + if (rpt1.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:rpt1: len != 8")); + return false; + } + + if (rpt2.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:rpt2: len != 8")); + return false; + } + + wxString dest = network_destination; + + if (dest.Len() == 0) + { + dest = wxT(" "); + } + + if (dest.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:network_destination: len != 8")); + return false; + } + + wxString msg; + + if (tx_message.Len() == 20) + { + unsigned int i; + for (i=0; i < tx_message.Len(); i++) + { + wxChar ch = tx_message.GetChar(i); + + if ((ch > 32) && (ch < 127)) + { + msg.Append(ch); + } + else + { + msg.Append(wxT('_')); + } + } + } + + return d->app->sendHeard( myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, + dest, msg, wxT("")); +} + + + +bool CIRCDDB::sendHeardWithTXStats( const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& 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)) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:num_dv_frames not in range 1-65535")); + return false; + } + + if (num_dv_silent_frames > num_dv_frames) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:num_dv_silent_frames > num_dv_frames")); + return false; + } + + if (num_bit_errors > (4*num_dv_frames)) // max 4 bit errors per frame + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:num_bit_errors > (4*num_dv_frames)")); + return false; + } + + if (myCall.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:myCall: len != 8")); + return false; + } + + if (myCallExt.Len() != 4) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:myCallExt: len != 4")); + return false; + } + + if (yourCall.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:yourCall: len != 8")); + return false; + } + + if (rpt1.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:rpt1: len != 8")); + return false; + } + + if (rpt2.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::sendHeard:rpt2: len != 8")); + return false; + } + + wxString stats = wxString::Format(wxT("%04x"), num_dv_frames); + + if (num_dv_silent_frames >= 0) + { + wxString s = wxString::Format(wxT("%02x"), (num_dv_silent_frames * 100) / num_dv_frames); + stats.Append(s); + + if (num_bit_errors >= 0) + { + s = wxString::Format(wxT("%02x"), (num_bit_errors * 125) / (num_dv_frames * 3)); + stats.Append(s); + } + else + { + stats.Append(wxT("__")); + } + } + else + { + stats.Append(wxT("____")); + } + + stats.Append(wxT("____________")); // stats string should have 20 chars + + return d->app->sendHeard( myCall, myCallExt, yourCall, rpt1, rpt2, flag1, flag2, flag3, + wxT(" "), wxT(""), stats); +} + + + +// Send query for a gateway/reflector, a false return implies a network error +bool CIRCDDB::findGateway(const wxString& gatewayCallsign) +{ + if (gatewayCallsign.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::findGateway: len != 8")); + return false; + } + + return d->app->findGateway( gatewayCallsign.Upper()); +} + + +bool CIRCDDB::findRepeater(const wxString& repeaterCallsign) +{ + if (repeaterCallsign.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::findRepeater: len != 8")); + return false; + } + + return d->app->findRepeater( repeaterCallsign.Upper()); +} + +// Send query for a user, a false return implies a network error +bool CIRCDDB::findUser(const wxString& userCallsign) +{ + if (userCallsign.Len() != 8) + { + wxLogVerbose(wxT("CIRCDDB::findUser: len != 8")); + return false; + } + + return d->app->findUser( userCallsign.Upper()); +} + +// The following functions are for processing received messages + +// Get the waiting message type +IRCDDB_RESPONSE_TYPE CIRCDDB::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 CIRCDDB::receiveRepeater(wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address, DSTAR_PROTOCOL& protocol) +{ + IRCDDB_RESPONSE_TYPE rt = d->app->getReplyMessageType(); + + if (rt != IDRT_REPEATER) + { + wxLogError(wxT("CIRCDDB::receiveRepeater: unexpected response type")); + return false; + } + + IRCMessage * m = d->app->getReplyMessage(); + + if (m == NULL) + { + wxLogError(wxT("CIRCDDB::receiveRepeater: no message")); + return false; + } + + if (!m->getCommand().IsSameAs(wxT("IDRT_REPEATER"))) + { + wxLogError(wxT("CIRCDDB::receiveRepeater: wrong message type")); + return false; + } + + if (m->getParamCount() != 3) + { + wxLogError(wxT("CIRCDDB::receiveRepeater: unexpected number of message parameters")); + 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 CIRCDDB::receiveGateway(wxString& gatewayCallsign, wxString& address, DSTAR_PROTOCOL& protocol) +{ + IRCDDB_RESPONSE_TYPE rt = d->app->getReplyMessageType(); + + if (rt != IDRT_GATEWAY) + { + wxLogError(wxT("CIRCDDB::receiveGateway: unexpected response type")); + return false; + } + + IRCMessage * m = d->app->getReplyMessage(); + + if (m == NULL) + { + wxLogError(wxT("CIRCDDB::receiveGateway: no message")); + return false; + } + + if (!m->getCommand().IsSameAs(wxT("IDRT_GATEWAY"))) + { + wxLogError(wxT("CIRCDDB::receiveGateway: wrong message type")); + return false; + } + + if (m->getParamCount() != 2) + { + wxLogError(wxT("CIRCDDB::receiveGateway: unexpected number of message parameters")); + 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 CIRCDDB::receiveUser(wxString& userCallsign, wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address) +{ + wxString dummy; + return receiveUser(userCallsign, repeaterCallsign, gatewayCallsign, address, dummy); +} + +bool CIRCDDB::receiveUser(wxString& userCallsign, wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address, + wxString& timeStamp) +{ + IRCDDB_RESPONSE_TYPE rt = d->app->getReplyMessageType(); + + if (rt != IDRT_USER) + { + wxLogError(wxT("CIRCDDB::receiveUser: unexpected response type")); + return false; + } + + IRCMessage * m = d->app->getReplyMessage(); + + if (m == NULL) + { + wxLogError(wxT("CIRCDDB::receiveUser: no message")); + return false; + } + + if (!m->getCommand().IsSameAs(wxT("IDRT_USER"))) + { + wxLogError(wxT("CIRCDDB::receiveUser: wrong message type")); + return false; + } + + if (m->getParamCount() != 5) + { + wxLogError(wxT("CIRCDDB::receiveUser: unexpected number of message parameters")); + 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 CIRCDDB::close() // Implictely kills any threads in the IRC code +{ + d->client -> stopWork(); + d->app -> stopWork(); +} + diff --git a/IRCDDB.h b/IRCDDB.h new file mode 100644 index 0000000..d58a748 --- /dev/null +++ b/IRCDDB.h @@ -0,0 +1,173 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 . + +*/ + + +#if !defined(_IRCDDB_H) +#define _IRCDDB_H + +#include + +enum IRCDDB_RESPONSE_TYPE { + IDRT_NONE, + IDRT_USER, + IDRT_GATEWAY, + IDRT_REPEATER +}; + +enum DSTAR_PROTOCOL { + DP_UNKNOWN, + DP_DEXTRA, + DP_DPLUS +}; + + +struct CIRCDDBPrivate; + +class CIRCDDB { +public: + CIRCDDB(const wxString& hostName, unsigned int port, const wxString& callsign, const wxString& password, + const wxString& versionInfo, const wxString& localAddr = wxEmptyString ); + ~CIRCDDB(); + + // A false return implies a network error, or unable to log in + bool open(); + + + // rptrQTH can be called multiple times if necessary + // 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( double latitude, double longitude, const wxString& desc1, + const wxString& desc2, const wxString& infoURL ); + + + + // rptrQRG can be called multiple times if necessary + // module letter of the module, valid values: "A", "B", "C", "D", "AD", "BD", "CD", "DD" + // 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 wxString& module, 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 wxString& 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 wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& 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 wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3, + const wxString& network_destination, + const wxString& 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 wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& 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 wxString& gatewayCallsign); + + // Send query for a repeater module, a false return implies a network error + bool findRepeater(const wxString& repeaterCallsign); + + // Send query for a user, a false return implies a network error + bool findUser(const wxString& userCallsign); + + // 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(wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address, DSTAR_PROTOCOL& protocol); + + // Get a gateway message, as a result of IDRT_GATEWAY returned from getMessageType() + // A false return implies a network error + bool receiveGateway(wxString& gatewayCallsign, wxString& address, DSTAR_PROTOCOL& protocol); + + // Get a user message, as a result of IDRT_USER returned from getMessageType() + // A false return implies a network error + bool receiveUser(wxString& userCallsign, wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address); + + bool receiveUser(wxString& userCallsign, wxString& repeaterCallsign, wxString& gatewayCallsign, wxString& address, + wxString& timeStamp ); + + void close(); // Implictely kills any threads in the IRC code + + +private: + struct CIRCDDBPrivate * const d; + +}; + +#endif // _IRCDDB_H + diff --git a/IRCDDBApp.cpp b/IRCDDBApp.cpp new file mode 100644 index 0000000..6bb8a1c --- /dev/null +++ b/IRCDDBApp.cpp @@ -0,0 +1,1333 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 . + +*/ + +#if defined(WIN32) + +#define WIN32_LEAN_AND_MEAN + +#include +#include + +#else + +#include +#include +#include + +#endif + + +#include "IRCDDBApp.h" +#include "IRCutils.h" + +#include +#include +#include + +class IRCDDBAppUserObject +{ + public: + + wxString nick; + wxString name; + wxString host; + bool op; + unsigned int usn; + + IRCDDBAppUserObject () + { + IRCDDBAppUserObject (wxT(""), wxT(""), wxT("")); + } + + IRCDDBAppUserObject ( const wxString& n, const wxString& nm, const wxString& h ) + { + nick = n; + name = nm; + host = h; + op = false; + usn = counter; + counter ++; + } + + static unsigned int counter; +}; + +unsigned int IRCDDBAppUserObject::counter = 0; + + +WX_DECLARE_STRING_HASH_MAP( IRCDDBAppUserObject, IRCDDBAppUserMap ); + + +class IRCDDBAppRptrObject +{ + public: + + wxString arearp_cs; + wxDateTime lastChanged; + wxString zonerp_cs; + + IRCDDBAppRptrObject () + { + } + + IRCDDBAppRptrObject (wxDateTime& dt, wxString& repeaterCallsign, wxString& gatewayCallsign) + { + arearp_cs = repeaterCallsign; + lastChanged = dt; + zonerp_cs = gatewayCallsign; + + if (dt.IsLaterThan(maxTime)) + { + maxTime = dt; + } + } + + static wxDateTime maxTime; +}; + +wxDateTime IRCDDBAppRptrObject::maxTime((time_t) 950000000); // February 2000 + + +WX_DECLARE_STRING_HASH_MAP( IRCDDBAppRptrObject, IRCDDBAppRptrMap ); + +WX_DECLARE_STRING_HASH_MAP( wxString, IRCDDBAppModuleMap ); + +class IRCDDBAppPrivate +{ + public: + + IRCDDBAppPrivate() + : tablePattern(wxT("^[0-9]$")), + datePattern(wxT("^20[0-9][0-9]-((1[0-2])|(0[1-9]))-((3[01])|([12][0-9])|(0[1-9]))$")), + timePattern(wxT("^((2[0-3])|([01][0-9])):[0-5][0-9]:[0-5][0-9]$")), + dbPattern(wxT("^[0-9A-Z_]{8}$")), + modulePattern(wxT("^[ABCD]D?$")) + { + wdTimer = 0; + wdCounter = 0; + } + + IRCMessageQueue * sendQ; + + IRCDDBAppUserMap user; + wxMutex userMapMutex; + + wxString currentServer; + wxString myNick; + + wxRegEx tablePattern; + wxRegEx datePattern; + wxRegEx timePattern; + wxRegEx dbPattern; + wxRegEx modulePattern; + + int state; + int timer; + int infoTimer; + + wxString updateChannel; + wxString channelTopic; + wxString bestServer; + + bool initReady; + + bool terminateThread; + + IRCDDBAppRptrMap rptrMap; + wxMutex rptrMapMutex; + + IRCMessageQueue replyQ; + + IRCDDBAppModuleMap moduleMap; + wxMutex moduleMapMutex; + + wxString rptrLocation; + wxString rptrInfoURL; + + wxString wdInfo; + int wdTimer; + int wdCounter; +}; + + +IRCDDBApp::IRCDDBApp( const wxString& u_chan ) + : wxThread(wxTHREAD_JOINABLE), + d(new IRCDDBAppPrivate) +{ + + d->sendQ = NULL; + d->initReady = false; + + userListReset(); + + d->state = 0; + d->timer = 0; + d->myNick = wxT("none"); + + d->updateChannel = u_chan; + + d->terminateThread = false; + +} + +IRCDDBApp::~IRCDDBApp() +{ + if (d->sendQ != NULL) + { + delete d->sendQ; + } + delete d; +} + + +void IRCDDBApp::rptrQTH( double latitude, double longitude, const wxString& desc1, + const wxString& desc2, const wxString& infoURL ) +{ + + wxString pos = wxString::Format(wxT("%+09.5f %+010.5f"), latitude, longitude); + + wxString d1 = desc1; + wxString d2 = desc2; + + d1.Append(wxT(' '), 20); + d2.Append(wxT(' '), 20); + + wxRegEx nonValid(wxT("[^a-zA-Z0-9 +&(),./'-]")); + + nonValid.Replace(&d1, wxEmptyString); + nonValid.Replace(&d2, wxEmptyString); + + pos.Replace( wxT(","), wxT(".")); + d1.Replace(wxT(" "), wxT("_")); + d2.Replace(wxT(" "), wxT("_")); + + d->rptrLocation = pos + wxT(" ") + d1.Mid(0, 20) + wxT(" ") + d2.Mid(0, 20); + + wxLogVerbose(wxT("QTH: ") + d->rptrLocation); + + wxRegEx urlNonValid(wxT("[^[:graph:]]")); + + d->rptrInfoURL = infoURL; + + urlNonValid.Replace( & d->rptrInfoURL, wxEmptyString ); + + wxLogVerbose(wxT("URL: ") + d->rptrInfoURL.Mid(0, 120)); + + d->infoTimer = 5; // send info in 5 seconds +} + + +void IRCDDBApp::rptrQRG( const wxString& module, double txFrequency, double duplexShift, + double range, double agl ) +{ + + if (d->modulePattern.Matches(module)) + { + wxString f = module + wxT(" ") + wxString::Format(wxT("%011.5f %+010.5f %06.2f %06.1f"), + txFrequency, duplexShift, range / 1609.344, agl ); + + f.Replace( wxT(","), wxT(".")); + + wxMutexLocker lock(d->moduleMapMutex); + d->moduleMap[module] = f; + + wxLogVerbose(wxT("QRG: ") + f); + + d->infoTimer = 5; // send info in 5 seconds + } +} + +void IRCDDBApp::kickWatchdog( const wxString& s ) +{ + if (s.Len() > 0) + { + wxRegEx nonValid(wxT("[^[:graph:]]")); + d->wdInfo = s; + nonValid.Replace(& d->wdInfo, wxEmptyString); + + if (d->wdInfo.Len() > 0) + { + if (d->wdTimer == 0) + { + d->wdTimer ++; + } + + d->wdCounter ++; + } + } +} + + +int IRCDDBApp::getConnectionState() +{ + return d->state; +} + +IRCDDB_RESPONSE_TYPE IRCDDBApp::getReplyMessageType() +{ + IRCMessage * m = d->replyQ.peekFirst(); + if (m == NULL) + { + return IDRT_NONE; + } + + wxString msgType = m->getCommand(); + + if (msgType.IsSameAs(wxT("IDRT_USER"))) + { + return IDRT_USER; + } + else if (msgType.IsSameAs(wxT("IDRT_REPEATER"))) + { + return IDRT_REPEATER; + } + else if (msgType.IsSameAs(wxT("IDRT_GATEWAY"))) + { + return IDRT_GATEWAY; + } + + wxLogError(wxT("IRCDDBApp::getMessageType: unknown msg type")); + + return IDRT_NONE; +} + + +IRCMessage * IRCDDBApp::getReplyMessage() +{ + return d->replyQ.getMessage(); +} + + + +bool IRCDDBApp::startWork() +{ + + if (Create() != wxTHREAD_NO_ERROR) + { + wxLogError(wxT("IRCClient::startWork: Could not create the worker thread!")); + return false; + } + + d->terminateThread = false; + + if (Run() != wxTHREAD_NO_ERROR) + { + wxLogError(wxT("IRCClient::startWork: Could not run the worker thread!")); + return false; + } + + return true; +} + +void IRCDDBApp::stopWork() +{ + d->terminateThread = true; + + Wait(); +} + + +void IRCDDBApp::userJoin (const wxString& nick, const wxString& name, const wxString& host) +{ + wxMutexLocker lock(d->userMapMutex); + + wxString lnick = nick; + lnick.MakeLower(); + + IRCDDBAppUserObject u( lnick, name, host ); + + d->user[lnick] = u; + + // wxLogVerbose(wxT("add %d: (") + u.nick + wxT(") (") + u.host + wxT(")"), d->user.size()); + + if (d->initReady) + { + int hyphenPos = nick.Find(wxT('-')); + + if ((hyphenPos >= 4) && (hyphenPos <= 6)) + { + wxString gatewayCallsign = nick.Mid(0, hyphenPos).Upper(); + + while (gatewayCallsign.Length() < 7) + { + gatewayCallsign.Append(wxT(' ')); + } + + gatewayCallsign.Append(wxT('G')); + + IRCMessage * m2 = new IRCMessage(wxT( "IDRT_GATEWAY")); + m2->addParam(gatewayCallsign); + m2->addParam(host); + d->replyQ.putMessage(m2); + } + } + + // wxLogVerbose(wxT("user %d"), u.usn ); +} + + + + +void IRCDDBApp::userLeave (const wxString& nick) +{ + wxMutexLocker lock(d->userMapMutex); + + wxString lnick = nick; + lnick.MakeLower(); + + d->user.erase(lnick); + + // wxLogVerbose(wxT("rm %d: ") + nick, d->user.size()); + + if (d->currentServer.Len() > 0) + { + if (d->user.count(d->myNick) != 1) + { + wxLogVerbose(wxT("IRCDDBApp::userLeave: could not find own nick")); + return; + } + + IRCDDBAppUserObject me = d->user[d->myNick]; + + if (me.op == false) + { + // if I am not op, then look for new server + + if (d->currentServer.IsSameAs(lnick)) + { + // currentServer = null; + d->state = 2; // choose new server + d->timer = 200; + d->initReady = false; + } + } + } +} + +void IRCDDBApp::userListReset() +{ + wxMutexLocker lock(d->userMapMutex); + + d->user.clear(); +} + +void IRCDDBApp::setCurrentNick(const wxString& nick) +{ + d->myNick = nick; + wxLogVerbose(wxT("IRCDDBApp::setCurrentNick ") + nick); +} + +void IRCDDBApp::setBestServer(const wxString& ircUser) +{ + d->bestServer = ircUser; + wxLogVerbose(wxT("IRCDDBApp::setBestServer ") + ircUser); +} + +void IRCDDBApp::setTopic(const wxString& topic) +{ + d->channelTopic = topic; +} + +bool IRCDDBApp::findServerUser() +{ + wxMutexLocker lock(d->userMapMutex); + + bool found = false; + + IRCDDBAppUserMap::iterator it; + + for( it = d->user.begin(); it != d->user.end(); ++it ) + { + IRCDDBAppUserObject u = it->second; + + if (u.nick.StartsWith(wxT("s-")) && u.op && !d->myNick.IsSameAs(u.nick) + && u.nick.IsSameAs(d->bestServer)) + { + d->currentServer = u.nick; + found = true; + break; + } + } + + if (found) + return true; + + if (d->bestServer.Len() == 8) + { + for( it = d->user.begin(); it != d->user.end(); ++it ) + { + IRCDDBAppUserObject u = it->second; + + if (u.nick.StartsWith(d->bestServer.Mid(0,7)) && u.op && + !d->myNick.IsSameAs(u.nick) ) + { + d->currentServer = u.nick; + found = true; + break; + } + } + } + + if (found) + return true; + + for( it = d->user.begin(); it != d->user.end(); ++it ) + { + IRCDDBAppUserObject u = it->second; + + if (u.nick.StartsWith(wxT("s-")) && u.op && !d->myNick.IsSameAs(u.nick)) + { + d->currentServer = u.nick; + found = true; + break; + } + } + + return found; +} + +void IRCDDBApp::userChanOp (const wxString& nick, bool op) +{ + wxMutexLocker lock(d->userMapMutex); + + wxString lnick = nick; + lnick.MakeLower(); + + if (d->user.count(lnick) == 1) + { + d->user[lnick].op = op; + } +} + + + +static const int numberOfTables = 2; + + + + + +wxString IRCDDBApp::getIPAddress(wxString& zonerp_cs) +{ + wxMutexLocker lock(d->userMapMutex); + wxString gw = zonerp_cs; + + gw.Replace(wxT("_"), wxT(" ")); + gw.MakeLower(); + + unsigned int max_usn = 0; + wxString ipAddr; + + int j; + + for (j=1; j <= 4; j++) + { + // int i = 0; + wxString ircUser = gw.Strip() + wxString::Format(wxT("-%d"), j); + + if (d->user.count(ircUser) == 1) + { + IRCDDBAppUserObject o = d->user[ ircUser ]; + + if (o.usn >= max_usn) + { + max_usn = o.usn; + ipAddr = o.host; + } + // i = 1; + } + // wxLogVerbose(wxT("getIP %d (") + ircUser + wxT(") (") + ipAddr + wxT(")"), i); + + } + + return ipAddr; +} + + +bool IRCDDBApp::findGateway(const wxString& gwCall) +{ + wxString s = gwCall.Mid(0,6); + + IRCMessage * m2 = new IRCMessage(wxT( "IDRT_GATEWAY")); + m2->addParam(gwCall); + m2->addParam(getIPAddress(s)); + d->replyQ.putMessage(m2); + + return true; +} + +static void findReflector( const wxString& rptrCall, IRCDDBAppPrivate * d ) +{ + wxString zonerp_cs; + wxString ipAddr; + +#define MAXIPV4ADDR 5 + struct sockaddr_in addr[MAXIPV4ADDR]; + unsigned int numAddr = 0; + + char host_name[80]; + + wxString host = rptrCall.Mid(0,6) + wxT(".reflector.ircddb.net"); + + safeStringCopy(host_name, host.mb_str(wxConvUTF8), sizeof host_name); + + if (getAllIPV4Addresses(host_name, 0, &numAddr, addr, MAXIPV4ADDR) == 0) + { + if (numAddr > 0) + { + unsigned char * a = (unsigned char *) &addr[0].sin_addr; + + ipAddr = wxString::Format(wxT("%d.%d.%d.%d"), a[0], a[1], a[2], a[3]); + zonerp_cs = rptrCall; + zonerp_cs.SetChar(7, wxT('G')); + } + } + + + IRCMessage * m2 = new IRCMessage(wxT("IDRT_REPEATER")); + m2->addParam(rptrCall); + m2->addParam(zonerp_cs); + m2->addParam(ipAddr); + d->replyQ.putMessage(m2); +} + +bool IRCDDBApp::findRepeater(const wxString& rptrCall) +{ + + if (rptrCall.StartsWith(wxT("XRF")) || rptrCall.StartsWith(wxT("REF"))) + { + findReflector(rptrCall, d); + return true; + } + + wxString arearp_cs = rptrCall; + arearp_cs.Replace(wxT(" "), wxT("_")); + + wxString zonerp_cs; + + wxMutexLocker lock(d->rptrMapMutex); + + wxString s = wxT("NONE"); + + if (d->rptrMap.count(arearp_cs) == 1) + { + IRCDDBAppRptrObject o = d->rptrMap[arearp_cs]; + zonerp_cs = o.zonerp_cs; + zonerp_cs.Replace(wxT("_"), wxT(" ")); + zonerp_cs.SetChar(7, wxT('G')); + s = o.zonerp_cs; + } + + IRCMessage * m2 = new IRCMessage(wxT("IDRT_REPEATER")); + m2->addParam(rptrCall); + m2->addParam(zonerp_cs); + m2->addParam(getIPAddress(s)); + d->replyQ.putMessage(m2); + + return true; +} + + +bool IRCDDBApp::sendHeard(const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3, + const wxString& destination, const wxString& tx_msg, + const wxString& tx_stats ) +{ + + wxString my = myCall; + wxString myext = myCallExt; + wxString ur = yourCall; + wxString r1 = rpt1; + wxString r2 = rpt2; + wxString dest = destination; + + wxRegEx nonValid(wxT("[^A-Z0-9/]")); + wxString underScore = wxT("_"); + + nonValid.Replace(&my, underScore); + nonValid.Replace(&myext, underScore); + nonValid.Replace(&ur, underScore); + nonValid.Replace(&r1, underScore); + nonValid.Replace(&r2, underScore); + nonValid.Replace(&dest, underScore); + + bool statsMsg = (tx_stats.Len() > 0); + + wxString srv = d->currentServer; + IRCMessageQueue * q = getSendQ(); + + if ((srv.Len() > 0) && (d->state >= 6) && (q != NULL)) + { + wxString cmd = wxT("UPDATE "); + + cmd.Append( getCurrentTime() ); + + cmd.Append(wxT(" ")); + + cmd.Append(my); + cmd.Append(wxT(" ")); + cmd.Append(r1); + cmd.Append(wxT(" ")); + if (!statsMsg) + { + cmd.Append(wxT("0 ")); + } + cmd.Append(r2); + cmd.Append(wxT(" ")); + cmd.Append(ur); + cmd.Append(wxT(" ")); + + wxString flags = wxString::Format(wxT("%02X %02X %02X"), flag1, flag2, flag3); + + cmd.Append(flags); + cmd.Append(wxT(" ")); + cmd.Append(myext); + + if (statsMsg) + { + cmd.Append(wxT(" # ")); + cmd.Append(tx_stats); + } + else + { + cmd.Append(wxT(" 00 ")); + cmd.Append(dest); + + if (tx_msg.Len() == 20) + { + cmd.Append(wxT(" ")); + cmd.Append(tx_msg); + } + } + + + IRCMessage * m = new IRCMessage(srv, cmd); + + q->putMessage(m); + return true; + } + else + { + return false; + } +} + +bool IRCDDBApp::findUser(const wxString& usrCall) +{ + wxString srv = d->currentServer; + IRCMessageQueue * q = getSendQ(); + + if ((srv.Len() > 0) && (d->state >= 6) && (q != NULL)) + { + wxString usr = usrCall; + + usr.Replace(wxT(" "), wxT("_")); + + IRCMessage * m = new IRCMessage(srv, + wxT("FIND ") + usr ); + + q->putMessage(m); + } + else + { + IRCMessage * m2 = new IRCMessage(wxT("IDRT_USER")); + m2->addParam(usrCall); + m2->addParam(wxT("")); + m2->addParam(wxT("")); + m2->addParam(wxT("")); + m2->addParam(wxT("")); + d->replyQ.putMessage(m2); + } + + return true; +} + + +void IRCDDBApp::msgChannel (IRCMessage * m) +{ + if (m->getPrefixNick().StartsWith(wxT("s-")) && (m->numParams >= 2)) // server msg + { + doUpdate(m->params[1]); + } +} + + +void IRCDDBApp::doNotFound ( wxString& msg, wxString& retval ) +{ + int tableID = 0; + + wxStringTokenizer tkz(msg); + + if (!tkz.HasMoreTokens()) + { + return; // no text in message + } + + wxString tk = tkz.GetNextToken(); + + + if (d->tablePattern.Matches(tk)) + { + long i; + + if (tk.ToLong(&i)) + { + tableID = i; + if ((tableID < 0) || (tableID >= numberOfTables)) + { + wxLogVerbose(wxT("invalid table ID %d"), tableID); + return; + } + } + else + { + return; // not a valid number + } + + if (!tkz.HasMoreTokens()) + { + return; // received nothing but the tableID + } + + tk = tkz.GetNextToken(); + } + + if (tableID == 0) + { + if (! d->dbPattern.Matches(tk)) + { + return; // no valid key + } + + retval = tk; + } +} + +void IRCDDBApp::doUpdate ( wxString& msg ) +{ + int tableID = 0; + + wxStringTokenizer tkz(msg); + + if (!tkz.HasMoreTokens()) + { + return; // no text in message + } + + wxString tk = tkz.GetNextToken(); + + + if (d->tablePattern.Matches(tk)) + { + long i; + + if (tk.ToLong(&i)) + { + tableID = i; + if ((tableID < 0) || (tableID >= numberOfTables)) + { + wxLogVerbose(wxT("invalid table ID %d"), tableID); + return; + } + } + else + { + return; // not a valid number + } + + if (!tkz.HasMoreTokens()) + { + return; // received nothing but the tableID + } + + tk = tkz.GetNextToken(); + } + + if (d->datePattern.Matches(tk)) + { + if (!tkz.HasMoreTokens()) + { + return; // nothing after date string + } + + wxString timeToken = tkz.GetNextToken(); + + if (! d->timePattern.Matches(timeToken)) + { + return; // no time string after date string + } + + wxDateTime dt; + + if (dt.ParseFormat(tk + wxT(" ") + timeToken, wxT("%Y-%m-%d %H:%M:%S")) == NULL) + { + return; // date+time parsing failed + } + + if ((tableID == 0) || (tableID == 1)) + { + if (!tkz.HasMoreTokens()) + { + return; // nothing after time string + } + + wxString key = tkz.GetNextToken(); + + if (! d->dbPattern.Matches(key)) + { + return; // no valid key + } + + if (!tkz.HasMoreTokens()) + { + return; // nothing after time string + } + + wxString value = tkz.GetNextToken(); + + if (! d->dbPattern.Matches(value)) + { + return; // no valid key + } + + //wxLogVerbose(wxT("TABLE %d ") + key + wxT(" ") + value, tableID ); + + + if (tableID == 1) + { + wxMutexLocker lock(d->rptrMapMutex); + + IRCDDBAppRptrObject newRptr(dt, key, value); + + d->rptrMap[key] = newRptr; + + if (d->initReady) + { + wxString arearp_cs = key; + wxString zonerp_cs = value; + + arearp_cs.Replace(wxT("_"), wxT(" ")); + zonerp_cs.Replace(wxT("_"), wxT(" ")); + zonerp_cs.SetChar(7, wxT('G')); + + IRCMessage * m2 = new IRCMessage(wxT("IDRT_REPEATER")); + m2->addParam(arearp_cs); + m2->addParam(zonerp_cs); + m2->addParam(getIPAddress(value)); + d->replyQ.putMessage(m2); + } + } + else if ((tableID == 0) && d->initReady) + { + wxMutexLocker lock(d->rptrMapMutex); + + wxString userCallsign = key; + wxString arearp_cs = value; + wxString zonerp_cs; + wxString ip_addr; + + userCallsign.Replace(wxT("_"), wxT(" ")); + arearp_cs.Replace(wxT("_"), wxT(" ")); + + if (d->rptrMap.count(value) == 1) + { + IRCDDBAppRptrObject o = d->rptrMap[value]; + zonerp_cs = o.zonerp_cs; + zonerp_cs.Replace(wxT("_"), wxT(" ")); + zonerp_cs.SetChar(7, wxT('G')); + + ip_addr = getIPAddress(o.zonerp_cs); + } + + IRCMessage * m2 = new IRCMessage(wxT("IDRT_USER")); + m2->addParam(userCallsign); + m2->addParam(arearp_cs); + m2->addParam(zonerp_cs); + m2->addParam(ip_addr); + m2->addParam(tk + wxT(" ") + timeToken); + d->replyQ.putMessage(m2); + + } + + + } + } + +} + + +static wxString getTableIDString( int tableID, bool spaceBeforeNumber ) +{ + if (tableID == 0) + { + return wxT(""); + } + else if ((tableID > 0) && (tableID < numberOfTables)) + { + if (spaceBeforeNumber) + { + return wxString::Format(wxT(" %d"),tableID); + } + else + { + return wxString::Format(wxT("%d "),tableID); + } + } + else + { + return wxT(" TABLE_ID_OUT_OF_RANGE "); + } +} + + +void IRCDDBApp::msgQuery (IRCMessage * m) +{ + + if (m->getPrefixNick().StartsWith(wxT("s-")) && (m->numParams >= 2)) // server msg + { + wxString msg = m->params[1]; + wxStringTokenizer tkz(msg); + + if (!tkz.HasMoreTokens()) + { + return; // no text in message + } + + wxString cmd = tkz.GetNextToken(); + + if (cmd.IsSameAs(wxT("UPDATE"))) + { + wxString restOfLine = tkz.GetString(); + doUpdate(restOfLine); + } + else if (cmd.IsSameAs(wxT("LIST_END"))) + { + if (d->state == 5) // if in sendlist processing state + { + d->state = 3; // get next table + } + } + else if (cmd.IsSameAs(wxT("LIST_MORE"))) + { + if (d->state == 5) // if in sendlist processing state + { + d->state = 4; // send next SENDLIST + } + } + else if (cmd.IsSameAs(wxT("NOT_FOUND"))) + { + wxString callsign; + wxString restOfLine = tkz.GetString(); + doNotFound(restOfLine, callsign); + + if (callsign.Len() > 0) + { + callsign.Replace(wxT("_"), wxT(" ")); + + IRCMessage * m2 = new IRCMessage(wxT("IDRT_USER")); + m2->addParam(callsign); + m2->addParam(wxT("")); + m2->addParam(wxT("")); + m2->addParam(wxT("")); + m2->addParam(wxT("")); + d->replyQ.putMessage(m2); + } + } + } +} + + +void IRCDDBApp::setSendQ( IRCMessageQueue * s ) +{ + d->sendQ = s; +} + +IRCMessageQueue * IRCDDBApp::getSendQ() +{ + return d->sendQ; +} + + +static wxString getLastEntryTime(int tableID) +{ + + if (tableID == 1) + { + wxString max = IRCDDBAppRptrObject::maxTime.Format( wxT("%Y-%m-%d %H:%M:%S") ); + return max; + } + + return wxT("DBERROR"); +} + + +static bool needsDatabaseUpdate( int tableID ) +{ + return (tableID == 1); +} + + +wxThread::ExitCode IRCDDBApp::Entry() +{ + + int sendlistTableID = 0; + + while (!d->terminateThread) + { + + if (d->timer > 0) + { + d->timer --; + } + + switch(d->state) + { + case 0: // wait for network to start + + if (getSendQ() != NULL) + { + d->state = 1; + } + break; + + case 1: + // connect to db + d->state = 2; + d->timer = 200; + break; + + case 2: // choose server + wxLogVerbose(wxT("IRCDDBApp: state=2 choose new 's-'-user")); + if (getSendQ() == NULL) + { + d->state = 10; + } + else + { + if (findServerUser()) + { + sendlistTableID = numberOfTables; + + d->state = 3; // next: send "SENDLIST" + } + else if (d->timer == 0) + { + d->state = 10; + IRCMessage * m = new IRCMessage(wxT("QUIT")); + + m->addParam(wxT("no op user with 's-' found.")); + + IRCMessageQueue * q = getSendQ(); + if (q != NULL) + { + q->putMessage(m); + } + } + } + break; + + case 3: + if (getSendQ() == NULL) + { + d->state = 10; // disconnect DB + } + else + { + sendlistTableID --; + if (sendlistTableID < 0) + { + d->state = 6; // end of sendlist + } + else + { + wxLogVerbose(wxT("IRCDDBApp: state=3 tableID=%d"), sendlistTableID); + d->state = 4; // send "SENDLIST" + d->timer = 900; // 15 minutes max for update + } + } + break; + + case 4: + if (getSendQ() == NULL) + { + d->state = 10; // disconnect DB + } + else + { + if (needsDatabaseUpdate(sendlistTableID)) + { + IRCMessage * m = new IRCMessage(d->currentServer, + wxT("SENDLIST") + getTableIDString(sendlistTableID, true) + + wxT(" ") + getLastEntryTime(sendlistTableID) ); + + IRCMessageQueue * q = getSendQ(); + if (q != NULL) + { + q->putMessage(m); + } + + d->state = 5; // wait for answers + } + else + { + d->state = 3; // don't send SENDLIST for this table, go to next table + } + } + break; + + case 5: // sendlist processing + if (getSendQ() == NULL) + { + d->state = 10; // disconnect DB + } + else if (d->timer == 0) + { + d->state = 10; // disconnect DB + IRCMessage * m = new IRCMessage(wxT("QUIT")); + + m->addParam(wxT("timeout SENDLIST")); + + IRCMessageQueue * q = getSendQ(); + if (q != NULL) + { + q->putMessage(m); + } + + } + break; + + case 6: + if (getSendQ() == NULL) + { + d->state = 10; // disconnect DB + } + else + { + wxLogVerbose(wxT( "IRCDDBApp: state=6 initialization completed")); + + d->infoTimer = 2; + + d->initReady = true; + d->state = 7; + } + break; + + + case 7: // standby state after initialization + if (getSendQ() == NULL) + { + d->state = 10; // disconnect DB + } + + if (d->infoTimer > 0) + { + d->infoTimer --; + + if (d->infoTimer == 0) + { + if (d->rptrLocation.Len() > 0) + { + IRCMessage * m = new IRCMessage(d->currentServer, + wxT("IRCDDB QTH: ") + d->rptrLocation); + + IRCMessageQueue * q = getSendQ(); + if (q != NULL) + { + q->putMessage(m); + } + } + + if (d->rptrInfoURL.Len() > 0) + { + IRCMessage * m = new IRCMessage(d->currentServer, + wxT("IRCDDB URL: ") + d->rptrInfoURL); + + IRCMessageQueue * q = getSendQ(); + if (q != NULL) + { + q->putMessage(m); + } + } + + wxMutexLocker lock(d->moduleMapMutex); + + IRCDDBAppModuleMap::iterator it; + for( it = d->moduleMap.begin(); it != d->moduleMap.end(); ++it ) + { + wxString value = it->second; + IRCMessage * m = new IRCMessage(d->currentServer, + wxT("IRCDDB QRG: ") + value); + + IRCMessageQueue * q = getSendQ(); + if (q != NULL) + { + q->putMessage(m); + } + } + } + } + + if (d->wdTimer > 0) + { + d->wdTimer --; + if (d->wdTimer == 0) + { + d->wdTimer = 900; // 15 minutes + + IRCMessage * m = new IRCMessage(d->currentServer, + wxT("IRCDDB WATCHDOG: ") + getCurrentTime() + + wxT(" ") + d->wdInfo.Mid(0,120) + + wxString::Format(wxT(" %d"), d->wdCounter )); + + IRCMessageQueue * q = getSendQ(); + if (q != NULL) + { + q->putMessage(m); + d->wdCounter = 0; + } + } + } + break; + + case 10: + // disconnect db + d->state = 0; + d->timer = 0; + d->initReady = false; + break; + + } + + wxThread::Sleep(1000); + + + + + } // while + + return 0; +} // Entry() + + + + diff --git a/IRCDDBApp.h b/IRCDDBApp.h new file mode 100644 index 0000000..fc5c7d8 --- /dev/null +++ b/IRCDDBApp.h @@ -0,0 +1,98 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 . + +*/ + +#if !defined(_IRCDDBAPP_H) +#define _IRCDDBAPP_H + + +#include "IRCDDB.h" +#include "IRCApplication.h" + +#include + +class IRCDDBAppPrivate; + +class IRCDDBApp : public IRCApplication, wxThread +{ + public: + IRCDDBApp(const wxString& update_channel); + + virtual ~IRCDDBApp(); + + virtual void userJoin (const wxString& nick, const wxString& name, const wxString& host); + + virtual void userLeave (const wxString& nick); + + virtual void userChanOp (const wxString& nick, bool op); + virtual void userListReset(); + + virtual void msgChannel (IRCMessage * m); + virtual void msgQuery (IRCMessage * m); + + virtual void setCurrentNick(const wxString& nick); + virtual void setTopic(const wxString& topic); + + virtual void setBestServer(const wxString& ircUser); + + virtual void setSendQ( IRCMessageQueue * s ); + virtual IRCMessageQueue * getSendQ (); + + bool startWork(); + void stopWork(); + + IRCDDB_RESPONSE_TYPE getReplyMessageType(); + + IRCMessage * getReplyMessage(); + + bool findUser ( const wxString& s ); + bool findRepeater ( const wxString& s ); + bool findGateway ( const wxString& s ); + + bool sendHeard(const wxString& myCall, const wxString& myCallExt, + const wxString& yourCall, const wxString& rpt1, + const wxString& rpt2, unsigned char flag1, + unsigned char flag2, unsigned char flag3, + const wxString& destination, const wxString& tx_msg, + const wxString& tx_stats); + + int getConnectionState(); + + void rptrQRG( const wxString& module, double txFrequency, double duplexShift, + double range, double agl ); + + void rptrQTH( double latitude, double longitude, const wxString& desc1, + const wxString& desc2, const wxString& infoURL ); + + void kickWatchdog( const wxString& wdInfo ); + + protected: + virtual wxThread::ExitCode Entry(); + + private: + void doUpdate ( wxString& msg ); + void doNotFound ( wxString& msg, wxString& retval ); + wxString getIPAddress( wxString& zonerp_cs ); + bool findServerUser(); + IRCDDBAppPrivate * d; +}; + + +#endif diff --git a/IRCMessage.cpp b/IRCMessage.cpp new file mode 100644 index 0000000..b8a9ebd --- /dev/null +++ b/IRCMessage.cpp @@ -0,0 +1,177 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 "IRCMessage.h" + + + +IRCMessage::IRCMessage () +{ + numParams = 0; + prefixParsed = false; +} + +IRCMessage::IRCMessage ( const wxString& toNick, const wxString& msg ) +{ + command = wxT("PRIVMSG"); + numParams = 2; + params.Add( toNick ); + params.Add( msg ); + prefixParsed = false; +} + +IRCMessage::IRCMessage ( const wxString& cmd ) +{ + command = cmd; + numParams = 0; + prefixParsed = false; +} + +IRCMessage::~IRCMessage() +{ +} + + +void IRCMessage::addParam( const wxString& p ) +{ + params.Add( p ); + numParams = params.GetCount(); +} + +int IRCMessage::getParamCount() +{ + return params.GetCount(); +} + +wxString IRCMessage::getParam( int pos ) +{ + return params[pos]; +} + +wxString IRCMessage::getCommand() +{ + return command; +} + + +void IRCMessage::parsePrefix() +{ + unsigned int i; + + for (i=0; i < 3; i++) + { + prefixComponents.Add(wxT("")); + } + + int state = 0; + + for (i=0; i < prefix.Len(); i++) + { + wxChar c = prefix.GetChar(i); + + switch (c) + { + case wxT('!'): + state = 1; // next is name + break; + + case wxT('@'): + state = 2; // next is host + break; + + default: + prefixComponents[state].Append(c); + break; + } + } + + prefixParsed = true; +} + +wxString& IRCMessage::getPrefixNick() +{ + if (!prefixParsed) + { + parsePrefix(); + } + + return prefixComponents[0]; +} + +wxString& IRCMessage::getPrefixName() +{ + if (!prefixParsed) + { + parsePrefix(); + } + + return prefixComponents[1]; +} + +wxString& IRCMessage::getPrefixHost() +{ + if (!prefixParsed) + { + parsePrefix(); + } + + return prefixComponents[2]; +} + +void IRCMessage::composeMessage ( wxString& output ) +{ +#if defined(DEBUG_IRC) + wxString d = wxT("T [") + prefix + wxT("] [") + command + wxT("]"); + for (int i=0; i < numParams; i++) + { + d.Append(wxT(" [") + params[i] + wxT("]") ); + } + d.Replace(wxT("%"), wxT("%%"), true); + d.Replace(wxT("\\"), wxT("\\\\"), true); + wxLogVerbose(d); +#endif + + wxString o; + + if (prefix.Len() > 0) + { + o = wxT(":") + prefix + wxT(" "); + } + + o.Append(command); + + for (int i=0; i < numParams; i++) + { + if (i == (numParams - 1)) + { + o.Append(wxT(" :") + params[i]); + } + else + { + o.Append(wxT(" ") + params[i]); + } + } + + o.Append(wxT("\r\n")); + + output = o; +} + diff --git a/IRCMessage.h b/IRCMessage.h new file mode 100644 index 0000000..25c33af --- /dev/null +++ b/IRCMessage.h @@ -0,0 +1,71 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 . + +*/ + +#if !defined(_IRC_MESSAGE_H) +#define _IRC_MESSAGE_H + +#include + + +class IRCMessage +{ + public: + + IRCMessage(); + + IRCMessage( const wxString& toNick, const wxString& msg ); + + IRCMessage( const wxString& command ); + + ~IRCMessage(); + + + + wxString prefix; + wxString command; + wxArrayString params; + + int numParams; + + wxString& getPrefixNick(); + wxString& getPrefixName(); + wxString& getPrefixHost(); + + void composeMessage ( wxString& output ); + + void addParam( const wxString& p ); + + wxString getCommand(); + + wxString getParam( int pos ); + + int getParamCount(); + + private: + + void parsePrefix(); + + wxArrayString prefixComponents; + bool prefixParsed; + +}; + +#endif diff --git a/IRCMessageQueue.cpp b/IRCMessageQueue.cpp new file mode 100644 index 0000000..b42d798 --- /dev/null +++ b/IRCMessageQueue.cpp @@ -0,0 +1,137 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 "IRCMessageQueue.h" + + +IRCMessageQueue::IRCMessageQueue() +{ + eof = false; + first = NULL; + last = NULL; + +} + +IRCMessageQueue::~IRCMessageQueue() +{ + while (messageAvailable()) + { + IRCMessage * m = getMessage(); + + delete m; + } +} + + +bool IRCMessageQueue::isEOF() +{ + return eof; +} + + +void IRCMessageQueue::signalEOF() +{ + eof = true; +} + + +bool IRCMessageQueue::messageAvailable() +{ + wxMutexLocker lock(accessMutex); + + return (first != NULL); +} + + +IRCMessage * IRCMessageQueue::peekFirst() +{ + wxMutexLocker lock(accessMutex); + + IRCMessageQueueItem * k = first; + + if ( k == NULL ) + { + return NULL; + } + + return k->msg; +} + + +IRCMessage * IRCMessageQueue::getMessage() +{ + wxMutexLocker lock(accessMutex); + + IRCMessageQueueItem * k; + + if (first == NULL) + { + return NULL; + } + + k = first; + + first = k -> next; + + if (k -> next == NULL) + { + last = NULL; + } + else + { + k -> next -> prev = NULL; + } + + + IRCMessage * msg = k -> msg; + + delete k; + + return msg; +} + + +void IRCMessageQueue::putMessage( IRCMessage * m ) +{ + wxMutexLocker lock(accessMutex); + + // wxLogVerbose(wxT("IRCMessageQueue::putMessage")); + + IRCMessageQueueItem * k = new IRCMessageQueueItem(m); + + k -> prev = last; + k -> next = NULL; + + if (last == NULL) + { + first = k; + } + else + { + last -> next = k; + } + + last = k; +} + + + + diff --git a/IRCMessageQueue.h b/IRCMessageQueue.h new file mode 100644 index 0000000..b405a40 --- /dev/null +++ b/IRCMessageQueue.h @@ -0,0 +1,79 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 . + +*/ + +#if !defined(_IRCMESSAGEQUEUE_H) +#define _IRCMESSAGEQUEUE_H + +#include + +#include "IRCMessage.h" + + +class IRCMessageQueueItem +{ + public: + IRCMessageQueueItem( IRCMessage * m ) + { + msg = m; + } + + ~IRCMessageQueueItem() + { + } + + IRCMessage * msg; + + IRCMessageQueueItem * prev; + IRCMessageQueueItem * next; +}; + + +class IRCMessageQueue +{ + public: + IRCMessageQueue(); + + ~IRCMessageQueue(); + + bool isEOF(); + + void signalEOF(); + + bool messageAvailable(); + + IRCMessage * getMessage(); + + IRCMessage * peekFirst(); + + void putMessage ( IRCMessage * m ); + + private: + + bool eof; + + IRCMessageQueueItem * first; + IRCMessageQueueItem * last; + + wxMutex accessMutex; + +}; + +#endif diff --git a/IRCProtocol.cpp b/IRCProtocol.cpp new file mode 100644 index 0000000..122b687 --- /dev/null +++ b/IRCProtocol.cpp @@ -0,0 +1,447 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 "IRCProtocol.h" + +#include + +#define CIRCDDB_VERSION "1.2.4" + +IRCProtocol::IRCProtocol ( IRCApplication * app, + const wxString& callsign, const wxString& password, const wxString& channel, + const wxString& versionInfo ) +{ + this -> password = password; + this -> channel = channel; + this -> app = app; + + this->versionInfo = wxT("CIRCDDB:"); + this->versionInfo.Append(wxT(CIRCDDB_VERSION)); + + if (versionInfo.Len() > 0) + { + this->versionInfo.Append(wxT(" ")); + this->versionInfo.Append(versionInfo); + } + + + int hyphenPos = callsign.find(wxT('-')); + + if (hyphenPos == wxNOT_FOUND) + { + wxString n; + + n = callsign + wxT("-1"); + nicks.Add(n); + n = callsign + wxT("-2"); + nicks.Add(n); + n = callsign + wxT("-3"); + nicks.Add(n); + n = callsign + wxT("-4"); + nicks.Add(n); + } + else + { + nicks.Add(callsign); + } + + name = callsign; + + pingTimer = 60; // 30 seconds + state = 0; + timer = 0; + + + chooseNewNick(); +} + +IRCProtocol::~IRCProtocol() +{ +} + +void IRCProtocol::chooseNewNick() +{ + int r = rand() % nicks.GetCount(); + + currentNick = nicks[r]; +} + +void IRCProtocol::setNetworkReady( bool b ) +{ + if (b == true) + { + if (state != 0) + { + wxLogError(wxT("IRCProtocol::setNetworkReady: unexpected state")); + } + + state = 1; + chooseNewNick(); + } + else + { + state = 0; + } +} + + +bool IRCProtocol::processQueues ( IRCMessageQueue * recvQ, IRCMessageQueue * sendQ ) +{ + if (timer > 0) + { + timer --; + } + + while (recvQ->messageAvailable()) + { + IRCMessage * m = recvQ -> getMessage(); + +#if defined(DEBUG_IRC) + wxString d = wxT("R [") + m->prefix + wxT("] [") + m->command + wxT("]"); + for (int i=0; i < m->numParams; i++) + { + d.Append(wxT(" [") + m->params[i] + wxT("]") ); + } + d.Replace(wxT("%"), wxT("%%"), true); + d.Replace(wxT("\\"), wxT("\\\\"), true); + wxLogVerbose(d); +#endif + + if (m->command.IsSameAs(wxT("004"))) + { + if (state == 4) + { + if (m->params.GetCount() > 1) + { + wxRegEx serverNamePattern(wxT("^grp[1-9]s[1-9].ircDDB$")); + + if (serverNamePattern.Matches( m->params[1] )) + { + app->setBestServer(wxT("s-") + m->params[1].Mid(0,6)); + } + } + state = 5; // next: JOIN + app->setCurrentNick(currentNick); + } + } + else if (m->command.IsSameAs(wxT("PING"))) + { + IRCMessage * m2 = new IRCMessage(); + m2->command = wxT("PONG"); + if (m->params.GetCount() > 0) + { + m2->numParams = 1; + m2->params.Add( m->params[0] ); + } + sendQ -> putMessage(m2); + } + else if (m->command.IsSameAs(wxT("JOIN"))) + { + if ((m->numParams >= 1) && m->params[0].IsSameAs(channel)) + { + if (m->getPrefixNick().IsSameAs(currentNick) && (state == 6)) + { + if (debugChannel.Len() > 0) + { + state = 7; // next: join debug_channel + } + else + { + state = 10; // next: WHO * + } + } + else if (app != NULL) + { + app->userJoin( m->getPrefixNick(), m->getPrefixName(), m->getPrefixHost()); + } + } + + if ((m->numParams >= 1) && m->params[0].IsSameAs(debugChannel)) + { + if (m->getPrefixNick().IsSameAs(currentNick) && (state == 8)) + { + state = 10; // next: WHO * + } + } + } + else if (m->command.IsSameAs(wxT("PONG"))) + { + if (state == 12) + { + timer = pingTimer; + state = 11; + } + } + else if (m->command.IsSameAs(wxT("PART"))) + { + if ((m->numParams >= 1) && m->params[0].IsSameAs(channel)) + { + if (app != NULL) + { + app->userLeave( m->getPrefixNick() ); + } + } + } + else if (m->command.IsSameAs(wxT("KICK"))) + { + if ((m->numParams >= 2) && m->params[0].IsSameAs(channel)) + { + if (m->params[1].IsSameAs(currentNick)) + { + // i was kicked!! + delete m; + return false; + } + else if (app != NULL) + { + app->userLeave( m->params[1] ); + } + } + } + else if (m->command.IsSameAs(wxT("QUIT"))) + { + if (app != NULL) + { + app->userLeave( m->getPrefixNick() ); + } + } + else if (m->command.IsSameAs(wxT("MODE"))) + { + if ((m->numParams >= 3) && m->params[0].IsSameAs(channel)) + { + if (app != NULL) + { + size_t i; + wxString mode = m->params[1]; + + for (i = 1; (i < mode.Len()) && ((size_t) m->numParams >= (i+2)); i++) + { + if ( mode[i] == wxT('o') ) + { + if ( mode[0] == wxT('+') ) + { + app->userChanOp(m->params[i+1], true); + } + else if ( mode[0] == wxT('-') ) + { + app->userChanOp(m->params[i+1], false); + } + } + } // for + } + } + } + else if (m->command.IsSameAs(wxT("PRIVMSG"))) + { + if ((m->numParams == 2) && (app != NULL)) + { + if (m->params[0].IsSameAs(channel)) + { + app->msgChannel(m); + } + else if (m->params[0].IsSameAs(currentNick)) + { + app->msgQuery(m); + } + } + } + else if (m->command.IsSameAs(wxT("352"))) // WHO list + { + if ((m->numParams >= 7) && m->params[0].IsSameAs(currentNick) + && m->params[1].IsSameAs(channel)) + { + if (app != NULL) + { + app->userJoin( m->params[5], m->params[2], m->params[3]); + app->userChanOp ( m->params[5], m->params[6].IsSameAs(wxT("H@"))); + } + } + } + else if (m->command.IsSameAs(wxT("433"))) // nick collision + { + if (state == 2) + { + state = 3; // nick collision, choose new nick + timer = 10; // wait 5 seconds.. + } + } + else if (m->command.IsSameAs(wxT("332")) || + m->command.IsSameAs(wxT("TOPIC"))) // topic + { + if ((m->numParams == 2) && (app != NULL) && + m->params[0].IsSameAs(channel) ) + { + app->setTopic(m->params[1]); + } + } + + delete m; + } + + IRCMessage * m; + + switch (state) + { + case 1: + m = new IRCMessage(); + m->command = wxT("PASS"); + m->numParams = 1; + m->params.Add(password); + sendQ->putMessage(m); + + m = new IRCMessage(); + m->command = wxT("NICK"); + m->numParams = 1; + m->params.Add(currentNick); + sendQ->putMessage(m); + + timer = 10; // wait for possible nick collision message + state = 2; + break; + + case 2: + if (timer == 0) + { + m = new IRCMessage(); + m->command = wxT("USER"); + m->numParams = 4; + m->params.Add(name); + m->params.Add(wxT("0")); + m->params.Add(wxT("*")); + m->params.Add(versionInfo); + sendQ->putMessage(m); + + timer = 30; + state = 4; // wait for login message + } + break; + + case 3: + if (timer == 0) + { + chooseNewNick(); + m = new IRCMessage(); + m->command = wxT("NICK"); + m->numParams = 1; + m->params.Add(currentNick); + sendQ->putMessage(m); + + timer = 10; // wait for possible nick collision message + state = 2; + } + break; + + case 4: + if (timer == 0) + { + // no login message received -> disconnect + return false; + } + break; + + case 5: + m = new IRCMessage(); + m->command = wxT("JOIN"); + m->numParams = 1; + m->params.Add(channel); + sendQ->putMessage(m); + + timer = 30; + state = 6; // wait for join message + break; + + case 6: + if (timer == 0) + { + // no join message received -> disconnect + return false; + } + break; + + case 7: + if (debugChannel.Len() == 0) + { + return false; // this state cannot be processed if there is no debug_channel + } + + m = new IRCMessage(); + m->command = wxT("JOIN"); + m->numParams = 1; + m->params.Add(debugChannel); + sendQ->putMessage(m); + + timer = 30; + state = 8; // wait for join message + break; + + case 8: + if (timer == 0) + { + // no join message received -> disconnect + return false; + } + break; + + case 10: + m = new IRCMessage(); + m->command = wxT("WHO"); + m->numParams = 2; + m->params.Add(channel); + m->params.Add(wxT("*")); + sendQ->putMessage(m); + + timer = pingTimer; + state = 11; // wait for timer and then send ping + + if (app != NULL) + { + app->setSendQ(sendQ); // this switches the application on + } + break; + + case 11: + if (timer == 0) + { + m = new IRCMessage(); + m->command = wxT("PING"); + m->numParams = 1; + m->params.Add(currentNick); + sendQ->putMessage(m); + + timer = pingTimer; + state = 12; // wait for pong + } + break; + + case 12: + if (timer == 0) + { + // no pong message received -> disconnect + return false; + } + break; + } + + return true; +} + + diff --git a/IRCProtocol.h b/IRCProtocol.h new file mode 100644 index 0000000..cf144aa --- /dev/null +++ b/IRCProtocol.h @@ -0,0 +1,65 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 . + +*/ + +#if !defined(_IRCPROTOCOL_H) +#define _IRCPROTOCOL_H + +#include + +#include "IRCMessageQueue.h" +#include "IRCApplication.h" + + +class IRCProtocol +{ + public: + IRCProtocol ( IRCApplication * app, + const wxString& callsign, const wxString& password, const wxString& channel, + const wxString& versionInfo ); + + ~IRCProtocol(); + + void setNetworkReady( bool state ); + + bool processQueues ( IRCMessageQueue * recvQ, IRCMessageQueue * sendQ ); + + private: + void chooseNewNick(); + + wxArrayString nicks; + wxString password; + wxString channel; + wxString name; + wxString currentNick; + wxString versionInfo; + + int state; + int timer; + int pingTimer; + + wxString debugChannel; + + IRCApplication * app; + +}; + + +#endif diff --git a/IRCReceiver.cpp b/IRCReceiver.cpp new file mode 100644 index 0000000..5c55fe1 --- /dev/null +++ b/IRCReceiver.cpp @@ -0,0 +1,249 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 . + +*/ + +#if defined(WIN32) + +#define WIN32_LEAN_AND_MEAN + +#include +#include + +#else + +#include +#include + +#endif + + + +#include "IRCReceiver.h" + +#include + +IRCReceiver::IRCReceiver(int sock, IRCMessageQueue * q ) + : wxThread(wxTHREAD_JOINABLE) +{ + this->sock = sock; + + recvQ = q; +} + +IRCReceiver::~IRCReceiver() +{ +} + +bool IRCReceiver::startWork() +{ + + if (Create() != wxTHREAD_NO_ERROR) + { + wxLogError(wxT("IRCReceiver::startWork: Could not create the worker thread!")); + return false; + } + + terminateThread = false; + + if (Run() != wxTHREAD_NO_ERROR) + { + wxLogError(wxT("IRCReceiver::startWork: Could not run the worker thread!")); + return false; + } + + return true; +} + +void IRCReceiver::stopWork() +{ + terminateThread = true; + + Wait(); +} + +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; + + res = select(sock+1, &rdset, NULL, &errset, &tv); + + if ( res < 0 ) + { + wxLogSysError(wxT("IRCReceiver::doRead: select")); + return -1; + } + else if ( res > 0 ) + { + if (FD_ISSET(sock, &errset)) + { + wxLogVerbose(wxT("IRCReceiver::doRead: select (FD_ISSET(sock, exceptfds))")); + return -1; + } + + if (FD_ISSET(sock, &rdset)) + { + res = recv(sock, buf, buf_size, 0); + + if (res < 0) + { + wxLogSysError(wxT("IRCReceiver::doRead: read")); + return -1; + } + else if (res == 0) + { + wxLogVerbose(wxT("IRCReceiver::doRead: EOF read==0")); + return -1; + } + else + { + return res; + } + } + + } + + return 0; +} + +wxThread::ExitCode IRCReceiver::Entry () +{ + IRCMessage * m = new IRCMessage(); + + int i; + int state = 0; + + while (!terminateThread) + { + + // wxLogVerbose(wxT("IRCReceiver: tick")); + + char buf[200]; + int r = doRead( sock, buf, sizeof buf ); + + if (r < 0) + { + recvQ -> signalEOF(); + delete m; // delete unfinished IRCMessage + break; + } + + for (i=0; i < r; i++) + { + char b = buf[i]; + + if (b > 0) + { + if (b == 10) + { + recvQ -> putMessage(m); + m = new IRCMessage(); + state = 0; + } + else if (b == 13) + { + // do nothing + } + else switch (state) + { + case 0: + if (b == ':') + { + state = 1; // prefix + } + else if (b == 32) + { + // do nothing + } + else + { + m -> command.Append(wxChar(b)); + state = 2; // command + } + break; + + case 1: + if (b == 32) + { + state = 2; // command is next + } + else + { + m -> prefix.Append(wxChar(b)); + } + break; + + case 2: + if (b == 32) + { + state = 3; // params + m -> numParams = 1; + m -> params.Add(wxT("")); + } + else + { + m -> command.Append(wxChar(b)); + } + break; + + case 3: + if (b == 32) + { + m -> numParams ++; + if (m -> numParams >= 15) + { + state = 5; // ignore the rest + } + + m -> params.Add(wxT("")); + } + else if ((b == ':') && (m -> params[ m -> numParams-1 ].Len() == 0)) + { + state = 4; // rest of line is this param + } + else + { + m -> params[ m -> numParams-1 ].Append(wxChar(b)); + } + break; + + case 4: + m -> params[ m -> numParams-1 ].Append(wxChar(b)); + break; + + + } // switch + } // if + } // for + } // while + + return 0; +} + diff --git a/IRCReceiver.h b/IRCReceiver.h new file mode 100644 index 0000000..0f371ce --- /dev/null +++ b/IRCReceiver.h @@ -0,0 +1,62 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 . + +*/ + + + +#if !defined(_IRCRECEIVER_H) +#define _IRCRECEIVER_H + +#include + +#include "IRCMessageQueue.h" + + +class IRCReceiver : public wxThread +{ + public: + + IRCReceiver(int sock, IRCMessageQueue * q); + + ~IRCReceiver(); + + + bool startWork(); + + void stopWork(); + + + protected: + + virtual wxThread::ExitCode Entry(); + + + + private: + + + bool terminateThread; + int sock; + IRCMessageQueue * recvQ; + +}; + + +#endif diff --git a/IRCutils.cpp b/IRCutils.cpp new file mode 100644 index 0000000..a72ede2 --- /dev/null +++ b/IRCutils.cpp @@ -0,0 +1,180 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010-2011 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 . + +*/ + + +#if defined(WIN32) + +#define WIN32_LEAN_AND_MEAN + +#include +#include + +#else + +#include +#include +#include + +#endif + + + +#include + +#include "IRCutils.h" + + +int getAllIPV4Addresses ( const char * name, unsigned short port, + unsigned int * num, struct sockaddr_in * addr, unsigned int max_addr ) +{ + + struct addrinfo hints; + struct addrinfo * res; + + memset(&hints, 0x00, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = SOCK_STREAM; + + int r = getaddrinfo( name, NULL, &hints, &res ); + + if (r == 0) + { + struct addrinfo * rp; + unsigned int numAddr = 0; + + for (rp = res; rp != NULL; rp = rp->ai_next) + { + if (rp->ai_family == AF_INET) + { + numAddr ++; + } + } + + if (numAddr > 0) + { + if (numAddr > max_addr) + { + numAddr = max_addr; + } + + int * shuffle = new int[numAddr]; + + unsigned int i; + + for (i=0; i < numAddr; i++) + { + shuffle[i] = i; + } + + for (i=0; i < (numAddr - 1); i++) + { + if (rand() & 1) + { + int tmp; + tmp = shuffle[i]; + shuffle[i] = shuffle[i+1]; + shuffle[i+1] = tmp; + } + } + + for (i=(numAddr - 1); i > 0; i--) + { + if (rand() & 1) + { + int tmp; + tmp = shuffle[i]; + shuffle[i] = shuffle[i-1]; + shuffle[i-1] = tmp; + } + } + + for (rp = res, i=0 ; (rp != NULL) && (i < numAddr); rp = rp->ai_next) + { + if (rp->ai_family == AF_INET) + { + memcpy( addr+shuffle[i], rp->ai_addr, sizeof (struct sockaddr_in) ); + + addr[shuffle[i]].sin_port = htons(port); + + i++; + } + } + + delete[] shuffle; + } + + *num = numAddr; + + freeaddrinfo(res); + + return 0; + + } + else + { + wxString e( gai_strerror(r), wxConvUTF8); + + wxLogWarning(wxT("getaddrinfo: ") + e ); + + return 1; + } + + +} + + + + + +void safeStringCopy (char * dest, const char * src, unsigned int buf_size) +{ + unsigned int i = 0; + + while (i < (buf_size - 1) && (src[i] != 0)) + { + dest[i] = src[i]; + i++; + } + + dest[i] = 0; +} + + +wxString getCurrentTime(void) +{ + time_t now = time(NULL); + struct tm* tm; + struct tm tm_buf; + char buffer[25]; + +#if defined(__WINDOWS__) + gmtime_s(&tm_buf, &now); + tm = &tm_buf; +#else + gmtime_r(&now, &tm_buf); + tm = &tm_buf; +#endif + + strftime(buffer, sizeof buffer, "%Y-%m-%d %H:%M:%S", tm); + + return wxString(buffer, wxConvLocal); +} + diff --git a/IRCutils.h b/IRCutils.h new file mode 100644 index 0000000..f2e44a6 --- /dev/null +++ b/IRCutils.h @@ -0,0 +1,31 @@ +/* + +CIRCDDB - ircDDB client library in C++ + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 . + +*/ + + + +int getAllIPV4Addresses ( const char * name, unsigned short port, + unsigned int * num, struct sockaddr_in * addr, unsigned int max_addr ); + + +void safeStringCopy (char * dest, const char * src, unsigned int buf_size); + +wxString getCurrentTime(void); + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b417c45 --- /dev/null +++ b/Makefile @@ -0,0 +1,153 @@ +# +# 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 . +# + +# locations for the executibles and other files are set here +# NOTE: IF YOU CHANGE THESE, YOU WILL NEED TO UPDATE THE service.* FILES AND +# SOME OF THE *.sh FILES TOO! +BINDIR=/usr/local/bin +CFGDIR=/usr/local/etc +LOGDIR=/var/log + +CPPFLAGS=-W -Wall -I/usr/include `wx-config --cppflags base` +LDFLAGS=-L/usr/lib `wx-config --libs base` + +PROGRAMS=g2_ircddb g2_link dvap_rptr + +IRCDDBOBJS = IRCDDB.o IRCClient.o IRCReceiver.o IRCMessageQueue.o IRCProtocol.o IRCMessage.o IRCDDBApp.o IRCutils.o golay23.o dstar_dv.o + +all : $(PROGRAMS) + +g2_ircddb : g2_ircddb.cpp $(IRCDDBOBJS) + g++ $(CPPFLAGS) -o g2_ircddb g2_ircddb.cpp $(IRCDDBOBJS) $(LDFLAGS) + +g2_link : g2_link.cpp + g++ -W -Wall -o g2_link g2_link.cpp -lrt -pthread + +dvap_rptr : dvap_rptr.cpp dstar_dv.o golay23.o + g++ -W -Wall -g -o dvap_rptr dvap_rptr.cpp golay23.o dstar_dv.o -I/usr/include -L/usr/lib -lrt + +IRCDDB.o : IRCDDB.cpp IRCDDB.h + g++ $(CPPFLAGS) -c $(CPPFLAGS) IRCDDB.cpp + +IRCClient.o : IRCClient.cpp IRCClient.h IRCutils.h + g++ -c $(CPPFLAGS) IRCClient.cpp + +IRCReceiver.o : IRCReceiver.cpp IRCReceiver.h IRCMessageQueue.h + g++ -c $(CPPFLAGS) IRCReceiver.cpp + +IRCMessageQueue.o : IRCMessageQueue.cpp IRCMessageQueue.h IRCMessage.h + g++ -c $(CPPFLAGS) IRCMessageQueue.cpp + +IRCProtocol.o : IRCProtocol.cpp IRCProtocol.h + g++ -c $(CPPFLAGS) IRCProtocol.cpp + +IRCMessage.o : IRCMessage.cpp IRCMessage.h + g++ -c $(CPPFLAGS) IRCMessage.cpp + +IRCDDBApp.o : IRCDDBApp.cpp IRCDDBApp.h IRCutils.h + g++ -c $(CPPFLAGS) IRCDDBApp.cpp + +golay23.o : golay23.cpp golay23.h + g++ -c $(CPPFLAGS) golay23.cpp + +dstar_dv.o : dstar_dv.cpp dstar_dv.h golay23.h + g++ -c $(CPPFLAGS) dstar_dv.cpp + +g2link_test : g2link_test.cpp + g++ -W -Wall -o g2link_test g2link_test.cpp -lrt + +g2link_test_audio : g2link_test_audio.cpp + g++ -W -Wall -o g2link_test_audio g2link_test_audio.cpp -lrt + +IRCApplication.h : IRCMessageQueue.h +IRCClient.h : IRCReceiver.h IRCMessageQueue.h IRCProtocol.h IRCApplication.h +IRCDDBApp.h : IRCDDB.h IRCApplication.h +IRCMessageQueue.h : IRCMessage.h +IRCProtocol.h : IRCMessageQueue.h IRCApplication.h +IRCReceiver.h : IRCMessageQueue.h + +clean : + /bin/rm -f *.o + +realclean : + /bin/rm -f *.o $(PROGRAMS) g2link_test g2link_text_audio + +install: + ######### g2_ircddb ######### + /bin/cp -f g2_ircddb $(BINDIR) + /bin/cp -f g2_ircddb.cfg $(CFGDIR) + /bin/cp -f service.g2_ircddb /etc/init.d/g2_ircddb + /usr/sbin/update-rc.d g2_ircddb defaults + /usr/sbin/update-rc.d g2_ircddb enable + ######### g2_link ######### + /bin/cp -f g2_link $(BINDIR) + /bin/cp -f g2_link.cfg $(CFGDIR) + /bin/cp -f announce/*.dat $(CFGDIR) + /bin/cp -f gwys.txt $(CFGDIR) + /bin/cp -f exec_?.sh $(CFGDIR) + /bin/cp -f service.g2_link /etc/init.d/g2_link + /usr/sbin/update-rc.d g2_link defaults + /usr/sbin/update-rc.d g2_link enable + ######### dvap_rptr ######### + /bin/cp -f dvap_rptr $(BINDIR) + /bin/cp -f dvap_rptr.sh $(BINDIR) + /bin/cp -f dvap_rptr.cfg $(CFGDIR) + /bin/cp -f service.dvap_rptr /etc/init.d/dvap_rptr + /usr/sbin/update-rc.d dvap_rptr defaults + /usr/sbin/update-rc.d dvap_rptr enable + +installdtmfs : g2link_test + /bin/cp -f g2link_test $(BINDIR) + /bin/cp -f proc_g2_ircddb_dtmfs.sh $(BINDIR) + /bin/cp -f service.proc_g2_ircddb_dtmfs /etc/init.d/proc_g2_ircddb_dtmfs + /usr/sbin/update-rc.d proc_g2_ircddb_dtmfs defaults + /usr/sbin/update-rc.d proc_g2_ircddb_dtmfs enable + +uninstalldtmfs: + /usr/sbin/service proc_g2_ircddb_dtmfs stop + /bin/rm -f /etc/init.d/proc_g2_ircddb_dtmfs + /usr/sbin/update-rc.d proc_g2_ircddb_dtmfs remove + /bin/rm -f $(BINDIR)/proc_g2_ircddb_dtmfs.sh + /bin/rm -f $(BINDIR)/g2link_test + +uninstall: + ######### g2_ircddb ######### + /usr/sbin/service g2_ircddb stop + /bin/rm -f /etc/init.d/g2_ircddb + /usr/sbin/update-rc.d g2_ircddb remove + /bin/rm -f $(BINDIR)/g2_ircddb + /bin/rm -f $(CFGDIR)/g2_ircddb.cfg + ######### g2_link ######### + /usr/sbin/service g2_link stop + /bin/rm -f /etc/init.d/g2_link + /usr/sbin/update-rc.d g2_link remove + /bin/rm -f $(BINDIR)/g2_link + /bin/rm -f $(CFGDIR)/g2_link.cfg + /bin/rm -f $(CFGDIR)/already_linked.dat + /bin/rm -f $(CFGDIR)/already_unlinked.dat + /bin/rm -f $(CFGDIR)/failed_linked.dat + /bin/rm -f $(CFGDIR)/id.dat + /bin/rm -f $(CFGDIR)/linked.dat + /bin/rm -f $(CFGDIR)/unlinked.dat + /bin/rm -f $(CFGDIR)/RPT_STATUS.txt + /bin/rm -f $(CFGDIR)/gwys.txt + /bin/rm -f $(CFGDIR)/exec_?.sh + /bin/rm -f /var/log/g2_link.log + ######### dvap_rptr ######### + /usr/sbin/service dvap_rptr stop + /bin/rm -f $(BINDIR)/dvap_rptr + /bin/rm -f $(BINDIR)/dvap_rptr.sh + /usr/sbin/update-rc.d dvap_rptr remove + /bin/rm -r $(CFGDIR)/dvap_rptr.cfg diff --git a/announce/already_linked.dat b/announce/already_linked.dat new file mode 100644 index 0000000000000000000000000000000000000000..ba1910a6d7d31262e83305124ec94eb7fd08305d GIT binary patch literal 3287 zcmaLaeK6a190&0DB}n8^5(M2zI}f+Z+N!lU4ri^Fs(MH~RCL-ZhNH&Xjy`E`RYyfO zqaIFlV`UyzBh}nYsp%?=dIY5-+obyNTBftESyS(oG`~5!O@8|csPfqx7 z+}+Qh6o)nH1lxy0L9Jk!C^h7Xd4l3MjuV^1WHKFhzE2(ZRDPIDSEpmXCqHL0y*xNx za=GDllDV5i2~sN(H2fVd&Uhcx{Segzsg_w2y;dU&(eCLVBEln!DjM1NuZjUg3tDZ8 zL%D~UGXP?8Qic4+UWbt=0M!MpmRXZNMn}p3)Y!dS05!Mo*56?nN@7(dalEtkqC#KR zD3sI^w2B^|+M^V_s%sKdU6YujtW%Q?)B}Rdp&FOwmKiQlJLQX%+Hc?XlU2syfj{JjRznYeeZRun3qXeJ@t! z=6u%20IHYjO;nA=&pn(5P-EU}g>^q_?2AAEHK*BY=&Qy(i~Rs%2d>?Si{pt26aaBc z9*+Hy_3Pyx0BSvbG~`ngC@cRNKz#O#i_bTNj)Y_Y3E$}lJJi0%RJ{X`SaCC!n6?=> zZ2=%@mKzfn=GW3V4j@^)ud>v+jB)oQfRrq|DY3Kka>5{h+Aq1+Sbmq{h|2&{fB9zV zTwr4+BM3m+P+)MUtDVX3)&SBomuIPExPLcC07Uh~gh$lyN{aKK23zWw;`ety6J-EN zWUGBS{|_X){3d{CL9D#l^=@Bs5`dU{(W@0nc5Kfc0M)0xo%3d=Y%-xITchFY;lneJ zv3a>bQZvW9??+j3(;etNgw3@?C~XumFM#^iod z03f9;UHq3z?U`^R0JZ#-j|v`lw?mHbm4y$V1~ zUHfFZdjUJq9zgZx4pe%)w&5i79@1DDmUl=4Dh;}Tq^9Wh`aAA<8Q%&(>_9i)@!^QR zc?y8I;Vkh-VO_u0ivZMWlU#q+`=_mx2q6AxKmf*t!HIKo4*uD61%MB z$9Q;1;D`(8>ypof?I)^6c+Z2^ke0VFA%1Eub`tqAkq0MxGTn?QJ0W*N{T zP%F=c8nl|5Nm_s;O%Qm+u+*3nya*s&&e%i_b$1h?)1svIV{CfK6n7MAuonhFwiD7x zw>!XTk@2B2YW<2SY92uJn-JtcVU(tX10ZJXa&^-8_r;zu0IK(&eswG4+rsfg05u+@ zQm{<|;f5}NnwcbF>}TGjjDzWB}s&KRrMVA!M(W1E}?S$<)6lzYaqK z5MT0Eh-bCyHb6+IY^p@k&bl`_zW!eA8$9*l^8@^sidKEZ=Z6G^g-lMq zHJ#-DpF}Wqb#9&3&gD$_V(T~20?T&IdN;~Drawt|=uO07+10t%qP%+_0*J@d%L?yB zn3@F;#nejqYOymPYL<{AT%o)tajGZTXo`5pY8;J#Bt73kGd~w?S27}G#2_4Z!B$Zz6~I` zd&l#wkvR=?7J!st_R2N#3v z1dvhs**2S&S3-jqfR+PMJ5n8jnhxIwklAHZd6`=3Q2PmhtgLiH19f6M`zV0yKgBzo zK3$*67y*z|mNFbExmGX-+7&CEWMf-^=YmUaK$2T9vWa(wa{E;;fXMBTEotM@@&|d) z0lPoimm$-fe{lpz;wD;0im%{~oc#_!JhpAJF<^I>Gz&m9Klq6}%RMwVA3(y`EJ1de zfzz=CKw{fBJ>LB%$$}mLNwRR=Z|>6T6W|S2l#D|>I!P`}AW3P7C`b@D#`S6eq~>~1 znxeGC_(uTJWGH+6&P2h-3IORH+?7Y72CR+HR2b6Hal&MOc+FZMX<3y!xi~GieFXtP zW>N6f3sUrQ{2c&UEr%NF*Qf*ub^x;PJWdt#%syqC1|TP={h3yM(D%xD0Ij-Ro_H+i zH5T6lkbCt>s=xGLgWXR6BJCAJo)40^zJbsI%M=Z6-oME%xfn>|+O+!l|9m0NTL2(_ zP&9bgvRs9Z0f=JJ0qM>)?`ZAw{mN6C-1pA3?xa?rH1@Ki>gsJ zfMi+FJ-6aKfRUVLJ zt32&}9&pr=^#F1%+6<)dT5qNK0BAMPJxCs`GhUeuAom&ejOT``)`$Q^@@+-^Wrx>3 zuZIrUP4~3L!oUxCG9ZaNSGYkaXb(#e0Em|r&&QXaPVs~8c+?nudZ$W{`sjcpp;4yT zMN=ips{kZcMsDB@CE07603_9SCW`id9UAl$K(gZPp(S3?WBMTgDUCJ>PKv*hH+KO@ zEswfGnq|~WpaIc}Y>loT*o)6Wr2zehGRJQ-Vm(QZfo2&7mqJ!`+=l#h09y8)kRlNp z+tpC{!Tk9w;*LM=kPnTPwRe+j-htg6%w(WhcH3rU>?w_003|t%3_fSis(mO_n^+aC z|K7Fv@3Xg70L^kW4#zJ#^O|d+zbho;J=g1!b!+^o&;gSUpLDd>XC8%)CazgweYJ1a zi{{UPKJn)rIen-NL;eClbmCx*KBje-0lHENE%b^m3(;2#TY;ph$TwBvGt{>vbG4?a w$e;Or#Z=_a{O&--)LzOGvGcKqz<_Ax`ZN{!UZo2qPV=B-;MB|7^kZuFKP3CkL;wH) literal 0 HcmV?d00001 diff --git a/announce/failed_linked.dat b/announce/failed_linked.dat new file mode 100644 index 0000000000000000000000000000000000000000..878ec343684e415e52911fac18a34e6cd603bd26 GIT binary patch literal 3229 zcmaLadrVVj6aetjmOh|Q+Cj#)P?1pH2xCOR(1-#7G+iwZ1spPyjX@$WGYGHM7a&l= z!-R);VHFv)#TBU1F*XL{Asr>4i3Od2U_qc8oeBsltTe`Qa=U>+tS(@Dbk5hs6pD{30qSfW-rbtt`l^-G+5y}L(Xiofd05J&H zzqxUI|AA%zv9(4bO%b7Y^|NS>4$6r$WYq3f2r2^8SWbO|8LsCZ4W?2w1jo3upnal^ z)fS`Z7xxWc&pDn1tQI@^&paQ0OIg$cpsBKceE3yfebFd@xSn;__RtJ$RRn(t&y5VPL`NV%PVyQ`$6s+R^JwaUc})AQB(;bj16Q(8OBjj(K2 zK7e$Q*vn~jB=EvZ04-FGmJUBu<>apd$kY$vBM^z|lYjSh z>x9ZD2YP}%V&=9fdd-&#fk^ax=%-l)KYkuB1`zY}up%qfDoHm0AXXXLmFUr{Zv7QN z(?KU`fFSeA$*Tb3l*7U0VLP+ey>KzGZ7F{#Pe=# zqveTs6c9=6nDdmmg^buu0Z5z0${FVi}Uy{v2sD;4zP zIRIi_Iw{a~4>mp<0uZ}IX%C2{om*Q3&{U@s&_b+*Y$brW{>6;v#U0=0WdVr4SNCRc zBxR)d3V?*pk0$X-j$l>-AhDhKL|tFMuC9n{Ov3xF)C)O2#2`SQC!B&9v}0aMjN_5Bz?s%Uar`a}|a2kHdczmGHA!V*Fq zp<9qX^>;3Q+LLSz72ZO#RHgWKI>;NgNTW-$ejXYGuAe zd-_z!Td2X5{n%{T`JrSU5Q*N@P|{@w{vyW!h$$TwA2}ACAO;(3v&h%^%z}XD2Sl1~ zEHzyBy0BcY0T6d}QMx%+BC#$95TCyvUCWf+_o4$x_^lXSqvCM}p~oAsRa@I4>Uy0C zRmg0xl=3_yypsfPM~ zssSQthPlrgo>J+xI}_tID{x}e&tUqQdXGqkA*VGwpfQa_q6}NPrk_%w2HQO?IB=s4 Tqq=@U(A;-q=dXz2&==u(X>Jjc@p3~?Y733ovkqCmkk0UnTNIq*LEIUPB_LXi8c!)vZh<^Bo&*fI5P}6XIgUTP&-m?+-R*Vn@6*29y$=hb zdA#Eo%kw(saFigLfte>E7#pn9Zn5RF=#Z#r1VI8f-$R2Vp1u$yEM#BIkst(#43Can zUw7I_B3ns}RI*>5D&USx^1XLFi#Af8R;?3xs#Tf1%g-Vfl>}%jV(6K*0OC-IT1*gf zlO_Sgqn0^G4SvHj0NN@xlW71F(C+>6F@f_d(40h6QZH*WaKT|oQeJ?{L*(fK%}GWj z&FV{5wql1$e2d(zY{hu%)#tB&&mVQmjc_;Y^btN4*A;v73<#}^; zTJ8oQ(^Me~{M+RKwi7_s&y^LXm+Ld<$^m4ThUey*(l0uW0qF3LPd0nez2oFA05L<^ zc{w>HdM^fa!OWR~9bSR6C8vNS_SYDrM~k4f+8IDxF`9I(r9OgM2q3=PF(&AI zK-=1cbKcVBn2BZp3HsRVoHL^SOA`PR_0QKgkyM0nZ>^5w7kEHnX#=^4^jmVJA?=mK=XT5mllkgV_ac0mLc$kHz1n;ba^D@io(g;2T9w;eP;VJEMGeFyqo!C!sH!U=ZewlDWbRXe-1! zW$zDC=I>0M0EQ(Q4xR0h%(YE-14z~xl9$%JgibpFw5vzYt{og$N`mIJmmQj}@a%KD zod6^$#m1F#$FAb*(65ZDWJzUC%-rq(AW72vM~i>`cE zUTG?Twl(_QY2S>!__i59Lc1%<_~LX{uMj}u6%VyJ>z*j)I)Efep;xDF{JDtF0396jZCFyCav=jiM(rf)qq(EYM|}WfHoQdmj!3{8E&|A!l4$N? zjOWIC0c3Yj)c0+EPxZJ5pu=Y6nS+W8W~&N77Yr@&l~+b`%dK=UOXU%A!C`S|x4$aY z=z)M8OwnC0(^+X!w%b)6!giI1D5d*rNAKIe2d$R0wNK!LEFH{J3awNgJH=)}+Y2Nu m9n5+`qin7gJRwU5v!0}(C2HBnRt}!NT{8S>Mc;TVtNkB9LYCwJ literal 0 HcmV?d00001 diff --git a/announce/linked.dat b/announce/linked.dat new file mode 100644 index 0000000000000000000000000000000000000000..21047f80812d65c44e30956582a5bf5e82ebfdb5 GIT binary patch literal 2707 zcmaLZdrVVj6aer`-@T>p3LR{6$7C+DEulc=A8e+A3`xMYt3VeR=^|i);4284Kn83m zZ7ph8>Ec~boCTUtd?02u(umqhAiyGXFg9c&W4ziSxV5sb6t+Li;oknyq`#c+`|fv6 zbIxv+GCe&F@jnU3ZiPz8L);7u2_lUND*eAHlN3q}!#-O7-m`1p>IcL2CdKbh-icxI z45i%X3lBtM|3@OYPHKD>CL5pq61@Ds@CohD?SOBErI%_3ugJo&!r*%P*bfQQGu9X~QI=!Q5%cgw}svjQ#43q%^XgIVDd?I|GIQ%F<^qP0EYO7It0 zpb}gbsKAK!Bwf{u3pAx{&oq!S@~_{hqTPRaQ#1fl>NY*Qe);9{#j5~Py+Svurf_I; z1c0ovq}J&?G5S!-s~C66#&uGcwYOR zxGeUU0J1vNWjS}BzVW>tz~HVoOWFDr&8-~(vZvKY$_lf4dwv6u<9w^LjoB>jCIQGb zCO&tjcs{TE89<(0{UZ9rXJu>`fc%!sxvbn|TnuGJFqWG?wD(SwDFKKSHn<7}TITS| zIDn#G=5B`|y$)PU;NUKxjb2%1?vkE}EPtg6`Qq?>67(m9E zL21UTk2jYv0c4&#_Qk+ywY_>2K(t+j-#f&$yP=e^T1;sD4q?O==wB8*YeN+Jgo2#g zz_M(IEXAn)^a;ZQAjip&mY?WH6(<4YHl#&`-kv;lAPPX2X%IkmH+ro@e16l>zX9Z!iy{xl7u&v;0?3^^ zWXpS*s?u2j(X>Jjc@p3~?Y733ovkqCmkk0UnTNIq*LEIUPB_LXi8c!)vZh<^Bo&*fI5P}6XIgUTP&-m?+-R*Vn@6*29y$=hb zdA#Eo%kw(saFigLfte>E7#pn9Zn5RF=#Z#r1VI8f-$R2Vp1u$yEM#BIkst(#43Can zUw7I_B3ns}RI*>5D&USx^1XLFi#Af8R;?3xs#Tf1%g-Vfl>}%jV(6K*0OC-IT1*gf zlO_Sgqn0^G4SvHj0NN@xlW71F(C+>6F@f_d(40h6QZH*WaKT|oQeJ?{L*(fK%}GWj z&FV{5wql1$e2d(zY{hu%)#tB&&mVQmjc_;Y^btN4*A;v73<#}^; zTJ8oQ(^Me~{M+RKwi7_s&y^LXm+Ld<$^m4ThUey*(l0uW0qF3LPd0nez2oFA05L<^ zc{w>HdM^fa!OWR~9bSR6C8vNS_SYDrM~k4f+8IDxF`9I(r9OgM2q3=PF(&AI zK-=1cbKcVBn2BZp3HsRVoHL^SOA`PR_0QKgkyM0nZ>^5w7kEHnX#=^4^jmVJA?=mK=XT5mllkgV_ac0mLc$kHz1n;ba^D@io(g;2T9w;eP;VJEMGeFyqo!C!sH!U=ZewlDWbRXe-1! zW$zDC=I>0M0EQ(Q4xR0h%(YE-14z~xl9$%JgibpFw5vzYt{og$N`mIJmmQj}@a%KD zod6^$#m1F#$FAb*(65ZDWJzUC%-rq(AW72vM~i>`cE zUTG?Twl(_QY2S>!__i59Lc1%<_~LX{uMj}u6%VyJ>z*j)I)Efep;xDF{JDtF0396jZCFyCav=jiM(rf)qq(EYM|}WfHoQdmj!3{8E&|A!l4$N? zjOWIC0c3Yj)c0+EPxZJ5pu=Y6nS+W8W~&N77Yr@&l~+b`%dK=UOXU%A!C`S|x4$aY z=z)M8OwnC0(^+X!w%b)6!giI1D5d*rNAKIe2d$R0wNK!LEFH{J3awNgJH=)}+Y2Nu m9n5+`qin7gJRwU5v!0}(C2HBnRt}!NT{8S>Mc;TVtNkB9LYCwJ literal 0 HcmV?d00001 diff --git a/build_wxBase b/build_wxBase new file mode 100755 index 0000000..660e32a --- /dev/null +++ b/build_wxBase @@ -0,0 +1,33 @@ +#!/bin/bash + +BAKEFILES=$(PWD)/bakefiles.zip + +if [ -e $BAKEFILES ] +then + # move to the home directory and get the source comressed archive for wxBase + cd ~ + wget http://sourceforge.net/projects/wxwindows/files/2.8.12/wxBase-2.8.12.tar.gz + tar -xzvf wxBase-2.8.12.tar.gz + + # move into the directory, configure and build it + cd wxBase-2.8.12 + ./configure --disable-gui --without-expat + make + + # add the bakefiles and install wxBase + cd build + unzip $BAKEFILES + cd .. + sudo make install + + # add /usr/local/lib to /etc/ld.so.conf, if needed and relink the libraries + grep -q /usr/local/lib /etc/ld.so.conf + RETVAL=$? + if [ $RETVAL -eq 1 ] + then + sudo echo /usr/local/lib >> /etc/ld.so.conf + fi + sudo ldconfig +else + echo "$BAKEFILES not found, aborting!" +fi diff --git a/dstar_dv.cpp b/dstar_dv.cpp new file mode 100644 index 0000000..caa3abb --- /dev/null +++ b/dstar_dv.cpp @@ -0,0 +1,253 @@ +/* + +ircDDB-mheard + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 "dstar_dv.h" +#include "golay23.h" + +int bit_pos1[] = +{ + 0, 0, 1, 1, 2, 2, + 0, 0, 1, 1, 2, 2, + 0, 0, 1, 1, 2, 2, + 0, 0, 1, 1, 2, 2, + 0, 0, 1, 1, 2, 2, + 0, 0, 1, 1, 2, 2, + + 0, 0, 1, 1, 2, 2, + 0, 0, 1, 1, 2, 2, + 0, 0, 1, 1, 2, 2, + 0, 0, 1, 1, 2, 2, + 0, 0, 1, 1, 2, 2, + 0, 0, 1, 1, 2, 2 +}; + +int bit_pos2[] = +{ + 23, 11, + 23, 11, + 23, 11, + + 22, 10, + 22, 10, + 22, 10, + + 21, 9, + 21, 9, + 21, 9, + + 20, 8, + 20, 8, + 20, 8, + + 19, 7, + 19, 7, + 19, 7, + + 18, 6, + 18, 6, + 18, 6, + + 17, 5, + 17, 5, + 17, 5, + + 16, 4, + 16, 4, + 16, 4, + + 15, 3, + 15, 3, + 15, 3, + + 14, 2, + 14, 2, + 14, 2, + + 13, 1, + 13, 1, + 13, 1, + + 12, 0, + 12, 0, + 12, 0 + +}; + + + +static long decoding_table[2048]; +static int prng[4096]; + +static void init_prng(void) +{ + int i; + + for (i=0; i < 4096; i++) + { + int mask = 0x800000; + int j; + int pr; + + prng[i] = 0; + pr = i << 4; + + for (j=0; j < 24; j++) + { + pr = ((173 * pr) + 13849) & 0xFFFF; + + if ((pr & 0x8000) != 0) + { + prng[i] |= mask; + } + + mask = mask >> 1; + } + } + +} + +void dstar_dv_init(void) +{ + long temp; + int i; + int a[4]; + + decoding_table[0] = 0; + decoding_table[1] = 1; + temp = 1; + for (i=2; i<= 23; i++) + { + temp = temp << 1; + decoding_table[get_syndrome(temp)] = temp; + } + + a[1] = 1; + a[2] = 2; + temp = arr2int(a,2); + decoding_table[get_syndrome(temp)] = temp; + for (i=1; i<253; i++) + { + nextcomb(23,2,a); + temp = arr2int(a,2); + decoding_table[get_syndrome(temp)] = temp; + } + + a[1] = 1; + a[2] = 2; + a[3] = 3; + temp = arr2int(a,3); + decoding_table[get_syndrome(temp)] = temp; + for (i=1; i<1771; i++) + { + nextcomb(23,3,a); + temp = arr2int(a,3); + decoding_table[get_syndrome(temp)] = temp; + } + + init_prng(); +} + + +static int golay2412 (int data, int *decoded) +{ + int block = (data >> 1) & 0x07fffff; + int corrected_block = block ^ decoding_table[get_syndrome(block)]; + + int errs = 0; + int parity_corr = 0; + int i; + + for (i = 0; i < 23; i++) + { + int mask = 1 << i; + + int bit_rcvd = block & mask; + int bit_corr = corrected_block & mask; + + if (bit_corr != 0) + { + parity_corr ++; + } + + if (bit_rcvd != bit_corr) + { + errs ++; + } + } + + if ((parity_corr & 0x01) != (data & 0x01)) + { + errs ++; + } + + *decoded = corrected_block >> 11; + + return errs; +} + + +int dstar_dv_decode_first_block (const unsigned char * d, int * errs) +{ + int bits[3]; + int i; + int data; + + for (i=0; i < 3; i++) + { + bits[i] = 0; + } + + for (i=0; i < 72; i++) + { + bits[ bit_pos1[i] ] |= (d[ i >> 3 ] & (0x80 >> (i & 0x07))) ? (1 << bit_pos2[i]) : 0; + } + + *errs = golay2412( bits[0], & data ); + + return data; + +} + +int dstar_dv_decode (const unsigned char * d, int data[3]) +{ + int bits[3]; + int i; + int errs; + + for (i=0; i < 3; i++) + { + bits[i] = 0; + } + + for (i=0; i < 72; i++) + { + bits[ bit_pos1[i] ] |= (d[ i >> 3 ] & (0x80 >> (i & 0x07))) ? (1 << bit_pos2[i]) : 0; + } + + errs = golay2412( bits[0], data ); + + errs += golay2412( bits[1] ^ prng[ data[0] & 0x0fff ], data + 1 ); + + data[2] = bits[2]; + + return errs; +} + diff --git a/dstar_dv.h b/dstar_dv.h new file mode 100644 index 0000000..59872e3 --- /dev/null +++ b/dstar_dv.h @@ -0,0 +1,48 @@ +/* + +ircDDB-mheard + +Copyright (C) 2010 Michael Dirska, DL1BFF (dl1bff@mdx.de) + +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 . + +*/ + +/* Call this function first before using any of the other functions */ +void dstar_dv_init(void); + +/* +This function decodes the first and most important Golay block +of a DSTAR Digital Voice frame. The function is provided with a +pointer to the 9-byte voice data. Result is the decoded +first block. The number of decoding errors is reported in the +variable errs and ranges from 0 to 4. Only 24 bits are +checked, the BER therefore is: BER = errs / 24 + */ +int dstar_dv_decode_first_block (const unsigned char * d, int * errs); + +/* +This function decodes the both Golay blocks +of a DSTAR Digital Voice frame. The function is provided with a +pointer to the 9-byte voice data. Function result is +the number of decoding errors (0 to 8). Only 48 bits are +checked, the BER therefore is: BER = errs / 48 + +The return value data[0] contains the first decoded golay block +(12 bits). The return value data[1] contains the second decoded +golay block (12 bits). The return value data[2] contains the +unprotected rest of the frame (24 bits). +*/ +int dstar_dv_decode (const unsigned char * d, int data[3]); + diff --git a/dvap_rptr.cfg b/dvap_rptr.cfg new file mode 100644 index 0000000..eca074f --- /dev/null +++ b/dvap_rptr.cfg @@ -0,0 +1,81 @@ + +# This is the configuration file for the DVAP device(2m dongle with antenna) + +# This is Dstar repeater callsign +# Use a correct repeater call. +# If you set RPTR different from the OWNER +# then only the RF user identified by RPTR callsign can talk on this repeater. +# This mode is called "restriction" mode and in some countries(UK,...) +# it is the only valid mode that you can use, while waiting for a repeater license. +# In a case like this, set RPTR to be your personal callsign +RPTR=AC2IE + +# This is the Gateway callsign +# This must be equal to the OWNER value in the gateway software(g2_ircddb.cfg) +OWNER=AC2IE + +# To protect the repeater owners from bad STN programs out there +# and to also protect the repeater owners from RF users that abuse the STN stuff +# Reject values in YRCALL that start with STN +# If you want to allow the local RF users to subscribe to remote STN groups, +# then set it to XXX or something that is invalid like 123 +INVALID_YRCALL_KEY=STN + +# This is the module of the Dstar repeater +# Please read the comments about RPTR_PORT below +RPTR_MOD=C + +# This identifies the IP for the repeater(controler) +# The DVAP device will run here +# For repeater module A: RPTR_PORT=19998 +# For repeater module B: RPTR_PORT=19999 +# For repeater module C: RPTR_PORT=20000 +RPTR_VIRTUAL_IP=0.0.0.0 +RPTR_PORT=20000 + +# This is the INTERNAL IP and PORT of your local G2 gateway +G2_INTERNAL_IP=127.0.0.1 +G2_PORT=19000 + +# This is the serial number of your DVAP device +DVP_SERIAL=AP052344 + +# This is your 2m frequency for the DVAP device, Between 144000000 and 148000000 +DVP_FREQ=146550000; + +# This is the power level for the DVAP device, Between -12 and 10 +DVP_PWR=10; + +# This is the squelch setting for the DVAP device, between -128 and -45 +DVP_SQL=-80; + +# This is the offset frequency for the DVAP device, between -2000 and 2000 +DVP_OFF=0; + +# How long before we say that the local gateway has stopped sending packets to our repeater. +# This value is in seconds. +# Lets say that you set REMOTE_TIMEOUT=2 seconds and WAIT_FOR_PACKETS=25 milliseconds +# The program does a small calculation: ( (REMOTE_TIMEOUT * 1000) / WAIT_FOR_PACKETS)) +# So that is (2000 / 25) = 80 = number_of_times_to_loop +# This calculated value of 80 is the number of times we will loop waiting for +# packets from the remote system. +# A value of 2 is sufficient. Minimum is 1 seconds. +REMOTE_TIMEOUT=2 + +# This is used in a loop to wait for packets from your local gateway +# This is in milliseconds +WAIT_FOR_PACKETS=25 + +# This is in seconds, +# It allows the dvap to re-initialize before we send the repeater ack back to it +DELAY_BEFORE=2 + +# This is in milliseconds +# Dstar packets should reach the dvap at a rate of about 17-23 milliseconds +# We use that when we send a repeater ack back to the DVAP +DELAY_BETWEEN=19 + +# Do you want a repeater ack sent to dvap, after the local RF radio user releases PTT ? +# Valid values are Y or N +RPTR_ACK=Y + diff --git a/dvap_rptr.cpp b/dvap_rptr.cpp new file mode 100644 index 0000000..b9582eb --- /dev/null +++ b/dvap_rptr.cpp @@ -0,0 +1,2395 @@ +/* + * Copyright (C) 2010, 2011 by Scott Lawson KI4LKF + * + * 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. + */ + +/*** + KI4LKF +***/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VERSION "2.32" +#define CALL_SIZE 8 +#define RPTR_SIZE 8 +#define IP_SIZE 15 + +/* data from dvap */ +struct dvap_hdr +{ + unsigned char hdr0; // 0x2f + unsigned char hdr1; // 0xa0 + unsigned char streamid[2]; + unsigned char framepos; + unsigned char seq; + unsigned char flag1; + unsigned char flag2; + unsigned char flag3; + unsigned char rpt1[8]; + unsigned char rpt2[8]; + unsigned char urcall[8]; + unsigned char mycall[8]; + unsigned char sfx[4]; + unsigned char pfcs[2]; +}; +struct dvap_voice +{ + unsigned char hdr0; // 0x12 + unsigned char hdr1; // 0xc0 + unsigned char streamid[2]; + unsigned char framepos; + unsigned char seq; + unsigned char voice[9]; + unsigned char txt[3]; +}; + +/* data from the local gateway */ +struct hdr +{ + unsigned char flags[3]; + unsigned char rpt2[8]; + unsigned char rpt1[8]; + unsigned char urcall[8]; + unsigned char mycall[8]; + unsigned char sfx[4]; + unsigned char pfcs[2]; +}; + +struct icm +{ + unsigned char icm_id; + unsigned char dst_rptr_id; + unsigned char snd_rptr_id; + unsigned char snd_term_id; + unsigned char streamid[2]; + unsigned char ctrl; +}; + +struct voice_and_text +{ + unsigned char voice[9]; + unsigned char text[3]; +}; + +struct audio +{ + unsigned char buff[sizeof(struct hdr)]; +}; + +struct pkt +{ + unsigned char pkt_id[4]; + unsigned char nothing1[2]; + unsigned char flags[2]; + unsigned char nothing2[2]; + struct icm myicm; + union + { + struct audio rf_audio; + struct hdr rf_hdr; + struct voice_and_text vat; + }; +}; + +struct dvap_ack_arg_type +{ + char mycall[8]; + float ber; +}; +dvap_ack_arg_type dvap_ack_arg; + +/* Default configuration data */ +static char RPTR[RPTR_SIZE + 1] = {"ABCDEF"}; +static char OWNER[RPTR_SIZE + 1] = {"ABCDEF"}; +static char RPTR_MOD = 'B'; +static char RPTR_VIRTUAL_IP[IP_SIZE + 1] = {"172.16.0.1"}; +static int RPTR_PORT = 20000; +static char G2_INTERNAL_IP[IP_SIZE + 1] = {"172.16.0.20"}; +static int G2_PORT = 20000; +static char DVP_SERIAL[64]; /* APxxxxxx */ +static u_int32_t DVP_FREQ = 145500000; /* between 144000000 and 148000000 */ +static int16_t DVP_PWR = 10; /* between -12 and 10 */ +static char DVP_SQL = -100; /* between -128 and -45 */ +static int16_t DVP_OFF = 0; /* between -2000 and 2000 */ +static int WAIT_FOR_PACKETS=25; /* wait 25 ms in reading from local G2 */ +static int REMOTE_TIMEOUT=1; /* 1 second */ +static int DELAY_BETWEEN = 20; +static int DELAY_BEFORE = 1; +static bool RPTR_ACK = true; +static char INVALID_YRCALL_KEY[CALL_SIZE + 1] = { "" }; + +/* helper data */ +static int32_t val32bits = 1; +#define isit_bigendian() ( (*(char*)&val32bits) == 0 ) +#define do_swap16(val) \ + ((int16_t) ( \ + (((u_int16_t) (val) & (u_int16_t) 0x00ffU) << 8) | \ + (((u_int16_t) (val) & (u_int16_t) 0xff00U) >> 8))) +#define do_swapu16(val) \ + ((u_int16_t) ( \ + (((u_int16_t) (val) & (u_int16_t) 0x00ffU) << 8) | \ + (((u_int16_t) (val) & (u_int16_t) 0xff00U) >> 8))) +#define do_swapu32(val) \ + ((u_int32_t) ( \ + (((u_int32_t) (val) & (u_int32_t) 0x000000ffU) << 24) | \ + (((u_int32_t) (val) & (u_int32_t) 0x0000ff00U) << 8) | \ + (((u_int32_t) (val) & (u_int32_t) 0x00ff0000U) >> 8) | \ + (((u_int32_t) (val) & (u_int32_t) 0xff000000U) >> 24))) +static unsigned char SND_TERM_ID = 0x00; +static char RPTR_and_G[9]; +static char RPTR_and_MOD[9]; +static int inactiveMax = 25; +static const int TRACE_BFSZ = 256; +static int insock = -1; +static struct sockaddr_in outaddr; +static int serfd = -1; +static bool busy20000 = false; +static bool keep_running = true; +static unsigned char DVP_RQST_NAME[] = {0x04, 0x20, 0x01, 0x00}; +static unsigned char DVP_REPL_NAME[] = {0x10, 0x00, 0x01, 0x00, 'D', 'V', 'A', 'P', ' ', 'D', 'o', 'n', 'g', 'l', 'e', 0x00}; +static unsigned char DVP_RQST_SER[] = {0x04, 0x20, 0x02, 0x00}; +static unsigned char DVP_REPL_SER[] = {0x0C, 0x00, 0x02, 0x00}; +static unsigned char DVP_RQST_FW[] = {0x05, 0x20, 0x04, 0x00, 0x01}; +static unsigned char DVP_REPL_FW[] = {0x07, 0x00, 0x04, 0x00, 0x01, 0x00, 0x00}; +static unsigned char DVP_RQST_MODU[] = {0x05, 0x00, 0x28, 0x00, 0x01}; +static unsigned char DVP_REPL_MODU[] = {0x05, 0x00, 0x28, 0x00, 0x01}; +static unsigned char DVP_RQST_MODE[] = {0x05, 0x00, 0x2A, 0x00, 0x00}; +static unsigned char DVP_REPL_MODE[] = {0x05, 0x00, 0x2A, 0x00, 0x00}; +static unsigned char DVP_RQST_SQL[] = {0x05, 0x00, 0x80, 0x00, 0x00}; +static unsigned char DVP_REPL_SQL[] = {0x05, 0x00, 0x80, 0x00, 0x00}; +static unsigned char DVP_RQST_PWR[] = {0x06, 0x00, 0x38, 0x01, 0x00, 0x00}; +static unsigned char DVP_REPL_PWR[] = {0x06, 0x00, 0x38, 0x01, 0x00, 0x00}; +static unsigned char DVP_RQST_OFF[] = {0x06, 0x00, 0x00, 0x04, 0x00, 0x00}; +static unsigned char DVP_REPL_OFF[] = {0x06, 0x00, 0x00, 0x04, 0x00, 0x00}; +static unsigned char DVP_RQST_FREQ[] = {0x08, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00}; +static unsigned char DVP_REPL_FREQ[] = {0x08, 0x00, 0x20, 0x02, 0x00, 0x00, 0x00, 0x00}; +static unsigned char DVP_RQST_START[] = {0x05, 0x00, 0x18, 0x00, 0x01}; +static unsigned char DVP_REPL_START[] = {0x05, 0x00, 0x18, 0x00, 0x01}; +static unsigned char DVP_RQST_STOP[] = {0x05, 0x00, 0x18, 0x00, 0x00}; +static unsigned char DVP_REPL_STOP[] = {0x05, 0x00, 0x18, 0x00, 0x00}; +static unsigned char DVP_HDR[] = +{ + 0x2F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static unsigned char DVP_REPL_HDR[] = +{ + 0x2F, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static unsigned char DVP_REPL_PTT[] = {0x05, 0x20, 0x18, 0x01, 0x00}; +static unsigned char DVP_DAT[] = +{ + 0x12, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; +static unsigned char DVP_STS[] = {0x07, 0x20, 0x90, 0x00, 0x00, 0x00, 0x00}; +static unsigned char DVP_ACK[] = {0x03, 0x60, 0x00}; +static unsigned int MAX_REPL_CNT = 20; +enum REPLY_TYPE +{ + RT_TIMEOUT, + RT_ERR, + RT_UNKNOWN, + RT_NAME, + RT_SER, + RT_FW, + RT_START, + RT_STOP, + RT_MODU, + RT_MODE, + RT_SQL, + RT_PWR, + RT_OFF, + RT_FREQ, + RT_STS, + RT_PTT, + RT_ACK, + RT_HDR, + RT_HDR_ACK, + RT_DAT +}; + +static unsigned int space = 0; +static unsigned int aseed = 0; + +static unsigned short crc_tabccitt[256] = +{ + 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 +}; + +/* helper routines */ +static void traceit(const char *fmt,...); +static int read_config(const char *cfgFile); +static void sig_catch(int signum); +static int open_sock(); +static bool open_ser(char *dvp); +static bool open_dvp(); +static int read_from_dvp(unsigned char* buf, unsigned int len); +static int write_to_dvp(unsigned char* buf, unsigned int len); +static bool get_name(); +static bool get_fw(); +static bool get_ser(char *dvp); +static bool set_modu(); +static bool set_mode(); +static bool set_sql(); +static bool set_pwr(); +static bool set_off(); +static bool set_freq(); +static bool start_dvap(); +static void readFrom20000(); +static REPLY_TYPE get_reply(unsigned char *buf, unsigned int *len); +static void syncit(); +static void calcPFCS(unsigned char *packet, unsigned char *pfcs); +static void *readFromRF(void *arg); +static void *rptr_ack(void *arg); + +/*** BER stuff ***/ +static int ber_data[3]; +static int ber_errs; +static int num_dv_frames; +static int num_bit_errors; +extern void dstar_dv_init(); +extern int dstar_dv_decode(const unsigned char *d, int data[3]); + +static void calcPFCS(unsigned char *packet, unsigned char *pfcs) +{ + unsigned short crc_dstar_ffff = 0xffff; + unsigned short tmp, short_c; + short int i; + + for (i = 0; i < 39 ; i++) + { + short_c = 0x00ff & (unsigned short)packet[i]; + tmp = (crc_dstar_ffff & 0x00ff) ^ short_c; + crc_dstar_ffff = (crc_dstar_ffff >> 8) ^ crc_tabccitt[tmp]; + } + crc_dstar_ffff = ~crc_dstar_ffff; + tmp = crc_dstar_ffff; + + pfcs[0] = (unsigned char)(crc_dstar_ffff & 0xff); + pfcs[1] = (unsigned char)((tmp >> 8) & 0xff); + + return; +} + +static void sig_catch(int signum) +{ + if ((signum == SIGTERM) || + (signum == SIGINT) || + (signum == SIGHUP)) + keep_running = false; + exit(0); +} + +/* log the event */ +static void traceit(const char *fmt,...) +{ + time_t ltime; + struct tm mytm; + char trace_buf[TRACE_BFSZ]; + + time(<ime); + localtime_r(<ime,&mytm); + + snprintf(trace_buf,TRACE_BFSZ - 1,"%02d%02d%02d at %02d:%02d:%02d:", + mytm.tm_mon+1,mytm.tm_mday,mytm.tm_year % 100, + mytm.tm_hour,mytm.tm_min,mytm.tm_sec); + + va_list args; + va_start(args,fmt); + vsnprintf(trace_buf + strlen(trace_buf), TRACE_BFSZ - strlen(trace_buf) - 1, fmt, args); + va_end(args); + + fprintf(stdout, "%s", trace_buf); + return; +} + +/* process configuration file */ +static int read_config(const char *cfgFile) +{ + short int valid_params = 18; + short int params = 0; + + FILE *cnf = NULL; + char inbuf[1024]; + char *p = NULL; + char *ptr; + unsigned short i; + + cnf = fopen(cfgFile, "r"); + if (!cnf) + { + traceit("Failed to open file %s\n", cfgFile); + return 1; + } + + traceit("Reading file %s\n", cfgFile); + while (fgets(inbuf, 1020, cnf) != NULL) + { + if (strchr(inbuf, '#')) + continue; + + p = strchr(inbuf, '\r'); + if (p) + *p = '\0'; + p = strchr(inbuf, '\n'); + if (p) + *p = '\0'; + + p = strchr(inbuf, '='); + if (!p) + continue; + *p = '\0'; + + if (strcmp(inbuf,"RPTR") == 0) + { + memset(RPTR,' ', sizeof(RPTR)); + RPTR[RPTR_SIZE] = '\0'; + + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if ((strlen(p + 1) < 1) || (strlen(p + 1) > (RPTR_SIZE - 2))) + traceit("RPTR value [%s] invalid\n", p + 1); + else + { + memcpy(RPTR, p + 1, strlen(p + 1)); + traceit("RPTR=[%s]\n",RPTR); + params ++; + } + } + else + if (strcmp(inbuf,"OWNER") == 0) + { + memset(OWNER,' ', sizeof(OWNER)); + OWNER[RPTR_SIZE] = '\0'; + + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if ((strlen(p + 1) < 1) || (strlen(p + 1) > (RPTR_SIZE - 2))) + traceit("OWNER value [%s] invalid\n", p + 1); + else + { + memcpy(OWNER, p + 1, strlen(p + 1)); + traceit("OWNER=[%s]\n",OWNER); + params ++; + } + } + else + if (strcmp(inbuf,"INVALID_YRCALL_KEY") == 0) + { + memset(INVALID_YRCALL_KEY, 0, sizeof(INVALID_YRCALL_KEY)); + + if ( (strlen(p + 1) < 1) || (strlen(p + 1) > CALL_SIZE) ) + traceit("INVALID_YRCALL_KEY value [%s] invalid\n", p + 1); + else + { + memcpy(INVALID_YRCALL_KEY, p + 1, strlen(p + 1)); + + for (i = 0; i < strlen(INVALID_YRCALL_KEY); i++) + INVALID_YRCALL_KEY[i] = toupper(INVALID_YRCALL_KEY[i]); + + traceit("INVALID_YRCALL_KEY=[%s]\n",INVALID_YRCALL_KEY); + params ++; + } + } + else + if (strcmp(inbuf,"RPTR_MOD") == 0) + { + RPTR_MOD = *(p + 1); + traceit("RPTR_MOD=[%c]\n", *(p + 1)); + params ++; + } + else + if (strcmp(inbuf,"RPTR_VIRTUAL_IP") == 0) + { + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if (strlen(p + 1) < 1) + traceit("RPTR_VIRTUAL_IP value [%s] invalid\n", p + 1); + else + { + strncpy(RPTR_VIRTUAL_IP, p + 1, IP_SIZE); + RPTR_VIRTUAL_IP[IP_SIZE] = '\0'; + traceit("RPTR_VIRTUAL_IP=[%s]\n", RPTR_VIRTUAL_IP); + params ++; + } + } + else + if (strcmp(inbuf,"RPTR_PORT") == 0) + { + RPTR_PORT = atoi(p + 1); + traceit("RPTR_PORT=[%d]\n",RPTR_PORT); + params ++; + } + else + if (strcmp(inbuf,"G2_INTERNAL_IP") == 0) + { + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if (strlen(p + 1) < 1) + traceit("G2_INTERNAL_IP value [%s] invalid\n", p + 1); + else + { + strncpy(G2_INTERNAL_IP, p + 1, IP_SIZE); + G2_INTERNAL_IP[IP_SIZE] = '\0'; + traceit("G2_INTERNAL_IP=[%s]\n", G2_INTERNAL_IP); + params ++; + } + } + else + if (strcmp(inbuf,"G2_PORT") == 0) + { + G2_PORT = atoi(p + 1); + traceit("G2_PORT=[%d]\n",G2_PORT); + params ++; + } + else + if (strcmp(inbuf,"DVP_SERIAL") == 0) + { + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if ((strlen(p + 1) > 63) || (strlen(p + 1) < 1)) + traceit("DVP_SERIAL value [%s] invalid\n", p + 1); + else + { + strcpy(DVP_SERIAL, p + 1); + traceit("DVP_SERIAL=[%s]\n", DVP_SERIAL); + params ++; + } + } + else + if (strcmp(inbuf,"DVP_FREQ") == 0) + { + DVP_FREQ = atoi(p + 1); + traceit("DVP_FREQ=[%u]\n", DVP_FREQ); + params ++; + } + else + if (strcmp(inbuf,"DVP_PWR") == 0) + { + DVP_PWR = atoi(p + 1); + traceit("DVP_PWR=[%d]\n", DVP_PWR); + params ++; + } + else + if (strcmp(inbuf,"DVP_SQL") == 0) + { + DVP_SQL = atoi(p + 1); + traceit("DVP_SQL=[%d]\n", DVP_SQL); + params ++; + } + else + if (strcmp(inbuf,"DVP_OFF") == 0) + { + DVP_OFF = atoi(p + 1); + traceit("DVP_OFF=[%u]\n", DVP_OFF); + params ++; + } + else + if (strcmp(inbuf,"WAIT_FOR_PACKETS") == 0) + { + WAIT_FOR_PACKETS = atoi(p + 1); + if (WAIT_FOR_PACKETS <= 5) + WAIT_FOR_PACKETS = 25; + traceit("WAIT_FOR_PACKETS=[%d]\n",WAIT_FOR_PACKETS); + params ++; + } + else + if (strcmp(inbuf,"REMOTE_TIMEOUT") == 0) + { + REMOTE_TIMEOUT = atoi(p + 1); + if (REMOTE_TIMEOUT < 1) + REMOTE_TIMEOUT = 1; + traceit("REMOTE_TIMEOUT=[%d]\n",REMOTE_TIMEOUT); + params ++; + } + else + if (strcmp(inbuf,"DELAY_BETWEEN") == 0) + { + DELAY_BETWEEN = atoi(p + 1); + if (DELAY_BETWEEN <= 0) + DELAY_BETWEEN = 20; + traceit("DELAY_BETWEEN=[%d]\n",DELAY_BETWEEN); + params ++; + } + else + if (strcmp(inbuf,"DELAY_BEFORE") == 0) + { + DELAY_BEFORE = atoi(p + 1); + if (DELAY_BEFORE <= 0) + DELAY_BEFORE = 1; + traceit("DELAY_BEFORE=[%d]\n",DELAY_BEFORE); + params ++; + } + else + if (strcmp(inbuf,"RPTR_ACK") == 0) + { + if (*(p + 1) == 'Y') + RPTR_ACK = true; + else + RPTR_ACK = false; + traceit("RPTR_ACK=[%c]\n", *(p + 1)); + params ++; + } + } + fclose(cnf); + + if (params != valid_params) + { + traceit("Configuration file %s invalid\n",cfgFile); + return 1; + } + + /********* HERE HERE check for valid values *************/ + /* check valid values */ + /********* HERE HERE check for valid values *************/ + + inactiveMax = (REMOTE_TIMEOUT * 1000) / WAIT_FOR_PACKETS; + traceit("Max loops = %d\n", inactiveMax); + + /* convert to Microseconds */ + WAIT_FOR_PACKETS = WAIT_FOR_PACKETS * 1000; + + return 0; +} + +static int open_sock() +{ + struct sockaddr_in inaddr; + int rc = -1; + + insock = socket(PF_INET, SOCK_DGRAM, 0); + if (insock == -1) + { + traceit("Failed to create insock, error=%d, message=%s\n",errno,strerror(errno)); + return -1; + } + + memset(&inaddr, 0, sizeof(inaddr)); + inaddr.sin_family = AF_INET; + inaddr.sin_port = htons(RPTR_PORT); + inaddr.sin_addr.s_addr = inet_addr(RPTR_VIRTUAL_IP); + rc = bind(insock, (struct sockaddr *)&inaddr, sizeof(inaddr)); + if (rc == -1) + { + traceit("Failed to bind server socket, error=%d, message=%s\n", errno,strerror(errno)); + close(insock); insock = -1; + return -1; + } + fcntl(insock,F_SETFL,O_NONBLOCK); + + memset(&outaddr, 0, sizeof(outaddr)); + outaddr.sin_family = AF_INET; + outaddr.sin_port = htons(G2_PORT); + outaddr.sin_addr.s_addr = inet_addr(G2_INTERNAL_IP); + + return 0; +} + +static bool open_ser(char *dvp) +{ + static termios t; + + serfd = open(dvp, O_RDWR | O_NOCTTY | O_NDELAY, 0); + if (serfd < 0) + { + traceit("Failed to open device [%s], error=%d, message=%s\n", dvp, errno, strerror(errno)); + return false; + } + + if (isatty(serfd) == 0) + { + traceit("Device %s is not a tty device\n", dvp); + close(serfd); serfd = -1; + return false; + } + + if (tcgetattr(serfd, &t) < 0) + { + traceit("tcgetattr failed for %s, error=%d\n", dvp, errno); + close(serfd); serfd = -1; + return false; + } + + t.c_lflag &= ~(ECHO | ECHOE | ICANON | IEXTEN | ISIG); + t.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON | IXOFF | IXANY); + t.c_cflag &= ~(CSIZE | CSTOPB | PARENB | CRTSCTS); + t.c_cflag |= CS8; + t.c_oflag &= ~(OPOST); + t.c_cc[VMIN] = 0; + t.c_cc[VTIME] = 10; + + cfsetospeed(&t, B230400); + cfsetispeed(&t, B230400); + + if (tcsetattr(serfd, TCSANOW, &t) < 0) + { + traceit("tcsetattr failed for %s, error=%d\n", dvp, errno); + close(serfd); serfd = -1; + return false; + } + + return true; +} + +static bool open_dvp() +{ + short int i; + bool ok = false; + char dvp_device[128]; + + do + { + for (i = 0; i < 32; i++) + { + sprintf(dvp_device, "/dev/ttyUSB%d", i); + + if (access(dvp_device, R_OK | W_OK) != 0) + continue; + + ok = open_ser(dvp_device); + if (!ok) + continue; + + if (flock(serfd, LOCK_EX | LOCK_NB) != 0) + { + close(serfd); serfd = -1; + ok = false; + traceit("Device %s is already locked/used by other dvap_rptr\n", dvp_device); + continue; + } + traceit("Device %s now locked for exclusive use\n", dvp_device); + + ok = get_ser(dvp_device); + if (!ok) + { + close(serfd); serfd = -1; + continue; + } + break; + } + if (!ok) + break; + + ok = get_name(); + if (!ok) + break; + + ok = get_fw(); + if (!ok) + break; + + + ok = set_modu(); + if (!ok) + break; + + ok = set_mode(); + if (!ok) + break; + + ok = set_sql(); + if (!ok) + break; + + ok = set_pwr(); + if (!ok) + break; + + ok = set_off(); + if (!ok) + break; + + ok = set_freq(); + if (!ok) + break; + + ok = start_dvap(); + if (!ok) + break; + + } while (false); + + if (!ok) + { + if (serfd != -1) + { + (void)write_to_dvp(DVP_RQST_STOP, 5); + close(serfd); + serfd = -1; + } + return false; + } + return true; +} + +static int read_from_dvp(unsigned char *buf, unsigned int len) +{ + unsigned int off = 0; + fd_set fds; + int n; + struct timeval tv; + ssize_t temp_len; + + if (len == 0) + return 0; + + while (off < len) + { + FD_ZERO(&fds); + FD_SET(serfd, &fds); + + if (off == 0) + { + tv.tv_sec = 0; + tv.tv_usec = 0; + n = select(serfd + 1, &fds, NULL, NULL, &tv); + if (n == 0) + return 0; // nothing to read from the dvap + } + else + n = select(serfd + 1, &fds, NULL, NULL, NULL); + + if (n < 0) + { + traceit("select error=%d on dvap\n", errno); + return -1; + } + + if (n > 0) + { + temp_len = read(serfd, buf + off, len - off); + if (temp_len > 0) + off += temp_len; + } + } + + return len; +} + +static int write_to_dvp(unsigned char *buf, unsigned int len) +{ + unsigned int ptr = 0; + ssize_t n; + + if (len == 0) + return 0; + + while (ptr < len) + { + n = write(serfd, buf + ptr, len - ptr); + if (n < 0) + { + traceit("Error %d writing to dvap\n", errno); + return -1; + } + + if (n > 0) + ptr += n; + } + + return len; +} + +static REPLY_TYPE get_reply(unsigned char *buf, unsigned int *len) +{ + unsigned int off = 2; + int rc = -1; + + rc = read_from_dvp(buf, 2); + if (rc == 0) + return RT_TIMEOUT; + if (rc != 2) + return RT_ERR; + + *len = buf[0] + (buf[1] & 0x1f) * 256; + if (*len > 50) + { + syncit(); + return RT_TIMEOUT; + } + + while (off < *len) + { + rc = read_from_dvp(buf + off, *len - off); + if (rc < 0) + return RT_TIMEOUT; + if (rc > 0) + off += rc; + } + + if (memcmp(buf, DVP_STS, 4) == 0) + return RT_STS; + else + if (memcmp(buf, DVP_DAT, 2) == 0) + return RT_DAT; + else + if (memcmp(buf, DVP_HDR, 2) == 0) + return RT_HDR; + else + if (memcmp(buf, DVP_REPL_HDR, 2) == 0) + return RT_HDR_ACK; + else + if (memcmp(buf, DVP_REPL_PTT, 4) == 0) + return RT_PTT; + else + if (memcmp(buf, DVP_REPL_START, 5) == 0) + return RT_START; + else + if (memcmp(buf, DVP_REPL_STOP, 5) == 0) + return RT_STOP; + else + if (memcmp(buf, DVP_REPL_OFF, 4) == 0) + return RT_OFF; + else + if (memcmp(buf, DVP_REPL_NAME, 4) == 0) + return RT_NAME; + else + if (memcmp(buf + 1, DVP_REPL_SER + 1, 3) == 0) + return RT_SER; + else + if (memcmp(buf, DVP_REPL_FW, 5) == 0) + return RT_FW; + else + if (memcmp(buf, DVP_REPL_FREQ, 4) == 0) + return RT_FREQ; + else + if (memcmp(buf, DVP_REPL_MODU, 5) == 0) + return RT_MODU; + else + if (memcmp(buf, DVP_REPL_MODE, 5) == 0) + return RT_MODE; + else + if (memcmp(buf, DVP_REPL_PWR, 4) == 0) + return RT_PWR; + else + if (memcmp(buf, DVP_REPL_SQL, 4) == 0) + return RT_SQL; + else + { + syncit(); + return RT_TIMEOUT; + } + + /* It should never get here */ + return RT_TIMEOUT; +} + +static void syncit() +{ + unsigned char data[7]; + unsigned char c; + int n; + struct timeval tv; + fd_set fds; + short cnt = 0; + + traceit("Starting syncing dvap\n"); + memset(data, 0x00, 7); + + while (memcmp(data, DVP_STS, 4) != 0) + { + FD_ZERO(&fds); + FD_SET(serfd, &fds); + tv.tv_sec = 0; + tv.tv_usec = 1000; + n = select(serfd + 1, &fds, NULL, NULL, &tv); + if (n <= 0) + { + cnt ++; + if (cnt > 100) + { + traceit("dvap is not responding,...stopping\n"); + keep_running = false; + return; + } + } + else + { + n = read_from_dvp(&c, 1); + if (n > 0) + { + data[0] = data[1]; + data[1] = data[2]; + data[2] = data[3]; + data[3] = data[4]; + data[4] = data[5]; + data[5] = data[6]; + data[6] = c; + + cnt = 0; + } + } + } + traceit("Stopping syncing dvap\n"); + return; +} + +static bool get_name() +{ + unsigned cnt = 0; + unsigned int len = 0; + REPLY_TYPE reply; + bool testit = false; + int rc = -1; + unsigned char dvp_buf[200]; + + rc = write_to_dvp(DVP_RQST_NAME, 4); + if (rc != 4) + { + traceit("Failed to send request to get dvap name\n"); + return false; + } + + do { + usleep(5000); + + reply = get_reply(dvp_buf, &len); + cnt ++; + if (cnt >= MAX_REPL_CNT) + { + traceit("Reached max number of requests to receive dvap name\n"); + return false; + } + } while (reply != RT_NAME); + + testit = (memcmp(dvp_buf, DVP_REPL_NAME, len) == 0); + + if (!testit) + { + traceit("Failed to receive dvap name\n"); + return false; + } + + traceit("Device name: %.*s\n", 11, dvp_buf + 4); + return true; +} + +static bool get_fw() +{ + unsigned cnt = 0; + unsigned int len = 0; + REPLY_TYPE reply; + unsigned int ver; + int rc = -1; + unsigned char dvp_buf[200]; + + rc = write_to_dvp(DVP_RQST_FW, 5); + if (rc != 5) + { + traceit("Failed to send request to get dvap fw\n"); + return false; + } + + do { + usleep(5000); + + reply = get_reply(dvp_buf, &len); + cnt ++; + if (cnt >= MAX_REPL_CNT) + { + traceit("Reached max number of requests to receive dvap fw\n"); + return false; + } + } while (reply != RT_FW); + + ver = dvp_buf[6] * 256 + dvp_buf[5]; + traceit("dvap fw ver: %u.%u\n", ver / 100, ver % 100); + + return true; +} + +static bool get_ser(char *dvp) +{ + unsigned cnt = 0; + unsigned int len = 0; + REPLY_TYPE reply; + int rc = -1; + unsigned char dvp_buf[200]; + + rc = write_to_dvp(DVP_RQST_SER, 4); + if (rc != 4) + { + traceit("Failed to send request to get dvap serial#\n"); + return false; + } + + do { + usleep(5000); + + reply = get_reply(dvp_buf, &len); + cnt ++; + if (cnt >= MAX_REPL_CNT) + { + traceit("Reached max number of requests to receive dvap serial#\n"); + return false; + } + } while (reply != RT_SER); + + if (strcmp((char *)(dvp_buf + 4), DVP_SERIAL) == 0) + { + traceit("Using %s: %s, because serial number matches your dvap_rptr.cfg\n", + dvp, DVP_SERIAL); + return true; + } + else + { + traceit("Device %s has serial %s, but does not match your config value %s\n", dvp, dvp_buf + 4, DVP_SERIAL); + return false; + } +} + +static bool set_modu() +{ + unsigned cnt = 0; + unsigned int len = 0; + REPLY_TYPE reply; + int rc = -1; + unsigned char dvp_buf[200]; + + rc = write_to_dvp(DVP_RQST_MODU, 5); + if (rc != 5) + { + traceit("Failed to send request to set dvap modulation\n"); + return false; + } + + do { + usleep(5000); + + reply = get_reply(dvp_buf, &len); + + cnt ++; + if (cnt >= MAX_REPL_CNT) + { + traceit("Reached max number of requests to set dvap modulation\n"); + return false; + } + } while (reply != RT_MODU); + + return true; +} + +static bool set_mode() +{ + unsigned cnt = 0; + unsigned int len = 0; + REPLY_TYPE reply; + int rc = -1; + unsigned char dvp_buf[200]; + + rc = write_to_dvp(DVP_RQST_MODE, 5); + if (rc != 5) + { + traceit("Failed to send request to set dvap mode\n"); + return false; + } + + do { + usleep(5000); + + reply = get_reply(dvp_buf, &len); + + cnt ++; + if (cnt >= MAX_REPL_CNT) + { + traceit("Reached max number of requests to set dvap mode\n"); + return false; + } + } while (reply != RT_MODE); + + return true; +} + +static bool set_sql() +{ + unsigned cnt = 0; + unsigned int len = 0; + REPLY_TYPE reply; + int rc = -1; + unsigned char buf[10]; + unsigned char dvp_buf[200]; + + memcpy(buf, DVP_RQST_SQL, 5); + memcpy(buf + 4, &DVP_SQL, 1); + + rc = write_to_dvp(buf, 5); + if (rc != 5) + { + traceit("Failed to send request to set dvap sql\n"); + return false; + } + + do { + usleep(5000); + + reply = get_reply(dvp_buf, &len); + + cnt ++; + if (cnt >= MAX_REPL_CNT) + { + traceit("Reached max number of requests to set dvap sql\n"); + return false; + } + } while (reply != RT_SQL); + + return true; +} + +static bool set_pwr() +{ + unsigned cnt = 0; + unsigned int len = 0; + REPLY_TYPE reply; + int rc = -1; + unsigned char buf[10]; + int16_t temp_pwr; + unsigned char dvp_buf[200]; + + memcpy(buf, DVP_RQST_PWR, 6); + temp_pwr = (isit_bigendian())?do_swap16(DVP_PWR):DVP_PWR; + memcpy(buf + 4, &temp_pwr, sizeof(int16_t)); + + rc = write_to_dvp(buf, 6); + if (rc != 6) + { + traceit("Failed to send request to set dvap pwr\n"); + return false; + } + + do { + usleep(5000); + + reply = get_reply(dvp_buf, &len); + + cnt ++; + if (cnt >= MAX_REPL_CNT) + { + traceit("Reached max number of requests to set dvap pwr\n"); + return false; + } + } while (reply != RT_PWR); + + return true; +} + +static bool set_off() +{ + unsigned cnt = 0; + unsigned int len = 0; + REPLY_TYPE reply; + int rc = -1; + unsigned char buf[10]; + int16_t temp_off; + unsigned char dvp_buf[200]; + + memcpy(buf, DVP_RQST_OFF, 6); + temp_off = (isit_bigendian())?do_swap16(DVP_OFF):DVP_OFF; + memcpy(buf + 4, &temp_off, sizeof(int16_t)); + + rc = write_to_dvp(buf, 6); + if (rc != 6) + { + traceit("Failed to send request to set dvap offset\n"); + return false; + } + + do { + usleep(5000); + + reply = get_reply(dvp_buf, &len); + + cnt ++; + if (cnt >= MAX_REPL_CNT) + { + traceit("Reached max number of requests to set dvap offset\n"); + return false; + } + } while (reply != RT_OFF); + + return true; +} + +static bool set_freq() +{ + unsigned cnt = 0; + unsigned int len = 0; + REPLY_TYPE reply; + int rc = -1; + unsigned char buf[10]; + u_int32_t temp_freq; + unsigned char dvp_buf[200]; + + memcpy(buf, DVP_RQST_FREQ, 8); + temp_freq = (isit_bigendian())?do_swapu32(DVP_FREQ):DVP_FREQ; + memcpy(buf + 4, &temp_freq, sizeof(u_int32_t)); + + rc = write_to_dvp(buf, 8); + if (rc != 8) + { + traceit("Failed to send request to set dvap frequency\n"); + return false; + } + + do { + usleep(5000); + + reply = get_reply(dvp_buf, &len); + + cnt ++; + if (cnt >= MAX_REPL_CNT) + { + traceit("Reached max number of requests to set dvap frequency\n"); + return false; + } + } while (reply != RT_FREQ); + + return true; +} + +static bool start_dvap() +{ + unsigned cnt = 0; + unsigned int len = 0; + REPLY_TYPE reply; + int rc = -1; + unsigned char dvp_buf[200]; + + rc = write_to_dvp(DVP_RQST_START, 5); + if (rc != 5) + { + traceit("Failed to send request to start the dvap dongle\n"); + return false; + } + + do { + usleep(5000); + + reply = get_reply(dvp_buf, &len); + + cnt ++; + if (cnt >= MAX_REPL_CNT) + { + traceit("Reached max number of requests to start the dvap dongle\n"); + return false; + } + } while (reply != RT_START); + + return true; +} + +static void readFrom20000() +{ + unsigned char dvp_buf[200]; + struct sockaddr_in from; + socklen_t fromlen; + int len; + fd_set readfd; + struct timeval tv; + int inactive = 0; + short seq_no = 0; + unsigned streamid[2] = {0x00, 0x00}; + u_int16_t sid; + unsigned char sync_codes[3] = {0x55, 0x2d, 0x16}; + struct pkt net_buf; + u_int16_t stream_id_to_dvap = 0; + u_int8_t frame_pos_to_dvap = 0; + u_int8_t seq_to_dvap = 0; + char silence[12] = + { + 0x4e,0x8d,0x32,0x88,0x26,0x1a,0x3f,0x61,0xe8, + 0x70,0x4f,0x93 + }; + + bool written_to_q = false; + unsigned char ctrl_in = 0x80; + + while (keep_running) + { + written_to_q = false; + + tv.tv_sec = 0; + tv.tv_usec = WAIT_FOR_PACKETS; + fromlen = sizeof(struct sockaddr); + FD_ZERO (&readfd); + FD_SET (insock, &readfd); + select(insock + 1, &readfd, NULL, NULL, &tv); + + if (FD_ISSET(insock, &readfd)) + { + len = recvfrom (insock, (char *)&net_buf, 58, 0, (struct sockaddr *)&from, &fromlen); + if (len == 58) + { + if (busy20000) + { + FD_CLR (insock, &readfd); + continue; + } + + /* check the module and gateway */ + if (net_buf.rf_hdr.rpt2[7] != RPTR_MOD) + { + FD_CLR (insock, &readfd); + break; + } + memcpy(net_buf.rf_hdr.rpt1, OWNER, 7); + net_buf.rf_hdr.rpt1[7] = 'G'; + + if (memcmp(RPTR, OWNER, RPTR_SIZE) != 0) + { + // restriction mode + memcpy(net_buf.rf_hdr.rpt1, RPTR, 7); + memcpy(net_buf.rf_hdr.rpt2, RPTR, 7); + + if (memcmp(net_buf.rf_hdr.mycall, OWNER, 7) == 0) + { + /* this is an ACK back */ + memcpy(net_buf.rf_hdr.mycall, RPTR, 7); + } + } + + if ((net_buf.rf_hdr.flags[0] != 0x00) && + (net_buf.rf_hdr.flags[0] != 0x01) && + (net_buf.rf_hdr.flags[0] != 0x08) && + (net_buf.rf_hdr.flags[0] != 0x20) && + (net_buf.rf_hdr.flags[0] != 0x28) && + (net_buf.rf_hdr.flags[0] != 0x40)) + { + FD_CLR (insock, &readfd); + break; + } + + if ((memcmp(net_buf.pkt_id, "DSTR", 4) != 0) || + (net_buf.flags[0] != 0x73) || + (net_buf.flags[1] != 0x12) || + (net_buf.myicm.icm_id != 0x20)) /* voice type */ + { + FD_CLR (insock, &readfd); + break; + } + + busy20000 = true; + + ctrl_in = 0x80; + written_to_q = true; + + traceit("Start G2: streamid=%d,%d, flags=%02x:%02x:%02x, my=%.8s, sfx=%.4s, ur=%.8s, rpt1=%.8s, rpt2=%.8s\n", + net_buf.myicm.streamid[0], net_buf.myicm.streamid[1], + net_buf.rf_hdr.flags[0], net_buf.rf_hdr.flags[1], net_buf.rf_hdr.flags[2], + net_buf.rf_hdr.mycall, net_buf.rf_hdr.sfx, net_buf.rf_hdr.urcall, + net_buf.rf_hdr.rpt2, net_buf.rf_hdr.rpt1); + + /* save the streamid that is winning */ + streamid[0] = net_buf.myicm.streamid[0]; + streamid[1] = net_buf.myicm.streamid[1]; + + if (net_buf.rf_hdr.flags[0] != 0x01) + { + + if (net_buf.rf_hdr.flags[0] == 0x00) + net_buf.rf_hdr.flags[0] = 0x40; + else + if (net_buf.rf_hdr.flags[0] == 0x08) + net_buf.rf_hdr.flags[0] = 0x48; + else + if (net_buf.rf_hdr.flags[0] == 0x20) + net_buf.rf_hdr.flags[0] = 0x60; + else + if (net_buf.rf_hdr.flags[0] == 0x28) + net_buf.rf_hdr.flags[0] = 0x68; + else + net_buf.rf_hdr.flags[0] = 0x40; + } + net_buf.rf_hdr.flags[1] = 0x00; + net_buf.rf_hdr.flags[2] = 0x00; + + // write the header packet to the dvap here + while ((space < 1) && keep_running) + usleep(5); + stream_id_to_dvap = (rand_r(&aseed) % 65535U) + 1U; + memcpy(dvp_buf, DVP_HDR, 47); + sid = (isit_bigendian())?do_swapu16(stream_id_to_dvap):stream_id_to_dvap; + memcpy(dvp_buf + 2, &sid, sizeof(u_int16_t)); + dvp_buf[4] = 0x80; + dvp_buf[5] = 0; + memset(dvp_buf + 6, ' ', 41); + dvp_buf[6] = net_buf.rf_hdr.flags[0]; + dvp_buf[7] = net_buf.rf_hdr.flags[1]; + dvp_buf[8] = net_buf.rf_hdr.flags[2]; + memcpy(dvp_buf + 9, net_buf.rf_hdr.rpt1, 8); + memcpy(dvp_buf + 17, net_buf.rf_hdr.rpt2, 8); + memcpy(dvp_buf + 25, net_buf.rf_hdr.urcall, 8); + memcpy(dvp_buf + 33, net_buf.rf_hdr.mycall, 8); + memcpy(dvp_buf + 41, net_buf.rf_hdr.sfx, 4); + calcPFCS(dvp_buf + 6, dvp_buf + 45); + frame_pos_to_dvap = 0; + seq_to_dvap = 0; + (void)write_to_dvp(dvp_buf, 47); + + inactive = 0; + seq_no = 0; + } + else + if (len == 29) + { + if (busy20000) + { + if ((net_buf.myicm.streamid[0] == streamid[0]) && + (net_buf.myicm.streamid[1] == streamid[1])) + { + if (net_buf.myicm.ctrl == ctrl_in) + { + /* do not update written_to_q, ctrl_in */ + ; // traceit("dup\n"); + } + else + { + ctrl_in = net_buf.myicm.ctrl; + written_to_q = true; + + if (seq_no == 0) + { + net_buf.rf_audio.buff[9] = 0x55; + net_buf.rf_audio.buff[10] = 0x2d; + net_buf.rf_audio.buff[11] = 0x16; + } + else + { + if ((net_buf.rf_audio.buff[9] == 0x55) && + (net_buf.rf_audio.buff[10] == 0x2d) && + (net_buf.rf_audio.buff[11] == 0x16)) + { + net_buf.rf_audio.buff[9] = 0x70; + net_buf.rf_audio.buff[10] = 0x4f; + net_buf.rf_audio.buff[11] = 0x93; + } + } + + // write the audio packet to the dvap here + while ((space < 1) && keep_running) + usleep(5); + memcpy(dvp_buf, DVP_DAT, 18); + if (memcmp(net_buf.rf_audio.buff + 9, sync_codes, 3) == 0) + frame_pos_to_dvap = 0; + sid = (isit_bigendian())?do_swapu16(stream_id_to_dvap):stream_id_to_dvap; + memcpy(dvp_buf + 2, &sid, sizeof(u_int16_t)); + dvp_buf[4] = frame_pos_to_dvap; + dvp_buf[5] = seq_to_dvap; + if ((net_buf.myicm.ctrl & 0x40) != 0) + dvp_buf[4] |= 0x40U; + memcpy(dvp_buf + 6, net_buf.rf_audio.buff, 12); + (void)write_to_dvp(dvp_buf, 18); + frame_pos_to_dvap ++; + seq_to_dvap ++; + + inactive = 0; + + seq_no ++; + if (seq_no == 21) + seq_no = 0; + + if ((net_buf.myicm.ctrl & 0x40) != 0) + { + traceit("End G2: streamid=%d,%d\n", net_buf.myicm.streamid[0], net_buf.myicm.streamid[1]); + + streamid[0] = 0x00; + streamid[1] = 0x00; + + inactive = 0; + FD_CLR (insock, &readfd); + // maybe put a sleep here to prevent fast voice-overs + + busy20000 = false; + break; + } + } + } + } + else + { + FD_CLR (insock, &readfd); + break; + } + } + else + { + if (!busy20000) + { + FD_CLR (insock, &readfd); + break; + } + } + FD_CLR (insock, &readfd); + } + + /* + If we received a dup or select() timed out or streamids dont match, + then written_to_q is false + */ + if (!written_to_q) /* nothing was written to the adapter */ + { + if (busy20000) + { + if (++inactive == inactiveMax) + { + traceit("G2 Timeout...\n"); + + streamid[0] = 0x00; + streamid[1] = 0x00; + + inactive = 0; + // maybe put a sleep here to prevent fast voice-overs + + busy20000 = false; + break; + } + else + { + if (space == 127) + { + if (seq_no == 0) + { + silence[9] = 0x55; + silence[10] = 0x2d; + silence[11] = 0x16; + } + else + { + silence[9] = 0x70; + silence[10] = 0x4f; + silence[11] = 0x93; + } + + memcpy(dvp_buf, DVP_DAT, 18); + if (memcmp(silence + 9, sync_codes, 3) == 0) + frame_pos_to_dvap = 0; + sid = (isit_bigendian())?do_swapu16(stream_id_to_dvap):stream_id_to_dvap; + memcpy(dvp_buf + 2, &sid, sizeof(u_int16_t)); + dvp_buf[4] = frame_pos_to_dvap; + dvp_buf[5] = seq_to_dvap; + memcpy(dvp_buf + 6, silence, 12); + (void)write_to_dvp(dvp_buf, 18); + frame_pos_to_dvap ++; + seq_to_dvap ++; + + seq_no ++; + if (seq_no == 21) + seq_no = 0; + } + } + } + else + break; + } + } + return; +} + +int main(int argc, const char **argv) +{ + struct sigaction act; + int rc = -1; + time_t tnow = 0; + time_t ackpoint = 0; + pthread_t readFromRF_t; + pthread_attr_t attr; + short cnt = 0; + + setvbuf(stdout, NULL, _IOLBF, 0); + traceit("dvap_rptr VERSION %s\n", VERSION); + + if (argc != 2) + { + traceit("Usage: dvap_rptr dvap_rptr.cfg\n"); + return 1; + } + + rc = read_config(argv[1]); + if (rc != 0) + { + traceit("Failed to process config file %s\n", argv[1]); + return 1; + } + + if (strlen(RPTR) != 8) + { + traceit("Bad RPTR value, length must be exactly 8 bytes\n"); + return 1; + } + if ((RPTR_MOD != 'A') && (RPTR_MOD != 'B') && (RPTR_MOD != 'C')) + { + traceit("Bad RPTR_MOD value, must be one of A or B or C\n"); + return 1; + } + + if (RPTR_MOD == 'A') + SND_TERM_ID = 0x03; + else + if (RPTR_MOD == 'B') + SND_TERM_ID = 0x01; + else + if (RPTR_MOD == 'C') + SND_TERM_ID = 0x02; + + strcpy(RPTR_and_G, RPTR); + RPTR_and_G[7] = 'G'; + + strcpy(RPTR_and_MOD, RPTR); + RPTR_and_MOD[7] = RPTR_MOD; + + time(&tnow); + aseed = tnow + getpid(); + + act.sa_handler = sig_catch; + sigemptyset(&act.sa_mask); + if (sigaction(SIGTERM, &act, 0) != 0) + { + traceit("sigaction-TERM failed, error=%d\n", errno); + return 1; + } + if (sigaction(SIGHUP, &act, 0) != 0) + { + traceit("sigaction-HUP failed, error=%d\n", errno); + return 1; + } + if (sigaction(SIGINT, &act, 0) != 0) + { + traceit("sigaction-INT failed, error=%d\n", errno); + return 1; + } + + /* open dvp */ + if (!open_dvp()) + return 1; + + rc = open_sock(); + if (rc != 0) + { + (void)write_to_dvp(DVP_RQST_STOP, 5); + close(serfd); + return 1; + } + + dstar_dv_init(); + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&readFromRF_t, &attr, readFromRF, (void *)0); + if (rc != 0) + { + keep_running = false; + traceit("failed to start thread readFromRF thread\n"); + } + else + traceit("Started thread readFromRF\n"); + pthread_attr_destroy(&attr); + + while (keep_running) + { + time(&tnow); + if ((tnow - ackpoint) > 2) + { + rc = write_to_dvp(DVP_ACK, 3); + if (rc < 0) + { + cnt ++; + if (cnt > 5) + { + traceit("Could not send KEEPALIVE signal to dvap 5 times...exiting\n"); + keep_running = false; + } + } + else + cnt = 0; + ackpoint = tnow; + } + readFrom20000(); + } + + close(insock); + traceit("dvap_rptr exiting\n"); + return 0; +} + +static void *rptr_ack(void *arg) +{ + char mycall[8]; + memcpy(mycall, ((struct dvap_ack_arg_type *)arg)->mycall, 8); + float ber = ((struct dvap_ack_arg_type *)arg)->ber; + + char RADIO_ID[21]; + + sprintf(RADIO_ID, "%20.2f", ber); + memcpy(RADIO_ID, "BER%", 4); + + struct sigaction act; + unsigned char dvp_buf[200]; + u_int16_t stream_id_to_dvap = 0; + u_int16_t sid; + time_t tnow = 0; + char silence[12] = + { + 0x4e,0x8d,0x32,0x88,0x26,0x1a,0x3f,0x61,0xe8, + 0x70,0x4f,0x93 + }; + unsigned int aseed_ack = 0; + struct timespec nanos; + + act.sa_handler = sig_catch; + sigemptyset(&act.sa_mask); + if (sigaction(SIGTERM, &act, 0) != 0) + { + traceit("sigaction-TERM failed, error=%d\n", errno); + traceit("thread rptr_ack exiting\n"); + pthread_exit(NULL); + } + if (sigaction(SIGHUP, &act, 0) != 0) + { + traceit("sigaction-HUP failed, error=%d\n", errno); + traceit("thread rptr_ack exiting\n"); + pthread_exit(NULL); + } + if (sigaction(SIGINT, &act, 0) != 0) + { + traceit("sigaction-INT failed, error=%d\n", errno); + traceit("thread rptr_ack exiting\n"); + pthread_exit(NULL); + } + + sleep(DELAY_BEFORE); + // traceit("ack-start\n"); + + time(&tnow); + aseed_ack = tnow + getpid(); + + stream_id_to_dvap = (rand_r(&aseed_ack) % 65535U) + 1U; + sid = (isit_bigendian())?do_swapu16(stream_id_to_dvap):stream_id_to_dvap; + + // HEADER + while ((space < 1) && keep_running) + usleep(5); + memcpy(dvp_buf, DVP_HDR, 47); + memcpy(dvp_buf + 2, &sid, sizeof(u_int16_t)); + dvp_buf[4] = 0x80; + dvp_buf[5] = 0; + memset(dvp_buf + 6, ' ', 41); + dvp_buf[6] = 0x01; + dvp_buf[7] = 0x00; + dvp_buf[8] = 0x00; + memcpy(dvp_buf + 9, RPTR_and_MOD, 8); + memcpy(dvp_buf + 17, RPTR_and_G, 8); + memcpy(dvp_buf + 25, mycall, 8); + memcpy(dvp_buf + 33, RPTR_and_MOD, 8); + memcpy(dvp_buf + 41, (unsigned char *)" ", 4); + calcPFCS(dvp_buf + 6, dvp_buf + 45); + (void)write_to_dvp(dvp_buf, 47); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + // SYNC + while ((space < 1) && keep_running) + usleep(5); + silence[9] = 0x55; silence[10] = 0x2d; silence[11] = 0x16; + memcpy(dvp_buf, DVP_DAT, 18); + memcpy(dvp_buf + 2, &sid, sizeof(u_int16_t)); + dvp_buf[4] = 0x00; + dvp_buf[5] = 0x00; + memcpy(dvp_buf + 6, silence, 12); + (void)write_to_dvp(dvp_buf, 18); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + // NOTHING + while ((space < 1) && keep_running) + usleep(5); + silence[9] = '@' ^ 0x70; + silence[10] = RADIO_ID[0] ^ 0x4f; + silence[11] = RADIO_ID[1] ^ 0x93; + memcpy(dvp_buf, DVP_DAT, 18); + memcpy(dvp_buf + 2, &sid, sizeof(u_int16_t)); + dvp_buf[4] = 0x01; + dvp_buf[5] = 0x01; + memcpy(dvp_buf + 6, silence, 12); + (void)write_to_dvp(dvp_buf, 18); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + while ((space < 1) && keep_running) + usleep(5); + silence[9] = RADIO_ID[2] ^ 0x70; + silence[10] = RADIO_ID[3] ^ 0x4f; + silence[11] = RADIO_ID[4] ^ 0x93; + memcpy(dvp_buf, DVP_DAT, 18); + memcpy(dvp_buf + 2, &sid, sizeof(u_int16_t)); + dvp_buf[4] = 0x02; + dvp_buf[5] = 0x02; + memcpy(dvp_buf + 6, silence, 12); + (void)write_to_dvp(dvp_buf, 18); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + while ((space < 1) && keep_running) + usleep(5); + silence[9] = 'A' ^ 0x70; + silence[10] = RADIO_ID[5] ^ 0x4f; + silence[11] = RADIO_ID[6] ^ 0x93; + memcpy(dvp_buf, DVP_DAT, 18); + memcpy(dvp_buf + 2, &sid, sizeof(u_int16_t)); + dvp_buf[4] = 0x03; + dvp_buf[5] = 0x03; + memcpy(dvp_buf + 6, silence, 12); + (void)write_to_dvp(dvp_buf, 18); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + while ((space < 1) && keep_running) + usleep(5); + silence[9] = RADIO_ID[7] ^ 0x70; + silence[10] = RADIO_ID[8] ^ 0x4f; + silence[11] = RADIO_ID[9] ^ 0x93; + memcpy(dvp_buf, DVP_DAT, 18); + memcpy(dvp_buf + 2, &sid, sizeof(u_int16_t)); + dvp_buf[4] = 0x04; + dvp_buf[5] = 0x04; + memcpy(dvp_buf + 6, silence, 12); + (void)write_to_dvp(dvp_buf, 18); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + while ((space < 1) && keep_running) + usleep(5); + silence[9] = 'B' ^ 0x70; + silence[10] = RADIO_ID[10] ^ 0x4f; + silence[11] = RADIO_ID[11] ^ 0x93; + memcpy(dvp_buf, DVP_DAT, 18); + memcpy(dvp_buf + 2, &sid, sizeof(u_int16_t)); + dvp_buf[4] = 0x05; + dvp_buf[5] = 0x05; + memcpy(dvp_buf + 6, silence, 12); + (void)write_to_dvp(dvp_buf, 18); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + while ((space < 1) && keep_running) + usleep(5); + silence[9] = RADIO_ID[12] ^ 0x70; + silence[10] = RADIO_ID[13] ^ 0x4f; + silence[11] = RADIO_ID[14] ^ 0x93; + memcpy(dvp_buf, DVP_DAT, 18); + memcpy(dvp_buf + 2, &sid, sizeof(u_int16_t)); + dvp_buf[4] = 0x06; + dvp_buf[5] = 0x06; + memcpy(dvp_buf + 6, silence, 12); + (void)write_to_dvp(dvp_buf, 18); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + while ((space < 1) && keep_running) + usleep(5); + silence[9] = 'C' ^ 0x70; + silence[10] = RADIO_ID[15] ^ 0x4f; + silence[11] = RADIO_ID[16] ^ 0x93; + memcpy(dvp_buf, DVP_DAT, 18); + memcpy(dvp_buf + 2, &sid, sizeof(u_int16_t)); + dvp_buf[4] = 0x07; + dvp_buf[5] = 0x07; + memcpy(dvp_buf + 6, silence, 12); + (void)write_to_dvp(dvp_buf, 18); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + while ((space < 1) && keep_running) + usleep(5); + silence[9] = RADIO_ID[17] ^ 0x70; + silence[10] = RADIO_ID[18] ^ 0x4f; + silence[11] = RADIO_ID[19] ^ 0x93; + memcpy(dvp_buf, DVP_DAT, 18); + memcpy(dvp_buf + 2, &sid, sizeof(u_int16_t)); + dvp_buf[4] = 0x08; + dvp_buf[5] = 0x08; + memcpy(dvp_buf + 6, silence, 12); + (void)write_to_dvp(dvp_buf, 18); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + // END + while ((space < 1) && keep_running) + usleep(5); + silence[0] = 0x55; silence[1] = 0xc8; silence[2] = 0x7a; + silence[9] = 0x55; silence[10] = 0x55; silence[11] = 0x55; + memcpy(dvp_buf, DVP_DAT, 18); + memcpy(dvp_buf + 2, &sid, sizeof(u_int16_t)); + dvp_buf[4] = (0x09 | 0x40); + dvp_buf[5] = 0x09; + memcpy(dvp_buf + 6, silence, 12); + (void)write_to_dvp(dvp_buf, 18); + + // traceit("ack-end\n"); + pthread_exit(NULL); +} + +static void *readFromRF(void *arg) +{ + REPLY_TYPE reply; + unsigned int len = 0; + unsigned char dvp_buf[200]; + struct pkt net_buf; + time_t tnow = 0; + time_t S_ctrl_msg_time = 0; + unsigned char S_packet[26]; + unsigned short C_COUNTER = 0; + time_t last_RF_time = 0; + struct sigaction act; + bool dvap_busy = false; + bool ptt = false; + bool the_end = true; + dvap_hdr *from_dvap_hdr = (dvap_hdr *)dvp_buf; + // dvap_voice *from_dvap_voice = (dvap_voice *)dvp_buf; + bool ok = true; + int i = 0; + u_int16_t streamid_raw = 0; + short int sequence = 0x00; + char mycall[8]; + pthread_t rptr_ack_t; + pthread_attr_t attr; + int rc = -1; + short int status_cntr = 3000; + char temp_yrcall[CALL_SIZE + 1]; + char *temp_ptr = NULL; + + num_dv_frames = 0; + num_bit_errors = 0; + + arg = arg; + act.sa_handler = sig_catch; + sigemptyset(&act.sa_mask); + if (sigaction(SIGTERM, &act, 0) != 0) + { + traceit("sigaction-TERM failed, error=%d\n", errno); + keep_running = false; + traceit("thread readFromRF exiting\n"); + pthread_exit(NULL); + } + if (sigaction(SIGHUP, &act, 0) != 0) + { + traceit("sigaction-HUP failed, error=%d\n", errno); + keep_running = false; + traceit("thread readFromRF exiting\n"); + pthread_exit(NULL); + } + if (sigaction(SIGINT, &act, 0) != 0) + { + traceit("sigaction-INT failed, error=%d\n", errno); + keep_running = false; + traceit("thread readFromRF exiting\n"); + pthread_exit(NULL); + } + + /* prepare the S server status packet */ + memcpy(S_packet, "DSTR", 4); + S_packet[4] = 0x00; + S_packet[5] = 0x00; + S_packet[6] = 0x73; + S_packet[7] = 0x21; + S_packet[8] = 0x00; + S_packet[9] = 0x10; + + while (keep_running) + { + time(&tnow); + + /* send the S packet if needed */ + if ((tnow - S_ctrl_msg_time) > 60) + { + S_packet[5] = (unsigned char)(C_COUNTER & 0xff); + S_packet[4] = ((C_COUNTER >> 8) & 0xff); + memcpy(S_packet + 10, OWNER, 8); S_packet[17] = 'S'; + memcpy(S_packet + 18, OWNER, 8); S_packet[25] = 'S'; + sendto(insock, (char *)S_packet, sizeof(S_packet), 0, (struct sockaddr *)&outaddr, sizeof(outaddr)); + C_COUNTER ++; + S_ctrl_msg_time = tnow; + } + + // local RF user went away ? + if (dvap_busy) + { + time(&tnow); + if ((tnow - last_RF_time) > 1) + dvap_busy = false; + } + + // read from the dvap and process + reply = get_reply(dvp_buf, &len); + if (reply == RT_ERR) + { + traceit("Detected ERROR event from DVAP dongle, stopping...n"); + break; + } + else + if (reply == RT_STOP) + { + traceit("Detected STOP event from DVAP dongle, stopping...\n"); + break; + } + else if (reply == RT_START) + traceit("Detected START event from DVAP dongle\n"); + else + if (reply == RT_PTT) + { + ptt = (dvp_buf[4] == 0x01); + // traceit("Detected PTT=%s\n", ptt?"on":"off"); + } + else + if (reply == RT_STS) + { + space = dvp_buf[6]; + if (status_cntr < 3000) + status_cntr += 20; + } + else + if (reply == RT_HDR) + { + num_dv_frames = 0; + num_bit_errors = 0; + + traceit("From DVAP: flags=%02x:%02x:%02x, my=%.8s, sfx=%.4s, ur=%.8s, rpt1=%.8s, rpt2=%.8s\n", + from_dvap_hdr->flag1, from_dvap_hdr->flag2, from_dvap_hdr->flag3, + from_dvap_hdr->mycall, from_dvap_hdr->sfx, from_dvap_hdr->urcall, + from_dvap_hdr->rpt2, from_dvap_hdr->rpt1); + + ok = true; + + /* Accept valid flags only */ + if (ok) + { + /* net flags */ + if ((from_dvap_hdr->flag1 != 0x00) && + (from_dvap_hdr->flag1 != 0x08) && + (from_dvap_hdr->flag1 != 0x20) && + (from_dvap_hdr->flag1 != 0x28) && + + /* rptr flags */ + (from_dvap_hdr->flag1 != 0x40) && + (from_dvap_hdr->flag1 != 0x48) && + (from_dvap_hdr->flag1 != 0x60) && + (from_dvap_hdr->flag1 != 0x68)) + ok = false; + } + + /* Reject those stupid STN stations */ + if (ok) + { + memcpy(temp_yrcall, from_dvap_hdr->urcall, CALL_SIZE); + temp_yrcall[CALL_SIZE] = '\0'; + temp_ptr = strstr(temp_yrcall, INVALID_YRCALL_KEY); + if (temp_ptr == temp_yrcall) // found it at first position + { + traceit("YRCALL value [%s] starts with the INVALID_YRCALL_KEY [%s], resetting to CQCQCQ\n", + temp_yrcall, INVALID_YRCALL_KEY); + memcpy(from_dvap_hdr->urcall, "CQCQCQ ", 8); + } + } + + /*** copy the dvap header ***/ + memcpy(net_buf.rf_audio.buff, dvp_buf + 6, 41); + + /* RPT1 must always be the repeater + module */ + memcpy(net_buf.rf_hdr.rpt1, RPTR_and_MOD, 8); + /* copy RPT2 */ + memcpy(net_buf.rf_hdr.rpt2, from_dvap_hdr->rpt1, 8); + + /* RPT2 must also be valid */ + if ((net_buf.rf_hdr.rpt2[7] == 'A') || + (net_buf.rf_hdr.rpt2[7] == 'B') || + (net_buf.rf_hdr.rpt2[7] == 'C') || + (net_buf.rf_hdr.rpt2[7] == 'G')) + memcpy(net_buf.rf_hdr.rpt2, RPTR, 7); + else + memset(net_buf.rf_hdr.rpt2, ' ', 8); + + if ((memcmp(net_buf.rf_hdr.urcall, "CQCQCQ", 6) != 0) && (net_buf.rf_hdr.rpt2[0] != ' ')) + memcpy(net_buf.rf_hdr.rpt2, RPTR_and_G, 8); + + /* 8th in rpt1, rpt2 must be diff */ + if (net_buf.rf_hdr.rpt2[7] == net_buf.rf_hdr.rpt1[7]) + memset(net_buf.rf_hdr.rpt2, ' ', 8); + + /* + Are we restricting the RF user ? + If RPTR is OWNER, then any RF user can talk. + If RPTR is not OWNER, + that means that mycall, rpt1, rpt2 must be equal to RPTR + otherwise we drop the rf data + */ + if (memcmp(RPTR, OWNER, RPTR_SIZE) != 0) + { + if (memcmp(net_buf.rf_hdr.mycall, RPTR, RPTR_SIZE) != 0) + { + traceit("mycall=[%.8s], not equal to %s\n", net_buf.rf_hdr.mycall, RPTR); + ok = false; + } + } + else + if (memcmp(net_buf.rf_hdr.mycall, " ", 8) == 0) + { + traceit("Invalid value for mycall=[%.8s]\n", net_buf.rf_hdr.mycall); + ok = false; + } + + if (ok) + { + for (i = 0; i < 8; i++) + { + if (!isupper(net_buf.rf_hdr.mycall[i]) && + !isdigit(net_buf.rf_hdr.mycall[i]) && + (net_buf.rf_hdr.mycall[i] != ' ')) + { + memset(net_buf.rf_hdr.mycall, ' ', 8); + ok = false; + traceit("Invalid value for MYCALL\n"); + break; + } + } + + for (i = 0; i < 4; i++) + { + if (!isupper(net_buf.rf_hdr.sfx[i]) && + !isdigit(net_buf.rf_hdr.sfx[i]) && + (net_buf.rf_hdr.sfx[i] != ' ')) + { + memset(net_buf.rf_hdr.sfx, ' ', 4); + break; + } + } + + for (i = 0; i < 8; i++) + { + if (!isupper(net_buf.rf_hdr.urcall[i]) && + !isdigit(net_buf.rf_hdr.urcall[i]) && + (net_buf.rf_hdr.urcall[i] != ' ') && + (net_buf.rf_hdr.urcall[i] != '/')) + { + memcpy(net_buf.rf_hdr.urcall, "CQCQCQ ", 8); + break; + } + } + + /*** what if YRCALL is all spaces, we can NOT allow that ***/ + if (memcmp(net_buf.rf_hdr.urcall, " ", 8) == 0) + memcpy(net_buf.rf_hdr.urcall, "CQCQCQ ", 8); + + /* change the rptr flags to net flags */ + if (from_dvap_hdr->flag1 == 0x40) + net_buf.rf_hdr.flags[0] = 0x00; + else + if (from_dvap_hdr->flag1 == 0x48) + net_buf.rf_hdr.flags[0] = 0x08; + else + if (from_dvap_hdr->flag1 == 0x60) + net_buf.rf_hdr.flags[0] = 0x20; + else + if (from_dvap_hdr->flag1 == 0x68) + net_buf.rf_hdr.flags[0] = 0x28; + else + net_buf.rf_hdr.flags[0] = 0x00; + net_buf.rf_hdr.flags[1] = 0x00; + net_buf.rf_hdr.flags[2] = 0x00; + + /* for icom g2 */ + S_packet[5] = (unsigned char)(C_COUNTER & 0xff); + S_packet[4] = ((C_COUNTER >> 8) & 0xff); + memcpy(S_packet + 10, net_buf.rf_hdr.mycall, 8); + memcpy(S_packet + 18, OWNER, 8); S_packet[25] = RPTR_MOD; + sendto(insock, (char *)S_packet, sizeof(S_packet), 0, (struct sockaddr *)&outaddr, sizeof(outaddr)); + C_COUNTER ++; + + /* + Before we send the data to the local gateway, + set RPT1, RPT2 to be the local gateway + */ + memcpy(net_buf.rf_hdr.rpt1, OWNER, 7); + if (net_buf.rf_hdr.rpt2[7] != ' ') + memcpy(net_buf.rf_hdr.rpt2, OWNER, 7); + + memcpy(net_buf.pkt_id, "DSTR", 4); + net_buf.nothing1[0] = ((C_COUNTER >> 8) & 0xff); + net_buf.nothing1[1] = (unsigned char)(C_COUNTER & 0xff); + net_buf.flags[0] = 0x73; + net_buf.flags[1] = 0x12; + net_buf.nothing2[0] = 0x00; + net_buf.nothing2[1] = 0x30; + net_buf.myicm.icm_id = 0x20; + net_buf.myicm.dst_rptr_id = 0x00; + net_buf.myicm.snd_rptr_id = 0x01; + net_buf.myicm.snd_term_id = SND_TERM_ID; + streamid_raw = (rand_r(&aseed) % 65535U) + 1U; + net_buf.myicm.streamid[0] = streamid_raw / 256U; + net_buf.myicm.streamid[1] = streamid_raw % 256U; + net_buf.myicm.ctrl = 0x80; sequence = 0x00; + calcPFCS((unsigned char *)&(net_buf.rf_hdr), net_buf.rf_hdr.pfcs); + sendto(insock, (char *)&net_buf, 58, 0, (struct sockaddr *)&outaddr, sizeof(outaddr)); + C_COUNTER ++; + + // local RF user keying up, start timer + dvap_busy = true; + time(&last_RF_time); + + // save mycall for the ack later + memcpy(mycall, from_dvap_hdr->mycall, 8); + + } + } + else + if (reply == RT_DAT) + { + /* have we already received a header ? */ + if (dvap_busy) + { + the_end = ((dvp_buf[4] & 0x40) == 0x40); + + net_buf.nothing1[0] = ((C_COUNTER >> 8) & 0xff); + net_buf.nothing1[1] = (unsigned char)(C_COUNTER & 0xff); + net_buf.nothing2[1] = 0x13; + net_buf.myicm.ctrl = sequence++; + if (the_end) + net_buf.myicm.ctrl = sequence | 0x40; + memcpy(net_buf.rf_audio.buff, dvp_buf + 6, 12); + sendto(insock, (char *)&net_buf, 29, 0, (struct sockaddr *)&outaddr, sizeof(outaddr)); + + ber_errs = dstar_dv_decode((unsigned char *)&net_buf + 17, ber_data); + if (ber_data[0] != 0xf85) + { + num_bit_errors += ber_errs; + num_dv_frames ++; + } + + C_COUNTER ++; + if (sequence > 0x14) + sequence = 0x00; + + // local RF user still talking, update timer + time(&last_RF_time); + if (the_end) + { + // local RF user stopped talking + dvap_busy = false; + traceit("End of dvap audio, ber=%.02f\n", + (num_dv_frames == 0)?0.00:100.00 * ((float)num_bit_errors / (float)(num_dv_frames * 24.00)) ); + + if (RPTR_ACK && !busy20000) + { + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + memcpy(dvap_ack_arg.mycall, mycall, 8); + dvap_ack_arg.ber = (num_dv_frames == 0)?0.00:100.00 * ((float)num_bit_errors / (float)(num_dv_frames * 24.00)); + rc = pthread_create(&rptr_ack_t, &attr, rptr_ack, (void *)&dvap_ack_arg); + if (rc != 0) + traceit("failed to start thread rptr_ack thread\n"); + pthread_attr_destroy(&attr); + } + } + } + } + usleep(1000); + status_cntr --; + if (status_cntr < 0) + break; + } + + /* stop dvap */ + (void)write_to_dvp(DVP_RQST_STOP, 5); + close(serfd); + + traceit("readFromRF thread exiting\n"); + + keep_running = false; + pthread_exit(NULL); +} + diff --git a/exec_H.sh b/exec_H.sh new file mode 100755 index 0000000..ee14f1e --- /dev/null +++ b/exec_H.sh @@ -0,0 +1,2 @@ +#!/bin/sh +halt diff --git a/exec_R.sh b/exec_R.sh new file mode 100755 index 0000000..d7cfcc0 --- /dev/null +++ b/exec_R.sh @@ -0,0 +1,2 @@ +#!/bin/sh +reboot diff --git a/g2_ircddb.cfg b/g2_ircddb.cfg new file mode 100644 index 0000000..864c674 --- /dev/null +++ b/g2_ircddb.cfg @@ -0,0 +1,177 @@ +# +# Copyright (C) 2010, 2011, 2012 by Scott Lawson KI4LKF +# +# 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. + +# +# g2_ircddb.cfg configuration file for g2_ircddb gateway + +# No SPACE before or after the EQUAL sign(=) +# That is true for all options. +# Example: +# You can NOT do this: OWNER = ... +# You can NOT do this: OWNER =... +# You can NOT do this: OWNER= ... +# VALID format is: OWNER=... + + +# Your G2 Gateway callsign can be up to 6 characters +# This will also be used to connect to the remote ircDDB server +# Must be in UPPERCASE, use the correct callsign of your Gateway +OWNER=AC2IE + +# PACKAGE_REV value will show on the German IRC status site under the column PACKAGE_REV +# Use the name of your repeater software or anything else you like +# Separate the name from the version using the underscore +# PACKAGE_REV=rptr_319 +# PACKAGE_REV=sound_116 +PACKAGE_REV=dvap-rptr_232 +# PACKAGE_REV=dvrptr_176 + +# This is for APRS/GPS +SEND_APRS=Y +# The repeater module specifications +# LATITUDE,LONGITUDE,RANGE,DESCRIPTION +# If you do NOT want to define the aprs parameters for a module, +# then leave the line BLANK + +RPTR_ID_A= +RPTR_ID_B= +RPTR_ID_C=42.88451,-73.78889,1,DVAP + +# Which local address on your Linux box will be used +# to report to the remote IRC database server(group2, group1,...) +# For most installations it is 0.0.0.0 (which binds to all local IP addresses) +# If you have more than one ethernet card, then you may set it to a specific local IP +# that is assigned to that specific ethernet card +# If you have one ethernet card, you can use 0.0.0.0 +LOCAL_IRC_IP=0.0.0.0 + +# This is for the Live IRC site +SEND_QRGS_MAPS=N +# LATITUDE, LONGITUDE, description1, description2, url +QTH=42.88, -73.79, Dstar-rptr C, g2-ircddb +# Frequency, shift, range, antenna's height above ground(agl) +QRG_A= +QRG_B= +QRG_C=146.55, 0.0, 1, 10 + +# You should NOT change the value for APRS_HOST +# Leave it set to: rotate.aprs.net +# The only time to change it is when you have your own APRS_HOST server +# or when you have a server that is closer to your QTH +APRS_HOST=rotate.aprs.net +APRS_PORT=14580 +# How often to report the aprs beacon, in minutes, Do NOT make it less than 40 +APRS_INTERVAL=40 +APRS_FILTER= + +# The G2 external port of this Gateway server +# Remote gateways will route data here +G2_EXTERNAL_IP=0.0.0.0 +G2_EXTERNAL_PORT=40000 + +# The G2 internal port of this Gateway server +# Our local repeater modules will send data here +G2_INTERNAL_IP=0.0.0.0 +G2_INTERNAL_PORT=19000 + +# Where is the g2_link running ? +TO_G2_LINK_IP=127.0.0.1 +TO_G2_LINK_PORT=18997 + +# The repeater modules of this repeater +TO_RPTR_IP_A=127.0.0.1 +TO_RPTR_PORT_A=19998 + +TO_RPTR_IP_B=127.0.0.1 +TO_RPTR_PORT_B=19999 + +TO_RPTR_IP_C=127.0.0.1 +TO_RPTR_PORT_C=20000 + +# Timeouts in seconds +# We need these timeouts because the END-OF-AUDIO packet can get lost. +# Of course it can get lost, because everything about Dstar is on UDP +# UDP is notorious for losing packets. +# Each packet arrives normally 20ms after previous packet, +# so giving it 1 second is more than enough. +# This 1-second timeout was used internally in the g2_ircddb software, +# but now the user has a chance to change it. +# We do not believe that it should be changed, +# As mentioned before, more than 1 second is more than +# enough to make sure that we should stop waiting for more packets. + +# Echotest-recording timeout: +# If after 1 second, audio stops coming in for echotest-recording, +# we will stop recording for ECHOTEST +ECHOTEST_REC_TIMEOUT=1 + +# Voicemail-recording timeout: +# If after 1 second, audio stops coming in for voicemail-recording, +# we will stop recording for voicemail +VOICEMAIL_REC_TIMEOUT=1 + +# If after 2 seconds, we do not receive any more packets from remote system, +# we will assume that the remote QSO has stopped +FROM_REMOTE_G2_TIMEOUT=2 + +# If after 1 second, we do not receive any more packets from +# our local repeater, we will assume that the local RF user has stopped talking. +FROM_LOCAL_RPTR_TIMEOUT=1 + +# print QSO details +QSO_DETAILS=Y + +# These 2 options limit the lines in the log. +# For production systems, we recommend: IRC_DEBUG=N +IRC_DEBUG=N +DTMF_DEBUG=N + +# Do you want to re-generate the HEADER if there is a timeout from +# messages arriving from remote systems(reflectors, gateways, ...) +REGEN_HDR=Y + +# LINK STATUS file +# This file is created by g2_link +# This gateway will ONLY read that status file +# to determine if any module is linked to anything +STATUS_FILE=/usr/local/etc/RPT_STATUS.txt + +DTMF_DIR=/tmp + +# Directory of recorded files created in ECHOTEST/playback mode +ECHOTEST_DIR=/tmp +# How many SECONDS to wait before starting to playback the recorded file. +# 1 second is enough, some repeaters require 2 seconds to re-initialize +# before they accept new streams again. +PLAY_WAIT=2 +# How many MILLIseconds to delay when playing back each of the recorded packets +# Normal VoIP is 20 milliseconds or a little less +PLAY_DELAY=19 + +# The remote ircDDB database +# For US, Canada and North America +IRC_DDB_HOST=group2-irc.ircddb.net +# For the German and EU +# IRC_DDB_HOST=stn570.dydns.org + +# The remote ircDDB port +IRC_DDB_PORT=9007 + +# Your ircDDB password given to you by the German ircDDB team. +# Put your correct password here. +IRC_PASS=1111111111111111 + diff --git a/g2_ircddb.cpp b/g2_ircddb.cpp new file mode 100644 index 0000000..0d3a786 --- /dev/null +++ b/g2_ircddb.cpp @@ -0,0 +1,5124 @@ +/* + * Copyright (C) 2010 by Scott Lawson KI4LKF + * + * 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. + */ + + +/* by KI4LKF */ +/* + g2_ircddb is a dstar G2 gateway, using irc routing + adapted from the OpenG2 G2 gateway + Version 2.61 or higher will use ONLY the irc mechanism of routing + and it will NOT use any local Postgres databases or any TRUST(s) +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include +using namespace std; + +#include + +#include + +#include "IRCDDB.h" + +#define VERSION "Linux-g2_ircddb-3.09" +#define OWNER_SIZE 8 +#define IP_SIZE 15 +#define MAXHOSTNAMELEN 64 +#define CALL_SIZE 8 +#define ECHO_CODE 'E' +#define STORE_VM_CODE 'S' +#define RECALL_VM_CODE 'R' +#define CLEAR_VM_CODE 'C' +#define LINK_CODE 'L' + +/* configuration data */ + +/* Gateway callsign */ +static char OWNER[OWNER_SIZE + 1]; +static char owner[OWNER_SIZE + 1]; +static char PACKAGE_REV[56]; + +static char LOCAL_IRC_IP[IP_SIZE + 1]; +static bool SEND_QRGS_MAPS = false; +static char QTH[257]; +static char QRG_A[257]; +static char QRG_B[257]; +static char QRG_C[257]; + +static char STATUS_FILE[FILENAME_MAX + 1]; + +static char silence[9] = { 0x4e,0x8d,0x32,0x88,0x26,0x1a,0x3f,0x61,0xe8 }; + +static char DTMF_DIR[FILENAME_MAX + 1]; +static char DTMF_FILE[FILENAME_MAX + 1]; +static const int MAX_DTMF_BUF = 32; +static char dtmf_chars[17] = "147*2580369#ABCD"; +static int dtmf_digit; +static FILE *dtmf_fp = NULL; +static char dtmf_buf[3][MAX_DTMF_BUF + 1] = { {""}, {""}, {""} }; +static int dtmf_buf_count[3] = {0, 0, 0}; +static unsigned int dtmf_counter[3] = {0, 0, 0}; +static int dtmf_last_frame[3] = {0, 0, 0}; + +static bool IRC_DEBUG = false; +static bool DTMF_DEBUG = false; +static bool REGEN_HDR = false; + +/* the aprs TCP socket */ +static int aprs_sock = -1; +static struct sockaddr_in aprs_addr; +static socklen_t aprs_addr_len; + +/* data needed for aprs login and aprs beacon */ +static struct +{ + char aprs_host[MAXHOSTNAMELEN + 1]; + int aprs_port; + int aprs_hash; + int aprs_interval; + char aprs_filter[512]; + + /* 0=A, 1=B, 2=C */ + char rptr[3][CALL_SIZE + 1]; /* KJ4NHF-B */ + char band[3][5]; /* 23cm ... */ + int range[3]; + double lat[3]; + double lon[3]; + char desc[3][64]; +} RPTR_ID; + +/* Gateway external IP and port for remote G2 routing */ +static char G2_EXTERNAL_IP[IP_SIZE + 1]; /* 0.0.0.0 */ +static int G2_EXTERNAL_PORT = 40000; + +/* + Internal Gateway internal IP and port, + so that the local repeater modules can talk to our g2_ircddb +*/ +static char G2_INTERNAL_IP[IP_SIZE + 1]; /* 0.0.0.0 */ +static int G2_INTERNAL_PORT = 19000; + +/* We send repeater data to our g2_link */ +static int TO_G2_LINK_PORT = 18997; +static char TO_G2_LINK_IP[IP_SIZE + 1]; + +/* local repeater module IP and port for each repeater module */ +/* all set to 127.0.0.1, unless running on other boxes */ +static char TO_RPTR_IP[3][IP_SIZE + 1]; +static int TO_RPTR_PORT[3]; /* Example: 19998 19999 20000 */ + +/* QSO details go to a log file */ +static bool QSO_DETAILS = false; + +static bool SEND_APRS = false; + +/* Recorded files are here */ +static char ECHOTEST_DIR[FILENAME_MAX + 1]; + +/* + How many SECONDS to wait before starting to playback the recorded file. + 1 second is enough, some repeaters require 2 seconds to re-initialize + before they accept new streams again. +*/ +static int PLAY_WAIT = 1; + +/* + How many MILLIseconds to delay when playing back each of the recorded packets + Normal VoIP is 20 milliseconds +*/ +static int PLAY_DELAY = 20; + +static char IRC_DDB_HOST[512]; +static int IRC_DDB_PORT = 9007; +static char IRC_PASS[512]; + +static int ECHOTEST_REC_TIMEOUT = 1; +static int VOICEMAIL_REC_TIMEOUT = 1; +static int FROM_REMOTE_G2_TIMEOUT = 1; +static int FROM_LOCAL_RPTR_TIMEOUT = 1; + +/* local repeater modules being recorded */ +/* This is for echotest */ +static struct +{ + time_t last_time; + unsigned char streamid[2]; + int fd; + char file[FILENAME_MAX + 1]; +} recd[3]; + +/* this is for vm */ +static struct +{ + time_t last_time; + unsigned char streamid[2]; + int fd; + char file[FILENAME_MAX + 1]; +} vm[3]; +static unsigned char recbuf[100]; /* 56 or 27, max is 56 */ + +/* the streamids going to remote Gateways from each local module */ +static struct +{ + unsigned char streamid[2]; + struct sockaddr_in toDst4; + time_t last_time; +} to_remote_g2[3]; /* 0=A, 1=B, 2=C */ + +/* input from remote G2 gateway */ +static int g2_sock = -1; +static unsigned char readBuffer2[2000]; /* 56 or 27, max is 56 */ +static struct sockaddr_in fromDst4; + +/* + Incoming data from remote systems + must be fed into our local repeater modules. +*/ +static struct +{ + /* help with header re-generation */ + unsigned char saved_hdr[58]; /* repeater format */ + uint32_t saved_adr; + + unsigned char streamid[2]; + uint32_t adr; + struct sockaddr_in band_addr; + time_t last_time; + unsigned short G2_COUNTER; + unsigned char sequence; +} toRptr[3]; /* 0=A, 1=B, 2=C */ + +/* input from our own local repeater modules */ +static int srv_sock = -1; +static unsigned char readBuffer[2000]; /* 58 or 29 or 32, max is 58 */ +static struct sockaddr_in fromRptr; + +static unsigned char end_of_audio[29]; + +static volatile bool keep_running = true; + +static time_t wd_timer = 0; + +/* send packets to g2_link */ +static struct sockaddr_in plug; + +/* for talking with the irc server */ +static CIRCDDB *ii; + +enum aprs_level +{ + al_none, + al_$1, + al_$2, + al_c1, + al_r1, + al_c2, + al_csum1, + al_csum2, + al_csum3, + al_csum4, + al_data, + al_end +}; + +enum slow_level +{ + sl_first, + sl_second +}; + +static struct +{ + aprs_level al; + unsigned char data[300]; + unsigned int len; + unsigned char buf[6]; + slow_level sl; + bool is_sent; +} aprs_pack[3]; + +/* lock down a stream per band */ +static struct +{ + unsigned char streamID[2]; + time_t last_time; +} aprs_streamID[3]; + +/* text coming from local repeater bands */ +static struct +{ + unsigned char streamID[2]; + unsigned char flags[3]; + char lh_mycall[CALL_SIZE + 1]; + char lh_sfx[5]; + char lh_yrcall[CALL_SIZE + 1]; + char lh_rpt1[CALL_SIZE + 1]; + char lh_rpt2[CALL_SIZE + 1]; + time_t last_time; + char txt[64]; /* Only 20 are used */ + unsigned short txt_cnt; + bool txt_stats_sent; + + char dest_rptr[CALL_SIZE + 1]; + + /* try to process GPS mode: GPRMC and ID */ + char temp_line[256]; + unsigned short temp_line_cnt; + char gprmc[256]; + char gpid[256]; + bool is_gps_sent; + time_t gps_last_time; + + int num_dv_frames; + int num_dv_silent_frames; + int num_bit_errors; + +} band_txt[3]; /* 0=A, 1=B, 2=C */ + +/* Used to validate MYCALL input */ +static regex_t preg; + +/* + CACHE used to cache users, repeaters, + gateways, IP numbers coming from the irc server +*/ +WX_DECLARE_STRING_HASH_MAP(wxString, user2rptr_type); +static user2rptr_type user2rptr_map; + +WX_DECLARE_STRING_HASH_MAP(wxString, rptr2gwy_type); +static rptr2gwy_type rptr2gwy_map; + +WX_DECLARE_STRING_HASH_MAP(wxString, gwy2ip_type); +static gwy2ip_type gwy2ip_map; + +static pthread_mutex_t irc_data_mutex = PTHREAD_MUTEX_INITIALIZER; + +static unsigned short crc_tabccitt[256] = +{ + 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 +}; + +static int g2_open(); +static int srv_open(); +static void calcPFCS(unsigned char *packet, int len); +static void *get_irc_data(void *arg); +static int get_yrcall_rptr_from_cache(char *call, char *arearp_cs, char *zonerp_cs, + char *mod, char *ip, char RoU); +static bool get_yrcall_rptr(char *call, char *arearp_cs, char *zonerp_cs, + char *mod, char *ip, char RoU); +static void traceit(const char *fmt,...); +static int read_config(char *); +static void runit(); +static void sigCatch(int signum); +static void *echotest(void *arg); +static void compute_aprs_hash(); +static void *send_aprs_beacon(void *arg); + +/* aprs functions, borrowed from my retired IRLP node 4201 */ +static bool aprs_write_data(short int rptr_idx, unsigned char *data); +static void aprs_sync_it(short int rptr_idx); +static void aprs_reset(short int rptr_idx); +static unsigned int aprs_get_data(short int rptr_idx, unsigned char *data, unsigned int len); +static void aprs_init(); +static void aprs_open(); +static bool aprs_add_data(short int rptr_idx, unsigned char *data); +static bool aprs_check_data(short int rptr_idx); +static unsigned int aprs_calc_crc(unsigned char* buf, unsigned int len); +static void aprs_select_band(short int rptr_idx, unsigned char *streamID); +static void aprs_process_text(unsigned char *streamID, + unsigned char seq, + unsigned char *buf, + unsigned int len); +static void gps_send(short int rptr_idx); +static bool verify_gps_csum(char *gps_text, char *csum_text); +static void build_aprs_from_gps_and_send(short int rptr_idx); +static ssize_t writen(char *buffer, size_t n); + +static void qrgs_and_maps(); + +static bool resolve_rmt(char *name, int type, struct sockaddr_in *addr); + +static void set_dest_rptr(int mod_ndx, char *dest_rptr); + +extern void dstar_dv_init(); +extern int dstar_dv_decode(const unsigned char *d, int data[3]); + +static bool resolve_rmt(char *name, int type, struct sockaddr_in *addr) +{ + struct addrinfo hints; + struct addrinfo *res; + struct addrinfo *rp; + int rc = 0; + bool found = false; + + memset(&hints, 0x00, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = type; + + rc = getaddrinfo(name, NULL, &hints, &res); + if (rc != 0) + { + traceit("getaddrinfo return error code %d for [%s]\n", rc, name); + return false; + } + + for (rp = res; rp != NULL; rp = rp->ai_next) + { + if ((rp->ai_family == AF_INET) && + (rp->ai_socktype == type)) + { + memcpy(addr, rp->ai_addr, sizeof(struct sockaddr_in)); + found = true; + break; + } + } + freeaddrinfo(res); + return found; +} + +static void set_dest_rptr(int mod_ndx, char *dest_rptr) +{ + FILE *statusfp = NULL; + char statusbuf[1024]; + char *status_local_mod = NULL; + char *status_remote_stm = NULL; + char *status_remote_mod = NULL; + const char *delim = ","; + char *saveptr = NULL; + char *p = NULL; + + statusfp = fopen(STATUS_FILE, "r"); + if (statusfp) + { + setvbuf(statusfp, (char *)NULL, _IOLBF, 0); + + while (fgets(statusbuf, 1020, statusfp) != NULL) + { + p = strchr(statusbuf, '\r'); + if (p) + *p = '\0'; + p = strchr(statusbuf, '\n'); + if (p) + *p = '\0'; + + status_local_mod = strtok_r(statusbuf, delim, &saveptr); + status_remote_stm = strtok_r(NULL, delim, &saveptr); + status_remote_mod = strtok_r(NULL, delim, &saveptr); + + if (!status_local_mod || !status_remote_stm || !status_remote_mod) + continue; + + if ( ((*status_local_mod == 'A') && (mod_ndx == 0)) || + ((*status_local_mod == 'B') && (mod_ndx == 1)) || + ((*status_local_mod == 'C') && (mod_ndx == 2)) ) + { + strncpy(dest_rptr, status_remote_stm, CALL_SIZE); + dest_rptr[7] = *status_remote_mod; + dest_rptr[CALL_SIZE] = '\0'; + break; + } + } + fclose(statusfp); + } + + return; +} + +/* compute checksum */ +static void calcPFCS(unsigned char *packet, int len) +{ + unsigned short crc_dstar_ffff = 0xffff; + unsigned short tmp, short_c; + short int i; + short int low; + short int high; + + if (len == 56) + { + low = 15; + high = 54; + } + else + if (len == 58) + { + low = 17; + high = 56; + } + else + return; + + + for (i = low; i < high ; i++) + { + short_c = 0x00ff & (unsigned short)packet[i]; + tmp = (crc_dstar_ffff & 0x00ff) ^ short_c; + crc_dstar_ffff = (crc_dstar_ffff >> 8) ^ crc_tabccitt[tmp]; + } + crc_dstar_ffff = ~crc_dstar_ffff; + tmp = crc_dstar_ffff; + + if (len == 56) + { + packet[54] = (unsigned char)(crc_dstar_ffff & 0xff); + packet[55] = (unsigned char)((tmp >> 8) & 0xff); + } + else + { + packet[56] = (unsigned char)(crc_dstar_ffff & 0xff); + packet[57] = (unsigned char)((tmp >> 8) & 0xff); + } + + return; +} + +/* log the event */ +static void traceit(const char *fmt,...) +{ + time_t ltime; + struct tm tm; + const short BFSZ = 512; + char buf[BFSZ]; + + time(<ime); + localtime_r(<ime, &tm); + + snprintf(buf,BFSZ - 1,"%02d%02d%02d at %d:%02d:%02d:", + tm.tm_mon+1,tm.tm_mday,tm.tm_year % 100, + tm.tm_hour,tm.tm_min,tm.tm_sec); + + va_list args; + va_start(args,fmt); + vsnprintf(buf + strlen(buf), BFSZ - strlen(buf) -1, fmt, args); + va_end(args); + + fprintf(stdout, "%s", buf); + return; +} + +/* process configuration file */ +static int read_config(char *cfgFile) +{ + short int valid_params = 44; + short int params = 0; + unsigned short i; + + FILE *cnf = NULL; + char inbuf[1024]; + char *p = NULL; + char *ptr; + char *tok = NULL; + const char *delim = ","; + + cnf = fopen(cfgFile, "r"); + if (!cnf) + { + traceit("Failed to open file %s\n", cfgFile); + return 1; + } + + traceit("Reading file %s\n", cfgFile); + while (fgets(inbuf, 1020, cnf) != NULL) + { + if (strchr(inbuf, '#')) + continue; + + p = strchr(inbuf, '\r'); + if (p) + *p = '\0'; + p = strchr(inbuf, '\n'); + if (p) + *p = '\0'; + + p = strchr(inbuf, '='); + if (!p) + continue; + *p = '\0'; + + if (strcmp(inbuf,"OWNER") == 0) + { + memset(OWNER,' ', sizeof(OWNER)); + OWNER[OWNER_SIZE] = '\0'; + strcpy(owner, OWNER); + + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if ((strlen(p + 1) < 3) || (strlen(p + 1) > (OWNER_SIZE - 2))) + traceit("OWNER value [%s] invalid\n", p + 1); + else + { + memcpy(OWNER, p + 1, strlen(p + 1)); + + for (i = 0; i < strlen(OWNER); i++) + OWNER[i] = toupper(OWNER[i]); + + traceit("OWNER=[%s]\n",OWNER); + + for (i = 0; i < strlen(OWNER); i++) + { + if (OWNER[i] == ' ') + { + owner[i] = '\0'; + break; + } + owner[i] = tolower(OWNER[i]); + } + params ++; + } + } + else + if (strcmp(inbuf,"STATUS_FILE") == 0) + { + memset(STATUS_FILE, '\0', sizeof(STATUS_FILE)); + strncpy(STATUS_FILE, p + 1,FILENAME_MAX); + traceit("STATUS_FILE=[%s]\n",STATUS_FILE); + params ++; + } + else + if (strcmp(inbuf,"PACKAGE_REV") == 0) + { + memset(PACKAGE_REV, 0, sizeof(PACKAGE_REV)); + strncpy(PACKAGE_REV, p + 1, 55); + traceit("PACKAGE_REV=[%s]\n", PACKAGE_REV); + params ++; + } + else + if (strcmp(inbuf,"LOCAL_IRC_IP") == 0) + { + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if (strlen(p + 1) < 1) + traceit("LOCAL_IRC_IP value [%s] invalid\n", p + 1); + else + { + strncpy(LOCAL_IRC_IP, p + 1, IP_SIZE); + LOCAL_IRC_IP[IP_SIZE] = '\0'; + traceit("LOCAL_IRC_IP=[%s]\n", LOCAL_IRC_IP); + params ++; + } + } + else + if (strcmp(inbuf,"SEND_QRGS_MAPS") == 0) + { + if (*(p + 1) == 'Y') + SEND_QRGS_MAPS = true; + else + SEND_QRGS_MAPS = false; + traceit("SEND_QRGS_MAPS=[%c]\n", *(p + 1)); + params ++; + } + else + if (strcmp(inbuf,"QTH") == 0) + { + memset(QTH, '\0', sizeof(QTH)); + strncpy(QTH, p + 1, 256); + traceit("QTH=[%s]\n", QTH); + params++; + } + else + if (strcmp(inbuf,"QRG_A") == 0) + { + memset(QRG_A, '\0', sizeof(QRG_A)); + strncpy(QRG_A, p + 1, 256); + traceit("QRG_A=[%s]\n", QRG_A); + params++; + } + else + if (strcmp(inbuf,"QRG_B") == 0) + { + memset(QRG_B, '\0', sizeof(QRG_B)); + strncpy(QRG_B, p + 1, 256); + traceit("QRG_B=[%s]\n", QRG_B); + params++; + } + else + if (strcmp(inbuf,"QRG_C") == 0) + { + memset(QRG_C, '\0', sizeof(QRG_C)); + strncpy(QRG_C, p + 1, 256); + traceit("QRG_C=[%s]\n", QRG_C); + params++; + } + else + if (strcmp(inbuf,"APRS_HOST") == 0) + { + memset(RPTR_ID.aprs_host, '\0', sizeof(RPTR_ID.aprs_host)); + strncpy(RPTR_ID.aprs_host, p + 1, MAXHOSTNAMELEN); + traceit("APRS_HOST=[%s]\n", RPTR_ID.aprs_host); + params++; + } + else + if (strcmp(inbuf,"APRS_PORT") == 0) + { + RPTR_ID.aprs_port = atoi(p + 1); + traceit("APRS_PORT=[%d]\n", RPTR_ID.aprs_port); + params++; + } + else + if (strcmp(inbuf,"APRS_INTERVAL") == 0) + { + RPTR_ID.aprs_interval = atoi(p + 1); + traceit("APRS_INTERVAL=[%d]\n", RPTR_ID.aprs_interval); + if (RPTR_ID.aprs_interval < 40) + { + RPTR_ID.aprs_interval = 40; + traceit("APRS_INTERVAL is low number, re-setting to 40\n"); + } + params++; + } + else + if (strcmp(inbuf,"APRS_FILTER") == 0) + { + memset(RPTR_ID.aprs_filter, '\0', sizeof(RPTR_ID.aprs_filter)); + strncpy(RPTR_ID.aprs_filter, p + 1, 511); + traceit("APRS_filter=[%s]\n", RPTR_ID.aprs_filter); + params++; + } + else + if (strcmp(inbuf, "RPTR_ID_A") == 0) + { + traceit("RPTR_ID_A=[%s]\n", p + 1); + + tok = strtok(p + 1, delim); + if (tok) + { + RPTR_ID.lat[0] = atof(tok); + tok = strtok(NULL, delim); + if (tok) + { + RPTR_ID.lon[0] = atof(tok); + tok = strtok(NULL,delim); + if (tok) + { + RPTR_ID.range[0] = atoi(tok); + tok = strtok(NULL, delim); + if (tok) + { + memset(RPTR_ID.desc[0], '\0', 64); + strncpy(RPTR_ID.desc[0], tok, 63); + params++; + + traceit("ModA: lat=[%lf], lon=[%lf], range=[%d], descr=[%s]\n", + RPTR_ID.lat[0], RPTR_ID.lon[0], + RPTR_ID.range[0], RPTR_ID.desc[0]); + } + else + traceit("ModA:Invalid value for description\n"); + } + else + traceit("ModA:Invalid value for range\n"); + } + else + traceit("ModA:Invalid values for longitude\n"); + } + else + { + params++; + traceit("ModA aprs parameters NOT defined...ok\n"); + } + } + else + if (strcmp(inbuf, "RPTR_ID_B") == 0) + { + traceit("RPTR_ID_B=[%s]\n", p + 1); + + tok = strtok(p + 1, delim); + if (tok) + { + RPTR_ID.lat[1] = atof(tok); + tok = strtok(NULL, delim); + if (tok) + { + RPTR_ID.lon[1] = atof(tok); + tok = strtok(NULL, delim); + if (tok) + { + RPTR_ID.range[1] = atoi(tok); + tok = strtok(NULL, delim); + if (tok) + { + memset(RPTR_ID.desc[1], '\0', 64); + strncpy(RPTR_ID.desc[1], tok, 63); + params++; + + traceit("ModB: lat=[%lf], lon=[%lf], range=[%d], descr=[%s]\n", + RPTR_ID.lat[1], RPTR_ID.lon[1], + RPTR_ID.range[1], RPTR_ID.desc[1]); + } + else + traceit("ModB:Invalid value for description\n"); + } + else + traceit("ModB:Invalid value for range\n"); + } + else + traceit("ModB:Invalid values for longitude\n"); + } + else + { + params++; + traceit("ModB aprs parameters NOT defined...ok\n"); + } + } + else + if (strcmp(inbuf, "RPTR_ID_C") == 0) + { + traceit("RPTR_ID_C=[%s]\n", p + 1); + + tok = strtok(p + 1, delim); + if (tok) + { + RPTR_ID.lat[2] = atof(tok); + tok = strtok(NULL, delim); + if (tok) + { + RPTR_ID.lon[2] = atof(tok); + tok = strtok(NULL, delim); + if (tok) + { + RPTR_ID.range[2] = atoi(tok); + tok = strtok(NULL, delim); + if (tok) + { + memset(RPTR_ID.desc[2], '\0', 64); + strncpy(RPTR_ID.desc[2], tok, 63); + params++; + + traceit("ModC: lat=[%lf], lon=[%lf], range=[%d], descr=[%s]\n", + RPTR_ID.lat[2], RPTR_ID.lon[2], + RPTR_ID.range[2], RPTR_ID.desc[2]); + } + else + traceit("ModC:Invalid value for description\n"); + } + else + traceit("ModC:Invalid value for range\n"); + } + else + traceit("ModC:Invalid values for longitude\n"); + } + else + { + params++; + traceit("ModC aprs parameters NOT defined...ok\n"); + } + } + else + if (strcmp(inbuf,"G2_EXTERNAL_IP") == 0) + { + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if (strlen(p + 1) < 1) + traceit("G2_EXTERNAL_IP value [%s] invalid\n", p + 1); + else + { + strncpy(G2_EXTERNAL_IP, p + 1, IP_SIZE); + G2_EXTERNAL_IP[IP_SIZE] = '\0'; + traceit("G2_EXTERNAL_IP=[%s]\n", G2_EXTERNAL_IP); + params ++; + } + } + else + if (strcmp(inbuf,"G2_EXTERNAL_PORT") == 0) + { + G2_EXTERNAL_PORT = atoi(p + 1); + traceit("G2_EXTERNAL_PORT=[%d]\n",G2_EXTERNAL_PORT); + params ++; + } + else + if (strcmp(inbuf,"G2_INTERNAL_IP") == 0) + { + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if (strlen(p + 1) < 1) + traceit("G2_INTERNAL_IP value [%s] invalid\n", p + 1); + else + { + strncpy(G2_INTERNAL_IP, p + 1, IP_SIZE); + G2_INTERNAL_IP[IP_SIZE] = '\0'; + traceit("G2_INTERNAL_IP=[%s]\n", G2_INTERNAL_IP); + params ++; + } + } + else + if (strcmp(inbuf,"G2_INTERNAL_PORT") == 0) + { + G2_INTERNAL_PORT = atoi(p + 1); + traceit("G2_INTERNAL_PORT=[%d]\n",G2_INTERNAL_PORT); + params ++; + } + else + if (strcmp(inbuf,"TO_G2_LINK_IP") == 0) + { + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if (strlen(p + 1) < 1) + traceit("TO_G2_LINK_IP value [%s] invalid\n", p + 1); + else + { + strncpy(TO_G2_LINK_IP, p + 1, IP_SIZE); + TO_G2_LINK_IP[IP_SIZE] = '\0'; + traceit("TO_G2_LINK_IP=[%s]\n", TO_G2_LINK_IP); + params ++; + } + } + else + if (strcmp(inbuf,"TO_G2_LINK_PORT") == 0) + { + TO_G2_LINK_PORT = atoi(p + 1); + traceit("TO_G2_LINK_PORT=[%d]\n",TO_G2_LINK_PORT); + params ++; + } + else + if (strcmp(inbuf,"TO_RPTR_IP_A") == 0) + { + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if (strlen(p + 1) < 1) + traceit("TO_RPTR_IP_A value [%s] invalid\n", p + 1); + else + { + strncpy(TO_RPTR_IP[0], p + 1, IP_SIZE); + TO_RPTR_IP[0][IP_SIZE] = '\0'; + traceit("TO_RPTR_IP_A=[%s]\n", TO_RPTR_IP[0]); + params ++; + } + } + else + if (strcmp(inbuf,"TO_RPTR_PORT_A") == 0) + { + TO_RPTR_PORT[0] = atoi(p + 1); + traceit("TO_RPTR_PORT_A=[%d]\n",TO_RPTR_PORT[0]); + params ++; + } + else + if (strcmp(inbuf,"TO_RPTR_IP_B") == 0) + { + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if (strlen(p + 1) < 1) + traceit("TO_RPTR_IP_B value [%s] invalid\n", p + 1); + else + { + strncpy(TO_RPTR_IP[1], p + 1, IP_SIZE); + TO_RPTR_IP[1][IP_SIZE] = '\0'; + traceit("TO_RPTR_IP_B=[%s]\n", TO_RPTR_IP[1]); + params ++; + } + } + else + if (strcmp(inbuf,"TO_RPTR_PORT_B") == 0) + { + TO_RPTR_PORT[1] = atoi(p + 1); + traceit("TO_RPTR_PORT_B=[%d]\n",TO_RPTR_PORT[1]); + params ++; + } + else + if (strcmp(inbuf,"TO_RPTR_IP_C") == 0) + { + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if (strlen(p + 1) < 1) + traceit("TO_RPTR_IP_C value [%s] invalid\n", p + 1); + else + { + strncpy(TO_RPTR_IP[2], p + 1, IP_SIZE); + TO_RPTR_IP[2][IP_SIZE] = '\0'; + traceit("TO_RPTR_IP_C=[%s]\n", TO_RPTR_IP[2]); + params ++; + } + } + else + if (strcmp(inbuf,"TO_RPTR_PORT_C") == 0) + { + TO_RPTR_PORT[2] = atoi(p + 1); + traceit("TO_RPTR_PORT_C=[%d]\n",TO_RPTR_PORT[2]); + params ++; + } + else + if (strcmp(inbuf,"QSO_DETAILS") == 0) + { + if (*(p + 1) == 'Y') + QSO_DETAILS = true; + else + QSO_DETAILS = false; + traceit("QSO_DETAILS=[%c]\n", *(p + 1)); + params ++; + } + else + if (strcmp(inbuf,"IRC_DEBUG") == 0) + { + if (*(p + 1) == 'Y') + IRC_DEBUG = true; + else + IRC_DEBUG = false; + traceit("IRC_DEBUG=[%c]\n", *(p + 1)); + params ++; + } + else + if (strcmp(inbuf,"DTMF_DEBUG") == 0) + { + if (*(p + 1) == 'Y') + DTMF_DEBUG = true; + else + DTMF_DEBUG = false; + traceit("DTMF_DEBUG=[%c]\n", *(p + 1)); + params ++; + } + else + if (strcmp(inbuf,"REGEN_HDR") == 0) + { + if (*(p + 1) == 'Y') + REGEN_HDR = true; + else + REGEN_HDR = false; + traceit("REGEN_HDR=[%c]\n", *(p + 1)); + params ++; + } + else + if (strcmp(inbuf,"SEND_APRS") == 0) + { + if (*(p + 1) == 'Y') + SEND_APRS = true; + else + SEND_APRS = false; + traceit("SEND_APRS=[%c]\n", *(p + 1)); + params ++; + } + else + if (strcmp(inbuf,"ECHOTEST_DIR") == 0) + { + memset(ECHOTEST_DIR, '\0', sizeof(ECHOTEST_DIR)); + strncpy(ECHOTEST_DIR, p + 1, FILENAME_MAX); + traceit("ECHOTEST_DIR=[%s]\n", ECHOTEST_DIR); + params ++; + } + else + if (strcmp(inbuf, "PLAY_WAIT") == 0) + { + PLAY_WAIT = atoi(p + 1); + if ((PLAY_WAIT > 10) || (PLAY_WAIT < 1)) + PLAY_WAIT = 1; + traceit("PLAY_WAIT=[%d]\n", PLAY_WAIT); + params ++; + } + else + if (strcmp(inbuf, "PLAY_DELAY") == 0) + { + PLAY_DELAY = atoi(p + 1); + if ((PLAY_DELAY > 50) || (PLAY_DELAY < 10)) + PLAY_DELAY = 20; + traceit("PLAY_DELAY=[%d]\n", PLAY_DELAY); + params ++; + } + else + if (strcmp(inbuf,"ECHOTEST_REC_TIMEOUT") == 0) + { + ECHOTEST_REC_TIMEOUT = atoi(p + 1); + traceit("ECHOTEST_REC_TIMEOUT=[%d]\n", ECHOTEST_REC_TIMEOUT); + params ++; + } + else + if (strcmp(inbuf,"VOICEMAIL_REC_TIMEOUT") == 0) + { + VOICEMAIL_REC_TIMEOUT = atoi(p + 1); + traceit("VOICEMAIL_REC_TIMEOUT=[%d]\n", VOICEMAIL_REC_TIMEOUT); + params ++; + } + else + if (strcmp(inbuf,"FROM_REMOTE_G2_TIMEOUT") == 0) + { + FROM_REMOTE_G2_TIMEOUT = atoi(p + 1); + traceit("FROM_REMOTE_G2_TIMEOUT=[%d]\n", FROM_REMOTE_G2_TIMEOUT); + params ++; + } + else + if (strcmp(inbuf,"FROM_LOCAL_RPTR_TIMEOUT") == 0) + { + FROM_LOCAL_RPTR_TIMEOUT = atoi(p + 1); + traceit("FROM_LOCAL_RPTR_TIMEOUT=[%d]\n", FROM_LOCAL_RPTR_TIMEOUT); + params ++; + } + else + if (strcmp(inbuf,"IRC_DDB_HOST") == 0) + { + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if (strlen(p + 1) < 1) + traceit("IRC_DDB_HOST value [%s] invalid\n", p + 1); + else + { + memset(IRC_DDB_HOST, '\0', sizeof(IRC_DDB_HOST)); + strncpy(IRC_DDB_HOST, p + 1, sizeof(IRC_DDB_HOST) - 1); + traceit("IRC_DDB_HOST=[%s]\n", IRC_DDB_HOST); + params ++; + } + } + else + if (strcmp(inbuf,"IRC_DDB_PORT") == 0) + { + IRC_DDB_PORT = atoi(p + 1); + traceit("IRC_DDB_PORT=[%d]\n",IRC_DDB_PORT); + params ++; + } + else + if (strcmp(inbuf,"IRC_PASS") == 0) + { + memset(IRC_PASS, '\0', sizeof(IRC_PASS)); + strncpy(IRC_PASS, p + 1, sizeof(IRC_PASS) - 1); + params ++; + } + else + if (strcmp(inbuf,"DTMF_DIR") == 0) + { + memset(DTMF_DIR, '\0', sizeof(DTMF_DIR)); + strncpy(DTMF_DIR, p + 1, FILENAME_MAX); + traceit("DTMF_DIR=[%s]\n", DTMF_DIR); + params ++; + } + } + fclose(cnf); + + if (params != valid_params) + { + traceit("Configuration file %s invalid\n",cfgFile); + return 1; + } + return 0; +} + +/* Create the 40000 external port */ +static int g2_open() +{ + struct sockaddr_in sin; + + g2_sock = socket(PF_INET,SOCK_DGRAM,0); + if (g2_sock == -1) + { + traceit("Failed to create g2 socket,errno=%d\n",errno); + return 1; + } + fcntl(g2_sock,F_SETFL,O_NONBLOCK); + + memset(&sin,0,sizeof(struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_port = htons(G2_EXTERNAL_PORT); + sin.sin_addr.s_addr = inet_addr(G2_EXTERNAL_IP); + + if (bind(g2_sock,(struct sockaddr *)&sin,sizeof(struct sockaddr_in)) != 0) + { + traceit("Failed to bind g2 socket on port %d, errno=%d\n",G2_EXTERNAL_PORT,errno); + close(g2_sock); + g2_sock = -1; + return 1; + } + return 0; +} + +/* Create the 19000 internal port */ +static int srv_open() +{ + struct sockaddr_in sin; + + srv_sock = socket(PF_INET,SOCK_DGRAM,0); + if (srv_sock == -1) + { + traceit("Failed to create srv socket,errno=%d\n",errno); + return 1; + } + fcntl(srv_sock,F_SETFL,O_NONBLOCK); + + memset(&sin,0,sizeof(struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_port = htons(G2_INTERNAL_PORT); + sin.sin_addr.s_addr = inet_addr(G2_INTERNAL_IP); + + if (bind(srv_sock,(struct sockaddr *)&sin,sizeof(struct sockaddr_in)) != 0) + { + traceit("Failed to bind srv socket on port %d, errno=%d\n", + G2_INTERNAL_PORT,errno); + close(srv_sock); + srv_sock = -1; + return 1; + } + + return 0; +} + +/* receive data from the irc server and save it */ +static void *get_irc_data(void *arg) +{ + struct timespec req; + + wxString user; + wxString rptr; + wxString gateway; + wxString ipaddr; + DSTAR_PROTOCOL proto; + IRCDDB_RESPONSE_TYPE type; + int rc = 0; + struct sigaction act; + short threshold = 0; + short THRESHOLD_MAX = 100; + short last_status = 0; + + arg = arg; + + act.sa_handler = sigCatch; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + if (sigaction(SIGTERM, &act, 0) != 0) + { + traceit("sigaction-TERM failed, error=%d\n", errno); + traceit("get_irc_data thread exiting...\n"); + keep_running = false; + pthread_exit(NULL); + } + if (sigaction(SIGINT, &act, 0) != 0) + { + traceit("sigaction-INT failed, error=%d\n", errno); + traceit("get_irc_data thread exiting...\n"); + keep_running = false; + pthread_exit(NULL); + } + if (sigaction(SIGPIPE, &act, 0) != 0) + { + traceit("sigaction-PIPE failed, error=%d\n", errno); + traceit("get_irc_data thread exiting...\n"); + keep_running = false; + pthread_exit(NULL); + } + + while (keep_running) + { + threshold ++; + if (threshold >= THRESHOLD_MAX) + { + rc = ii->getConnectionState(); + if ((rc == 0) || (rc == 10)) + { + if (last_status != 0) + { + traceit("irc status=%d, probable disconnect...\n", rc); + last_status = 0; + } + } + else + if (rc == 7) + { + if (last_status != 2) + { + traceit("irc status=%d, probable connect...\n", rc); + last_status = 2; + } + } + else + { + if (last_status != 1) + { + traceit("irc status=%d, probable connect...\n", rc); + last_status = 1; + } + } + threshold = 0; + } + + while (((type = ii->getMessageType()) != IDRT_NONE) && keep_running) + { + if (type == IDRT_USER) + { + ii->receiveUser(user, rptr, gateway, ipaddr); + if (!user.IsEmpty()) + { + if (!rptr.IsEmpty() && !gateway.IsEmpty() && !ipaddr.IsEmpty()) + { + if (IRC_DEBUG) + traceit("C-u:%s,%s,%s,%s\n", user.mb_str(), rptr.mb_str(), gateway.mb_str(), ipaddr.mb_str()); + + pthread_mutex_lock(&irc_data_mutex); + + user2rptr_map[user] = rptr; + rptr2gwy_map[rptr] = gateway; + gwy2ip_map[gateway] = ipaddr; + + pthread_mutex_unlock(&irc_data_mutex); + + // traceit("%d users, %d repeaters, %d gateways\n", user2rptr_map.size(), rptr2gwy_map.size(), gwy2ip_map.size()); + + } + } + } + else + if (type == IDRT_REPEATER) + { + ii->receiveRepeater(rptr, gateway, ipaddr, proto); + if (!rptr.IsEmpty()) + { + if (!gateway.IsEmpty() && !ipaddr.IsEmpty()) + { + if (IRC_DEBUG) + traceit("C-r:%s,%s,%s\n", rptr.mb_str(), gateway.mb_str(), ipaddr.mb_str()); + + pthread_mutex_lock(&irc_data_mutex); + + rptr2gwy_map[rptr] = gateway; + gwy2ip_map[gateway] = ipaddr; + + pthread_mutex_unlock(&irc_data_mutex); + + // traceit("%d repeaters, %d gateways\n", rptr2gwy_map.size(), gwy2ip_map.size()); + + } + } + } + else + if (type == IDRT_GATEWAY) + { + ii->receiveGateway(gateway, ipaddr, proto); + if (!gateway.IsEmpty() && !ipaddr.IsEmpty()) + { + if (IRC_DEBUG) + traceit("C-g:%s,%s\n", gateway.mb_str(),ipaddr.mb_str()); + + pthread_mutex_lock(&irc_data_mutex); + + gwy2ip_map[gateway] = ipaddr; + + pthread_mutex_unlock(&irc_data_mutex); + + // traceit("%d gateways\n", gwy2ip_map.size()); + + } + } + } + req.tv_sec = 0; + req.tv_nsec = 500000000; // 500 milli + nanosleep(&req, NULL); + } + traceit("get_irc_data thread exiting...\n"); + pthread_exit(NULL); +} + +/* return codes: 0=OK(found it), 1=TRY AGAIN, 2=FAILED(bad data) */ +static int get_yrcall_rptr_from_cache(char *call, char *arearp_cs, char *zonerp_cs, + char *mod, char *ip, char RoU) +{ + user2rptr_type::iterator user_pos = user2rptr_map.end(); + rptr2gwy_type::iterator rptr_pos = rptr2gwy_map.end(); + gwy2ip_type::iterator gwy_pos = gwy2ip_map.end(); + char temp[CALL_SIZE + 1]; + + memset(arearp_cs, ' ', CALL_SIZE); arearp_cs[CALL_SIZE] = '\0'; + memset(zonerp_cs, ' ', CALL_SIZE); zonerp_cs[CALL_SIZE] = '\0'; + *mod = ' '; + + /* find the user in the CACHE */ + if (RoU == 'U') + { + user_pos = user2rptr_map.find(call); + if (user_pos != user2rptr_map.end()) + { + memcpy(arearp_cs, user_pos->second.c_str(), 7); + *mod = user_pos->second.c_str()[7]; + } + else + return 1; + } + else + if (RoU == 'R') + { + memcpy(arearp_cs, call, 7); + *mod = call[7]; + } + else + { + traceit("Invalid specification %c for RoU\n", RoU); + return 2; + } + + if ((*mod != 'A') && (*mod != 'B') && (*mod != 'C')) + { + traceit("Invalid module %c\n", *mod); + return 2; + } + + memcpy(temp, arearp_cs, 7); temp[7] = *mod; + temp[CALL_SIZE] = '\0'; + + rptr_pos = rptr2gwy_map.find(temp); + if (rptr_pos != rptr2gwy_map.end()) + { + memcpy(zonerp_cs, rptr_pos->second.c_str(), CALL_SIZE); + zonerp_cs[CALL_SIZE] = '\0'; + + gwy_pos = gwy2ip_map.find(zonerp_cs); + if (gwy_pos != gwy2ip_map.end()) + { + strncpy(ip, gwy_pos->second.c_str(),IP_SIZE); + ip[IP_SIZE] = '\0'; + return 0; + } + else + { + /* traceit("Could not find IP for Gateway %s\n", zonerp_cs); */ + return 1; + } + } + else + { + /* traceit("Could not find Gateway for repeater %s\n", temp); */ + return 1; + } + + return 2; +} + +static bool get_yrcall_rptr(char *call, char *arearp_cs, char *zonerp_cs, + char *mod, char *ip, char RoU) +{ + int rc = 2; + int status = 0; + + pthread_mutex_lock(&irc_data_mutex); + rc = get_yrcall_rptr_from_cache(call, arearp_cs, zonerp_cs, mod, ip, RoU); + pthread_mutex_unlock(&irc_data_mutex); + if (rc == 0) + return true; + else + if (rc == 2) + return false; + + /* at this point, the data is not in cache */ + /* report the irc status */ + status = ii->getConnectionState(); + // traceit("irc status=%d\n", status); + if (status != 7) + { + traceit("Remote irc database not ready, irc status is not 7, try again\n"); + return false; + } + + /* request data from irc server */ + if (RoU == 'U') + { + traceit("User [%s] not in local cache, try again\n", call); + /*** YRCALL=KJ4NHFBL ***/ + if (((call[6] == 'A') || (call[6] == 'B') || (call[6] == 'C')) && + (call[7] == LINK_CODE)) + traceit("If this was a gateway link request, that is ok\n"); + + if (!ii->findUser(call)) + { + traceit("findUser(%s): Network error\n", call); + return false; + } + } + else + if (RoU == 'R') + { + traceit("Repeater [%s] not in local cache, try again\n", call); + if (!ii->findRepeater(call)) + { + traceit("findRepeater(%s): Network error\n", call); + return false; + } + } + + return false; +} + +/* signal catching function */ +static void sigCatch(int signum) +{ + /* do NOT do any serious work here */ + if ((signum == SIGTERM) || (signum == SIGINT)) + keep_running = false; + + return; +} + +/* run the main loop for g2_ircddb */ +static void runit() +{ + fd_set fdset; + struct timeval tv; + + socklen_t fromlen; + int recvlen; + int recvlen2; + + short i,j; + + bool result = false; + int mycall_valid = REG_NOERROR; + char temp_radio_user[CALL_SIZE + 1]; + char temp_mod; + time_t t_now; + + char arearp_cs[CALL_SIZE + 1]; + char zonerp_cs[CALL_SIZE + 1]; + char ip[IP_SIZE + 1]; + + int rc = 0; + char tempfile[FILENAME_MAX + 1]; + pthread_t echo_thread; + pthread_attr_t attr; + long num_recs = 0L; + short int rec_len = 56; + + pthread_t aprs_beacon_thread; + pthread_t irc_data_thread; + + /* START: TEXT crap */ + bool new_group[3] = { true, true, true }; + int header_type = 0; + short to_print[3] = { 0, 0, 0 }; + bool ABC_grp[3] = { false, false, false }; + bool C_seen[3] = { false, false, false }; + unsigned char tmp_txt[3]; + /* END: TEXT crap */ + + int ber_data[3]; + int ber_errs; + + int max_nfds = 0; + + dstar_dv_init(); + + if (g2_sock > max_nfds) + max_nfds = g2_sock; + if (srv_sock > max_nfds) + max_nfds = srv_sock; + traceit("g2=%d, srv=%d, MAX+1=%d\n", + g2_sock, srv_sock, max_nfds + 1); + + /* start the beacon thread */ + if (SEND_APRS) + { + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&aprs_beacon_thread,&attr,send_aprs_beacon,(void *)0); + if (rc != 0) + traceit("failed to start the aprs beacon thread\n"); + else + traceit("APRS beacon thread started\n"); + pthread_attr_destroy(&attr); + } + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&irc_data_thread,&attr,get_irc_data,(void *)0); + if (rc != 0) + { + traceit("failed to start the get_irc_data thread\n"); + keep_running = false; + } + else + traceit("get_irc_data thread started\n"); + pthread_attr_destroy(&attr); + + while (keep_running) + { + time(&t_now); + if ((t_now - wd_timer) >= 900) + { + ii->kickWatchdog(PACKAGE_REV); + wd_timer = t_now; + } + + for (i = 0; i < 3; i++) + { + /* echotest recording timed out? */ + if (recd[i].last_time != 0) + { + time(&t_now); + if ((t_now - recd[i].last_time) > ECHOTEST_REC_TIMEOUT) + { + traceit("Inactivity on echotest recording mod %d, removing stream id=%d,%d\n", + i,recd[i].streamid[0], recd[i].streamid[1]); + + recd[i].streamid[0] = 0x00; + recd[i].streamid[1] = 0x00; + recd[i].last_time = 0; + close(recd[i].fd); recd[i].fd = -1; + // traceit("Closed echotest audio file:[%s]\n", recd[i].file); + + /* START: echotest thread setup */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&echo_thread, &attr, echotest, (void *)recd[i].file); + if (rc != 0) + { + traceit("failed to start echotest thread\n"); + /* when the echotest thread runs, it deletes the file, + Because the echotest thread did NOT start, we delete the file here + */ + unlink(recd[i].file); + } + pthread_attr_destroy(&attr); + /* END: echotest thread setup */ + } + } + + /* voicemail recording timed out? */ + if (vm[i].last_time != 0) + { + time(&t_now); + if ((t_now - vm[i].last_time) > VOICEMAIL_REC_TIMEOUT) + { + traceit("Inactivity on voicemail recording mod %d, removing stream id=%d,%d\n", + i,vm[i].streamid[0], vm[i].streamid[1]); + + vm[i].streamid[0] = 0x00; + vm[i].streamid[1] = 0x00; + vm[i].last_time = 0; + close(vm[i].fd); vm[i].fd = -1; + // traceit("Closed voicemail audio file:[%s]\n", vm[i].file); + } + } + + /* any stream going to local repeater timed out? */ + if (toRptr[i].last_time != 0) + { + time(&t_now); + /* + The stream can be from a cross-band, or from a remote system, + so we could use either FROM_LOCAL_RPTR_TIMEOUT or FROM_REMOTE_G2_TIMEOUT + but FROM_REMOTE_G2_TIMEOUT makes more sense, probably is a bigger number + */ + if ((t_now - toRptr[i].last_time) > FROM_REMOTE_G2_TIMEOUT) + { + traceit("Inactivity to local rptr mod index %d, removing stream id %d,%d\n", + i, toRptr[i].streamid[0], toRptr[i].streamid[1]); + /* + Send end_of_audio to local repeater. + Let the repeater re-initialize + */ + end_of_audio[5] = (unsigned char)(toRptr[i].G2_COUNTER & 0xff); + end_of_audio[4] = (unsigned char)((toRptr[i].G2_COUNTER >> 8) & 0xff); + if (i == 0) + end_of_audio[13] = 0x03; + else + if (i == 1) + end_of_audio[13] = 0x01; + else + end_of_audio[13] = 0x02; + end_of_audio[14] = toRptr[i].streamid[0]; + end_of_audio[15] = toRptr[i].streamid[1]; + end_of_audio[16] = toRptr[i].sequence | 0x40; + + for (j = 0; j < 2; j++) + sendto(srv_sock, (char *)end_of_audio,29,0, + (struct sockaddr *)&toRptr[i].band_addr, + sizeof(struct sockaddr_in)); + + toRptr[i].G2_COUNTER ++; + + toRptr[i].streamid[0] = '\0'; + toRptr[i].streamid[1] = '\0'; + toRptr[i].adr = 0; + toRptr[i].last_time = 0; + } + } + + /* any stream coming from local repeater timed out ? */ + if (band_txt[i].last_time != 0) + { + time(&t_now); + if ((t_now - band_txt[i].last_time) > FROM_LOCAL_RPTR_TIMEOUT) + { + /* This local stream never went to a remote system, so trace the timeout */ + if (to_remote_g2[i].toDst4.sin_addr.s_addr == 0) + traceit("Inactivity from local rptr band %d, removing stream id %d,%d\n", + i, band_txt[i].streamID[0], band_txt[i].streamID[1]); + + band_txt[i].streamID[0] = 0x00; + band_txt[i].streamID[1] = 0x00; + band_txt[i].flags[0] = 0x00; + band_txt[i].flags[1] = 0x00; + band_txt[i].flags[2] = 0x00; + band_txt[i].lh_mycall[0] = '\0'; + band_txt[i].lh_sfx[0] = '\0'; + band_txt[i].lh_yrcall[0] = '\0'; + band_txt[i].lh_rpt1[0] = '\0'; + band_txt[i].lh_rpt2[0] = '\0'; + + band_txt[i].last_time = 0; + + band_txt[i].txt[0] = '\0'; + band_txt[i].txt_cnt = 0; + + band_txt[i].dest_rptr[0] = '\0'; + + band_txt[i].num_dv_frames = 0; + band_txt[i].num_dv_silent_frames = 0; + band_txt[i].num_bit_errors = 0; + } + } + + /* any stream from local repeater to a remote gateway timed out ? */ + if (to_remote_g2[i].toDst4.sin_addr.s_addr != 0) + { + time(&t_now); + if ((t_now - to_remote_g2[i].last_time) > FROM_LOCAL_RPTR_TIMEOUT) + { + traceit("Inactivity from local rptr mod %d, removing stream id %d,%d\n", + i, to_remote_g2[i].streamid[0], to_remote_g2[i].streamid[1]); + + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].streamid[0] = '\0'; + to_remote_g2[i].streamid[1] = '\0'; + to_remote_g2[i].last_time = 0; + } + } + } + + /* wait 20 ms max */ + FD_ZERO(&fdset); + FD_SET(g2_sock,&fdset); + FD_SET(srv_sock,&fdset); + tv.tv_sec = 0; + tv.tv_usec = 20000; /* 20 ms */ + (void)select(max_nfds + 1,&fdset,0,0,&tv); + + /* process packets coming from remote G2 */ + if (FD_ISSET(g2_sock, &fdset)) + { + fromlen = sizeof(struct sockaddr_in); + recvlen2 = recvfrom(g2_sock,(char *)readBuffer2,2000, + 0,(struct sockaddr *)&fromDst4,&fromlen); + + if ( ((recvlen2 == 56) || + (recvlen2 == 27)) && + (memcmp(readBuffer2, "DSVT", 4) == 0) && + ((readBuffer2[4] == 0x10) || /* header */ + (readBuffer2[4] == 0x20)) && /* details */ + (readBuffer2[8] == 0x20)) /* voice type */ + { + if (recvlen2 == 56) + { + + /* + Find out the local repeater module IP/port + to send the data to + */ + i = -1; + if (readBuffer2[25] == 'A') + i = 0; + else + if (readBuffer2[25] == 'B') + i = 1; + else + if (readBuffer2[25] == 'C') + i = 2; + + /* valid repeater module? */ + if (i >= 0) + { + /* + toRptr[i] is active if a remote system is talking to it or + toRptr[i] is receiving data from a cross-band + */ + if ((toRptr[i].last_time == 0) && (band_txt[i].last_time == 0) && + ((readBuffer2[15] == 0x00) || + (readBuffer2[15] == 0x01) || /* allow the announcements from g2_link */ + (readBuffer2[15] == 0x08) || + (readBuffer2[15] == 0x20) || + (readBuffer2[15] == 0x28) || + (readBuffer2[15] == 0x40))) + { + if (QSO_DETAILS) + traceit("START from g2: streamID=%d,%d, flags=%02x:%02x:%02x, my=%.8s, sfx=%.4s, ur=%.8s, rpt1=%.8s, rpt2=%.8s, %d bytes fromIP=%s\n", + readBuffer2[12],readBuffer2[13], + readBuffer2[15], readBuffer2[16], readBuffer2[17], + &readBuffer2[42], + &readBuffer2[50], &readBuffer2[34], + &readBuffer2[18], &readBuffer2[26], + recvlen2,inet_ntoa(fromDst4.sin_addr)); + + memcpy(readBuffer,"DSTR", 4); + readBuffer[5] = (unsigned char)(toRptr[i].G2_COUNTER & 0xff); + readBuffer[4] = (unsigned char)((toRptr[i].G2_COUNTER >> 8) & 0xff); + readBuffer[6] = 0x73; + readBuffer[7] = 0x12; + readBuffer[8] = 0x00; + readBuffer[9] = 0x30; + readBuffer[10] = 0x20; + memcpy(readBuffer + 11, readBuffer2 + 9, 47); + sendto(srv_sock, (char *)readBuffer,58,0, + (struct sockaddr *)&toRptr[i].band_addr, + sizeof(struct sockaddr_in)); + + /* save the header */ + memcpy(toRptr[i].saved_hdr, readBuffer, 58); + toRptr[i].saved_adr = fromDst4.sin_addr.s_addr; + + /* This is the active streamid */ + toRptr[i].streamid[0] = readBuffer2[12]; + toRptr[i].streamid[1] = readBuffer2[13]; + toRptr[i].adr = fromDst4.sin_addr.s_addr; + + /* time it, in case stream times out */ + time(&toRptr[i].last_time); + + /* bump the G2 counter */ + toRptr[i].G2_COUNTER ++; + + toRptr[i].sequence = readBuffer[16]; + } + } + } + else + { + if ((readBuffer2[14] & 0x40) != 0) + { + if (QSO_DETAILS) + traceit("END from g2: streamID=%d,%d, %d bytes from IP=%s\n", + readBuffer2[12],readBuffer2[13], + recvlen2,inet_ntoa(fromDst4.sin_addr)); + } + + /* find out which repeater module to send the data to */ + for (i = 0; i < 3; i++) + { + /* streamid match ? */ + if ((memcmp(toRptr[i].streamid, readBuffer2 + 12, 2) == 0) && + (toRptr[i].adr == fromDst4.sin_addr.s_addr)) + { + memcpy(readBuffer,"DSTR", 4); + readBuffer[5] = (unsigned char)(toRptr[i].G2_COUNTER & 0xff); + readBuffer[4] = (unsigned char)((toRptr[i].G2_COUNTER >> 8) & 0xff); + readBuffer[6] = 0x73; + readBuffer[7] = 0x12; + readBuffer[8] = 0x00; + readBuffer[9] = 0x13; + readBuffer[10] = 0x20; + memcpy(readBuffer + 11, readBuffer2 + 9, 18); + + sendto(srv_sock, (char *)readBuffer,29,0, + (struct sockaddr *)&toRptr[i].band_addr, + sizeof(struct sockaddr_in)); + + /* timeit */ + time(&toRptr[i].last_time); + + /* bump G2 counter */ + toRptr[i].G2_COUNTER ++; + + toRptr[i].sequence = readBuffer[16]; + + /* End of stream ? */ + if ((readBuffer2[14] & 0x40) != 0) + { + /* clear the saved header */ + memset(toRptr[i].saved_hdr, 0, sizeof(toRptr[i].saved_hdr)); + toRptr[i].saved_adr = 0; + + toRptr[i].last_time = 0; + toRptr[i].streamid[0] = '\0'; + toRptr[i].streamid[1] = '\0'; + toRptr[i].adr = 0; + } + break; + } + } + + /* no match ? */ + if ((i == 3) && REGEN_HDR) + { + /* check if this a continuation of audio that timed out */ + + if ((readBuffer2[14] & 0x40) != 0) + ; /* we do not care about end-of-QSO */ + else + { + /* for which repeater this stream has timed out ? */ + for (i = 0; i < 3; i++) + { + /* match saved stream ? */ + if ((memcmp(toRptr[i].saved_hdr + 14, readBuffer2 + 12, 2) == 0) && + (toRptr[i].saved_adr == fromDst4.sin_addr.s_addr)) + { + /* repeater module is inactive ? */ + if ((toRptr[i].last_time == 0) && (band_txt[i].last_time == 0)) + { + traceit("Re-generating header for streamID=%d,%d\n", readBuffer2[12],readBuffer2[13]); + + toRptr[i].saved_hdr[5] = (unsigned char)(toRptr[i].G2_COUNTER & 0xff); + toRptr[i].saved_hdr[4] = (unsigned char)((toRptr[i].G2_COUNTER >> 8) & 0xff); + + /* re-generate/send the header */ + sendto(srv_sock, (char *)toRptr[i].saved_hdr,58,0, + (struct sockaddr *)&toRptr[i].band_addr, + sizeof(struct sockaddr_in)); + + /* bump G2 counter */ + toRptr[i].G2_COUNTER ++; + + /* send this audio packet to repeater */ + memcpy(readBuffer,"DSTR", 4); + readBuffer[5] = (unsigned char)(toRptr[i].G2_COUNTER & 0xff); + readBuffer[4] = (unsigned char)((toRptr[i].G2_COUNTER >> 8) & 0xff); + readBuffer[6] = 0x73; + readBuffer[7] = 0x12; + readBuffer[8] = 0x00; + readBuffer[9] = 0x13; + readBuffer[10] = 0x20; + memcpy(readBuffer + 11, readBuffer2 + 9, 18); + + sendto(srv_sock, (char *)readBuffer,29,0, + (struct sockaddr *)&toRptr[i].band_addr, + sizeof(struct sockaddr_in)); + + /* make sure that any more audio arriving will be accepted */ + toRptr[i].streamid[0] = readBuffer2[12]; + toRptr[i].streamid[1] = readBuffer2[13]; + toRptr[i].adr = fromDst4.sin_addr.s_addr; + + /* time it, in case stream times out */ + time(&toRptr[i].last_time); + + /* bump the G2 counter */ + toRptr[i].G2_COUNTER ++; + + toRptr[i].sequence = readBuffer[16]; + + } + break; + } + } + } + } + } + } + FD_CLR (g2_sock,&fdset); + } + + /* process data coming from local repeater modules */ + if (FD_ISSET(srv_sock, &fdset)) + { + fromlen = sizeof(struct sockaddr_in); + recvlen = recvfrom(srv_sock,(char *)readBuffer,2000, + 0,(struct sockaddr *)&fromRptr,&fromlen); + + /* DV */ + if ( ((recvlen == 58) || + (recvlen == 29) || + (recvlen == 32)) && + (readBuffer[6] == 0x73) && + (readBuffer[7] == 0x12) && + (memcmp(readBuffer,"DSTR", 4) == 0) && + (readBuffer[10] == 0x20) && + (readBuffer[8] == 0x00) && + ((readBuffer[9] == 0x30) || /* 48 bytes follow */ + (readBuffer[9] == 0x13) || /* 19 bytes follow */ + (readBuffer[9] == 0x16)) ) /* 22 bytes follow */ + { + + if (recvlen == 58) + { + if (QSO_DETAILS) + traceit("START from rptr: cntr=%02x %02x, streamID=%d,%d, flags=%02x:%02x:%02x, my=%.8s, sfx=%.4s, ur=%.8s, rpt1=%.8s, rpt2=%.8s, %d bytes fromIP=%s\n", + readBuffer[4], readBuffer[5], + readBuffer[14], readBuffer[15], + readBuffer[17], readBuffer[18], readBuffer[19], + readBuffer + 44, readBuffer + 52, readBuffer + 36, + readBuffer + 28, readBuffer + 20, recvlen, inet_ntoa(fromRptr.sin_addr)); + + if ((memcmp(readBuffer + 28, OWNER, 7) == 0) && /* rpt1 is this repeater */ + /*** (memcmp(readBuffer + 44, OWNER, 7) != 0) && ***/ /* MYCALL is NOT this repeater */ + ((readBuffer[17] == 0x00) || /* normal */ + (readBuffer[17] == 0x08) || /* EMR */ + (readBuffer[17] == 0x20) || /* BREAK */ + (readBuffer[17] == 0x28))) /* EMR + BREAK */ + { + + i = -1; + if (readBuffer[35] == 'A') + i = 0; + else + if (readBuffer[35] == 'B') + i = 1; + else + if (readBuffer[35] == 'C') + i = 2; + + if (i >= 0) + { + dtmf_last_frame[i] = 0; + dtmf_counter[i] = 0; + memset(dtmf_buf[i], 0, sizeof(dtmf_buf[i])); + dtmf_buf_count[i] = 0; + + /* Initialize the LAST HEARD data for the band */ + + band_txt[i].streamID[0] = readBuffer[14]; + band_txt[i].streamID[1] = readBuffer[15]; + + band_txt[i].flags[0] = readBuffer[17]; + band_txt[i].flags[1] = readBuffer[18]; + band_txt[i].flags[2] = readBuffer[19]; + + memcpy(band_txt[i].lh_mycall, readBuffer + 44, CALL_SIZE); + band_txt[i].lh_mycall[CALL_SIZE] = '\0'; + + memcpy(band_txt[i].lh_sfx, readBuffer + 52, 4); + band_txt[i].lh_sfx[4] = '\0'; + + memcpy(band_txt[i].lh_yrcall, readBuffer + 36, CALL_SIZE); + band_txt[i].lh_yrcall[CALL_SIZE] = '\0'; + + memcpy(band_txt[i].lh_rpt1, readBuffer + 28, CALL_SIZE); + band_txt[i].lh_rpt1[CALL_SIZE] = '\0'; + + memcpy(band_txt[i].lh_rpt2, readBuffer + 20, CALL_SIZE); + band_txt[i].lh_rpt2[CALL_SIZE] = '\0'; + + time(&band_txt[i].last_time); + + band_txt[i].txt[0] = '\0'; + band_txt[i].txt_cnt = 0; + band_txt[i].txt_stats_sent = false; + + band_txt[i].dest_rptr[0] = '\0'; + + /* try to process GPS mode: GPRMC and ID */ + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + band_txt[i].gprmc[0] = '\0'; + band_txt[i].gpid[0] = '\0'; + band_txt[i].is_gps_sent = false; + // band_txt[i].gps_last_time = 0; DO NOT reset it + + new_group[i] = true; + to_print[i] = 0; + ABC_grp[i] = false; + + band_txt[i].num_dv_frames = 0; + band_txt[i].num_dv_silent_frames = 0; + band_txt[i].num_bit_errors = 0; + + /* select the band for aprs processing, and lock on the stream ID */ + if (SEND_APRS) + aprs_select_band(i, readBuffer + 14); + } + } + + /* Is MYCALL valid ? */ + memset(temp_radio_user, ' ', CALL_SIZE); + memcpy(temp_radio_user, readBuffer + 44, 8); + temp_radio_user[CALL_SIZE] = '\0'; + + mycall_valid = regexec(&preg, temp_radio_user, 0, NULL, 0); + + if (mycall_valid == REG_NOERROR) + ; // traceit("MYCALL [%s] passed IRC expression validation\n", temp_radio_user); + else + { + if (mycall_valid == REG_NOMATCH) + traceit("MYCALL [%s] failed IRC expression validation\n", temp_radio_user); + else + traceit("Failed to validate MYCALL [%s], regexec error=%d\n", temp_radio_user, mycall_valid); + } + + /* send data g2_link */ + if (mycall_valid == REG_NOERROR) + sendto(srv_sock, (char *)readBuffer, recvlen,0, + (struct sockaddr *)&plug, + sizeof(struct sockaddr_in)); + + if ((mycall_valid == REG_NOERROR) && + (memcmp(readBuffer + 36, "XRF", 3) != 0) && /* not a reflector */ + (memcmp(readBuffer + 36, "REF", 3) != 0) && /* not a reflector */ + (memcmp(readBuffer + 36, "DCS", 3) != 0) && /* not a reflector */ + (readBuffer[36] != ' ') && /* must have something */ + (memcmp(readBuffer + 36, "CQCQCQ", 6) != 0)) /* urcall is NOT CQCQCQ */ + { + if ((readBuffer[36] == '/') && /* urcall starts with a slash */ + (memcmp(readBuffer + 28, OWNER, 7) == 0) && /* rpt1 is this repeater */ + ((readBuffer[35] == 'A') || + (readBuffer[35] == 'B') || + (readBuffer[35] == 'C')) && /* mod is A,B,C */ + (memcmp(readBuffer + 20, OWNER, 7) == 0) && /* rpt2 is this repeater */ + (readBuffer[27] == 'G') && /* local Gateway */ + /*** (memcmp(readBuffer + 44, OWNER, 7) != 0) && ***/ /* mycall is NOT this repeater */ + + ((readBuffer[17] == 0x00) || /* normal */ + (readBuffer[17] == 0x08) || /* EMR */ + (readBuffer[17] == 0x20) || /* BK */ + (readBuffer[17] == 0x28)) /* EMR + BK */ + ) + { + if (memcmp(readBuffer + 37, OWNER, 6) != 0) /* the value after the slash in urcall, is NOT this repeater */ + { + i = -1; + if (readBuffer[35] == 'A') + i = 0; + else + if (readBuffer[35] == 'B') + i = 1; + else + if (readBuffer[35] == 'C') + i = 2; + + if (i >= 0) + { + /* one radio user on a repeater module at a time */ + if (to_remote_g2[i].toDst4.sin_addr.s_addr == 0) + { + /* YRCALL=/repeater + mod */ + /* YRCALL=/KJ4NHFB */ + + memset(temp_radio_user, ' ', CALL_SIZE); + memcpy(temp_radio_user, readBuffer + 37, 6); + temp_radio_user[6] = ' '; + temp_radio_user[7] = readBuffer[43]; + if (temp_radio_user[7] == ' ') + temp_radio_user[7] = 'A'; + temp_radio_user[CALL_SIZE] = '\0'; + + result = get_yrcall_rptr(temp_radio_user, arearp_cs, zonerp_cs, &temp_mod, ip, 'R'); + if (result) /* it is a repeater */ + { + /* set the destination */ + memcpy(to_remote_g2[i].streamid, readBuffer + 14, 2); + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].toDst4.sin_family = AF_INET; + to_remote_g2[i].toDst4.sin_port = htons(G2_EXTERNAL_PORT); + to_remote_g2[i].toDst4.sin_addr.s_addr = inet_addr(ip); + + memcpy(readBuffer2, "DSVT", 4); + readBuffer2[4] = 0x10; + readBuffer2[5] = 0x00; + readBuffer2[6] = 0x00; + readBuffer2[7] = 0x00; + readBuffer2[8] = readBuffer[10]; + readBuffer2[9] = readBuffer[11]; + readBuffer2[10] = readBuffer[12]; + readBuffer2[11] = readBuffer[13]; + memcpy(readBuffer2 + 12, readBuffer + 14, 44); + /* set rpt1 */ + memset(readBuffer2 + 18, ' ', CALL_SIZE); + memcpy(readBuffer2 + 18, arearp_cs, strlen(arearp_cs)); + readBuffer2[25] = temp_mod; + /* set rpt2 */ + memset(readBuffer2 + 26, ' ', CALL_SIZE); + memcpy(readBuffer2 + 26, zonerp_cs, strlen(zonerp_cs)); + readBuffer2[33] = 'G'; + /* set yrcall, can NOT let it be slash and repeater + module */ + memcpy(readBuffer2 + 34, "CQCQCQ ", 8); + + /* set PFCS */ + calcPFCS(readBuffer2, 56); + + /* + The remote repeater has been set, lets fill in the dest_rptr + so that later we can send that to the LIVE web site + */ + memcpy(band_txt[i].dest_rptr, readBuffer2 + 18, CALL_SIZE); + band_txt[i].dest_rptr[CALL_SIZE] = '\0'; + + /* send to remote gateway */ + for (j = 0; j < 5; j++) + sendto(g2_sock, (char *)readBuffer2, 56, + 0,(struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + + traceit("Routing to IP=%s, streamID=%d,%d, my=%.8s, sfx=%.4s, ur=%.8s, rpt1=%.8s, rpt2=%.8s, %d bytes\n", + inet_ntoa(to_remote_g2[i].toDst4.sin_addr), + readBuffer2[12],readBuffer2[13],&readBuffer2[42], + &readBuffer2[50], &readBuffer2[34], + &readBuffer2[18], &readBuffer2[26], + 56); + + time(&(to_remote_g2[i].last_time)); + } + } + } + } + } + else + if ((memcmp(readBuffer + 36, OWNER, 7) != 0) && /* urcall is not this repeater */ + (memcmp(readBuffer + 28, OWNER, 7) == 0) && /* rpt1 is this repeater */ + ((readBuffer[35] == 'A') || + (readBuffer[35] == 'B') || + (readBuffer[35] == 'C')) && /* mod is A,B,C */ + (memcmp(readBuffer + 20, OWNER, 7) == 0) && /* rpt2 is this repeater */ + (readBuffer[27] == 'G') && /* local Gateway */ + /*** (memcmp(readBuffer + 44, OWNER, 7) != 0) && ***/ /* mycall is NOT this repeater */ + + ((readBuffer[17] == 0x00) || /* normal */ + (readBuffer[17] == 0x08) || /* EMR */ + (readBuffer[17] == 0x20) || /* BK */ + (readBuffer[17] == 0x28)) /* EMR + BK */ + ) + { + + memset(temp_radio_user, ' ', CALL_SIZE); + memcpy(temp_radio_user, readBuffer + 36, CALL_SIZE); + temp_radio_user[CALL_SIZE] = '\0'; + result = get_yrcall_rptr(temp_radio_user, arearp_cs, zonerp_cs, &temp_mod, ip, 'U'); + if (result) + { + /* destination is a remote system */ + if (memcmp(zonerp_cs, OWNER, 7) != 0) + { + i = -1; + if (readBuffer[35] == 'A') + i = 0; + else + if (readBuffer[35] == 'B') + i = 1; + else + if (readBuffer[35] == 'C') + i = 2; + + if (i >= 0) + { + /* one radio user on a repeater module at a time */ + if (to_remote_g2[i].toDst4.sin_addr.s_addr == 0) + { + /* set the destination */ + memcpy(to_remote_g2[i].streamid, readBuffer + 14, 2); + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].toDst4.sin_family = AF_INET; + to_remote_g2[i].toDst4.sin_port = htons(G2_EXTERNAL_PORT); + to_remote_g2[i].toDst4.sin_addr.s_addr = inet_addr(ip); + + memcpy(readBuffer2, "DSVT", 4); + readBuffer2[4] = 0x10; + readBuffer2[5] = 0x00; + readBuffer2[6] = 0x00; + readBuffer2[7] = 0x00; + readBuffer2[8] = readBuffer[10]; + readBuffer2[9] = readBuffer[11]; + readBuffer2[10] = readBuffer[12]; + readBuffer2[11] = readBuffer[13]; + memcpy(readBuffer2 + 12, readBuffer + 14, 44); + /* set rpt1 */ + memset(readBuffer2 + 18, ' ', CALL_SIZE); + memcpy(readBuffer2 + 18, arearp_cs, strlen(arearp_cs)); + readBuffer2[25] = temp_mod; + /* set rpt2 */ + memset(readBuffer2 + 26, ' ', CALL_SIZE); + memcpy(readBuffer2 + 26, zonerp_cs, strlen(zonerp_cs)); + readBuffer2[33] = 'G'; + /* set PFCS */ + calcPFCS(readBuffer2, 56); + + /* + The remote repeater has been set, lets fill in the dest_rptr + so that later we can send that to the LIVE web site + */ + memcpy(band_txt[i].dest_rptr, readBuffer2 + 18, CALL_SIZE); + band_txt[i].dest_rptr[CALL_SIZE] = '\0'; + + /* send to remote gateway */ + for (j = 0; j < 5; j++) + sendto(g2_sock, (char *)readBuffer2, 56, + 0,(struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + + traceit("Routing to IP=%s, streamID=%d,%d, my=%.8s, sfx=%.4s, ur=%.8s, rpt1=%.8s, rpt2=%.8s, %d bytes\n", + inet_ntoa(to_remote_g2[i].toDst4.sin_addr), + readBuffer2[12],readBuffer2[13],&readBuffer2[42], + &readBuffer2[50], &readBuffer2[34], + &readBuffer2[18], &readBuffer2[26], + 56); + + time(&(to_remote_g2[i].last_time)); + } + } + } + else + { + i = -1; + if (readBuffer[35] == 'A') + i = 0; + else + if (readBuffer[35] == 'B') + i = 1; + else + if (readBuffer[35] == 'C') + i = 2; + + if (i >= 0) + { + /* the user we are trying to contact is on our gateway */ + /* make sure they are on a different module */ + if (temp_mod != readBuffer[35]) + { + /* + The remote repeater has been set, lets fill in the dest_rptr + so that later we can send that to the LIVE web site + */ + memcpy(band_txt[i].dest_rptr, readBuffer + 20, CALL_SIZE); + band_txt[i].dest_rptr[7] = temp_mod; + band_txt[i].dest_rptr[CALL_SIZE] = '\0'; + + i = -1; + if (temp_mod == 'A') + i = 0; + else + if (temp_mod == 'B') + i = 1; + else + if (temp_mod == 'C') + i = 2; + + /* valid destination repeater module? */ + if (i >= 0) + { + /* + toRptr[i] : receiving from a remote system or cross-band + band_txt[i] : local RF is talking. + */ + if ((toRptr[i].last_time == 0) && (band_txt[i].last_time == 0)) + { + traceit("CALLmode cross-banding from mod %c to %c\n", readBuffer[35], temp_mod); + + readBuffer[27] = temp_mod; + readBuffer[35] = 'G'; + calcPFCS(readBuffer, 58); + + sendto(srv_sock, (char *)readBuffer,58,0, + (struct sockaddr *)&toRptr[i].band_addr, + sizeof(struct sockaddr_in)); + + /* This is the active streamid */ + toRptr[i].streamid[0] = readBuffer[14]; + toRptr[i].streamid[1] = readBuffer[15]; + toRptr[i].adr = fromRptr.sin_addr.s_addr; + + /* time it, in case stream times out */ + time(&toRptr[i].last_time); + + /* bump the G2 counter */ + toRptr[i].G2_COUNTER ++; + + toRptr[i].sequence = readBuffer[16]; + } + } + } + else + traceit("icom rule: no routing from %.8s to %s%c\n", readBuffer + 28, arearp_cs,temp_mod); + } + } + } + } + } + else + if ((readBuffer[43] == '0') && + (readBuffer[42] == CLEAR_VM_CODE) && + (readBuffer[36] == ' ')) + { + i = -1; + if (readBuffer[35] == 'A') + i = 0; + else + if (readBuffer[35] == 'B') + i = 1; + else + if (readBuffer[35] == 'C') + i = 2; + + if (i >= 0) + { + /* voicemail file is closed */ + if ((vm[i].fd == -1) && (vm[i].file[0] != '\0')) + { + unlink(vm[i].file); + traceit("removed voicemail file: %s\n", vm[i].file); + vm[i].file[0] = '\0'; + } + else + traceit("No voicemail to clear or still recording\n"); + } + } + else + if ((readBuffer[43] == '0') && + (readBuffer[42] == RECALL_VM_CODE) && + (readBuffer[36] == ' ')) + { + i = -1; + if (readBuffer[35] == 'A') + i = 0; + else + if (readBuffer[35] == 'B') + i = 1; + else + if (readBuffer[35] == 'C') + i = 2; + + if (i >= 0) + { + /* voicemail file is closed */ + if ((vm[i].fd == -1) && (vm[i].file[0] != '\0')) + { + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&echo_thread, &attr, echotest, (void *)vm[i].file); + if (rc != 0) + traceit("failed to start playing back voicemail\n"); + pthread_attr_destroy(&attr); + } + else + traceit("No voicemail to recall or still recording\n"); + } + } + else + if ((readBuffer[43] == '0') && + (readBuffer[42] == STORE_VM_CODE) && + (readBuffer[36] == ' ')) + { + i = -1; + if (readBuffer[35] == 'A') + i = 0; + else + if (readBuffer[35] == 'B') + i = 1; + else + if (readBuffer[35] == 'C') + i = 2; + + if (i >= 0) + { + if (vm[i].fd >= 0) + traceit("Already recording for voicemail on mod %d\n", i); + else + { + memset(tempfile, '\0', sizeof(tempfile)); + snprintf(tempfile, FILENAME_MAX, "%s/%c_%s", + ECHOTEST_DIR, + readBuffer[35], + "voicemail.dat"); + + vm[i].fd = open(tempfile, + O_CREAT | O_WRONLY | O_TRUNC | O_APPEND, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (vm[i].fd < 0) + traceit("Failed to create file %s for voicemail\n", tempfile); + else + { + strcpy(vm[i].file, tempfile); + traceit("Recording mod %c for voicemail into file:[%s]\n", + readBuffer[35], + vm[i].file); + + time(&vm[i].last_time); + memcpy(vm[i].streamid, readBuffer + 14, 2); + + memcpy(recbuf, "DSVT", 4); + recbuf[4] = 0x10; + recbuf[5] = 0x00; + recbuf[6] = 0x00; + recbuf[7] = 0x00; + recbuf[8] = readBuffer[10]; + recbuf[9] = readBuffer[11]; + recbuf[10] = readBuffer[12]; + recbuf[11] = readBuffer[13]; + memcpy(recbuf + 12, readBuffer + 14, 44); + memset(recbuf + 18, ' ', CALL_SIZE); + memcpy(recbuf + 18, OWNER, strlen(OWNER)); + recbuf[25] = readBuffer[35]; + memset(recbuf + 26, ' ', CALL_SIZE); + memcpy(recbuf + 26, OWNER, strlen(OWNER)); + recbuf[33] = 'G'; + memcpy(recbuf + 34, "CQCQCQ ", 8); + + calcPFCS(recbuf,56); + + rec_len = 56; + (void)write(vm[i].fd, "DVTOOL", 6); + (void)write(vm[i].fd, &num_recs, 4); + (void)write(vm[i].fd, &rec_len, 2); + (void)write(vm[i].fd, (char *)recbuf, rec_len); + } + } + } + } + else + if ((readBuffer[43] == ECHO_CODE) && + (readBuffer[36] == ' ')) + { + i = -1; + if (readBuffer[35] == 'A') + i = 0; + else + if (readBuffer[35] == 'B') + i = 1; + else + if (readBuffer[35] == 'C') + i = 2; + + if (i >= 0) + { + if (recd[i].fd >= 0) + traceit("Already recording for echotest on mod %d\n", i); + else + { + memset(tempfile, '\0', sizeof(tempfile)); + snprintf(tempfile, FILENAME_MAX, "%s/%c_%s", + ECHOTEST_DIR, + readBuffer[35], + "echotest.dat"); + + recd[i].fd = open(tempfile, + O_CREAT | O_WRONLY | O_EXCL | O_TRUNC | O_APPEND, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (recd[i].fd < 0) + traceit("Failed to create file %s for echotest\n", tempfile); + else + { + strcpy(recd[i].file, tempfile); + traceit("Recording mod %c for echotest into file:[%s]\n", + readBuffer[35], + recd[i].file); + + time(&recd[i].last_time); + memcpy(recd[i].streamid, readBuffer + 14, 2); + + memcpy(recbuf, "DSVT", 4); + recbuf[4] = 0x10; + recbuf[5] = 0x00; + recbuf[6] = 0x00; + recbuf[7] = 0x00; + recbuf[8] = readBuffer[10]; + recbuf[9] = readBuffer[11]; + recbuf[10] = readBuffer[12]; + recbuf[11] = readBuffer[13]; + memcpy(recbuf + 12, readBuffer + 14, 44); + memset(recbuf + 18, ' ', CALL_SIZE); + memcpy(recbuf + 18, OWNER, strlen(OWNER)); + recbuf[25] = readBuffer[35]; + memset(recbuf + 26, ' ', CALL_SIZE); + memcpy(recbuf + 26, OWNER, strlen(OWNER)); + recbuf[33] = 'G'; + memcpy(recbuf + 34, "CQCQCQ ", 8); + + calcPFCS(recbuf,56); + + rec_len = 56; + (void)write(recd[i].fd, "DVTOOL", 6); + (void)write(recd[i].fd, &num_recs, 4); + (void)write(recd[i].fd, &rec_len, 2); + (void)write(recd[i].fd, (char *)recbuf, rec_len); + } + } + } + } + else + /* check for cross-banding */ + if ((memcmp(readBuffer + 36, "CQCQCQ", 6) == 0) && /* yrcall is CQCQCQ */ + (memcmp(readBuffer + 28, OWNER, 7) == 0) && /* rpt1 is this repeater */ + (memcmp(readBuffer + 20, OWNER, 7) == 0) && /* rpt2 is this repeater */ + ((readBuffer[35] == 'A') || + (readBuffer[35] == 'B') || + (readBuffer[35] == 'C')) && /* mod of rpt1 is A,B,C */ + ((readBuffer[27] == 'A') || + (readBuffer[27] == 'B') || + (readBuffer[27] == 'C')) && /* !!! usually a G of rpt2, but we see A,B,C */ + (readBuffer[35] != readBuffer[27])) /* cross-banding? make sure NOT the same */ + { + i = -1; + if (readBuffer[35] == 'A') + i = 0; + else + if (readBuffer[35] == 'B') + i = 1; + else + if (readBuffer[35] == 'C') + i = 2; + + if (i >= 0) + { + /* + The remote repeater has been set, lets fill in the dest_rptr + so that later we can send that to the LIVE web site + */ + memcpy(band_txt[i].dest_rptr, readBuffer + 20, CALL_SIZE); + band_txt[i].dest_rptr[CALL_SIZE] = '\0'; + } + + i = -1; + if (readBuffer[27] == 'A') + i = 0; + else + if (readBuffer[27] == 'B') + i = 1; + else + if (readBuffer[27] == 'C') + i = 2; + + /* valid destination repeater module? */ + if (i >= 0) + { + /* + toRptr[i] : receiving from a remote system or cross-band + band_txt[i] : local RF is talking. + */ + if ((toRptr[i].last_time == 0) && (band_txt[i].last_time == 0)) + { + traceit("ZONEmode cross-banding from mod %c to %c\n", readBuffer[35], readBuffer[27]); + + readBuffer[35] = 'G'; + calcPFCS(readBuffer, 58); + + sendto(srv_sock, (char *)readBuffer,58,0, + (struct sockaddr *)&toRptr[i].band_addr, + sizeof(struct sockaddr_in)); + + /* This is the active streamid */ + toRptr[i].streamid[0] = readBuffer[14]; + toRptr[i].streamid[1] = readBuffer[15]; + toRptr[i].adr = fromRptr.sin_addr.s_addr; + + /* time it, in case stream times out */ + time(&toRptr[i].last_time); + + /* bump the G2 counter */ + toRptr[i].G2_COUNTER ++; + + toRptr[i].sequence = readBuffer[16]; + } + } + } + } + else + { + for (i = 0; i < 3; i++) + { + if (memcmp(band_txt[i].streamID, readBuffer + 14, 2) == 0) + { + time(&band_txt[i].last_time); + + if ((readBuffer[16] & 0x40) != 0) + { + if (dtmf_buf_count[i] > 0) + { + memset(DTMF_FILE, 0, sizeof(DTMF_FILE)); + snprintf(DTMF_FILE, FILENAME_MAX, "%s/%d_mod_DTMF_NOTIFY", DTMF_DIR, i); + if (DTMF_DEBUG) + traceit("Saving dtmfs=[%s] into file: [%s]\n", dtmf_buf[i], DTMF_FILE); + dtmf_fp = fopen(DTMF_FILE, "w"); + if (!dtmf_fp) + traceit("Failed to create dtmf file %s\n", DTMF_FILE); + else + { + fprintf(dtmf_fp, "%s\n%s", dtmf_buf[i], band_txt[i].lh_mycall); + fclose(dtmf_fp); dtmf_fp = NULL; + } + memset(dtmf_buf[i], 0, sizeof(dtmf_buf[i])); + dtmf_buf_count[i] = 0; + dtmf_counter[i] = 0; + dtmf_last_frame[i] = 0; + } + + ii->sendHeardWithTXStats(band_txt[i].lh_mycall, + band_txt[i].lh_sfx, + (strstr(band_txt[i].lh_yrcall,"REF") == NULL)?band_txt[i].lh_yrcall:"CQCQCQ ", + band_txt[i].lh_rpt1, + band_txt[i].lh_rpt2, + band_txt[i].flags[0], + band_txt[i].flags[1], + band_txt[i].flags[2], + band_txt[i].num_dv_frames, + band_txt[i].num_dv_silent_frames, + band_txt[i].num_bit_errors); + + band_txt[i].streamID[0] = 0x00; + band_txt[i].streamID[1] = 0x00; + band_txt[i].flags[0] = 0x00; + band_txt[i].flags[1] = 0x00; + band_txt[i].flags[2] = 0x00; + band_txt[i].lh_mycall[0] = '\0'; + band_txt[i].lh_sfx[0] = '\0'; + band_txt[i].lh_yrcall[0] = '\0'; + band_txt[i].lh_rpt1[0] = '\0'; + band_txt[i].lh_rpt2[0] = '\0'; + + band_txt[i].last_time = 0; + + band_txt[i].txt[0] = '\0'; + band_txt[i].txt_cnt = 0; + + band_txt[i].dest_rptr[0] = '\0'; + + band_txt[i].num_dv_frames = 0; + band_txt[i].num_dv_silent_frames = 0; + band_txt[i].num_bit_errors = 0; + + } + else + { + ber_errs = dstar_dv_decode(readBuffer + 17, ber_data); + if (ber_data[0] == 0xf85) + band_txt[i].num_dv_silent_frames ++; + band_txt[i].num_bit_errors += ber_errs; + band_txt[i].num_dv_frames ++; + + if ((ber_data[0] & 0x0ffc) == 0xfc0) + { + dtmf_digit = (ber_data[0] & 0x03) | ((ber_data[2] & 0x60) >> 3); + if (dtmf_counter[i] > 0) + { + if (dtmf_last_frame[i] != dtmf_digit) + dtmf_counter[i] = 0; + } + dtmf_last_frame[i] = dtmf_digit; + dtmf_counter[i] ++; + + if ((dtmf_counter[i] == 5) && (dtmf_digit >= 0) && (dtmf_digit <= 15)) + { + if (dtmf_buf_count[i] < MAX_DTMF_BUF) + { + dtmf_buf[i][ dtmf_buf_count[i] ] = dtmf_chars[dtmf_digit]; + dtmf_buf_count[i] ++; + } + } + memcpy(readBuffer + 17, silence, 9); + } + else + dtmf_counter[i] = 0; + } + break; + } + } + + if (recvlen == 29) + memcpy(tmp_txt, readBuffer + 26, 3); + else + memcpy(tmp_txt, readBuffer + 29, 3); + + // traceit("%x%x%x\n", tmp_txt[0], tmp_txt[1], tmp_txt[2]); + // traceit("%c%c%c\n", tmp_txt[0] ^ 0x70, tmp_txt[1] ^ 0x4f, tmp_txt[2] ^ 0x93); + + /* extract 20-byte RADIO ID */ + if ((tmp_txt[0] != 0x55) || (tmp_txt[1] != 0x2d) || (tmp_txt[2] != 0x16)) + { + // traceit("%x%x%x\n", tmp_txt[0], tmp_txt[1], tmp_txt[2]); + // traceit("%c%c%c\n", tmp_txt[0] ^ 0x70, tmp_txt[1] ^ 0x4f, tmp_txt[2] ^ 0x93); + + for (i = 0; i < 3; i++) + { + if (memcmp(band_txt[i].streamID, readBuffer + 14, 2) == 0) + { + if (new_group[i]) + { + tmp_txt[0] = tmp_txt[0] ^ 0x70; + header_type = tmp_txt[0] & 0xf0; + + if ((header_type == 0x50) || /* header */ + (header_type == 0xc0)) /* squelch */ + { + new_group[i] = false; + to_print[i] = 0; + ABC_grp[i] = false; + } + else + if (header_type == 0x30) /* GPS or GPS id or APRS */ + { + new_group[i] = false; + to_print[i] = tmp_txt[0] & 0x0f; + ABC_grp[i] = false; + if (to_print[i] > 5) + to_print[i] = 5; + else + if (to_print[i] < 1) + to_print[i] = 1; + + if ((to_print[i] > 1) && (to_print[i] <= 5)) + { + /* something went wrong? all bets are off */ + if (band_txt[i].temp_line_cnt > 200) + { + traceit("Reached the limit in the OLD gps mode\n"); + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + + /* fresh GPS string, re-initialize */ + if ((to_print[i] == 5) && ((tmp_txt[1] ^ 0x4f) == '$')) + { + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + + /* do not copy CR, NL */ + if (((tmp_txt[1] ^ 0x4f) != '\r') && ((tmp_txt[1] ^ 0x4f) != '\n')) + { + band_txt[i].temp_line[band_txt[i].temp_line_cnt] = tmp_txt[1] ^ 0x4f; + band_txt[i].temp_line_cnt++; + } + if (((tmp_txt[2] ^ 0x93) != '\r') && ((tmp_txt[2] ^ 0x93) != '\n')) + { + band_txt[i].temp_line[band_txt[i].temp_line_cnt] = tmp_txt[2] ^ 0x93; + band_txt[i].temp_line_cnt++; + } + + if (((tmp_txt[1] ^ 0x4f) == '\r') || ((tmp_txt[2] ^ 0x93) == '\r')) + { + if (memcmp(band_txt[i].temp_line, "$GPRMC", 6) == 0) + { + memcpy(band_txt[i].gprmc, band_txt[i].temp_line, band_txt[i].temp_line_cnt); + band_txt[i].gprmc[band_txt[i].temp_line_cnt] = '\0'; + } + else + if (band_txt[i].temp_line[0] != '$') + { + memcpy(band_txt[i].gpid, band_txt[i].temp_line, band_txt[i].temp_line_cnt); + band_txt[i].gpid[band_txt[i].temp_line_cnt] = '\0'; + if (SEND_APRS && !band_txt[i].is_gps_sent) + gps_send(i); + } + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + else + if (((tmp_txt[1] ^ 0x4f) == '\n') || ((tmp_txt[2] ^ 0x93) == '\n')) + { + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + to_print[i] -= 2; + } + else + { + /* something went wrong? all bets are off */ + if (band_txt[i].temp_line_cnt > 200) + { + traceit("Reached the limit in the OLD gps mode\n"); + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + + /* do not copy CR, NL */ + if (((tmp_txt[1] ^ 0x4f) != '\r') && ((tmp_txt[1] ^ 0x4f) != '\n')) + { + band_txt[i].temp_line[band_txt[i].temp_line_cnt] = tmp_txt[1] ^ 0x4f; + band_txt[i].temp_line_cnt++; + } + + if ((tmp_txt[1] ^ 0x4f) == '\r') + { + if (memcmp(band_txt[i].temp_line, "$GPRMC", 6) == 0) + { + memcpy(band_txt[i].gprmc, band_txt[i].temp_line, band_txt[i].temp_line_cnt); + band_txt[i].gprmc[band_txt[i].temp_line_cnt] = '\0'; + } + else + if (band_txt[i].temp_line[0] != '$') + { + memcpy(band_txt[i].gpid, band_txt[i].temp_line, band_txt[i].temp_line_cnt); + band_txt[i].gpid[band_txt[i].temp_line_cnt] = '\0'; + if (SEND_APRS && !band_txt[i].is_gps_sent) + gps_send(i); + } + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + else + if ((tmp_txt[1] ^ 0x4f) == '\n') + { + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + to_print[i] --; + } + } + else + if (header_type == 0x40) /* ABC text */ + { + new_group[i] = false; + to_print[i] = 3; + ABC_grp[i] = true; + C_seen[i] = ((tmp_txt[0] & 0x0f) == 0x03)?true:false; + + band_txt[i].txt[band_txt[i].txt_cnt] = tmp_txt[1] ^ 0x4f; + band_txt[i].txt_cnt++; + + band_txt[i].txt[band_txt[i].txt_cnt] = tmp_txt[2] ^ 0x93; + band_txt[i].txt_cnt++; + + /* + We should NOT see any more text, + if we already processed text, + so blank out the codes. + */ + if (band_txt[i].txt_stats_sent) + { + if (recvlen == 29) + { + readBuffer[26] = 0x70; + readBuffer[27] = 0x4f; + readBuffer[28] = 0x93; + } + else + { + readBuffer[29] = 0x70; + readBuffer[30] = 0x4f; + readBuffer[31] = 0x93; + } + } + + if (band_txt[i].txt_cnt >= 20) + { + band_txt[i].txt[band_txt[i].txt_cnt] = '\0'; + /*** + ii->sendHeardWithTXMsg(band_txt[i].lh_mycall, + band_txt[i].lh_sfx, + (strstr(band_txt[i].lh_yrcall,"REF") == NULL)?band_txt[i].lh_yrcall:"CQCQCQ ", + band_txt[i].lh_rpt1, + band_txt[i].lh_rpt2, + band_txt[i].flags[0], + band_txt[i].flags[1], + band_txt[i].flags[2], + band_txt[i].dest_rptr, + band_txt[i].txt); + ***/ + // traceit("TEXT1=[%s]\n", band_txt[i].txt); + band_txt[i].txt_cnt = 0; + } + } + else + { + new_group[i] = false; + to_print[i] = 0; + ABC_grp[i] = false; + } + } + else + { + if (to_print[i] == 3) + { + if (ABC_grp[i]) + { + band_txt[i].txt[band_txt[i].txt_cnt] = tmp_txt[0] ^ 0x70; + band_txt[i].txt_cnt ++; + + band_txt[i].txt[band_txt[i].txt_cnt] = tmp_txt[1] ^ 0x4f; + band_txt[i].txt_cnt ++; + + band_txt[i].txt[band_txt[i].txt_cnt] = tmp_txt[2] ^ 0x93; + band_txt[i].txt_cnt ++; + + /* + We should NOT see any more text, + if we already processed text, + so blank out the codes. + */ + if (band_txt[i].txt_stats_sent) + { + if (recvlen == 29) + { + readBuffer[26] = 0x70; + readBuffer[27] = 0x4f; + readBuffer[28] = 0x93; + } + else + { + readBuffer[29] = 0x70; + readBuffer[30] = 0x4f; + readBuffer[31] = 0x93; + } + } + + if ((band_txt[i].txt_cnt >= 20) || C_seen[i]) + { + band_txt[i].txt[band_txt[i].txt_cnt] = '\0'; + if (!band_txt[i].txt_stats_sent) + { + /*** if YRCALL is CQCQCQ, set dest_rptr ***/ + if (memcmp(band_txt[i].lh_yrcall, "CQCQCQ", 6) == 0) + { + set_dest_rptr(i, band_txt[i].dest_rptr); + if (memcmp(band_txt[i].dest_rptr, "REF", 3) == 0) + band_txt[i].dest_rptr[0] = '\0'; + } + + ii->sendHeardWithTXMsg(band_txt[i].lh_mycall, + band_txt[i].lh_sfx, + (strstr(band_txt[i].lh_yrcall,"REF") == NULL)?band_txt[i].lh_yrcall:"CQCQCQ ", + band_txt[i].lh_rpt1, + band_txt[i].lh_rpt2, + band_txt[i].flags[0], + band_txt[i].flags[1], + band_txt[i].flags[2], + band_txt[i].dest_rptr, + band_txt[i].txt); + // traceit("TEXT2=[%s], destination repeater=[%s]\n", band_txt[i].txt, band_txt[i].dest_rptr); + band_txt[i].txt_stats_sent = true; + } + band_txt[i].txt_cnt = 0; + } + if (C_seen[i]) + C_seen[i] = false; + } + else + { + /* something went wrong? all bets are off */ + if (band_txt[i].temp_line_cnt > 200) + { + traceit("Reached the limit in the OLD gps mode\n"); + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + + /* do not copy CR, NL */ + if (((tmp_txt[0] ^ 0x70) != '\r') && ((tmp_txt[0] ^ 0x70) != '\n')) + { + band_txt[i].temp_line[band_txt[i].temp_line_cnt] = tmp_txt[0] ^ 0x70; + band_txt[i].temp_line_cnt++; + } + if (((tmp_txt[1] ^ 0x4f) != '\r') && ((tmp_txt[1] ^ 0x4f) != '\n')) + { + band_txt[i].temp_line[band_txt[i].temp_line_cnt] = tmp_txt[1] ^ 0x4f; + band_txt[i].temp_line_cnt++; + } + if (((tmp_txt[2] ^ 0x93) != '\r') && ((tmp_txt[2] ^ 0x93) != '\n')) + { + band_txt[i].temp_line[band_txt[i].temp_line_cnt] = tmp_txt[2] ^ 0x93; + band_txt[i].temp_line_cnt++; + } + + if ( + ((tmp_txt[0] ^ 0x70) == '\r') || + ((tmp_txt[1] ^ 0x4f) == '\r') || + ((tmp_txt[2] ^ 0x93) == '\r') + ) + { + if (memcmp(band_txt[i].temp_line, "$GPRMC", 6) == 0) + { + memcpy(band_txt[i].gprmc, band_txt[i].temp_line, band_txt[i].temp_line_cnt); + band_txt[i].gprmc[band_txt[i].temp_line_cnt] = '\0'; + } + else + if (band_txt[i].temp_line[0] != '$') + { + memcpy(band_txt[i].gpid, band_txt[i].temp_line, band_txt[i].temp_line_cnt); + band_txt[i].gpid[band_txt[i].temp_line_cnt] = '\0'; + if (SEND_APRS && !band_txt[i].is_gps_sent) + gps_send(i); + } + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + else + if (((tmp_txt[0] ^ 0x70) == '\n') || + ((tmp_txt[1] ^ 0x4f) == '\n') || + ((tmp_txt[2] ^ 0x93) == '\n')) + { + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + } + } + else + if (to_print[i] == 2) + { + /* something went wrong? all bets are off */ + if (band_txt[i].temp_line_cnt > 200) + { + traceit("Reached the limit in the OLD gps mode\n"); + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + + /* do not copy CR, NL */ + if (((tmp_txt[0] ^ 0x70) != '\r') && ((tmp_txt[0] ^ 0x70) != '\n')) + { + band_txt[i].temp_line[band_txt[i].temp_line_cnt] = tmp_txt[0] ^ 0x70; + band_txt[i].temp_line_cnt++; + } + if (((tmp_txt[1] ^ 0x4f) != '\r') && ((tmp_txt[1] ^ 0x4f) != '\n')) + { + band_txt[i].temp_line[band_txt[i].temp_line_cnt] = tmp_txt[1] ^ 0x4f; + band_txt[i].temp_line_cnt++; + } + + if (((tmp_txt[0] ^ 0x70) == '\r') || ((tmp_txt[1] ^ 0x4f) == '\r')) + { + if (memcmp(band_txt[i].temp_line, "$GPRMC", 6) == 0) + { + memcpy(band_txt[i].gprmc, band_txt[i].temp_line, band_txt[i].temp_line_cnt); + band_txt[i].gprmc[band_txt[i].temp_line_cnt] = '\0'; + } + else + if (band_txt[i].temp_line[0] != '$') + { + memcpy(band_txt[i].gpid, band_txt[i].temp_line, band_txt[i].temp_line_cnt); + band_txt[i].gpid[band_txt[i].temp_line_cnt] = '\0'; + if (SEND_APRS && !band_txt[i].is_gps_sent) + gps_send(i); + } + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + else + if (((tmp_txt[0] ^ 0x70) == '\n') || ((tmp_txt[1] ^ 0x4f) == '\n')) + { + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + } + else + if (to_print[i] == 1) + { + /* something went wrong? all bets are off */ + if (band_txt[i].temp_line_cnt > 200) + { + traceit("Reached the limit in the OLD gps mode\n"); + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + + /* do not copy CR, NL */ + if (((tmp_txt[0] ^ 0x70) != '\r') && ((tmp_txt[0] ^ 0x70) != '\n')) + { + band_txt[i].temp_line[band_txt[i].temp_line_cnt] = tmp_txt[0] ^ 0x70; + band_txt[i].temp_line_cnt++; + } + + if ((tmp_txt[0] ^ 0x70) == '\r') + { + if (memcmp(band_txt[i].temp_line, "$GPRMC", 6) == 0) + { + memcpy(band_txt[i].gprmc, band_txt[i].temp_line, band_txt[i].temp_line_cnt); + band_txt[i].gprmc[band_txt[i].temp_line_cnt] = '\0'; + } + else + if (band_txt[i].temp_line[0] != '$') + { + memcpy(band_txt[i].gpid, band_txt[i].temp_line, band_txt[i].temp_line_cnt); + band_txt[i].gpid[band_txt[i].temp_line_cnt] = '\0'; + if (SEND_APRS && !band_txt[i].is_gps_sent) + gps_send(i); + } + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + else + if ((tmp_txt[0] ^ 0x70) == '\n') + { + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + } + } + new_group[i] = true; + to_print[i] = 0; + ABC_grp[i] = false; + } + break; + } + } + } + + /* send data to g2_link */ + sendto(srv_sock, (char *)readBuffer, recvlen,0, + (struct sockaddr *)&plug, + sizeof(struct sockaddr_in)); + + /* aprs processing */ + if (SEND_APRS) + aprs_process_text(readBuffer + 14, /* stream ID */ + readBuffer[16], /* seq */ + readBuffer + 17, /* audio + text */ + (recvlen == 29)?12:15); /* size */ + + for (i = 0; i < 3; i++) + { + /* find out if data must go to the remote G2 */ + if (memcmp(to_remote_g2[i].streamid, readBuffer + 14, 2) == 0) + { + memcpy(readBuffer2, "DSVT", 4); + readBuffer2[4] = 0x20; + readBuffer2[5] = 0x00; + readBuffer2[6] = 0x00; + readBuffer2[7] = 0x00; + readBuffer2[8] = readBuffer[10]; + readBuffer2[9] = readBuffer[11]; + readBuffer2[10] = readBuffer[12]; + readBuffer2[11] = readBuffer[13]; + memcpy(readBuffer2 + 12, readBuffer + 14, 3); + if (recvlen == 29) + memcpy(readBuffer2 + 15, readBuffer + 17, 12); + else + memcpy(readBuffer2 + 15, readBuffer + 20, 12); + + sendto(g2_sock, (char *)readBuffer2, 27, + 0,(struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + + time(&(to_remote_g2[i].last_time)); + + /* Is this the end-of-stream */ + if ((readBuffer[16] & 0x40) != 0) + { + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].streamid[0] = '\0'; + to_remote_g2[i].streamid[1] = '\0'; + to_remote_g2[i].last_time = 0; + } + break; + } + else + /* Is the data to be recorded for echotest */ + if ((recd[i].fd >= 0) && + (memcmp(recd[i].streamid, readBuffer + 14, 2) == 0)) + { + time(&recd[i].last_time); + + memcpy(recbuf, "DSVT", 4); + recbuf[4] = 0x20; + recbuf[5] = 0x00; + recbuf[6] = 0x00; + recbuf[7] = 0x00; + recbuf[8] = readBuffer[10]; + recbuf[9] = readBuffer[11]; + recbuf[10] = readBuffer[12]; + recbuf[11] = readBuffer[13]; + memcpy(recbuf + 12, readBuffer + 14, 3); + if (recvlen == 29) + memcpy(recbuf + 15, readBuffer + 17, 12); + else + memcpy(recbuf + 15, readBuffer + 20, 12); + + rec_len = 27; + (void)write(recd[i].fd, &rec_len, 2); + (void)write(recd[i].fd, (char *)recbuf, rec_len); + + if ((readBuffer[16] & 0x40) != 0) + { + recd[i].streamid[0] = 0x00; + recd[i].streamid[1] = 0x00; + recd[i].last_time = 0; + close(recd[i].fd); recd[i].fd = -1; + // traceit("Closed echotest audio file:[%s]\n", recd[i].file); + + /* we are in echotest mode, so play it back */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&echo_thread, &attr, echotest, (void *)recd[i].file); + if (rc != 0) + { + traceit("failed to start echotest thread\n"); + /* + When the echotest thread runs, it deletes the file, + Because the echotest thread did NOT start, we delete the file here + */ + unlink(recd[i].file); + } + pthread_attr_destroy(&attr); + } + break; + } + else + /* Is the data to be recorded for voicemail */ + if ((vm[i].fd >= 0) && + (memcmp(vm[i].streamid, readBuffer + 14, 2) == 0)) + { + time(&vm[i].last_time); + + memcpy(recbuf, "DSVT", 4); + recbuf[4] = 0x20; + recbuf[5] = 0x00; + recbuf[6] = 0x00; + recbuf[7] = 0x00; + recbuf[8] = readBuffer[10]; + recbuf[9] = readBuffer[11]; + recbuf[10] = readBuffer[12]; + recbuf[11] = readBuffer[13]; + memcpy(recbuf + 12, readBuffer + 14, 3); + if (recvlen == 29) + memcpy(recbuf + 15, readBuffer + 17, 12); + else + memcpy(recbuf + 15, readBuffer + 20, 12); + + rec_len = 27; + (void)write(vm[i].fd, &rec_len, 2); + (void)write(vm[i].fd, (char *)recbuf, rec_len); + + if ((readBuffer[16] & 0x40) != 0) + { + vm[i].streamid[0] = 0x00; + vm[i].streamid[1] = 0x00; + vm[i].last_time = 0; + close(vm[i].fd); vm[i].fd = -1; + // traceit("Closed voicemail audio file:[%s]\n", vm[i].file); + } + break; + } + else + /* or maybe this is cross-banding data */ + if ((memcmp(toRptr[i].streamid, readBuffer + 14, 2) == 0) && + (toRptr[i].adr == fromRptr.sin_addr.s_addr)) + { + sendto(srv_sock, (char *)readBuffer,29,0, + (struct sockaddr *)&toRptr[i].band_addr, + sizeof(struct sockaddr_in)); + + /* timeit */ + time(&toRptr[i].last_time); + + /* bump G2 counter */ + toRptr[i].G2_COUNTER ++; + + toRptr[i].sequence = readBuffer[16]; + + /* End of stream ? */ + if ((readBuffer[16] & 0x40) != 0) + { + toRptr[i].last_time = 0; + toRptr[i].streamid[0] = '\0'; + toRptr[i].streamid[1] = '\0'; + toRptr[i].adr = 0; + } + break; + } + } + + if ((readBuffer[16] & 0x40) != 0) + { + if (QSO_DETAILS) + traceit("END from rptr: cntr=%02x %02x, streamID=%d,%d, %d bytes\n", + readBuffer[4], readBuffer[5], + readBuffer[14],readBuffer[15],recvlen); + } + } + } + FD_CLR (srv_sock,&fdset); + } + } +} + +static void compute_aprs_hash() +{ + short hash = 0x73e2; + short i = 0; + short len = 0; + char *p = NULL; + char rptr_sign[CALL_SIZE + 1]; + + strcpy(rptr_sign, OWNER); + p = strchr(rptr_sign, ' '); + if (!p) + { + traceit("Failed to build repeater callsign for aprs hash\n"); + return; + } + *p = '\0'; + p = rptr_sign; + len = strlen(rptr_sign); + + while (i < len) + { + hash ^= (*p++) << 8; + hash ^= (*p++); + i += 2; + } + traceit("aprs hash code=[%d] for %s\n", hash, OWNER); + RPTR_ID.aprs_hash = hash; + + return; +} + +static void aprs_open() +{ + struct timespec req; + bool ok = false; + + fd_set fdset; + struct timeval tv; + short int MAX_WAIT = 15; /* 15 seconds wait time MAX */ + int val = 1; + socklen_t val_len; + char snd_buf[512]; + char rcv_buf[512]; + int rc = 0; + + /* do some mimimal checking */ + if (RPTR_ID.aprs_host[0] == '\0') + { + traceit("Invalid value for APRS_HOST\n"); + return; + } + if (RPTR_ID.aprs_port == 0) + { + traceit("Invalid value for APRS_PORT\n"); + return; + } + if (RPTR_ID.aprs_interval == 0) + { + traceit("Invalid value for APRS_INTERVAL\n"); + return; + } + + ok = resolve_rmt(RPTR_ID.aprs_host, SOCK_STREAM, &aprs_addr); + if (!ok) + { + traceit("Can not resolve APRS_HOST %s\n", RPTR_ID.aprs_host); + return; + } + + /* fill it in */ + aprs_addr.sin_family = AF_INET; + aprs_addr.sin_port = htons(RPTR_ID.aprs_port); + + aprs_addr_len = sizeof(aprs_addr); + + aprs_sock = socket(PF_INET,SOCK_STREAM,0); + if (aprs_sock == -1) + { + traceit("Failed to create aprs socket,error=%d\n",errno); + return; + } + fcntl(aprs_sock,F_SETFL,O_NONBLOCK); + + val = 1; + if (setsockopt(aprs_sock,IPPROTO_TCP,TCP_NODELAY,(char *)&val, sizeof(val)) == -1) + { + traceit("setsockopt TCP_NODELAY TCP for aprs socket failed,error=%d\n",errno); + close(aprs_sock); + aprs_sock = -1; + return; + } + + traceit("Trying to connect to APRS...\n"); + rc = connect(aprs_sock, (struct sockaddr *)&aprs_addr, aprs_addr_len); + if (rc != 0) + { + if (errno == EINPROGRESS) + { + traceit("Waiting for up to %d seconds for APRS_HOST\n", MAX_WAIT); + while (MAX_WAIT > 0) + { + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&fdset); + FD_SET(aprs_sock, &fdset); + rc = select(aprs_sock + 1, NULL, &fdset, NULL, &tv); + + if (rc < 0) + { + traceit("Failed to connect to APRS...select,error=%d\n", errno); + close(aprs_sock); + aprs_sock = -1; + return; + } + else + if (rc == 0) /* timeout */ + { + MAX_WAIT--; + sleep(1); + } + else + { + val = 1; /* Assume it fails */ + val_len = sizeof(val); + if (getsockopt(aprs_sock, SOL_SOCKET, SO_ERROR, (char *) &val, &val_len) < 0) + { + traceit("Failed to connect to APRS...getsockopt, error=%d\n", errno); + close(aprs_sock); + aprs_sock = -1; + return; + } + else + if (val == 0) + break; + + MAX_WAIT--; + sleep(1); + } + } + if (MAX_WAIT == 0) + { + traceit("Failed to connect to APRS...timeout\n"); + close(aprs_sock); + aprs_sock = -1; + return; + } + } + else + { + traceit("Failed to connect to APRS, error=%d\n", errno); + close(aprs_sock); + aprs_sock = -1; + return; + } + } + traceit("Connected to APRS %s:%d\n", RPTR_ID.aprs_host, RPTR_ID.aprs_port); + + /* login to aprs */ + sprintf(snd_buf, "user %s pass %d vers g2_ircddb 2.99 UDP 5 ", + OWNER, RPTR_ID.aprs_hash); + + /* add the user's filter */ + if (RPTR_ID.aprs_filter[0] != '\0') + { + strcat(snd_buf, "filter "); + strcat(snd_buf, RPTR_ID.aprs_filter); + } + // traceit("APRS login command:[%s]\n", snd_buf); + strcat(snd_buf, "\r\n"); + + while (true) + { + rc = writen(snd_buf, strlen(snd_buf)); + if (rc < 0) + { + if (errno == EWOULDBLOCK) + { + recv(aprs_sock, rcv_buf, sizeof(rcv_buf), 0); + req.tv_sec = 0; + req.tv_nsec = 100000000; // 100 milli + nanosleep(&req, NULL); + } + else + { + traceit("APRS login command failed, error=%d\n", errno); + break; + } + } + else + { + // traceit("APRS login command sent\n"); + break; + } + } + recv(aprs_sock, rcv_buf, sizeof(rcv_buf), 0); + + return; +} + +void *send_aprs_beacon(void *arg) +{ + struct timespec req; + + int rc; + char snd_buf[512]; + char rcv_buf[512]; + float tmp_lat; + float tmp_lon; + float lat; + float lon; + char lat_s[15]; + char lon_s[15]; + time_t last_beacon_time = 0; + time_t last_keepalive_time = 0; + time_t tnow = 0; + short int i; + struct sigaction act; + + /* + Every 20 seconds, the remote APRS host sends a KEEPALIVE packet-comment + on the TCP/APRS port. + If we have not received any KEEPALIVE packet-comment after 5 minutes + we must assume that the remote APRS host is down or disappeared + or has dropped the connection. In these cases, we must re-connect. + There are 3 keepalive packets in one minute, or every 20 seconds. + In 5 minutes, we should have received a total of 15 keepalive packets. + */ + short THRESHOLD_COUNTDOWN = 15; + + arg = arg; + + act.sa_handler = sigCatch; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + if (sigaction(SIGTERM, &act, 0) != 0) + { + traceit("sigaction-TERM failed, error=%d\n", errno); + traceit("beacon thread exiting...\n"); + pthread_exit(NULL); + } + if (sigaction(SIGINT, &act, 0) != 0) + { + traceit("sigaction-INT failed, error=%d\n", errno); + traceit("beacon thread exiting...\n"); + pthread_exit(NULL); + } + if (sigaction(SIGPIPE, &act, 0) != 0) + { + traceit("sigaction-PIPE failed, error=%d\n", errno); + traceit("beacon thread exiting...\n"); + pthread_exit(NULL); + } + + time(&last_keepalive_time); + + /* This thread is also saying to the APRS_HOST that we are ALIVE */ + while (keep_running) + { + if (aprs_sock == -1) + { + aprs_open(); + if (aprs_sock == -1) + sleep(1); + else + THRESHOLD_COUNTDOWN = 15; + } + + time(&tnow); + if ((tnow - last_beacon_time) > (RPTR_ID.aprs_interval * 60)) + { + for (i = 0; i < 3; i++) + { + if (RPTR_ID.desc[i][0] != '\0') + { + tmp_lat = fabs(RPTR_ID.lat[i]); + tmp_lon = fabs(RPTR_ID.lon[i]); + lat = floor(tmp_lat); + lon = floor(tmp_lon); + lat = (tmp_lat - lat) * 60.0F + lat * 100.0F; + lon = (tmp_lon - lon) * 60.0F + lon * 100.0F; + + if (lat >= 1000.0F) + sprintf(lat_s, "%.2f", lat); + else + if (lat >= 100.0F) + sprintf(lat_s, "0%.2f", lat); + else + if (lat >= 10.0F) + sprintf(lat_s, "00%.2f", lat); + else + sprintf(lat_s, "000%.2f", lat); + + if (lon >= 10000.0F) + sprintf(lon_s, "%.2f", lon); + else + if (lon >= 1000.0F) + sprintf(lon_s, "0%.2f", lon); + else + if (lon >= 100.0F) + sprintf(lon_s, "00%.2f", lon); + else + if (lon >= 10.0F) + sprintf(lon_s, "000%.2f", lon); + else + sprintf(lon_s, "0000%.2f", lon); + + /* send to aprs */ + sprintf(snd_buf, "%s>APJI23,TCPIP*,qAC,%sS:!%s%cD%s%c&RNG%04u %s %s", + RPTR_ID.rptr[i], RPTR_ID.rptr[i], + lat_s, (RPTR_ID.lat[i] < 0.0F) ? 'S' : 'N', + lon_s, (RPTR_ID.lon[i] < 0.0F) ? 'W' : 'E', + RPTR_ID.range[i], RPTR_ID.band[i], RPTR_ID.desc[i]); + + // traceit("APRS Beacon =[%s]\n", snd_buf); + strcat(snd_buf, "\r\n"); + + while (keep_running) + { + if (aprs_sock == -1) + { + aprs_open(); + if (aprs_sock == -1) + sleep(1); + else + THRESHOLD_COUNTDOWN = 15; + } + else + { + rc = writen(snd_buf, strlen(snd_buf)); + if (rc < 0) + { + if ((errno == EPIPE) || + (errno == ECONNRESET) || + (errno == ETIMEDOUT) || + (errno == ECONNABORTED) || + (errno == ESHUTDOWN) || + (errno == EHOSTUNREACH) || + (errno == ENETRESET) || + (errno == ENETDOWN) || + (errno == ENETUNREACH) || + (errno == EHOSTDOWN) || + (errno == ENOTCONN)) + { + traceit("send_aprs_beacon: APRS_HOST closed connection,error=%d\n",errno); + close(aprs_sock); + aprs_sock = -1; + } + else + if (errno == EWOULDBLOCK) + { + req.tv_sec = 0; + req.tv_nsec = 100000000; // 100 milli + nanosleep(&req, NULL); + } + else + { + /* Cant do nothing about it */ + traceit("send_aprs_beacon failed, error=%d\n", errno); + break; + } + } + else + { + // traceit("APRS beacon sent\n"); + break; + } + } + rc = recv(aprs_sock, rcv_buf, sizeof(rcv_buf), 0); + if (rc > 0) + THRESHOLD_COUNTDOWN = 15; + } + } + rc = recv(aprs_sock, rcv_buf, sizeof(rcv_buf), 0); + if (rc > 0) + THRESHOLD_COUNTDOWN = 15; + } + time(&last_beacon_time); + } + /* + Are we still receiving from APRS host ? + */ + rc = recv(aprs_sock, rcv_buf, sizeof(rcv_buf), 0); + if (rc < 0) + { + if ((errno == EPIPE) || + (errno == ECONNRESET) || + (errno == ETIMEDOUT) || + (errno == ECONNABORTED) || + (errno == ESHUTDOWN) || + (errno == EHOSTUNREACH) || + (errno == ENETRESET) || + (errno == ENETDOWN) || + (errno == ENETUNREACH) || + (errno == EHOSTDOWN) || + (errno == ENOTCONN)) + { + traceit("send_aprs_beacon: recv error: APRS_HOST closed connection,error=%d\n",errno); + close(aprs_sock); + aprs_sock = -1; + } + } + else + if (rc == 0) + { + traceit("send_aprs_beacon: recv: APRS shutdown\n"); + close(aprs_sock); + aprs_sock = -1; + } + else + THRESHOLD_COUNTDOWN = 15; + + req.tv_sec = 0; + req.tv_nsec = 100000000; // 100 milli + nanosleep(&req, NULL); + + /* 20 seconds passed already ? */ + time(&tnow); + if ((tnow - last_keepalive_time) > 20) + { + /* we should be receving keepalive packets ONLY if the connection is alive */ + if (aprs_sock >= 0) + { + if (THRESHOLD_COUNTDOWN > 0) + THRESHOLD_COUNTDOWN--; + + if (THRESHOLD_COUNTDOWN == 0) + { + traceit("APRS host keepalive timeout\n"); + close(aprs_sock); + aprs_sock = -1; + } + } + /* reset timer */ + time(&last_keepalive_time); + } + } + traceit("beacon thread exiting...\n"); + pthread_exit(NULL); +} + +static void *echotest(void *arg) +{ + char *file = (char *)arg; + struct timespec req; + + FILE *fp = NULL; + unsigned short rlen = 0; + size_t nread = 0; + unsigned char dstar_buf[56]; + unsigned char rptr_buf[58]; + short int i = 0; + struct sigaction act; + + act.sa_handler = sigCatch; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + if (sigaction(SIGTERM, &act, 0) != 0) + { + traceit("sigaction-TERM failed, error=%d\n", errno); + traceit("echotest thread exiting...\n"); + pthread_exit(NULL); + } + if (sigaction(SIGINT, &act, 0) != 0) + { + traceit("sigaction-INT failed, error=%d\n", errno); + traceit("echotest thread exiting...\n"); + pthread_exit(NULL); + } + if (sigaction(SIGPIPE, &act, 0) != 0) + { + traceit("sigaction-PIPE failed, error=%d\n", errno); + traceit("echotest thread exiting...\n"); + pthread_exit(NULL); + } + + traceit("File to playback:[%s]\n", file); + + fp = fopen(file, "rb"); + if (!fp) + { + traceit("Failed to open file %s\n", file); + pthread_exit(NULL); + } + + nread = fread(dstar_buf, 10, 1, fp); + if (nread != 1) + { + traceit("Cant read first 10 bytes in %s\n", file); + fclose(fp); + pthread_exit(NULL); + } + + if (memcmp(dstar_buf, "DVTOOL", 6) != 0) + { + traceit("DVTOOL keyword not found in %s\n", file); + fclose(fp); + pthread_exit(NULL); + } + + sleep(PLAY_WAIT); + while (keep_running) + { + nread = fread(&rlen, 2, 1, fp); + if (nread != 1) + break; + + if ((rlen != 56) && (rlen != 27)) + { + traceit("Expected 56 bytes or 27 bytes, found %d\n", rlen); + break; + } + nread = fread(dstar_buf, rlen, 1, fp); + if (nread == 1) + { + if (memcmp(dstar_buf, "DSVT", 4) != 0) + { + traceit("DVST keyword not found in %s\n", file); + break; + } + + if (dstar_buf[8] != 0x20) + { + traceit("Not Voice type in %s\n", file); + break; + } + + if ((dstar_buf[4] != 0x10) && (dstar_buf[4] != 0x20)) + { + traceit("Not a valid record type in %s\n",file); + break; + } + + if (rlen == 56) + { + /* which module is this recording for? */ + if (dstar_buf[25] == 'A') + i = 0; + else + if (dstar_buf[25] == 'B') + i = 1; + else + if (dstar_buf[25] == 'C') + i = 2; + + + /*** + WARNING: G2_COUNTER is accessed by both threads. + It should be protected with a MUTEX, + but since this version is NOT for RP2C, but for home-brew + it does not really matter. + Anyway, accessing G2_COUNTER and adding 1 to it, + should be an atomic operation. + ***/ + + memcpy(rptr_buf, "DSTR", 4); + rptr_buf[5] = (unsigned char)(toRptr[i].G2_COUNTER & 0xff); + rptr_buf[4] = (unsigned char)((toRptr[i].G2_COUNTER >> 8) & 0xff); + rptr_buf[6] = 0x73; + rptr_buf[7] = 0x12; + rptr_buf[8] = 0x00; + rptr_buf[9] = 0x30; + rptr_buf[10] = 0x20; + memcpy(rptr_buf + 11, dstar_buf + 9, 47); + + /* We did not change anything */ + // calcPFCS(rptr_buf, 58); + } + else + { + rptr_buf[5] = (unsigned char)(toRptr[i].G2_COUNTER & 0xff); + rptr_buf[4] = (unsigned char)((toRptr[i].G2_COUNTER >> 8) & 0xff); + rptr_buf[9] = 0x13; + memcpy(rptr_buf + 11, dstar_buf + 9, 18); + } + + sendto(srv_sock, (char *)rptr_buf, rlen + 2, 0, + (struct sockaddr *)&toRptr[i].band_addr, + sizeof(struct sockaddr_in)); + + toRptr[i].G2_COUNTER ++; + + req.tv_sec = 0; + req.tv_nsec = PLAY_DELAY * 1000000; + nanosleep(&req, NULL); + } + } + fclose(fp); + if (!strstr(file, "voicemail.dat")) + unlink(file); + traceit("Finished playing\n"); + pthread_exit(NULL); +} + +static void qrgs_and_maps() +{ + const char *delim = ","; + + char *lat_s = NULL; + double lat = 0.00; + char *lon_s = NULL; + double lon = 0.00; + char *desc1 = NULL; + char *desc2 = NULL; + char *url = NULL; + + char *freq_s = NULL; + double freq = 0.00; + char *shift_s = NULL; + double shift = 0.00; + char *range_s = NULL; + double range = 0.00; + char *agl_s = NULL; + double agl = 0.00; + + char *saveptr = NULL; + + if (QTH[0] != '\0') + { + lat_s = strtok_r(QTH, delim, &saveptr); + lon_s = strtok_r(NULL, delim, &saveptr); + desc1 = strtok_r(NULL, delim, &saveptr); + desc2 = strtok_r(NULL, delim, &saveptr); + url = strtok_r(NULL, delim, &saveptr); + + if (lat_s) + lat = atof(lat_s); + if (lon_s) + lon = atof(lon_s); + + ii->rptrQTH(lat, lon, (desc1)?desc1:"", (desc2)?desc2:"", (url)?url:""); + } + + saveptr = NULL; + + if (QRG_A[0] != '\0') + { + freq_s = strtok_r(QRG_A, delim, &saveptr); + shift_s = strtok_r(NULL, delim, &saveptr); + range_s = strtok_r(NULL, delim, &saveptr); + agl_s = strtok_r(NULL, delim, &saveptr); + + if (freq_s) + freq = atof(freq_s); + if (shift_s) + shift = atof(shift_s); + if (range_s) + range = atof(range_s); + if (agl_s) + agl = atof(agl_s); + + ii->rptrQRG("A", freq, shift, range, agl); + } + + saveptr = NULL; + + if (QRG_B[0] != '\0') + { + freq_s = strtok_r(QRG_B, delim, &saveptr); + shift_s = strtok_r(NULL, delim, &saveptr); + range_s = strtok_r(NULL, delim, &saveptr); + agl_s = strtok_r(NULL, delim, &saveptr); + + if (freq_s) + freq = atof(freq_s); + if (shift_s) + shift = atof(shift_s); + if (range_s) + range = atof(range_s); + if (agl_s) + agl = atof(agl_s); + + ii->rptrQRG("B", freq, shift, range, agl); + } + + saveptr = NULL; + + if (QRG_C[0] != '\0') + { + freq_s = strtok_r(QRG_C, delim, &saveptr); + shift_s = strtok_r(NULL, delim, &saveptr); + range_s = strtok_r(NULL, delim, &saveptr); + agl_s = strtok_r(NULL, delim, &saveptr); + + if (freq_s) + freq = atof(freq_s); + if (shift_s) + shift = atof(shift_s); + if (range_s) + range = atof(range_s); + if (agl_s) + agl = atof(agl_s); + + ii->rptrQRG("C", freq, shift, range, agl); + } + + return; +} + +int main(int argc, char **argv) +{ + short int i; + struct sigaction act; + bool ok; + char *p = NULL; + + int rc = 0; + + setvbuf(stdout, (char *)NULL, _IOLBF, 0); + + traceit("VERSION %s\n", VERSION); + if (argc != 2) + { + traceit("Example: g2_ircddb g2_ircddb.cfg\n"); + return 1; + } + + /* Used to validate MYCALL input */ + rc = regcomp(&preg, + "^(([1-9][A-Z])|([A-Z][0-9])|([A-Z][A-Z][0-9]))[0-9A-Z]*[A-Z][ ]*[ A-RT-Z]$", + REG_EXTENDED | REG_NOSUB); + if (rc != REG_NOERROR) + { + traceit("The IRC regular expression is NOT valid\n"); + return 1; + } + + act.sa_handler = sigCatch; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + if (sigaction(SIGTERM, &act, 0) != 0) + { + traceit("sigaction-TERM failed, error=%d\n", errno); + return 1; + } + if (sigaction(SIGINT, &act, 0) != 0) + { + traceit("sigaction-INT failed, error=%d\n", errno); + return 1; + } + if (sigaction(SIGPIPE, &act, 0) != 0) + { + traceit("sigaction-PIPE failed, error=%d\n", errno); + return 1; + } + + RPTR_ID.aprs_host[0] = '\0'; + RPTR_ID.aprs_port = 0; + RPTR_ID.aprs_hash = -1; + RPTR_ID.aprs_interval = 0; + RPTR_ID.aprs_filter[0] = '\0'; + for (i = 0; i < 3; i++) + { + RPTR_ID.lat[i] = 0.00; + RPTR_ID.lon[i] = 0.00; + RPTR_ID.range[i] = 0; + RPTR_ID.desc[i][0] = '\0'; + } + strcpy(RPTR_ID.band[0], "23cm"); + strcpy(RPTR_ID.band[1], "70cm"); + strcpy(RPTR_ID.band[2], "2m"); + + for (i = 0; i < 3; i++) + { + aprs_streamID[i].streamID[0] = 0x00; + aprs_streamID[i].streamID[1] = 0x00; + aprs_streamID[i].last_time = 0; + } + + for (i = 0; i < 3; i++) + { + band_txt[i].streamID[0] = 0x00; + band_txt[i].streamID[1] = 0x00; + band_txt[i].flags[0] = 0x00; + band_txt[i].flags[1] = 0x00; + band_txt[i].flags[2] = 0x00; + band_txt[i].lh_mycall[0] = '\0'; + band_txt[i].lh_sfx[0] = '\0'; + band_txt[i].lh_yrcall[0] = '\0'; + band_txt[i].lh_rpt1[0] = '\0'; + band_txt[i].lh_rpt2[0] = '\0'; + + band_txt[i].last_time = 0; + + band_txt[i].txt[0] = '\0'; + band_txt[i].txt_cnt = 0; + band_txt[i].txt_stats_sent = false; + + band_txt[i].dest_rptr[0] = '\0'; + + band_txt[i].temp_line[0] = '\0'; + band_txt[i].temp_line_cnt = 0; + band_txt[i].gprmc[0] = '\0'; + band_txt[i].gpid[0] = '\0'; + band_txt[i].is_gps_sent = false; + band_txt[i].gps_last_time = 0; + + band_txt[i].num_dv_frames = 0; + band_txt[i].num_dv_silent_frames = 0; + band_txt[i].num_bit_errors = 0; + } + + /* process configuration file */ + rc = read_config(argv[1]); + if (rc != 0) + { + traceit("Failed to process config file %s\n", argv[1]); + return 1; + } + + /* build the repeater callsigns for aprs */ + strcpy(RPTR_ID.rptr[0], OWNER); + p = strchr(RPTR_ID.rptr[0], ' '); + if (!p) + { + traceit("Failed to build repeater callsigns for aprs\n"); + return 1; + } + *p = '\0'; + strcpy(RPTR_ID.rptr[1], RPTR_ID.rptr[0]); + strcpy(RPTR_ID.rptr[2], RPTR_ID.rptr[0]); + strcat(RPTR_ID.rptr[0], "-A"); + strcat(RPTR_ID.rptr[1], "-B"); + strcat(RPTR_ID.rptr[2], "-C"); + traceit("Repeater callsigns: [%s] [%s] [%s]\n", + RPTR_ID.rptr[0], RPTR_ID.rptr[1], RPTR_ID.rptr[2]); + + aprs_init(); + compute_aprs_hash(); + + ok = ::wxInitialize(); + if (!ok) + { + traceit("Failed to initialize wx\n"); + return 1; + } + + ii = new CIRCDDB(IRC_DDB_HOST, IRC_DDB_PORT, owner, IRC_PASS, VERSION, LOCAL_IRC_IP); + ok = ii->open(); + if (!ok) + { + traceit("irc open failed\n"); + ::wxUninitialize(); + return 1; + } + + rc = ii->getConnectionState(); + traceit("Waiting for irc connection status of 2\n"); + i = 0; + while (rc < 2) + { + traceit("irc status=%d\n", rc); + if (rc < 2) + { + i++; + sleep(5); + } + else + break; + + if (!keep_running) + break; + + if (i > 5) + { + traceit("We can not wait any longer...\n"); + break; + } + rc = ii->getConnectionState(); + } + + do + { + /* udp port 40000 must open first */ + rc = g2_open(); + if (rc != 0) + { + traceit("g2_open() failed\n"); + break; + } + + /* Open udp INTERNAL port */ + rc = srv_open(); + if (rc != 0) + { + traceit("srv_open() failed\n"); + break; + } + + /* recording for echotest on local repeater modules */ + for (i = 0; i < 3; i++) + { + recd[i].last_time = 0; + recd[i].streamid[0] = 0x00; + recd[i].streamid[1] = 0x00; + recd[i].fd = -1; + memset(recd[i].file, 0, sizeof(recd[i].file)); + } + + /* recording for voicemail on local repeater modules */ + for (i = 0; i < 3; i++) + { + vm[i].last_time = 0; + vm[i].streamid[0] = 0x00; + vm[i].streamid[1] = 0x00; + vm[i].fd = -1; + memset(vm[i].file, 0, sizeof(vm[i].file)); + + if (i == 0) + snprintf(vm[i].file, FILENAME_MAX, "%s/%c_%s", + ECHOTEST_DIR,'A',"voicemail.dat"); + else + if (i == 1) + snprintf(vm[i].file, FILENAME_MAX, "%s/%c_%s", + ECHOTEST_DIR,'B',"voicemail.dat"); + else + snprintf(vm[i].file, FILENAME_MAX, "%s/%c_%s", + ECHOTEST_DIR,'C',"voicemail.dat"); + + if (access(vm[i].file, F_OK) != 0) + memset(vm[i].file, 0, sizeof(vm[i].file)); + else + traceit("Loaded voicemail file: %s for mod %d\n", + vm[i].file, i); + } + + /* the repeater modules run on these ports */ + for (i = 0; i < 3; i++) + { + memset(&toRptr[i],0,sizeof(toRptr[i])); + + memset(toRptr[i].saved_hdr, 0, sizeof(toRptr[i].saved_hdr)); + toRptr[i].saved_adr = 0; + + toRptr[i].streamid[0] = '\0'; + toRptr[i].streamid[1] = '\0'; + toRptr[i].adr = 0; + + toRptr[i].band_addr.sin_family = AF_INET; + toRptr[i].band_addr.sin_addr.s_addr = inet_addr(TO_RPTR_IP[i]); + toRptr[i].band_addr.sin_port = htons(TO_RPTR_PORT[i]); + + toRptr[i].last_time = 0; + toRptr[i].G2_COUNTER = 0; + + toRptr[i].sequence = 0x00; + } + + /* + Initialize the end_of_audio that will be sent to the local repeater + when audio from remote G2 has timed out + */ + memcpy(end_of_audio, "DSTR", 4); + end_of_audio[6] = 0x73; + end_of_audio[7] = 0x12; + end_of_audio[8] = 0x00; + end_of_audio[9] = 0x13; + end_of_audio[10] = 0x20; + end_of_audio[11] = 0x00; + end_of_audio[12] = 0x01; + memset(end_of_audio + 17, '\0', 9); + end_of_audio[26] = 0x70; + end_of_audio[27] = 0x4f; + end_of_audio[28] = 0x93; + + /* to remote systems */ + for (i = 0; i < 3; i++) + { + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].streamid[0] = '\0'; + to_remote_g2[i].streamid[1] = '\0'; + to_remote_g2[i].last_time = 0; + } + + /* where to send packets to g2_link */ + memset(&plug,0,sizeof(struct sockaddr_in)); + plug.sin_family = AF_INET; + plug.sin_port = htons(TO_G2_LINK_PORT); + plug.sin_addr.s_addr = inet_addr(TO_G2_LINK_IP); + + traceit("g2_ircddb...entering processing loop\n"); + + if (SEND_QRGS_MAPS) + qrgs_and_maps(); + + runit(); + traceit("Leaving processing loop...\n"); + + } while (false); + + if (srv_sock != -1) + { + close(srv_sock); + traceit("Closed G2_INTERNAL_PORT\n"); + } + + if (g2_sock != -1) + { + close(g2_sock); + traceit("Closed G2_EXTERNAL_PORT\n"); + } + + if (aprs_sock != -1) + { + close(aprs_sock); + traceit("Closed APRS\n"); + } + + for (i = 0; i < 3; i++) + { + recd[i].last_time = 0; + recd[i].streamid[0] = 0x00; + recd[i].streamid[1] = 0x00; + if (recd[i].fd >= 0) + { + close(recd[i].fd); + unlink(recd[i].file); + } + } + + ii->close(); + ::wxUninitialize(); + traceit("g2_ircddb exiting\n"); + return rc; +} + +static void aprs_init() +{ + short int rptr_idx; + + /* Initialize the statistics on the APRS packets */ + for (rptr_idx = 0; rptr_idx < 3; rptr_idx++) + { + aprs_pack[rptr_idx].al = al_none; + aprs_pack[rptr_idx].data[0] = '\0'; + aprs_pack[rptr_idx].len = 0; + aprs_pack[rptr_idx].buf[0] = '\0'; + aprs_pack[rptr_idx].sl = sl_first; + aprs_pack[rptr_idx].is_sent = false; + } + + /* Initialize the APRS host */ + memset(&aprs_addr,0,sizeof(struct sockaddr_in)); + aprs_addr_len = sizeof(aprs_addr); + + return; +} + +// This is called when header comes in from repeater +static void aprs_select_band(short int rptr_idx, unsigned char *streamID) +{ + if ((rptr_idx < 0) || (rptr_idx > 2)) + { + traceit("ERROR in aprs_select_band, invalid mod %d\n", rptr_idx); + return; + } + + /* lock on the streamID */ + aprs_streamID[rptr_idx].streamID[0] = streamID[0]; + aprs_streamID[rptr_idx].streamID[1] = streamID[1]; + // aprs_streamID[rptr_idx].last_time = 0; + + aprs_reset(rptr_idx); + return; +} + +// This is called when data(text) comes in from repeater +// Parameter buf is either: +// 12 bytes(packet from repeater was 29 bytes) or +// 15 bytes(packet from repeater was 32 bytes) +// Parameter len is either 12 or 15, because we took passed over the first 17 bytes +// in the repeater data +// Paramter seq is the byte at pos# 16(counting from zero) in the repeater data +static void aprs_process_text(unsigned char *streamID, + unsigned char seq, + unsigned char *buf, + unsigned int len) +{ + bool done = false; + unsigned char aprs_data[200]; + unsigned int aprs_len; + char *p = NULL; + char *hdr = NULL; + char *aud = NULL; + char aprs_buf[1024]; + int rc; + time_t tnow = 0; + + short int i; + short int rptr_idx = -1; + + len = len; + + for (i = 0; i < 3; i++) + { + if (memcmp(streamID, aprs_streamID[i].streamID, 2) == 0) + { + rptr_idx = i; + break; + } + } + + if ((rptr_idx < 0) || (rptr_idx > 2)) + { + // traceit("ERROR in aprs_process_text: rptr_idx %d is invalid\n", rptr_idx); + return; + } + + if ((seq & 0x40) == 0x40) + return; + + if ((seq & 0x1f) == 0x00) + { + aprs_sync_it(rptr_idx); + return; + } + + done = aprs_write_data(rptr_idx, buf + 9); + if (!done) + return; + + aprs_len = aprs_get_data(rptr_idx, aprs_data, 200); + aprs_data[aprs_len] = '\0'; + + time(&tnow); + if ((tnow - aprs_streamID[rptr_idx].last_time) < 30) + return; + + if (aprs_sock == -1) + return; + + p = strchr((char*)aprs_data, ':'); + if (!p) + { + aprs_reset(rptr_idx); + return; + } + *p = '\0'; + + + hdr = (char *)aprs_data; + aud = p + 1; + if (strchr(hdr, 'q') != NULL) + return; + + p = strchr(aud, '\r'); + *p = '\0'; + + sprintf(aprs_buf, "%s,qAR,%s:%s\r\n", hdr, RPTR_ID.rptr[rptr_idx], aud); + // traceit("GPS-A=%s", aprs_buf); + rc = writen(aprs_buf, strlen(aprs_buf)); + if (rc == -1) + { + if ((errno == EPIPE) || + (errno == ECONNRESET) || + (errno == ETIMEDOUT) || + (errno == ECONNABORTED) || + (errno == ESHUTDOWN) || + (errno == EHOSTUNREACH) || + (errno == ENETRESET) || + (errno == ENETDOWN) || + (errno == ENETUNREACH) || + (errno == EHOSTDOWN) || + (errno == ENOTCONN)) + { + traceit("aprs_process_text: APRS_HOST closed connection,error=%d\n",errno); + close(aprs_sock); + aprs_sock = -1; + } + else /* if it is WOULDBLOCK, we will not go into a loop here */ + traceit("aprs_process_text: send error=%d\n", errno); + } + + time(&aprs_streamID[rptr_idx].last_time); + + return; +} + +static bool aprs_write_data(short int rptr_idx, unsigned char *data) +{ + + if ((rptr_idx < 0) || (rptr_idx > 2)) + { + traceit("ERROR in aprs_write_data: rptr_idx %d is invalid\n", rptr_idx); + return false; + } + + if (aprs_pack[rptr_idx].is_sent) + return false; + + switch (aprs_pack[rptr_idx].sl) + { + case sl_first: + aprs_pack[rptr_idx].buf[0] = data[0] ^ 0x70; + aprs_pack[rptr_idx].buf[1] = data[1] ^ 0x4f; + aprs_pack[rptr_idx].buf[2] = data[2] ^ 0x93; + aprs_pack[rptr_idx].sl = sl_second; + return false; + + case sl_second: + aprs_pack[rptr_idx].buf[3] = data[0] ^ 0x70; + aprs_pack[rptr_idx].buf[4] = data[1] ^ 0x4f; + aprs_pack[rptr_idx].buf[5] = data[2] ^ 0x93; + aprs_pack[rptr_idx].sl = sl_first; + break; + } + + if ((aprs_pack[rptr_idx].buf[0] & 0xf0) != 0x30) + return false; + + return aprs_add_data(rptr_idx, aprs_pack[rptr_idx].buf + 1); + +} + + +static void aprs_reset(short int rptr_idx) +{ + if ((rptr_idx < 0) || (rptr_idx > 2)) + { + traceit("ERROR in aprs_reset: rptr_idx %d is invalid\n", rptr_idx); + return; + } + + aprs_pack[rptr_idx].al = al_none; + aprs_pack[rptr_idx].len = 0; + aprs_pack[rptr_idx].sl = sl_first; + aprs_pack[rptr_idx].is_sent = false; + + return; +} + +static void aprs_sync_it(short int rptr_idx) +{ + if ((rptr_idx < 0) || (rptr_idx > 2)) + { + traceit("ERROR in aprs_sync_it: rptr_idx %d is invalid\n", rptr_idx); + return; + } + + aprs_pack[rptr_idx].sl = sl_first; + return; +} + +static bool aprs_add_data(short int rptr_idx, unsigned char *data) +{ + unsigned int i; + unsigned char c; + bool ok; + + if ((rptr_idx < 0) || (rptr_idx > 2)) + { + traceit("ERROR in aprs_add_data: rptr_idx %d is invalid\n", rptr_idx); + return false; + } + + for (i = 0; i < 5; i++) + { + c = data[i]; + + if ((aprs_pack[rptr_idx].al == al_none) && (c == '$')) + { + aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; + aprs_pack[rptr_idx].len++; + aprs_pack[rptr_idx].al = al_$1; + } + else + if ((aprs_pack[rptr_idx].al == al_$1) && (c == '$')) + { + aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; + aprs_pack[rptr_idx].len++; + aprs_pack[rptr_idx].al = al_$2; + } + else + if ((aprs_pack[rptr_idx].al == al_$2) && (c == 'C')) + { + aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; + aprs_pack[rptr_idx].len++; + aprs_pack[rptr_idx].al = al_c1; + } + else + if ((aprs_pack[rptr_idx].al == al_c1) && (c == 'R')) + { + aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; + aprs_pack[rptr_idx].len++; + aprs_pack[rptr_idx].al = al_r1; + } + else + if ((aprs_pack[rptr_idx].al == al_r1) && (c == 'C')) + { + aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; + aprs_pack[rptr_idx].len++; + aprs_pack[rptr_idx].al = al_c2; + } + else + if (aprs_pack[rptr_idx].al == al_c2) + { + aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; + aprs_pack[rptr_idx].len++; + aprs_pack[rptr_idx].al = al_csum1; + } + else + if (aprs_pack[rptr_idx].al == al_csum1) + { + aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; + aprs_pack[rptr_idx].len++; + aprs_pack[rptr_idx].al = al_csum2; + } + else + if (aprs_pack[rptr_idx].al == al_csum2) + { + aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; + aprs_pack[rptr_idx].len++; + aprs_pack[rptr_idx].al = al_csum3; + } + else + if (aprs_pack[rptr_idx].al == al_csum3) + { + aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; + aprs_pack[rptr_idx].len++; + aprs_pack[rptr_idx].al = al_csum4; + } + else + if ((aprs_pack[rptr_idx].al == al_csum4) && (c == ',')) + { + aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; + aprs_pack[rptr_idx].len++; + aprs_pack[rptr_idx].al = al_data; + } + else + if ((aprs_pack[rptr_idx].al == al_data) && (c != '\r')) + { + aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; + aprs_pack[rptr_idx].len++; + + if (aprs_pack[rptr_idx].len >= 300) + { + traceit("ERROR in aprs_add_data: Expected END of APRS data\n"); + aprs_pack[rptr_idx].len = 0; + aprs_pack[rptr_idx].al = al_none; + } + } + else + if ((aprs_pack[rptr_idx].al == al_data) && (c == '\r')) + { + aprs_pack[rptr_idx].data[aprs_pack[rptr_idx].len] = c; + aprs_pack[rptr_idx].len++; + + + ok = aprs_check_data(rptr_idx); + if (ok) + { + aprs_pack[rptr_idx].al = al_end; + return true; + } + else + { + traceit("BAD checksum in APRS data\n"); + aprs_pack[rptr_idx].al = al_none; + aprs_pack[rptr_idx].len = 0; + } + } + else + { + aprs_pack[rptr_idx].al = al_none; + aprs_pack[rptr_idx].len = 0; + } + } + return false; +} + +static unsigned int aprs_get_data(short int rptr_idx, unsigned char *data, unsigned int len) +{ + unsigned int l; + + if ((rptr_idx < 0) || (rptr_idx > 2)) + { + traceit("ERROR in aprs_get_data: rptr_idx %d is invalid\n", rptr_idx); + return 0; + } + + l = aprs_pack[rptr_idx].len - 10; + + if (l > len) + l = len; + + memcpy(data, aprs_pack[rptr_idx].data + 10, l); + + aprs_pack[rptr_idx].al = al_none; + aprs_pack[rptr_idx].len = 0; + aprs_pack[rptr_idx].is_sent = true; + + return l; + +} + +static bool aprs_check_data(short int rptr_idx) +{ + unsigned int my_sum; + char buf[5]; + + if ((rptr_idx < 0) || (rptr_idx > 2)) + { + traceit("ERROR in aprs_check_data: rptr_idx %d is invalid\n", rptr_idx); + return false; + } + my_sum = aprs_calc_crc(aprs_pack[rptr_idx].data + 10, + aprs_pack[rptr_idx].len - 10); + + sprintf(buf, "%04X", my_sum); + + return (memcmp(buf, + aprs_pack[rptr_idx].data + 5, + 4) == 0); + +} + +static unsigned int aprs_calc_crc(unsigned char* buf, unsigned int len) +{ + unsigned int my_crc = 0xffff; + unsigned int i,j; + unsigned char c; + bool xor_val; + + if (!buf) + return 0; + + if (len <= 0) + return 0; + + for (j = 0; j < len; j++) + { + c = buf[j]; + + for (i = 0; i < 8; i++) + { + xor_val = (((my_crc ^ c) & 0x01) == 0x01); + my_crc >>= 1; + + if (xor_val) + my_crc ^= 0x8408; + + c >>= 1; + } + } + return (~my_crc & 0xffff); +} + +static void gps_send(short int rptr_idx) +{ + time_t tnow = 0; + char *p = NULL; + bool ok = false; + static char old_mycall[CALL_SIZE + 1] = { " " }; + + if ((rptr_idx < 0) || (rptr_idx > 2)) + { + traceit("ERROR in gps_send: rptr_idx %d is invalid\n", rptr_idx); + return; + } + + if (band_txt[rptr_idx].gprmc[0] == '\0') + { + band_txt[rptr_idx].gpid[0] = '\0'; + traceit("missing GPS ID\n"); + return; + } + if (band_txt[rptr_idx].gpid[0] == '\0') + { + band_txt[rptr_idx].gprmc[0] = '\0'; + traceit("Missing GPSRMC\n"); + return; + } + if (memcmp(band_txt[rptr_idx].gpid, band_txt[rptr_idx].lh_mycall, CALL_SIZE) != 0) + { + traceit("MYCALL [%s] does not match first 8 characters of GPS ID [%.8s]\n", + band_txt[rptr_idx].lh_mycall, band_txt[rptr_idx].gpid); + band_txt[rptr_idx].gprmc[0] = '\0'; + band_txt[rptr_idx].gpid[0] = '\0'; + return; + } + + /* if new station, reset last time */ + if (strcmp(old_mycall, band_txt[rptr_idx].lh_mycall) != 0) + { + strcpy(old_mycall, band_txt[rptr_idx].lh_mycall); + band_txt[rptr_idx].gps_last_time = 0; + } + + /* do NOT process often */ + time(&tnow); + if ((tnow - band_txt[rptr_idx].gps_last_time) < 31) + return; + + traceit("GPRMC=[%s]\n", band_txt[rptr_idx].gprmc); + traceit("GPS id=[%s]\n",band_txt[rptr_idx].gpid); + + p = strrchr(band_txt[rptr_idx].gprmc, '*'); + if (!p) + { + /* BAD news, something went wrong */ + traceit("Missing asterisk before checksum in GPRMC\n"); + band_txt[rptr_idx].gprmc[0] = '\0'; + band_txt[rptr_idx].gpid[0] = '\0'; + return; + } + else + { + *p = '\0'; + /* verify csum in GPRMC */ + ok = verify_gps_csum(band_txt[rptr_idx].gprmc + 1, p + 1); + if (!ok) + { + traceit("csum in GPRMC not good\n"); + band_txt[rptr_idx].gprmc[0] = '\0'; + band_txt[rptr_idx].gpid[0] = '\0'; + return; + } + } + + p = strrchr(band_txt[rptr_idx].gpid, '*'); + if (!p) + { + /* BAD news, something went wrong */ + traceit("Missing asterisk before checksum in GPS id\n"); + band_txt[rptr_idx].gprmc[0] = '\0'; + band_txt[rptr_idx].gpid[0] = '\0'; + return; + } + else + { + *p = '\0'; + /* verify csum in GPS id */ + ok = verify_gps_csum(band_txt[rptr_idx].gpid, p + 1); + if (!ok) + { + traceit("csum in GPS id not good\n"); + band_txt[rptr_idx].gprmc[0] = '\0'; + band_txt[rptr_idx].gpid[0] = '\0'; + return; + } + } + + /* now convert GPS into APRS and send it */ + build_aprs_from_gps_and_send(rptr_idx); + + band_txt[rptr_idx].is_gps_sent = true; + time(&(band_txt[rptr_idx].gps_last_time)); + return; +} + +static void build_aprs_from_gps_and_send(short int rptr_idx) +{ + char buf[512]; + char *p = NULL; + const char *delim = ","; + int rc = 0; + + char *saveptr = NULL; + + /* breakdown of GPRMC */ + char *GPRMC = NULL; + char *time_utc = NULL; + char *nav = NULL; + char *lat_str = NULL; + char *lat_NS = NULL; + char *lon_str = NULL; + char *lon_EW = NULL; + /*** dont care about the rest */ + + strcpy(buf, band_txt[rptr_idx].lh_mycall); + p = strchr(buf, ' '); + if (p) + { + if (band_txt[rptr_idx].lh_mycall[7] != ' ') + { + *p = '-'; + *(p + 1) = band_txt[rptr_idx].lh_mycall[7]; + *(p + 2) = '>'; + *(p + 3) = '\0'; + } + else + { + *p = '>'; + *(p + 1) = '\0'; + } + } + else + strcat(buf, ">"); + + strcat(buf, "APDPRS,DSTAR*,qAR,"); + strcat(buf, RPTR_ID.rptr[rptr_idx]); + strcat(buf, ":!"); + + GPRMC = strtok_r(band_txt[rptr_idx].gprmc, delim, &saveptr); + time_utc = strtok_r(NULL, delim, &saveptr); + nav = strtok_r(NULL, delim, &saveptr); + lat_str = strtok_r(NULL, delim, &saveptr); + lat_NS = strtok_r(NULL, delim, &saveptr); + lon_str = strtok_r(NULL, delim, &saveptr); + lon_EW = strtok_r(NULL, delim, &saveptr); + + if (lat_str && lat_NS) + { + if ((*lat_NS != 'N') && (*lat_NS != 'S')) + { + traceit("Invalid North or South indicator in latitude\n"); + return; + } + if (strlen(lat_str) != 9) + { + traceit("Invalid latitude\n"); + return; + } + if (lat_str[4] != '.') + { + traceit("Invalid latitude\n"); + return; + } + lat_str[7] = '\0'; + strcat(buf, lat_str); + strcat(buf, lat_NS); + } + else + { + traceit("Invalid latitude\n"); + return; + } + /* secondary table */ + strcat(buf, "\\"); + + if (lon_str && lon_EW) + { + if ((*lon_EW != 'E') && (*lon_EW != 'W')) + { + traceit("Invalid East or West indicator in longitude\n"); + return; + } + if (strlen(lon_str) != 10) + { + traceit("Invalid longitude\n"); + return; + } + if (lon_str[5] != '.') + { + traceit("Invalid longitude\n"); + return; + } + lon_str[8] = '\0'; + strcat(buf, lon_str); + strcat(buf, lon_EW); + } + else + { + traceit("Invalid longitude\n"); + return; + } + + /* Just this symbolcode only */ + strcat(buf, "k"); + strncat(buf, band_txt[rptr_idx].gpid + 13, 32); + + // traceit("Built APRS from old GPS mode=[%s]\n", buf); + strcat(buf, "\r\n"); + + rc = writen(buf, strlen(buf)); + if (rc == -1) + { + if ((errno == EPIPE) || + (errno == ECONNRESET) || + (errno == ETIMEDOUT) || + (errno == ECONNABORTED) || + (errno == ESHUTDOWN) || + (errno == EHOSTUNREACH) || + (errno == ENETRESET) || + (errno == ENETDOWN) || + (errno == ENETUNREACH) || + (errno == EHOSTDOWN) || + (errno == ENOTCONN)) + { + traceit("build_aprs_from_gps_and_send: APRS_HOST closed connection,error=%d\n",errno); + close(aprs_sock); + aprs_sock = -1; + } + else + traceit("build_aprs_from_gps_and_send: send error=%d\n", errno); + } + return; +} + +static bool verify_gps_csum(char *gps_text, char *csum_text) +{ + short int len; + short int i; + char c; + short computed_csum = 0; + char computed_csum_text[16]; + char *p = NULL; + + len = strlen(gps_text); + for (i = 0; i < len; i++) + { + c = gps_text[i]; + if (computed_csum == 0) + computed_csum = (char)c; + else + computed_csum = computed_csum ^ ((char)c); + } + sprintf(computed_csum_text, "%02X", computed_csum); + // traceit("computed_csum_text=[%s]\n", computed_csum_text); + + p = strchr(csum_text, ' '); + if (p) + *p = '\0'; + + if (strcmp(computed_csum_text, csum_text) == 0) + return true; + else + return false; +} + +static ssize_t writen(char *buffer, size_t n) +{ + ssize_t num_written = 0; + size_t tot_written = 0; + char *buf; + + buf = buffer; + for (tot_written = 0; tot_written < n;) + { + num_written = write(aprs_sock, buf, n - tot_written); + if (num_written <= 0) + { + if ((num_written == -1) && (errno == EINTR)) + continue; + else + return num_written; + } + tot_written += num_written; + buf += num_written; + } + return tot_written; +} + + diff --git a/g2_link.cfg b/g2_link.cfg new file mode 100644 index 0000000..157d158 --- /dev/null +++ b/g2_link.cfg @@ -0,0 +1,96 @@ + +# The ONLY values to change are: LOGIN_CALL, OWNER and ADMIN +# + +# Callsign to use to login to remote REF reflector +# This is your personal user callsign +# Remove the # sign and set your callsign correctly +# +LOGIN_CALL=AC2IE + +# Because ONLY_ADMIN_LOGIN=Y +# only these 3 users can connect to your gateway +ADMIN=AC2IE +ONLY_ADMIN_LOGIN=Y + +# If you set ONLY_LINK_UNLINK=Y +# then ONLY user callsigns identified by the option LINK_UNLINK_USER +# can link or unlink your repeater. +# If you set ONLY_LINK_UNLINK=N +# then ANY user callsign can link or unlink your repeater. +# +ONLY_LINK_UNLINK=N +LINK_UNLINK_USER=XX0XXX +LINK_UNLINK_USER=YY0YYY +LINK_UNLINK_USER=ZZ0ZZZ + +# Local gateway callsign +# This is your local G2 Gateway callsign +# You should register this with the remote irc database server +# Remove the # sign and set the gateway callsign correctly. +# +OWNER=AC2IE + +# remote friends +RMT_XRF_PORT=30001 +RMT_REF_PORT=20001 +RMT_DCS_PORT=30051 + +# Our server port so that the local g2 gateway can contact us +# our g2_link will create/open this UDP port as a server +MY_G2_LINK_IP=0.0.0.0 +MY_G2_LINK_PORT=18997 + +# The external g2 port of the local G2 +TO_G2_EXTERNAL_IP=127.0.0.1 +TO_G2_EXTERNAL_PORT=40000 + +QSO_DETAILS=N + +# The reflectors +GWYS=/usr/local/etc/gwys.txt + +#LINK STATUS file +STATUS_FILE=/usr/local/etc/RPT_STATUS.txt + +# SEND an AUDIO announcements on LINK/UNLINK/INFO ? +ANNOUNCE=Y +# The directory where the "*.dat" files are located +ANNOUNCE_DIR=/usr/local/etc + +# Send a TEXT announcement on every key-up ? +RPTR_ACK=N + +# While sending back the announcement, +# what is the delay in MILLISECONDS between each packet +DELAY_BETWEEN=19 + +# How many seconds to delay before sending the announcement +# Set it to 1 +# If your local repeater requires more time to initialize after releasing PTT, then set it to 2 +DELAY_BEFORE=1 + +# Try to link to this system after initialization +# Leave the option blank, if you do not want automatic linking at startup +# +# Link your local repeater mod B to the remote reflector XRF005 mod A +# +LINK_AT_STARTUP=CXRF901A + +# Maximum number of incoming hotspotNode or dongle connections(r2g2_p, r2g2_x, HotSpotNode,...) +# If you do NOT want incoming HotSpotNode or dongle connections, set this to 0 +MAX_DONGLES=5 + +# Each RF band has an inactivity timer, in minutes. +# If the timer is 0, then there is NO timer at all. +# If the timer is greater than 0 and that RF band is linked +# to a remote system, and no local RF activity has taken place +# in the last minutes as set in the timer, then +# automatically the RF band will unlink from the remote system. +# This is done to protect the owner from local RF users that +# link the RF band to a remote system, +# and then go away. +RF_INACTIVITY_TIMER_A=0 +RF_INACTIVITY_TIMER_B=0 +RF_INACTIVITY_TIMER_C=0 + diff --git a/g2_link.cpp b/g2_link.cpp new file mode 100644 index 0000000..a035153 --- /dev/null +++ b/g2_link.cpp @@ -0,0 +1,5142 @@ + +/* + * Copyright (C) 2010 by Scott Lawson KI4LKF + * + * 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. + */ + + +/* by KI4LKF */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include + +/* Required for Binary search trees using C++ STL */ +#include +#include +#include +#include +using namespace std; + +/*** version number must be x.xx ***/ +#define VERSION "3.11+" // DG1HT +#define CALL_SIZE 8 +#define IP_SIZE 15 +#define QUERY_SIZE 56 +#define MAXHOSTNAMELEN 64 +#define TIMEOUT 50 +#define LINK_CODE 'L' +#define UNLINK_CODE 'U' +#define INFO_CODE 'I' +#define EXEC_CODE 'X' +#define DONGLE_CODE 'D' +#define FILE_REFRESH_GWYS_CODE 'F' + +/* configuration data */ +static char LOGIN_CALL[CALL_SIZE + 1]; +static char ADMIN[CALL_SIZE + 1]; +static bool ONLY_ADMIN_LOGIN = false; +static bool ONLY_LINK_UNLINK = false; +static char LINK_UNLINK_USER[CALL_SIZE + 1]; +static char OWNER[CALL_SIZE + 1]; +static int RMT_XRF_PORT = 30001; +static int RMT_REF_PORT = 20001; +static int RMT_DCS_PORT = 30051; +static char MY_G2_LINK_IP[IP_SIZE + 1]; +static int MY_G2_LINK_PORT = 18997; +static char TO_G2_EXTERNAL_IP[IP_SIZE + 1]; +static int TO_G2_EXTERNAL_PORT = 40000; +static bool QSO_DETAILS = false; +static char GWYS[FILENAME_MAX + 1]; +static char STATUS_FILE[FILENAME_MAX + 1]; +static bool RPTR_ACK = true; +static int DELAY_BETWEEN = 20; +static int DELAY_BEFORE = 1; +static bool ANNOUNCE = false; +static char ANNOUNCE_DIR[FILENAME_MAX + 1]; +static char LINK_AT_STARTUP[CALL_SIZE + 1]; +static unsigned int MAX_DONGLES = 5; +static unsigned int SAVED_MAX_DONGLES = 5; +static long RF_INACTIVITY_TIMER[3] = { 3, 3, 3 }; + +static unsigned char REF_ACK[3] = { 3, 96, 0 }; + +/* + This is the data payload in the map: inbound_list + This is for inbound dongles +*/ + +struct inbound +{ + /* the callsign of the remote */ + char call[CALL_SIZE + 1]; + + /* IP and port of remote */ + struct sockaddr_in sin; + + /* if countdown expires, the connection is terminated */ + short countdown; + + /* This user talked on this module */ + char mod; /* A B C */ + + /* dvap, dvdongle, ... */ + char client; + +}; +/* the Key in this inbound_list map is the unique IP address of the remote */ +typedef map inbound_type; +static inbound_type inbound_list; + +typedef set admin_type; +static admin_type admin; + +typedef set link_unlink_user_type; +static link_unlink_user_type link_unlink_user; + +#define LH_MAX_SIZE 39 +typedef map dt_lh_type; +static dt_lh_type dt_lh_list; + +/* + index 0 is from_mod=A, + index 1 is from_mod=B, + index 2 is from_mod=C +*/ +static struct +{ + char to_call[CALL_SIZE + 1]; + struct sockaddr_in toDst4; + char from_mod; + char to_mod; + short countdown; + bool is_connected; + unsigned char in_streamid[2]; /* incoming from remote systems */ + unsigned char out_streamid[2]; /* outgoing to remote systems */ +} to_remote_g2[3]; + +/* broadcast for data arriving from xrf to local rptr */ +static struct +{ + unsigned char xrf_streamid[2]; /* streamid from xrf */ + unsigned char rptr_streamid[2][2]; /* generated streamid to rptr(s) */ +} brd_from_xrf; +static unsigned char from_xrf_torptr_brd[56]; +static short brd_from_xrf_idx = 0; + +/* broadcast for data arriving from local rptr to xrf */ +static struct +{ + unsigned char from_rptr_streamid[2]; + unsigned char to_rptr_streamid[2][2]; +} brd_from_rptr; +static unsigned char fromrptr_torptr_brd[56]; +static short brd_from_rptr_idx = 0; + +/* + index 0 is local mod=A, + index 1 is local mod=B, + index 2 is local mod=C +*/ +static struct +{ + unsigned char streamid[2]; + time_t last_time; /* last time RF user talked */ +} tracing[3] = { { {0,0}, 0 }, + { {0,0}, 0 }, + { {0,0}, 0 } }; + +/* input from remote */ +static int xrf_g2_sock = -1; +static int ref_g2_sock = -1; +static int dcs_g2_sock = -1; +static unsigned char dcs_buf[1000]; +static unsigned char readBuffer2[1024]; +static struct sockaddr_in fromDst4; +/* + After we receive it from remote g2, + we must feed it to our local repeater. +*/ +static struct sockaddr_in toLocalg2; + +/* input from our own local repeater */ +static int rptr_sock = -1; +static unsigned char readBuffer[100]; /* 58 or 29 or 32, max is 58 */ +static struct sockaddr_in fromRptr; + +static fd_set fdset; +static struct timeval tv; + +static bool keep_running = true; + +/* Used to validate incoming donglers */ +static regex_t preg; + +const char* G2_html = "" + "" + "" + "
" + "REPEATER G2_IRCDDB Gateway v3.09+" + "
"; + +static unsigned short crc_tabccitt[256] = +{ + 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 +}; + +/* the map of remotes */ +/* key is the callsign, data is the host */ +typedef map gwy_list_type; +static gwy_list_type gwy_list; + +static unsigned char queryCommand[QUERY_SIZE]; + +/* START: TEXT crap */ +static char dtmf_mycall[3][CALL_SIZE + 1] = { {""}, {""}, {""} }; +static bool new_group[3] = { true, true, true }; +static int header_type = 0; +static bool GPS_seen[3] = { false, false, false }; +unsigned char tmp_txt[3]; +static char *p_tmp2 = NULL; +/* END: TEXT crap */ + +/* this is used for the "dashboard and QSO_DETAILS" to avoid processing multiple headers */ +static struct +{ + unsigned char sid[2]; +} old_sid[3] = { { {0x00, 0x00} }, + { {0x00, 0x00} }, + { {0x00, 0x00} } }; + +static bool load_gwys(char *filename); +static void calcPFCS(unsigned char *packet, int len); +static void traceit(const char *fmt,...); +static bool read_config(char *); +static bool srv_open(); +static void srv_close(); +static void sigCatch(int signum); +static void g2link(char from_mod, char *call, char to_mod); +static void runit(); +static void print_status_file(); +static void send_heartbeat(); +static bool resolve_rmt(char *name, int type, struct sockaddr_in *addr); + +static void audio_notify(char *notify_msg); +static void rptr_ack(short i); + +static void *audio_notify_run(void *arg); +static void *rptr_ack_run(void *arg); + +static bool resolve_rmt(char *name, int type, struct sockaddr_in *addr) +{ + struct addrinfo hints; + struct addrinfo *res; + struct addrinfo *rp; + int rc = 0; + bool found = false; + + memset(&hints, 0x00, sizeof(struct addrinfo)); + hints.ai_family = AF_INET; + hints.ai_socktype = type; + + rc = getaddrinfo(name, NULL, &hints, &res); + if (rc != 0) + { + traceit("getaddrinfo return error code %d for [%s]\n", rc, name); + return false; + } + + for (rp = res; rp != NULL; rp = rp->ai_next) + { + if ((rp->ai_family == AF_INET) && + (rp->ai_socktype == type)) + { + memcpy(addr, rp->ai_addr, sizeof(struct sockaddr_in)); + found = true; + break; + } + } + freeaddrinfo(res); + return found; +} + +/* send keepalive to donglers */ +static void send_heartbeat() +{ + inbound_type::iterator pos; + inbound *inbound_ptr; + bool removed = false; + + for (pos = inbound_list.begin(); pos != inbound_list.end(); pos++) + { + inbound_ptr = (inbound *)pos->second; + sendto(ref_g2_sock,(char *)REF_ACK,3,0, + (struct sockaddr *)&(inbound_ptr->sin), + sizeof(struct sockaddr_in)); + + if (inbound_ptr->countdown >= 0) + inbound_ptr->countdown --; + + if (inbound_ptr->countdown < 0) + { + removed = true; + traceit("call=%s timeout, removing %s, users=%d\n", + inbound_ptr->call, + pos->first.c_str(), + inbound_list.size() - 1); + + free(pos->second); + pos->second = NULL; + inbound_list.erase(pos); + } + } + if (removed) + print_status_file(); +} + +static void rptr_ack(short i) +{ + pthread_t rptr_ack_thread; + pthread_attr_t attr; + int rc = 0; + static char mod_and_RADIO_ID[3][22]; + + struct tm tmp; + time_t t; + char outstr[200]; + + memset(mod_and_RADIO_ID[i], ' ', 21); + mod_and_RADIO_ID[i][21] = '\0'; + + if (i == 0) + mod_and_RADIO_ID[i][0] = 'A'; + else + if (i == 1) + mod_and_RADIO_ID[i][0] = 'B'; + else + if (i == 2) + mod_and_RADIO_ID[i][0] = 'C'; + + if (to_remote_g2[i].is_connected) + { + memcpy(mod_and_RADIO_ID[i] + 1, "LINKED TO ", 10); + memcpy(mod_and_RADIO_ID[i] + 11, to_remote_g2[i].to_call, CALL_SIZE); + mod_and_RADIO_ID[i][11 + CALL_SIZE] = to_remote_g2[i].to_mod; + } + else + if (to_remote_g2[i].to_call[0] != '\0') + { + memcpy(mod_and_RADIO_ID[i] + 1, "TRYING ", 10); + memcpy(mod_and_RADIO_ID[i] + 11, to_remote_g2[i].to_call, CALL_SIZE); + mod_and_RADIO_ID[i][11 + CALL_SIZE] = to_remote_g2[i].to_mod; + } + else + { + time(&t); + if (!localtime_r(&t,&tmp)) + memcpy(mod_and_RADIO_ID[i] + 1, "NOT LINKED", 10); + else + { + if (strftime(outstr, sizeof(outstr), "%Y%m%d %H:%M " ,&tmp) == 0) + memcpy(mod_and_RADIO_ID[i] + 1, "NOT LINKED", 10); + else + memcpy(mod_and_RADIO_ID[i] + 1, outstr, 15); + } + } + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&rptr_ack_thread, &attr, rptr_ack_run, (void *)(mod_and_RADIO_ID[i])); + if (rc != 0) + traceit("failed to start rptr_ack thread for mod %c\n", mod_and_RADIO_ID[i][0]); + pthread_attr_destroy(&attr); + return; +} + +static void *rptr_ack_run(void *arg) +{ + char from_mod = *((char *)arg); + char RADIO_ID[21]; + memcpy(RADIO_ID, (char *)arg + 1, 21); + unsigned char rptr_ack[56]; + struct timespec nanos; + unsigned int aseed; + time_t tnow = 0; + char silence[12] = + { + 0x4e,0x8d,0x32,0x88,0x26,0x1a,0x3f,0x61,0xe8, + 0x70,0x4f,0x93 + }; + struct sigaction act; + + act.sa_handler = sigCatch; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + if (sigaction(SIGTERM, &act, 0) != 0) + { + traceit("sigaction-TERM failed, error=%d\n", errno); + traceit("rptr_ack thread exiting...\n"); + pthread_exit(NULL); + } + if (sigaction(SIGINT, &act, 0) != 0) + { + traceit("sigaction-INT failed, error=%d\n", errno); + traceit("rptr_ack thread exiting...\n"); + pthread_exit(NULL); + } + + time(&tnow); + aseed = tnow + pthread_self(); + + u_int16_t streamid_raw = (::rand_r(&aseed) % 65535U) + 1U; + + sleep(DELAY_BEFORE); + + traceit("sending ACK+text, mod:[%c], RADIO_ID=[%s]\n", from_mod, RADIO_ID); + + memcpy(rptr_ack,"DSVT", 4); + rptr_ack[4] = 0x10; + rptr_ack[5] = 0x00; + rptr_ack[6] = 0x00; + rptr_ack[7] = 0x00; + + rptr_ack[8] = 0x20; + rptr_ack[9] = 0x00; + rptr_ack[10] = 0x01; + rptr_ack[11] = 0x00; + + rptr_ack[12] = streamid_raw / 256U; + rptr_ack[13] = streamid_raw % 256U; + rptr_ack[14] = 0x80; + rptr_ack[15] = 0x01; /* we do not want to set this to 0x01 */ + rptr_ack[16] = 0x00; + rptr_ack[17] = 0x00; + + memcpy(rptr_ack + 18, OWNER, 8); + rptr_ack[25] = from_mod; + + memcpy(rptr_ack + 26, OWNER, 8); + rptr_ack[33] = 'G'; + + memcpy(rptr_ack + 34, "CQCQCQ ", 8); + + memcpy(rptr_ack + 42, OWNER, 8); + rptr_ack[49] = from_mod; + + memcpy(rptr_ack + 50, "RPTR", 4); + calcPFCS(rptr_ack,56); + (void)sendto(rptr_sock,(char *)rptr_ack,56,0,(struct sockaddr *)&toLocalg2,sizeof(toLocalg2)); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + rptr_ack[4] = 0x20; + memcpy((char *)rptr_ack + 15, silence, 9); + + /* start sending silence + announcement text */ + + rptr_ack[14] = 0x00; + rptr_ack[24] = 0x55; + rptr_ack[25] = 0x2d; + rptr_ack[26] = 0x16; + (void)sendto(rptr_sock,(char *)rptr_ack,27,0,(struct sockaddr *)&toLocalg2,sizeof(toLocalg2)); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + rptr_ack[14] = 0x01; + rptr_ack[24] = '@' ^ 0x70; + rptr_ack[25] = RADIO_ID[0] ^ 0x4f; + rptr_ack[26] = RADIO_ID[1] ^ 0x93; + (void)sendto(rptr_sock,(char *)rptr_ack,27,0,(struct sockaddr *)&toLocalg2,sizeof(toLocalg2)); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + rptr_ack[14] = 0x02; + rptr_ack[24] = RADIO_ID[2] ^ 0x70; + rptr_ack[25] = RADIO_ID[3] ^ 0x4f; + rptr_ack[26] = RADIO_ID[4] ^ 0x93; + (void)sendto(rptr_sock,(char *)rptr_ack,27,0,(struct sockaddr *)&toLocalg2,sizeof(toLocalg2)); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + rptr_ack[14] = 0x03; + rptr_ack[24] = 'A' ^ 0x70; + rptr_ack[25] = RADIO_ID[5] ^ 0x4f; + rptr_ack[26] = RADIO_ID[6] ^ 0x93; + (void)sendto(rptr_sock,(char *)rptr_ack,27,0,(struct sockaddr *)&toLocalg2,sizeof(toLocalg2)); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + rptr_ack[14] = 0x04; + rptr_ack[24] = RADIO_ID[7] ^ 0x70; + rptr_ack[25] = RADIO_ID[8] ^ 0x4f; + rptr_ack[26] = RADIO_ID[9] ^ 0x93; + (void)sendto(rptr_sock,(char *)rptr_ack,27,0,(struct sockaddr *)&toLocalg2,sizeof(toLocalg2)); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + rptr_ack[14] = 0x05; + rptr_ack[24] = 'B' ^ 0x70; + rptr_ack[25] = RADIO_ID[10] ^ 0x4f; + rptr_ack[26] = RADIO_ID[11] ^ 0x93; + (void)sendto(rptr_sock,(char *)rptr_ack,27,0,(struct sockaddr *)&toLocalg2,sizeof(toLocalg2)); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + rptr_ack[14] = 0x06; + rptr_ack[24] = RADIO_ID[12] ^ 0x70; + rptr_ack[25] = RADIO_ID[13] ^ 0x4f; + rptr_ack[26] = RADIO_ID[14] ^ 0x93; + (void)sendto(rptr_sock,(char *)rptr_ack,27,0,(struct sockaddr *)&toLocalg2,sizeof(toLocalg2)); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + rptr_ack[14] = 0x07; + rptr_ack[24] = 'C' ^ 0x70; + rptr_ack[25] = RADIO_ID[15] ^ 0x4f; + rptr_ack[26] = RADIO_ID[16] ^ 0x93; + (void)sendto(rptr_sock,(char *)rptr_ack,27,0,(struct sockaddr *)&toLocalg2,sizeof(toLocalg2)); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + rptr_ack[14] = 0x08; + rptr_ack[24] = RADIO_ID[17] ^ 0x70; + rptr_ack[25] = RADIO_ID[18] ^ 0x4f; + rptr_ack[26] = RADIO_ID[19] ^ 0x93; + (void)sendto(rptr_sock,(char *)rptr_ack,27,0,(struct sockaddr *)&toLocalg2,sizeof(toLocalg2)); + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + + rptr_ack[14] = 0x09 | 0x40; + memset((char *)rptr_ack + 15, 0, 9); + rptr_ack[24] = 0x70; + rptr_ack[25] = 0x4f; + rptr_ack[26] = 0x93; + (void)sendto(rptr_sock,(char *)rptr_ack,27,0,(struct sockaddr *)&toLocalg2,sizeof(toLocalg2)); + traceit("finished sending ACK+text to mod:[%c]\n", from_mod); + pthread_exit(NULL); +} + +static void print_status_file() +{ + struct tm tm1; + time_t tnow; + FILE *statusfp = NULL; + short i; + inbound *inbound_ptr; + inbound_type::iterator pos; + + statusfp = fopen(STATUS_FILE, "w"); + if (!statusfp) + traceit("Failed to create status file %s\n", STATUS_FILE); + else + { + setvbuf(statusfp, (char *)NULL, _IOLBF, 0); + + time(&tnow); + localtime_r(&tnow, &tm1); + + /* print connected donglers */ + for (pos = inbound_list.begin(); pos != inbound_list.end(); pos++) + { + inbound_ptr = (inbound *)pos->second; + fprintf(statusfp, + "%c,%s,%c,%s,%02d%02d%02d,%02d:%02d:%02d\n", + 'p', + inbound_ptr->call, + 'p', + pos->first.c_str(), + tm1.tm_mon+1,tm1.tm_mday,tm1.tm_year % 100, + tm1.tm_hour,tm1.tm_min,tm1.tm_sec); + } + + /* print linked repeaters-reflectors */ + for (i = 0; i < 3; i++) + { + if (to_remote_g2[i].is_connected) + { + fprintf(statusfp, + "%c,%s,%c,%s,%02d%02d%02d,%02d:%02d:%02d\n", + to_remote_g2[i].from_mod, + to_remote_g2[i].to_call, + to_remote_g2[i].to_mod, + inet_ntoa(to_remote_g2[i].toDst4.sin_addr), + tm1.tm_mon+1,tm1.tm_mday,tm1.tm_year % 100, + tm1.tm_hour,tm1.tm_min,tm1.tm_sec); + } + } + fclose(statusfp); + } +} + +/* Open text file of repeaters, reflectors */ +static bool load_gwys(char *filename) +{ + FILE *fp = NULL; + char inbuf[1024]; + char *p = NULL; + const char *delim = " "; + + char *tok; + char call[CALL_SIZE + 1]; + char host[MAXHOSTNAMELEN + 1]; + char port[5 + 1]; + + /* host + space + port + NULL */ + char payload[MAXHOSTNAMELEN + 1 + 5 + 1]; + unsigned short j; + + gwy_list_type::iterator gwy_pos; + pair gwy_insert_pair; + + traceit("Trying to open file %s\n", filename); + fp = fopen(filename, "r"); + if (fp == NULL) + { + traceit("Failed to open file %s\n", filename); + return false; + } + traceit("Opened file %s OK\n", filename); + + while (fgets(inbuf, 1020, fp) != NULL) + { + p = strchr(inbuf, '\r'); + if (p) + *p = '\0'; + + p = strchr(inbuf, '\n'); + if (p) + *p = '\0'; + + p = strchr(inbuf, '#'); + if (p) + { + traceit("Comment line:[%s]\n", inbuf); + continue; + } + + /* get the call */ + tok = strtok(inbuf, delim); + if (!tok) + continue; + if ((strlen(tok) > CALL_SIZE) || (strlen(tok) < 3)) + { + traceit("Invalid call [%s]\n", tok); + continue; + } + memset(call, ' ', CALL_SIZE); + call[CALL_SIZE] = '\0'; + memcpy(call, tok, strlen(tok)); + for (j = 0; j < strlen(call); j++) + call[j] = toupper(call[j]); + if (strcmp(call, OWNER) == 0) + { + traceit("Call [%s] will not be loaded\n", call); + continue; + } + + /* get the host */ + tok = strtok(NULL, delim); + if (!tok) + { + traceit("Call [%s] has no host\n", call); + continue; + } + strncpy(host,tok,MAXHOSTNAMELEN); + host[MAXHOSTNAMELEN] = '\0'; + if (strcmp(host, "0.0.0.0") == 0) + { + traceit("call %s has invalid host %s\n", call, host); + continue; + } + + /* get the port */ + tok = strtok(NULL, delim); + if (!tok) + { + traceit("Call [%s] has no port\n", call); + continue; + } + if (strlen(tok) > 5) + { + traceit("call %s has invalid port [%s]\n", call, tok); + continue; + } + strcpy(port, tok); + + /* at this point, we have: call host port */ + /* copy the payload(host port) */ + sprintf(payload, "%s %s", host, port); + + gwy_pos = gwy_list.find(call); + if (gwy_pos == gwy_list.end()) + { + gwy_insert_pair = gwy_list.insert(pair(call,payload)); + if (gwy_insert_pair.second) + traceit("Added Call=[%s], payload=[%s]\n",call, payload); + else + traceit("Failed to add: Call=[%s], payload=[%s]\n",call, payload); + } + else + traceit("Call [%s] is duplicate\n", call); + } + fclose(fp); + + traceit("Added %d gateways\n", gwy_list.size()); + return true; +} + +/* compute checksum */ +static void calcPFCS(unsigned char *packet, int len) +{ + unsigned short crc_dstar_ffff = 0xffff; + unsigned short tmp, short_c; + short int i; + short int low; + short int high; + + if (len == 56) + { + low = 15; + high = 54; + } + else + if (len == 58) + { + low = 17; + high = 56; + } + else + return; + + for (i = low; i < high ; i++) + { + short_c = 0x00ff & (unsigned short)packet[i]; + tmp = (crc_dstar_ffff & 0x00ff) ^ short_c; + crc_dstar_ffff = (crc_dstar_ffff >> 8) ^ crc_tabccitt[tmp]; + } + crc_dstar_ffff = ~crc_dstar_ffff; + tmp = crc_dstar_ffff; + + if (len == 56) + { + packet[54] = (unsigned char)(crc_dstar_ffff & 0xff); + packet[55] = (unsigned char)((tmp >> 8) & 0xff); + } + else + { + packet[56] = (unsigned char)(crc_dstar_ffff & 0xff); + packet[57] = (unsigned char)((tmp >> 8) & 0xff); + } + return; +} + +/* log the event */ +static void traceit(const char *fmt,...) +{ + time_t ltime; + struct tm mytm; + const short BFSZ = 1094; + char buf[BFSZ]; + + time(<ime); + localtime_r(<ime,&mytm); + + snprintf(buf,BFSZ - 1,"%02d%02d%02d at %02d:%02d:%02d:", + mytm.tm_mon+1,mytm.tm_mday,mytm.tm_year % 100, + mytm.tm_hour,mytm.tm_min,mytm.tm_sec); + + va_list args; + va_start(args,fmt); + vsnprintf(buf + strlen(buf), BFSZ - strlen(buf) - 1, fmt, args); + va_end(args); + + fprintf(stdout, "%s", buf); + + return; +} + +/* process configuration file */ +static bool read_config(char *cfgFile) +{ + bool admin_found = false; + bool link_unlink_user_found = false; + unsigned short i = 0; + short int valid_params = 26; + short int params = 0; + + admin_type::iterator pos; + link_unlink_user_type::iterator link_unlink_user_pos; + + FILE *cnf = NULL; + char inbuf[1024]; + char *p = NULL; + char *ptr; + short int j; + + cnf = fopen(cfgFile, "r"); + if (!cnf) + { + traceit("Failed to open file %s\n", cfgFile); + return false; + } + + traceit("Reading file %s\n", cfgFile); + while (fgets(inbuf, 1020, cnf) != NULL) + { + if (strchr(inbuf, '#')) + continue; + + p = strchr(inbuf, '\r'); + if (p) + *p = '\0'; + p = strchr(inbuf, '\n'); + if (p) + *p = '\0'; + + p = strchr(inbuf, '='); + if (!p) + continue; + *p = '\0'; + + if (strcmp(inbuf,"LOGIN_CALL") == 0) + { + memset(LOGIN_CALL,' ', sizeof(LOGIN_CALL)); + LOGIN_CALL[CALL_SIZE] = '\0'; + + /* no spaces after the equal sign */ + if (p[1] == ' ') + traceit("LOGIN_CALL: no spaces after the equal sign\n"); + else + { + /* take up to 8 characters, throw away the rest */ + p[CALL_SIZE + 1] = '\0'; + + /* valid length? */ + if ((strlen(p + 1) < 3) || (strlen(p + 1) > CALL_SIZE)) + traceit("LOGIN_CALL value [%s] invalid\n", p + 1); + else + { + memcpy(LOGIN_CALL, p + 1, strlen(p + 1)); + + /* uppercase it */ + for (j = 0; j < CALL_SIZE; j++) + LOGIN_CALL[j] = toupper(LOGIN_CALL[j]); + + traceit("LOGIN_CALL=[%s]\n",LOGIN_CALL); + params ++; + } + } + } + else + if (strcmp(inbuf,"ONLY_ADMIN_LOGIN") == 0) + { + if (*(p + 1) == 'Y') + ONLY_ADMIN_LOGIN = true; + else + ONLY_ADMIN_LOGIN = false; + traceit("ONLY_ADMIN_LOGIN=[%c]\n", *(p + 1)); + params ++; + } + else + if (strcmp(inbuf,"ONLY_LINK_UNLINK") == 0) + { + if (*(p + 1) == 'Y') + ONLY_LINK_UNLINK = true; + else + ONLY_LINK_UNLINK = false; + traceit("ONLY_LINK_UNLINK=[%c]\n", *(p + 1)); + params ++; + } + else + if (strcmp(inbuf,"ADMIN") == 0) + { + if (!admin_found) + { + admin_found = true; + params ++; + } + + memset(ADMIN,' ', CALL_SIZE); + ADMIN[CALL_SIZE] = '\0'; + + if ( (strlen(p + 1) < 1) || (strlen(p + 1) > CALL_SIZE) ) + traceit("ADMIN value [%s] invalid\n", p + 1); + else + { + memcpy(ADMIN, p + 1, strlen(p + 1)); + + for (i = 0; i < strlen(ADMIN); i++) + ADMIN[i] = toupper(ADMIN[i]); + + traceit("ADMIN=[%s]\n",ADMIN); + + /* check for duplicates */ + pos = admin.find(ADMIN); + if (pos != admin.end()) + traceit("[%s] already an administrator\n", ADMIN); + else + { + if (admin.insert(ADMIN).second) + traceit("[%s] is now an administrator\n", ADMIN); + else + traceit("failed to add [%s] as an administrator\n", ADMIN); + } + } + } + else + if (strcmp(inbuf,"LINK_UNLINK_USER") == 0) + { + if (!link_unlink_user_found) + { + link_unlink_user_found = true; + params ++; + } + + memset(LINK_UNLINK_USER,' ', CALL_SIZE); + LINK_UNLINK_USER[CALL_SIZE] = '\0'; + + if ( (strlen(p + 1) < 1) || (strlen(p + 1) > CALL_SIZE) ) + traceit("LINK_UNLINK_USER value [%s] invalid\n", p + 1); + else + { + memcpy(LINK_UNLINK_USER, p + 1, strlen(p + 1)); + + for (i = 0; i < strlen(LINK_UNLINK_USER); i++) + LINK_UNLINK_USER[i] = toupper(LINK_UNLINK_USER[i]); + + traceit("LINK_UNLINK_USER=[%s]\n",LINK_UNLINK_USER); + + /* check for duplicates */ + link_unlink_user_pos = link_unlink_user.find(LINK_UNLINK_USER); + if (link_unlink_user_pos != link_unlink_user.end()) + traceit("[%s] already in link_unlink_user list\n", LINK_UNLINK_USER); + else + { + if (link_unlink_user.insert(LINK_UNLINK_USER).second) + traceit("[%s] added to link_unlink_user list\n", LINK_UNLINK_USER); + else + traceit("failed to add [%s] to link_unlink_user list\n", LINK_UNLINK_USER); + } + } + } + else + if (strcmp(inbuf,"OWNER") == 0) + { + memset(OWNER,' ', sizeof(OWNER)); + OWNER[CALL_SIZE] = '\0'; + + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if ((strlen(p + 1) < 3) || (strlen(p + 1) > (CALL_SIZE - 2))) + traceit("OWNER value [%s] invalid\n", p + 1); + else + { + memcpy(OWNER, p + 1, strlen(p + 1)); + + /* uppercase it */ + for (j = 0; j < CALL_SIZE; j++) + OWNER[j] = toupper(OWNER[j]); + + traceit("OWNER=[%s]\n",OWNER); + params ++; + } + } + else + if (strcmp(inbuf,"RMT_XRF_PORT") == 0) + { + RMT_XRF_PORT = atoi(p + 1); + traceit("RMT_XRF_PORT=[%d]\n",RMT_XRF_PORT); + params ++; + } + else + if (strcmp(inbuf,"RMT_DCS_PORT") == 0) + { + RMT_DCS_PORT = atoi(p + 1); + traceit("RMT_DCS_PORT=[%d]\n",RMT_DCS_PORT); + params ++; + } + else + if (strcmp(inbuf,"RMT_REF_PORT") == 0) + { + RMT_REF_PORT = atoi(p + 1); + traceit("RMT_REF_PORT=[%d]\n",RMT_REF_PORT); + params ++; + } + else + if (strcmp(inbuf,"MY_G2_LINK_IP") == 0) + { + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if (strlen(p + 1) < 1) + traceit("MY_G2_LINK_IP value [%s] invalid\n", p + 1); + else + { + strncpy(MY_G2_LINK_IP, p + 1, IP_SIZE); + MY_G2_LINK_IP[IP_SIZE] = '\0'; + traceit("MY_G2_LINK_IP=[%s]\n", MY_G2_LINK_IP); + params ++; + } + } + else + if (strcmp(inbuf,"MY_G2_LINK_PORT") == 0) + { + MY_G2_LINK_PORT = atoi(p + 1); + traceit("MY_G2_LINK_PORT=[%d]\n",MY_G2_LINK_PORT); + params ++; + } + else + if (strcmp(inbuf,"TO_G2_EXTERNAL_IP") == 0) + { + ptr = strchr(p + 1, ' '); + if (ptr) + *ptr = '\0'; + + if (strlen(p + 1) < 1) + traceit("TO_G2_EXTERNAL_IP value [%s] invalid\n", p + 1); + else + { + strncpy(TO_G2_EXTERNAL_IP, p + 1, IP_SIZE); + TO_G2_EXTERNAL_IP[IP_SIZE] = '\0'; + traceit("TO_G2_EXTERNAL_IP=[%s]\n", TO_G2_EXTERNAL_IP); + params ++; + } + } + else + if (strcmp(inbuf,"TO_G2_EXTERNAL_PORT") == 0) + { + TO_G2_EXTERNAL_PORT = atoi(p + 1); + traceit("TO_G2_EXTERNAL_PORT=[%d]\n",TO_G2_EXTERNAL_PORT); + params ++; + } + else + if (strcmp(inbuf,"QSO_DETAILS") == 0) + { + if (*(p + 1) == 'Y') + QSO_DETAILS = true; + else + QSO_DETAILS = false; + traceit("QSO_DETAILS=[%c]\n", *(p + 1)); + params ++; + } + else + if (strcmp(inbuf,"GWYS") == 0) + { + memset(GWYS, '\0', sizeof(GWYS)); + strncpy(GWYS, p + 1,FILENAME_MAX); + traceit("GWYS=[%s]\n", GWYS); + params ++; + } + else + if (strcmp(inbuf,"STATUS_FILE") == 0) + { + memset(STATUS_FILE, '\0', sizeof(STATUS_FILE)); + strncpy(STATUS_FILE, p + 1,FILENAME_MAX); + traceit("STATUS_FILE=[%s]\n",STATUS_FILE); + params ++; + } + else + if (strcmp(inbuf,"DELAY_BETWEEN") == 0) + { + DELAY_BETWEEN = atoi(p + 1); + if (DELAY_BETWEEN <= 0) + DELAY_BETWEEN = 20; + traceit("DELAY_BETWEEN=[%d]\n",DELAY_BETWEEN); + params ++; + } + else + if (strcmp(inbuf,"RPTR_ACK") == 0) + { + if (*(p + 1) == 'Y') + RPTR_ACK = true; + else + RPTR_ACK = false; + traceit("RPTR_ACK=[%c]\n", *(p + 1)); + params ++; + } + else + if (strcmp(inbuf,"ANNOUNCE") == 0) + { + if (*(p + 1) == 'Y') + ANNOUNCE = true; + else + ANNOUNCE = false; + traceit("ANNOUNCE=[%c]\n", *(p + 1)); + params ++; + } + else + if (strcmp(inbuf,"ANNOUNCE_DIR") == 0) + { + memset(ANNOUNCE_DIR, '\0', sizeof(ANNOUNCE_DIR)); + strncpy(ANNOUNCE_DIR, p + 1, FILENAME_MAX); + traceit("ANNOUNCE_DIR=[%s]\n", ANNOUNCE_DIR); + params ++; + } + else + if (strcmp(inbuf,"DELAY_BEFORE") == 0) + { + DELAY_BEFORE = atoi(p + 1); + if (DELAY_BEFORE <= 0) + DELAY_BEFORE = 1; + traceit("DELAY_BEFORE=[%d]\n",DELAY_BEFORE); + params ++; + } + else + if (strcmp(inbuf,"LINK_AT_STARTUP") == 0) + { + memset(LINK_AT_STARTUP, '\0', sizeof(LINK_AT_STARTUP)); + strncpy(LINK_AT_STARTUP, p + 1, CALL_SIZE); + traceit("LINK_AT_STARTUP=[%s]\n", LINK_AT_STARTUP); + params ++; + } + else + if (strcmp(inbuf,"MAX_DONGLES") == 0) + { + MAX_DONGLES = atoi(p + 1); + traceit("MAX_DONGLES=[%d]\n",MAX_DONGLES); + SAVED_MAX_DONGLES = MAX_DONGLES; + params ++; + } + else + if (strcmp(inbuf,"RF_INACTIVITY_TIMER_A") == 0) + { + RF_INACTIVITY_TIMER[0] = atol(p + 1); + if (RF_INACTIVITY_TIMER[0] < 0) + RF_INACTIVITY_TIMER[0] = 10; + traceit("RF_INACTIVITY_TIMER_A=[%ld]\n",RF_INACTIVITY_TIMER[0]); + RF_INACTIVITY_TIMER[0] = RF_INACTIVITY_TIMER[0] * 60; + params ++; + } + else + if (strcmp(inbuf,"RF_INACTIVITY_TIMER_B") == 0) + { + RF_INACTIVITY_TIMER[1] = atol(p + 1); + if (RF_INACTIVITY_TIMER[1] < 0) + RF_INACTIVITY_TIMER[1] = 10; + traceit("RF_INACTIVITY_TIMER_B=[%ld]\n",RF_INACTIVITY_TIMER[1]); + RF_INACTIVITY_TIMER[1] = RF_INACTIVITY_TIMER[1] * 60; + params ++; + } + else + if (strcmp(inbuf,"RF_INACTIVITY_TIMER_C") == 0) + { + RF_INACTIVITY_TIMER[2] = atol(p + 1); + if (RF_INACTIVITY_TIMER[2] < 0) + RF_INACTIVITY_TIMER[2] = 10; + traceit("RF_INACTIVITY_TIMER_C=[%ld]\n",RF_INACTIVITY_TIMER[2]); + RF_INACTIVITY_TIMER[2] = RF_INACTIVITY_TIMER[2] * 60; + params ++; + } + } + fclose(cnf); + + if (params != valid_params) + { + traceit("Configuration file %s invalid\n",cfgFile); + return false; + } + + return true; +} + +/* create our server */ +static bool srv_open() +{ + struct sockaddr_in sin; + short i; + + /* create our XRF gateway socket */ + xrf_g2_sock = socket(PF_INET,SOCK_DGRAM,0); + if (xrf_g2_sock == -1) + { + traceit("Failed to create gateway socket for XRF,errno=%d\n",errno); + return false; + } + fcntl(xrf_g2_sock,F_SETFL,O_NONBLOCK); + + memset(&sin,0,sizeof(struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr(MY_G2_LINK_IP); + sin.sin_port = htons(RMT_XRF_PORT); + if (bind(xrf_g2_sock,(struct sockaddr *)&sin,sizeof(struct sockaddr_in)) != 0) + { + traceit("Failed to bind gateway socket on port %d for XRF, errno=%d\n", + RMT_XRF_PORT ,errno); + close(xrf_g2_sock); + xrf_g2_sock = -1; + return false; + } + + /* create the dcs socket */ + dcs_g2_sock = socket(PF_INET,SOCK_DGRAM,0); + if (dcs_g2_sock == -1) + { + traceit("Failed to create gateway socket for DCS,errno=%d\n",errno); + close(xrf_g2_sock); + xrf_g2_sock = -1; + return false; + } + fcntl(dcs_g2_sock,F_SETFL,O_NONBLOCK); + + /* socket for REF */ + ref_g2_sock = socket(PF_INET,SOCK_DGRAM,0); + if (ref_g2_sock == -1) + { + traceit("Failed to create gateway socket for REF, errno=%d\n",errno); + close(dcs_g2_sock); + dcs_g2_sock = -1; + close(xrf_g2_sock); + xrf_g2_sock = -1; + return false; + } + fcntl(ref_g2_sock,F_SETFL,O_NONBLOCK); + + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr(MY_G2_LINK_IP); + sin.sin_port = htons(RMT_REF_PORT); + if (bind(ref_g2_sock,(struct sockaddr *)&sin,sizeof(struct sockaddr_in)) != 0) + { + traceit("Failed to bind gateway socket on port %d for REF, errno=%d\n", + RMT_REF_PORT ,errno); + close(dcs_g2_sock); + dcs_g2_sock = -1; + close(xrf_g2_sock); + xrf_g2_sock = -1; + close(ref_g2_sock); + ref_g2_sock = -1; + return false; + } + + /* create our repeater socket */ + rptr_sock = socket(PF_INET,SOCK_DGRAM,0); + if (rptr_sock == -1) + { + traceit("Failed to create repeater socket,errno=%d\n",errno); + close(dcs_g2_sock); + dcs_g2_sock = -1; + close(xrf_g2_sock); + xrf_g2_sock = -1; + close(ref_g2_sock); + ref_g2_sock = -1; + return false; + } + fcntl(rptr_sock,F_SETFL,O_NONBLOCK); + + memset(&sin,0,sizeof(struct sockaddr_in)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = inet_addr(MY_G2_LINK_IP); + sin.sin_port = htons(MY_G2_LINK_PORT); + if (bind(rptr_sock,(struct sockaddr *)&sin,sizeof(struct sockaddr_in)) != 0) + { + traceit("Failed to bind repeater socket on port %d, errno=%d\n", + MY_G2_LINK_PORT,errno); + close(dcs_g2_sock); + dcs_g2_sock = -1; + close(rptr_sock); + rptr_sock = -1; + close(xrf_g2_sock); + xrf_g2_sock = -1; + close(ref_g2_sock); + ref_g2_sock = -1; + return false; + } + + /* the local G2 external runs on this IP and port */ + memset(&toLocalg2,0,sizeof(struct sockaddr_in)); + toLocalg2.sin_family = AF_INET; + toLocalg2.sin_addr.s_addr = inet_addr(TO_G2_EXTERNAL_IP); + toLocalg2.sin_port = htons(TO_G2_EXTERNAL_PORT); + + /* initialize all remote links */ + for (i = 0; i < 3; i++) + { + to_remote_g2[i].to_call[0] = '\0'; + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].from_mod = ' '; + to_remote_g2[i].to_mod = ' '; + to_remote_g2[i].countdown = 0; + to_remote_g2[i].is_connected = false; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + to_remote_g2[i].out_streamid[0] = 0x00; + to_remote_g2[i].out_streamid[1] = 0x00; + } + return true; +} + +/* destroy our server */ +static void srv_close() +{ + if (xrf_g2_sock != -1) + { + close(xrf_g2_sock); + traceit("Closed RMT_XRF_PORT\n"); + } + + if (dcs_g2_sock != -1) + { + close(dcs_g2_sock); + traceit("Closed RMT_DCS_PORT\n"); + } + + if (rptr_sock != -1) + { + close(rptr_sock); + traceit("Closed MY_G2_LINK_PORT\n"); + } + + if (ref_g2_sock != -1) + { + close(ref_g2_sock); + traceit("Closed RMT_REF_PORT\n"); + } + + return; +} + +/* find the repeater IP by callsign and link to it */ +static void g2link(char from_mod, char *call, char to_mod) +{ + short i,j, counter; + + char linked_remote_system[CALL_SIZE + 1]; + char *space_p = 0; + char notify_msg[64]; + + char host[MAXHOSTNAMELEN + 1]; + char port_s[5 + 1]; + int port_i; + + /* host + space + port + NULL */ + char payload[MAXHOSTNAMELEN + 1 + 5 + 1]; + char *p = NULL; + + gwy_list_type::iterator gwy_pos; + char link_request[519]; + + bool ok = false; + + memset(link_request, 0, sizeof(link_request)); + + host[0] = '\0'; + port_s[0] = '\0'; + payload[0] = '\0'; + + if (from_mod == 'A') + i = 0; + else + if (from_mod == 'B') + i = 1; + else + if (from_mod == 'C') + i = 2; + else + { + traceit("from_mod %c invalid\n", from_mod); + return; + } + + memset(&to_remote_g2[i], 0, sizeof(to_remote_g2[i])); + + strcpy(to_remote_g2[i].to_call, call); + to_remote_g2[i].to_mod = to_mod; + + if ((memcmp(call, "REF", 3) == 0) || + (memcmp(call, "DCS", 3) == 0)) + { + for (counter = 0; counter < 3; counter++) + { + if (counter != i) + { + if ( (to_remote_g2[counter].to_call[0] != '\0') && + (strcmp(to_remote_g2[counter].to_call,to_remote_g2[i].to_call) == 0) && + (to_remote_g2[counter].to_mod == to_remote_g2[i].to_mod) ) + break; + } + } + to_remote_g2[i].to_call[0] = '\0'; + to_remote_g2[i].to_mod = ' '; + + if (counter < 3) + { + traceit("Another mod(%c) is already linked to %s %c\n", + to_remote_g2[counter].from_mod, + to_remote_g2[counter].to_call, + to_remote_g2[counter].to_mod); + + return; + } + } + + gwy_pos = gwy_list.find(call); + if (gwy_pos == gwy_list.end()) + { + traceit("%s not found in gwy list\n", call); + return; + } + + strcpy(payload, gwy_pos->second.c_str()); + + /* extract host and port */ + p = strchr(payload, ' '); + if (!p) + { + traceit("Invalid payload [%s] for call [%s]\n", payload, call); + return; + } + *p = '\0'; + + strcpy(host, payload); + strcpy(port_s, p + 1); + port_i = atoi(port_s); + + if (host[0] != '\0') + { + ok = resolve_rmt(host, SOCK_DGRAM, &(to_remote_g2[i].toDst4)); + if (!ok) + { + traceit("Call %s is host %s but could not resolve to IP\n", + call, host); + memset(&to_remote_g2[i], 0, sizeof(to_remote_g2[i])); + return; + } + + strcpy(to_remote_g2[i].to_call, call); + to_remote_g2[i].toDst4.sin_family = AF_INET; + to_remote_g2[i].toDst4.sin_port = htons(port_i); + to_remote_g2[i].from_mod = from_mod; + to_remote_g2[i].to_mod = to_mod; + to_remote_g2[i].countdown = TIMEOUT; + to_remote_g2[i].is_connected = false; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + + /* is it XRF? */ + if (port_i == RMT_XRF_PORT) + { + strcpy(link_request, OWNER); + link_request[8] = from_mod; + link_request[9] = to_mod; + link_request[10] = '\0'; + + traceit("sending link request from mod %c to link with: [%s] mod %c [%s]\n", + to_remote_g2[i].from_mod, + to_remote_g2[i].to_call, to_remote_g2[i].to_mod, payload); + + for (j = 0; j < 5; j++) + sendto(xrf_g2_sock,link_request, CALL_SIZE + 3,0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + } + else + if (port_i == RMT_DCS_PORT) + { + strcpy(link_request, OWNER); + link_request[8] = from_mod; + link_request[9] = to_mod; + link_request[10] = '\0'; + memcpy(link_request + 11, to_remote_g2[i].to_call, 8); + strcpy(link_request + 19, G2_html); + + traceit("sending link request from mod %c to link with: [%s] mod %c [%s]\n", + to_remote_g2[i].from_mod, + to_remote_g2[i].to_call, to_remote_g2[i].to_mod, payload); +// Login form 5 to 1 + for (j = 0; j < 1; j++) + sendto(dcs_g2_sock,link_request, 519,0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + } + else + if (port_i == RMT_REF_PORT) + { + for (counter = 0; counter < 3; counter++) + { + if (counter != i) + { + if ( (to_remote_g2[counter].to_call[0] != '\0') && + (strcmp(to_remote_g2[counter].to_call,to_remote_g2[i].to_call) == 0) ) + break; + } + } + if (counter > 2) + { + traceit("sending link command from mod %c to: [%s] mod %c [%s]\n", + to_remote_g2[i].from_mod, + to_remote_g2[i].to_call, to_remote_g2[i].to_mod, payload); + + queryCommand[0] = 5; + queryCommand[1] = 0; + queryCommand[2] = 24; + queryCommand[3] = 0; + queryCommand[4] = 1; + + for (j = 0; j < 1; j++) + sendto(ref_g2_sock,(char *)queryCommand,5,0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + } + else + { + if (to_remote_g2[counter].is_connected) + { + to_remote_g2[i].is_connected = true; + traceit("Local module %c is also connected to %s %c\n", + from_mod, call, to_mod); + + print_status_file(); + tracing[i].last_time = time(NULL); + + // announce it here + strcpy(linked_remote_system, to_remote_g2[i].to_call); + space_p = strchr(linked_remote_system, ' '); + if (space_p) + *space_p = '\0'; + sprintf(notify_msg, "%c_linked.dat_LINKED_%s_%c", + to_remote_g2[i].from_mod, + linked_remote_system, + to_remote_g2[i].to_mod); + audio_notify(notify_msg); + } + else + traceit("status from %s %c pending\n", to_remote_g2[i].to_call, to_remote_g2[i].to_mod); + } + } + } + return; +} + +/* signal catching function */ +static void sigCatch(int signum) +{ + /* do NOT do any serious work here */ + if ((signum == SIGTERM) || (signum == SIGINT)) + keep_running = false; + return; +} + +static void runit() +{ + socklen_t fromlen; + int recvlen; + int recvlen2; + short i,j,k; + char temp_repeater[CALL_SIZE + 1]; + time_t tnow = 0, hb = 0; + int rc = 0; + + char *p = NULL; + + char notify_msg[64]; + char *space_p = 0; + char linked_remote_system[CALL_SIZE + 1]; + char unlink_request[CALL_SIZE + 3]; + + char system_cmd[FILENAME_MAX + 1]; + int max_nfds = 0; + + char tmp1[CALL_SIZE + 1]; + char tmp2[36]; // 8 for rpt1 + 24 for time_t in string format + dt_lh_type::iterator dt_lh_pos; + dt_lh_type::reverse_iterator r_dt_lh_pos; + + gwy_list_type::iterator gwy_pos; + + char call[CALL_SIZE + 1]; + char ip[IP_SIZE + 1]; + inbound *inbound_ptr; + inbound_type::iterator pos; + pair insert_pair; + bool found = false; + set::iterator it; + + char cmd_2_dcs[23]; + unsigned char dcs_seq[3] = { 0x00, 0x00, 0x00 }; + struct { + char mycall[9]; + char sfx[5]; + unsigned int dcs_rptr_seq; + } rptr_2_dcs[3] = { {" ", " ", 0}, + {" ", " ", 0}, + {" ", " ", 0} }; + struct { + char mycall[9]; + char sfx[5]; + unsigned int dcs_rptr_seq; + } ref_2_dcs[3] = { {" ", " ", 0}, + {" ", " ", 0}, + {" ", " ", 0} }; + struct { + char mycall[9]; + char sfx[5]; + unsigned int dcs_rptr_seq; + } xrf_2_dcs[3] = { {" ", " ", 0}, + {" ", " ", 0}, + {" ", " ", 0} }; + + u_int16_t streamid_raw; + + char source_stn[9]; + + memset(notify_msg, '\0', sizeof(notify_msg)); + time(&hb); + + if (xrf_g2_sock > max_nfds) + max_nfds = xrf_g2_sock; + if (ref_g2_sock > max_nfds) + max_nfds = ref_g2_sock; + if (rptr_sock > max_nfds) + max_nfds = rptr_sock; + if (dcs_g2_sock > max_nfds) + max_nfds = dcs_g2_sock; + + traceit("xrf=%d, dcs=%d, ref=%d, rptr=%d, MAX+1=%d\n", + xrf_g2_sock, dcs_g2_sock, ref_g2_sock, rptr_sock, max_nfds + 1); + + if (strlen(LINK_AT_STARTUP) >= 8) + { + if ((LINK_AT_STARTUP[0] == 'A') || + (LINK_AT_STARTUP[0] == 'B') || + (LINK_AT_STARTUP[0] == 'C')) + { + memset(temp_repeater, ' ', CALL_SIZE); + memcpy(temp_repeater, LINK_AT_STARTUP + 1, 6); + temp_repeater[CALL_SIZE] = '\0'; + g2link(LINK_AT_STARTUP[0], temp_repeater, LINK_AT_STARTUP[7]); + } + memset(LINK_AT_STARTUP, '\0', sizeof(LINK_AT_STARTUP)); + } + + while (keep_running) + { + time(&tnow); + if ((tnow - hb) > 0) + { + /* send heartbeat to connected donglers */ + send_heartbeat(); + + /* send heartbeat to linked XRF repeaters/reflectors */ + if (to_remote_g2[0].toDst4.sin_port == htons(RMT_XRF_PORT)) + sendto(xrf_g2_sock,OWNER, CALL_SIZE + 1, 0, + (struct sockaddr *)&(to_remote_g2[0].toDst4), + sizeof(to_remote_g2[0].toDst4)); + + if ((to_remote_g2[1].toDst4.sin_port == htons(RMT_XRF_PORT)) && + (strcmp(to_remote_g2[1].to_call, to_remote_g2[0].to_call) != 0)) + sendto(xrf_g2_sock,OWNER, CALL_SIZE + 1, 0, + (struct sockaddr *)&(to_remote_g2[1].toDst4), + sizeof(to_remote_g2[1].toDst4)); + + if ((to_remote_g2[2].toDst4.sin_port == htons(RMT_XRF_PORT)) && + (strcmp(to_remote_g2[2].to_call, to_remote_g2[0].to_call) != 0) && + (strcmp(to_remote_g2[2].to_call, to_remote_g2[1].to_call) != 0)) + sendto(xrf_g2_sock,OWNER, CALL_SIZE + 1, 0, + (struct sockaddr *)&(to_remote_g2[2].toDst4), + sizeof(to_remote_g2[2].toDst4)); + + /* send heartbeat to linked DCS reflectors */ + if (to_remote_g2[0].toDst4.sin_port == htons(RMT_DCS_PORT)) + { + strcpy(cmd_2_dcs, OWNER); cmd_2_dcs[7] = to_remote_g2[0].from_mod; + memcpy(cmd_2_dcs + 9, to_remote_g2[0].to_call, 8); cmd_2_dcs[16] = to_remote_g2[0].to_mod; + sendto(dcs_g2_sock, cmd_2_dcs, 17, 0, + (struct sockaddr *)&(to_remote_g2[0].toDst4), + sizeof(to_remote_g2[0].toDst4)); + } + if (to_remote_g2[1].toDst4.sin_port == htons(RMT_DCS_PORT)) + { + strcpy(cmd_2_dcs, OWNER); cmd_2_dcs[7] = to_remote_g2[1].from_mod; + memcpy(cmd_2_dcs + 9, to_remote_g2[1].to_call, 8); cmd_2_dcs[16] = to_remote_g2[1].to_mod; + sendto(dcs_g2_sock, cmd_2_dcs, 17, 0, + (struct sockaddr *)&(to_remote_g2[1].toDst4), + sizeof(to_remote_g2[1].toDst4)); + } + if (to_remote_g2[2].toDst4.sin_port == htons(RMT_DCS_PORT)) + { + strcpy(cmd_2_dcs, OWNER); cmd_2_dcs[7] = to_remote_g2[2].from_mod; + memcpy(cmd_2_dcs + 9, to_remote_g2[2].to_call, 8); cmd_2_dcs[16] = to_remote_g2[2].to_mod; + sendto(dcs_g2_sock, cmd_2_dcs, 17, 0, + (struct sockaddr *)&(to_remote_g2[2].toDst4), + sizeof(to_remote_g2[2].toDst4)); + } + + /* send heartbeat to linked REF reflectors */ + if (to_remote_g2[0].is_connected && + (to_remote_g2[0].toDst4.sin_port == htons(RMT_REF_PORT))) + sendto(ref_g2_sock,(char *)REF_ACK,3,0, + (struct sockaddr *)&(to_remote_g2[0].toDst4), + sizeof(to_remote_g2[0].toDst4)); + + if (to_remote_g2[1].is_connected && + (to_remote_g2[1].toDst4.sin_port == htons(RMT_REF_PORT)) && + (strcmp(to_remote_g2[1].to_call, to_remote_g2[0].to_call) != 0)) + sendto(ref_g2_sock,(char *)REF_ACK,3,0, + (struct sockaddr *)&(to_remote_g2[1].toDst4), + sizeof(to_remote_g2[1].toDst4)); + + if (to_remote_g2[2].is_connected && + (to_remote_g2[2].toDst4.sin_port == htons(RMT_REF_PORT)) && + (strcmp(to_remote_g2[2].to_call, to_remote_g2[0].to_call) != 0) && + (strcmp(to_remote_g2[2].to_call, to_remote_g2[1].to_call) != 0)) + sendto(ref_g2_sock,(char *)REF_ACK,3,0, + (struct sockaddr *)&(to_remote_g2[2].toDst4), + sizeof(to_remote_g2[2].toDst4)); + + for (i = 0; i < 3; i++) + { + /* check for timeouts from remote */ + if (to_remote_g2[i].to_call[0] != '\0') + { + if (to_remote_g2[i].countdown >= 0) + to_remote_g2[i].countdown--; + + if (to_remote_g2[i].countdown < 0) + { + /* maybe remote system has changed IP */ + traceit("Unlinked from [%s] mod %c, TIMEOUT...\n", + to_remote_g2[i].to_call, to_remote_g2[i].to_mod); + + sprintf(notify_msg, "%c_unlinked.dat_UNLINKED_TIMEOUT", to_remote_g2[i].from_mod); + audio_notify(notify_msg); + + to_remote_g2[i].to_call[0] = '\0'; + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].from_mod = ' '; + to_remote_g2[i].to_mod = ' '; + to_remote_g2[i].countdown = 0; + to_remote_g2[i].is_connected = false; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + + print_status_file(); + + } + } + + /*** check for RF inactivity ***/ + if (to_remote_g2[i].is_connected) + { + if (((tnow - tracing[i].last_time) > RF_INACTIVITY_TIMER[i]) && (RF_INACTIVITY_TIMER[i] > 0)) + { + tracing[i].last_time = 0; + + traceit("Unlinked from [%s] mod %c, local RF inactivity...\n", + to_remote_g2[i].to_call, to_remote_g2[i].to_mod); + + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT)) + { + queryCommand[0] = 5; + queryCommand[1] = 0; + queryCommand[2] = 24; + queryCommand[3] = 0; + queryCommand[4] = 0; + sendto(ref_g2_sock,(char *)queryCommand,5,0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + + /* zero out any other entries here that match that system */ + for (j = 0; j < 3; j++) + { + if (j != i) + { + if ((to_remote_g2[j].toDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[j].toDst4.sin_port == htons(RMT_REF_PORT))) + { + to_remote_g2[j].to_call[0] = '\0'; + memset(&(to_remote_g2[j].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[j].from_mod = ' '; + to_remote_g2[j].to_mod = ' '; + to_remote_g2[j].countdown = 0; + to_remote_g2[j].is_connected = false; + to_remote_g2[j].in_streamid[0] = 0x00; + to_remote_g2[j].in_streamid[1] = 0x00; + } + } + } + } + else + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT)) + { + strcpy(unlink_request, OWNER); + unlink_request[8] = to_remote_g2[i].from_mod; + unlink_request[9] = ' '; + unlink_request[10] = '\0'; + + for (j = 0; j < 5; j++) + sendto(xrf_g2_sock,unlink_request, CALL_SIZE + 3,0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + } + else + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_DCS_PORT)) + { + strcpy(cmd_2_dcs, OWNER); + cmd_2_dcs[8] = to_remote_g2[i].from_mod; + cmd_2_dcs[9] = ' '; + cmd_2_dcs[10] = '\0'; + memcpy(cmd_2_dcs + 11, to_remote_g2[i].to_call, 8); + + for (j = 0; j < 2; j++) + sendto(dcs_g2_sock, cmd_2_dcs, 19 ,0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + } + + sprintf(notify_msg, "%c_unlinked.dat_UNLINKED_TIMEOUT", to_remote_g2[i].from_mod); + audio_notify(notify_msg); + + to_remote_g2[i].to_call[0] = '\0'; + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].from_mod = ' '; + to_remote_g2[i].to_mod = ' '; + to_remote_g2[i].countdown = 0; + to_remote_g2[i].is_connected = false; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + + print_status_file(); + } + } + } + time(&hb); + } + + FD_ZERO(&fdset); + FD_SET(xrf_g2_sock,&fdset); + FD_SET(dcs_g2_sock,&fdset); + FD_SET(ref_g2_sock,&fdset); + FD_SET(rptr_sock,&fdset); + tv.tv_sec = 0; + tv.tv_usec = 20000; + (void)select(max_nfds + 1,&fdset,0,0,&tv); + + if (FD_ISSET(xrf_g2_sock, &fdset)) + { + fromlen = sizeof(struct sockaddr_in); + recvlen2 = recvfrom(xrf_g2_sock,(char *)readBuffer2,100, + 0,(struct sockaddr *)&fromDst4,&fromlen); + + strncpy(ip, inet_ntoa(fromDst4.sin_addr),IP_SIZE); + ip[IP_SIZE] = '\0'; + strncpy(call, (char *)readBuffer2,CALL_SIZE); + call[CALL_SIZE] = '\0'; + + /* a packet of length (CALL_SIZE + 1) is a keepalive from a repeater/reflector */ + /* If it is from a dongle, it is either a keepalive or a request to connect */ + + if (recvlen2 == (CALL_SIZE + 1)) + { + found = false; + /* Find out if it is a keepalive from a repeater */ + for (i = 0; i < 3; i++) + { + if ((fromDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT))) + { + found = true; + if (!to_remote_g2[i].is_connected) + { + tracing[i].last_time = time(NULL); + + to_remote_g2[i].is_connected = true; + traceit("Connected from: %.*s\n", recvlen2 - 1, readBuffer2); + print_status_file(); + + strcpy(linked_remote_system, to_remote_g2[i].to_call); + space_p = strchr(linked_remote_system, ' '); + if (space_p) + *space_p = '\0'; + sprintf(notify_msg, "%c_linked.dat_LINKED_%s_%c", + to_remote_g2[i].from_mod, + linked_remote_system, + to_remote_g2[i].to_mod); + audio_notify(notify_msg); + + } + to_remote_g2[i].countdown = TIMEOUT; + } + } + } + else + /* A packet of length (CALL_SIZE + 6) is either an ACK or a NAK from repeater-reflector */ + /* Because we sent a request before asking to link */ + if (recvlen2 == (CALL_SIZE + 6)) + { + for (i = 0; i < 3; i++) + { + if ((fromDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT))) + { + if ((memcmp((char *)readBuffer2 + 10, "ACK", 3) == 0) && + (to_remote_g2[i].from_mod == readBuffer2[8])) + { + if (!to_remote_g2[i].is_connected) + { + tracing[i].last_time = time(NULL); + + to_remote_g2[i].is_connected = true; + traceit("Connected from: [%s] %c\n", + to_remote_g2[i].to_call, to_remote_g2[i].to_mod); + print_status_file(); + + strcpy(linked_remote_system, to_remote_g2[i].to_call); + space_p = strchr(linked_remote_system, ' '); + if (space_p) + *space_p = '\0'; + sprintf(notify_msg, "%c_linked.dat_LINKED_%s_%c", + to_remote_g2[i].from_mod, + linked_remote_system, + to_remote_g2[i].to_mod); + audio_notify(notify_msg); + } + } + else + if ((memcmp((char *)readBuffer2 + 10, "NAK", 3) == 0) && + (to_remote_g2[i].from_mod == readBuffer2[8])) + { + traceit("Link module %c to [%s] %c is rejected\n", + to_remote_g2[i].from_mod, to_remote_g2[i].to_call, + to_remote_g2[i].to_mod); + + sprintf(notify_msg, "%c_failed_linked.dat_FAILED_TO_LINK", + to_remote_g2[i].from_mod); + audio_notify(notify_msg); + + to_remote_g2[i].to_call[0] = '\0'; + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].from_mod = ' '; + to_remote_g2[i].to_mod = ' '; + to_remote_g2[i].countdown = 0; + to_remote_g2[i].is_connected = false; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + + print_status_file(); + } + } + } + } + else + /* + A packet of length (CALL_SIZE + 3) is a request + from a remote repeater to link-unlink with our repeater + */ + if (recvlen2 == CALL_SIZE + 3) + { + /* Check our linked repeaters/reflectors */ + for (i = 0; i < 3; i++) + { + if ((fromDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT))) + { + if (to_remote_g2[i].to_mod == readBuffer2[8]) + { + /* unlink request from remote repeater that we know */ + if (readBuffer2[9] == ' ') + { + traceit("Received: %.*s\n", recvlen2 - 1, readBuffer2); + traceit("Module %c to [%s] %c is unlinked\n", + to_remote_g2[i].from_mod, + to_remote_g2[i].to_call, to_remote_g2[i].to_mod); + + sprintf(notify_msg, "%c_unlinked.dat_UNLINKED", to_remote_g2[i].from_mod); + audio_notify(notify_msg); + + to_remote_g2[i].to_call[0] = '\0'; + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].from_mod = ' '; + to_remote_g2[i].to_mod = ' '; + to_remote_g2[i].countdown = 0; + to_remote_g2[i].is_connected = false; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + + print_status_file(); + } + else + /* link request from a remote repeater that we know */ + if ( + ((i == 0) && (readBuffer2[9] == 'A')) || + ((i == 1) && (readBuffer2[9] == 'B')) || + ((i == 2) && (readBuffer2[9] == 'C')) + ) + { + + /* + I HAVE TO ADD CODE here to PREVENT the REMOTE NODE + from LINKING one of their remote modules to + more than one of our local modules + */ + + traceit("Received: %.*s\n", recvlen2 - 1, readBuffer2); + + strncpy(to_remote_g2[i].to_call, (char *)readBuffer2,CALL_SIZE); + to_remote_g2[i].to_call[CALL_SIZE] = '\0'; + memcpy(&(to_remote_g2[i].toDst4), &fromDst4, sizeof(struct sockaddr_in)); + to_remote_g2[i].toDst4.sin_port = htons(RMT_XRF_PORT); + to_remote_g2[i].to_mod = readBuffer2[8]; + to_remote_g2[i].countdown = TIMEOUT; + to_remote_g2[i].is_connected = true; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + + traceit("Module %c to [%s] %c linked\n", + readBuffer2[9], + to_remote_g2[i].to_call, to_remote_g2[i].to_mod); + + tracing[i].last_time = time(NULL); + + print_status_file(); + + /* send back an ACK */ + memcpy(readBuffer2 + 10, "ACK", 4); + sendto(xrf_g2_sock, (char *)readBuffer2, CALL_SIZE + 6, + 0,(struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + + if (to_remote_g2[i].from_mod != readBuffer2[9]) + { + to_remote_g2[i].from_mod = readBuffer2[9]; + + strcpy(linked_remote_system, to_remote_g2[i].to_call); + space_p = strchr(linked_remote_system, ' '); + if (space_p) + *space_p = '\0'; + sprintf(notify_msg, "%c_linked.dat_LINKED_%s_%c", + to_remote_g2[i].from_mod, + linked_remote_system, + to_remote_g2[i].to_mod); + audio_notify(notify_msg); + } + } + } + } + } + + /* link request from remote repeater that is not yet linked to our system */ + /* find out which of our local modules the remote repeater is interested in */ + i = -1; + if (readBuffer2[9] == 'A') + i = 0; + else + if (readBuffer2[9] == 'B') + i = 1; + else + if (readBuffer2[9] == 'C') + i = 2; + + /* Is this repeater listed in gwys.txt? */ + gwy_pos = gwy_list.find(call); + if (gwy_pos == gwy_list.end()) + { + /* We did NOT find this repeater in gwys.txt, reject the incoming link request */ + traceit("Incoming link from %s,%s but not found in gwys.txt\n",call,ip); + i = -1; + } + else + { + rc = regexec(&preg, call, 0, NULL, 0); + if (rc != 0) + { + traceit("Invalid repeater %s,%s requesting to link\n", call, ip); + i = -1; + } + } + + if (i >= 0) + { + /* Is the local repeater module linked to anything ? */ + if (to_remote_g2[i].to_mod == ' ') + { + if ((readBuffer2[8] == 'A') || (readBuffer2[8] == 'B') || (readBuffer2[8] == 'C') || + (readBuffer2[8] == 'D') || (readBuffer2[8] == 'E')) + { + /* + I HAVE TO ADD CODE here to PREVENT the REMOTE NODE + from LINKING one of their remote modules to + more than one of our local modules + */ + + /* now it can be added as a repeater */ + strcpy(to_remote_g2[i].to_call, call); + to_remote_g2[i].to_call[CALL_SIZE] = '\0'; + memcpy(&(to_remote_g2[i].toDst4), &fromDst4, sizeof(struct sockaddr_in)); + to_remote_g2[i].toDst4.sin_port = htons(RMT_XRF_PORT); + to_remote_g2[i].from_mod = readBuffer2[9]; + to_remote_g2[i].to_mod = readBuffer2[8]; + to_remote_g2[i].countdown = TIMEOUT; + to_remote_g2[i].is_connected = true; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + + print_status_file(); + + tracing[i].last_time = time(NULL); + + traceit("Received: %.*s\n", recvlen2 - 1, readBuffer2); + traceit("Module %c to [%s] %c linked\n", + to_remote_g2[i].from_mod, + to_remote_g2[i].to_call, to_remote_g2[i].to_mod); + + strcpy(linked_remote_system, to_remote_g2[i].to_call); + space_p = strchr(linked_remote_system, ' '); + if (space_p) + *space_p = '\0'; + sprintf(notify_msg, "%c_linked.dat_LINKED_%s_%c", + to_remote_g2[i].from_mod, + linked_remote_system, + to_remote_g2[i].to_mod); + audio_notify(notify_msg); + + /* send back an ACK */ + memcpy(readBuffer2 + 10, "ACK", 4); + sendto(xrf_g2_sock, (char *)readBuffer2, CALL_SIZE + 6, + 0,(struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + } + } + else + { + if (fromDst4.sin_addr.s_addr != to_remote_g2[i].toDst4.sin_addr.s_addr) + { + /* Our repeater module is linked to another repeater-reflector */ + memcpy(readBuffer2 + 10, "NAK", 4); + fromDst4.sin_port = htons(RMT_XRF_PORT); + sendto(xrf_g2_sock, (char *)readBuffer2, CALL_SIZE + 6, + 0,(struct sockaddr *)&fromDst4, + sizeof(struct sockaddr_in)); + } + } + } + } + else + if ( ((recvlen2 == 56) || + (recvlen2 == 27)) && + (memcmp(readBuffer2, "DSVT", 4) == 0) && + ((readBuffer2[4] == 0x10) || + (readBuffer2[4] == 0x20)) && + (readBuffer2[8] == 0x20)) + { + /* reset countdown and protect against hackers */ + + found = false; + for (i = 0; i < 3; i++) + { + if ((fromDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT))) + { + to_remote_g2[i].countdown = TIMEOUT; + found = true; + } + } + + /* process header */ + + if ((recvlen2 == 56) && found) + { + memset(source_stn, ' ', 9); source_stn[8] = '\0'; + + /* some bad hotspot programs out there using INCORRECT flag */ + if (readBuffer2[15] == 0x40) + readBuffer2[15] = 0x00; + else + if (readBuffer2[15] == 0x48) + readBuffer2[15] = 0x08; + else + if (readBuffer2[15] == 0x60) + readBuffer2[15] = 0x20; + else + if (readBuffer2[15] == 0x68) + readBuffer2[15] = 0x28; + + /* A reflector will send to us its own RPT1 */ + /* A repeater will send to us our RPT1 */ + + for (i = 0; i < 3; i++) + { + if ((fromDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT))) + { + /* it is a reflector, reflector's rpt1 */ + if ((memcmp(readBuffer2 + 18, to_remote_g2[i].to_call, 7) == 0) && + (readBuffer2[25] == to_remote_g2[i].to_mod)) + { + memcpy(&readBuffer2[18], OWNER, CALL_SIZE); + readBuffer2[25] = to_remote_g2[i].from_mod; + memcpy(&readBuffer2[34], "CQCQCQ ", 8); + + memcpy(source_stn, to_remote_g2[i].to_call, 8); source_stn[7] = to_remote_g2[i].to_mod; + break; + } + else + /* it is a repeater, our rpt1 */ + if ((memcmp(readBuffer2 + 18, OWNER, 7)) && + (readBuffer2[25] == to_remote_g2[i].from_mod)) + { + memcpy(source_stn, to_remote_g2[i].to_call, 8); source_stn[7] = to_remote_g2[i].to_mod; + break; + } + } + } + + /* somebody's crazy idea of having a personal callsign in RPT2 */ + /* we must set it to our gateway callsign */ + memcpy(&readBuffer2[26], OWNER, CALL_SIZE); + readBuffer2[33] = 'G'; + calcPFCS(readBuffer2,56); + + /* At this point, all data have our RPT1 and RPT2 */ + + /* send the data to the repeater/reflector that is linked to our RPT1 */ + i = -1; + if (readBuffer2[25] == 'A') + i = 0; + else + if (readBuffer2[25] == 'B') + i = 1; + else + if (readBuffer2[25] == 'C') + i = 2; + + /* are we sure that RPT1 is our system? */ + if ((memcmp(readBuffer2 + 18, OWNER, 7) == 0) && (i >= 0)) + { + /* Last Heard */ + if (memcmp(old_sid[i].sid, readBuffer2 + 12, 2) != 0) + { + if (QSO_DETAILS) + traceit("START from remote g2: streamID=%d,%d, flags=%02x:%02x:%02x, my=%.8s, sfx=%.4s, ur=%.8s, rpt1=%.8s, rpt2=%.8s, %d bytes fromIP=%s, source=%.8s\n", + readBuffer2[12],readBuffer2[13], + readBuffer2[15], readBuffer2[16], readBuffer2[17], + &readBuffer2[42], + &readBuffer2[50], &readBuffer2[34], + &readBuffer2[18], &readBuffer2[26], + recvlen2,inet_ntoa(fromDst4.sin_addr), source_stn); + + // put user into tmp1 + memcpy(tmp1, readBuffer2 + 42, 8); tmp1[8] = '\0'; + + // delete the user if exists + for (dt_lh_pos = dt_lh_list.begin(); dt_lh_pos != dt_lh_list.end(); dt_lh_pos++) + { + if (strcmp((char *)dt_lh_pos->second.c_str(), tmp1) == 0) + { + dt_lh_list.erase(dt_lh_pos); + break; + } + } + /* Limit?, delete oldest user */ + if (dt_lh_list.size() == LH_MAX_SIZE) + { + dt_lh_pos = dt_lh_list.begin(); + dt_lh_list.erase(dt_lh_pos); + } + // add user + time(&tnow); + sprintf(tmp2, "%ld=r%.6s%c%c", tnow, source_stn, source_stn[7], readBuffer2[25]); + dt_lh_list[tmp2] = tmp1; + + memcpy(old_sid[i].sid, readBuffer2 + 12, 2); + } + + /* relay data to our local G2 */ + sendto(rptr_sock, (char *)readBuffer2,56,0,(struct sockaddr *)&toLocalg2,sizeof(struct sockaddr_in)); + + /* send data to donglers */ + /* no changes here */ + for (pos = inbound_list.begin(); pos != inbound_list.end(); pos++) + { + inbound_ptr = (inbound *)pos->second; + if (fromDst4.sin_addr.s_addr != inbound_ptr->sin.sin_addr.s_addr) + { + readBuffer[0] = (unsigned char)(58 & 0xFF); + readBuffer[1] = (unsigned char)(58 >> 8 & 0x1F); + readBuffer[1] = (unsigned char)(readBuffer[1] | 0xFFFFFF80); + memcpy(readBuffer + 2, readBuffer2, 56); + + sendto(ref_g2_sock, (char *)readBuffer, 58, 0, + (struct sockaddr *)&(inbound_ptr->sin), + sizeof(struct sockaddr_in)); + } + else + inbound_ptr->mod = readBuffer2[25]; + } + + /* send the data to the repeater/reflector that is linked to our RPT1 */ + + /* Is there another local module linked to the remote same xrf mod ? */ + /* If Yes, then broadcast */ + k = i + 1; + + if (k < 3) + { + brd_from_xrf_idx = 0; + streamid_raw = (readBuffer2[12] * 256U) + readBuffer2[13]; + + /* We can only enter this loop up to 2 times max */ + for (j = k; j < 3; j++) + { + /* it is a remote gateway, not a dongle user */ + if ((fromDst4.sin_addr.s_addr == to_remote_g2[j].toDst4.sin_addr.s_addr) && + /* it is xrf */ + (to_remote_g2[j].toDst4.sin_port == htons(RMT_XRF_PORT)) && + (memcmp(to_remote_g2[j].to_call, "XRF", 3) == 0) && + /* it is the same xrf and xrf module */ + (memcmp(to_remote_g2[j].to_call, to_remote_g2[i].to_call, 8) == 0) && + (to_remote_g2[j].to_mod == to_remote_g2[i].to_mod)) + { + /* send the packet to another module of our local repeater: this is multi-link */ + + /* generate new packet */ + memcpy(from_xrf_torptr_brd, readBuffer2, 56); + + /* different repeater module */ + from_xrf_torptr_brd[25] = to_remote_g2[j].from_mod; + + /* assign new streamid */ + streamid_raw ++; + if (streamid_raw == 0) + streamid_raw ++; + from_xrf_torptr_brd[12] = streamid_raw / 256U; + from_xrf_torptr_brd[13] = streamid_raw % 256U; + + calcPFCS(from_xrf_torptr_brd, 56); + + /* send the data to the local gateway/repeater */ + sendto(rptr_sock, (char *)from_xrf_torptr_brd,56,0, + (struct sockaddr *)&toLocalg2,sizeof(struct sockaddr_in)); + + /* save streamid for use with the audio packets that will arrive after this header */ + + brd_from_xrf.xrf_streamid[0] = readBuffer2[12]; + brd_from_xrf.xrf_streamid[1] = readBuffer2[13]; + brd_from_xrf.rptr_streamid[brd_from_xrf_idx][0] = from_xrf_torptr_brd[12]; + brd_from_xrf.rptr_streamid[brd_from_xrf_idx][1] = from_xrf_torptr_brd[13]; + brd_from_xrf_idx ++; + } + } + } + + if ((to_remote_g2[i].toDst4.sin_addr.s_addr != fromDst4.sin_addr.s_addr) && + to_remote_g2[i].is_connected) + { + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT)) + { + if ( /*** (memcmp(readBuffer2 + 42, OWNER, 8) != 0) && ***/ /* block repeater announcements */ + (memcmp(readBuffer2 + 34, "CQCQCQ", 6) == 0) && /* CQ calls only */ + ((readBuffer2[15] == 0x00) || /* normal */ + (readBuffer2[15] == 0x08) || /* EMR */ + (readBuffer2[15] == 0x20) || /* BK */ + (readBuffer2[15] == 0x28)) && /* EMR + BK */ + (memcmp(readBuffer2 + 26, OWNER, 7) == 0) && /* rpt2 must be us */ + (readBuffer2[33] == 'G')) + { + to_remote_g2[i].in_streamid[0] = readBuffer2[12]; + to_remote_g2[i].in_streamid[1] = readBuffer2[13]; + + /* inform XRF about the source */ + readBuffer2[11] = to_remote_g2[i].from_mod; + + memcpy((char *)readBuffer2 + 18, to_remote_g2[i].to_call, CALL_SIZE); + readBuffer2[25] = to_remote_g2[i].to_mod; + memcpy((char *)readBuffer2 + 26, to_remote_g2[i].to_call, CALL_SIZE); + readBuffer2[33] = 'G'; + calcPFCS(readBuffer2, 56); + + sendto(xrf_g2_sock, (char *)readBuffer2, 56, 0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + } + } + else + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT)) + { + if ( /*** (memcmp(readBuffer2 + 42, OWNER, 8) != 0) && ***/ /* block repeater announcements */ + (memcmp(readBuffer2 + 34, "CQCQCQ", 6) == 0) && /* CQ calls only */ + ((readBuffer2[15] == 0x00) || /* normal */ + (readBuffer2[15] == 0x08) || /* EMR */ + (readBuffer2[15] == 0x20) || /* BK */ + (readBuffer2[15] == 0x28)) && /* EMR + BK */ + (memcmp(readBuffer2 + 26, OWNER, 7) == 0) && /* rpt2 must be us */ + (readBuffer2[33] == 'G')) + { + to_remote_g2[i].in_streamid[0] = readBuffer2[12]; + to_remote_g2[i].in_streamid[1] = readBuffer2[13]; + + readBuffer[0] = (unsigned char)(58 & 0xFF); + readBuffer[1] = (unsigned char)(58 >> 8 & 0x1F); + readBuffer[1] = (unsigned char)(readBuffer[1] | 0xFFFFFF80); + + memcpy(readBuffer + 2, readBuffer2, 56); + + memset(readBuffer + 20, ' ', CALL_SIZE); + memcpy(readBuffer + 20, to_remote_g2[i].to_call, + strlen(to_remote_g2[i].to_call)); + readBuffer[27] = to_remote_g2[i].to_mod; + memset(readBuffer + 28, ' ', CALL_SIZE); + memcpy(readBuffer + 28, to_remote_g2[i].to_call, + strlen(to_remote_g2[i].to_call)); + readBuffer[35] = 'G'; + memcpy(&readBuffer[36], "CQCQCQ ", 8); + + calcPFCS(readBuffer + 2, 56); + + sendto(ref_g2_sock, (char *)readBuffer, 58, 0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + } + } + else + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_DCS_PORT)) + { + if ( /*** (memcmp(readBuffer2 + 42, OWNER, 8) != 0) && ***/ /* block repeater announcements */ + (memcmp(readBuffer2 + 34, "CQCQCQ", 6) == 0) && /* CQ calls only */ + ((readBuffer2[15] == 0x00) || /* normal */ + (readBuffer2[15] == 0x08) || /* EMR */ + (readBuffer2[15] == 0x20) || /* BK */ + (readBuffer2[15] == 0x28)) && /* EMR + BK */ + (memcmp(readBuffer2 + 26, OWNER, 7) == 0) && /* rpt2 must be us */ + (readBuffer2[33] == 'G')) + { + to_remote_g2[i].in_streamid[0] = readBuffer2[12]; + to_remote_g2[i].in_streamid[1] = readBuffer2[13]; + + memcpy(xrf_2_dcs[i].mycall, readBuffer2 + 42, 8); + memcpy(xrf_2_dcs[i].sfx, readBuffer2 + 50, 4); + xrf_2_dcs[i].dcs_rptr_seq = 0; + } + } + } + } + } + else + if (found) + { + if ((readBuffer2[14] & 0x40) != 0) + { + for (i = 0; i < 3; i++) + { + if (memcmp(old_sid[i].sid, readBuffer2 + 12, 2) == 0) + { + if (QSO_DETAILS) + traceit("END from remote g2: streamID=%d,%d, %d bytes from IP=%s\n", + readBuffer2[12],readBuffer2[13],recvlen2,inet_ntoa(fromDst4.sin_addr)); + + memset(old_sid[i].sid, 0x00, 2); + + break; + } + } + } + + /* relay data to our local G2 */ + sendto(rptr_sock, (char *)readBuffer2,27,0,(struct sockaddr *)&toLocalg2,sizeof(struct sockaddr_in)); + + /* send data to donglers */ + /* no changes here */ + for (pos = inbound_list.begin(); pos != inbound_list.end(); pos++) + { + inbound_ptr = (inbound *)pos->second; + if (fromDst4.sin_addr.s_addr != inbound_ptr->sin.sin_addr.s_addr) + { + readBuffer[0] = (unsigned char)(29 & 0xFF); + readBuffer[1] = (unsigned char)(29 >> 8 & 0x1F); + readBuffer[1] = (unsigned char)(readBuffer[1] | 0xFFFFFF80); + + memcpy(readBuffer + 2, readBuffer2, 27); + + sendto(ref_g2_sock, (char *)readBuffer, 29, + 0,(struct sockaddr *)&(inbound_ptr->sin), + sizeof(struct sockaddr_in)); + } + } + + /* do we have to broadcast ? */ + if (memcmp(brd_from_xrf.xrf_streamid, readBuffer2 + 12, 2) == 0) + { + memcpy(from_xrf_torptr_brd, readBuffer2, 27); + + if ((brd_from_xrf.rptr_streamid[0][0] != 0x00) || + (brd_from_xrf.rptr_streamid[0][1] != 0x00)) + { + from_xrf_torptr_brd[12] = brd_from_xrf.rptr_streamid[0][0]; + from_xrf_torptr_brd[13] = brd_from_xrf.rptr_streamid[0][1]; + sendto(rptr_sock, (char *)from_xrf_torptr_brd,27,0,(struct sockaddr *)&toLocalg2,sizeof(struct sockaddr_in)); + } + + if ((brd_from_xrf.rptr_streamid[1][0] != 0x00) || + (brd_from_xrf.rptr_streamid[1][1] != 0x00)) + { + from_xrf_torptr_brd[12] = brd_from_xrf.rptr_streamid[1][0]; + from_xrf_torptr_brd[13] = brd_from_xrf.rptr_streamid[1][1]; + sendto(rptr_sock, (char *)from_xrf_torptr_brd,27,0,(struct sockaddr *)&toLocalg2,sizeof(struct sockaddr_in)); + } + + if ((readBuffer2[14] & 0x40) != 0) + { + brd_from_xrf.xrf_streamid[0] = brd_from_xrf.xrf_streamid[1] = 0x00; + brd_from_xrf.rptr_streamid[0][0] = brd_from_xrf.rptr_streamid[0][1] = 0x00; + brd_from_xrf.rptr_streamid[1][0] = brd_from_xrf.rptr_streamid[1][1] = 0x00; + brd_from_xrf_idx = 0; + } + } + + for (i = 0; i < 3; i++) + { + if ((to_remote_g2[i].is_connected) && + (to_remote_g2[i].toDst4.sin_addr.s_addr != fromDst4.sin_addr.s_addr) && + (memcmp(to_remote_g2[i].in_streamid, readBuffer2 + 12, 2) == 0)) + { + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT)) + { + /* inform XRF about the source */ + readBuffer2[11] = to_remote_g2[i].from_mod; + + sendto(xrf_g2_sock, (char *)readBuffer2, 27, 0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + } + else + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT)) + { + readBuffer[0] = (unsigned char)(29 & 0xFF); + readBuffer[1] = (unsigned char)(29 >> 8 & 0x1F); + readBuffer[1] = (unsigned char)(readBuffer[1] | 0xFFFFFF80); + + memcpy(readBuffer + 2, readBuffer2, 27); + + sendto(ref_g2_sock, (char *)readBuffer, 29, + 0,(struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + } + else + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_DCS_PORT)) + { + memset(dcs_buf, 0x00, 600); + dcs_buf[0] = dcs_buf[1] = dcs_buf[2] = '0'; + dcs_buf[3] = '1'; + dcs_buf[4] = dcs_buf[5] = dcs_buf[6] = 0x00; + memcpy(dcs_buf + 7, to_remote_g2[i].to_call, 8); + dcs_buf[14] = to_remote_g2[i].to_mod; + memcpy(dcs_buf + 15, OWNER, 8); + dcs_buf[22] = to_remote_g2[i].from_mod; + memcpy(dcs_buf + 23, "CQCQCQ ", 8); + memcpy(dcs_buf + 31, xrf_2_dcs[i].mycall, 8); + memcpy(dcs_buf + 39, xrf_2_dcs[i].sfx, 4); + dcs_buf[43] = readBuffer2[12]; /* streamid0 */ + dcs_buf[44] = readBuffer2[13]; /* streamid1 */ + dcs_buf[45] = readBuffer2[14]; /* cycle sequence */ + memcpy(dcs_buf + 46, readBuffer2 + 15, 12); + + dcs_buf[58] = (xrf_2_dcs[i].dcs_rptr_seq >> 0) & 0xff; + dcs_buf[59] = (xrf_2_dcs[i].dcs_rptr_seq >> 8) & 0xff; + dcs_buf[60] = (xrf_2_dcs[i].dcs_rptr_seq >> 16) & 0xff; + + xrf_2_dcs[i].dcs_rptr_seq ++; + + dcs_buf[61] = 0x01; + dcs_buf[62] = 0x00; + + sendto(dcs_g2_sock, dcs_buf, 100, 0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + } + + if ((readBuffer2[14] & 0x40) != 0) + { + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + } + break; + } + } + } + } + FD_CLR (xrf_g2_sock,&fdset); + } + + if (FD_ISSET(ref_g2_sock, &fdset)) + { + fromlen = sizeof(struct sockaddr_in); + recvlen2 = recvfrom(ref_g2_sock,(char *)readBuffer2,100, + 0,(struct sockaddr *)&fromDst4,&fromlen); + + strncpy(ip, inet_ntoa(fromDst4.sin_addr),IP_SIZE); + ip[IP_SIZE] = '\0'; + + found = false; + + /* LH */ + if ((recvlen2 == 4) && + (readBuffer2[0] == 4) && + (readBuffer2[1] == 192) && + (readBuffer2[2] == 7) && + (readBuffer2[3] == 0)) + { + unsigned short j_idx = 0; + unsigned short k_idx = 0; + unsigned char tmp[2]; + + pos = inbound_list.find(ip); + if (pos != inbound_list.end()) + { + inbound_ptr = (inbound *)pos->second; + // traceit("Remote station %s %s requested LH list\n", inbound_ptr->call, ip); + + /* header is 10 bytes */ + + /* reply type */ + readBuffer2[2] = 7; + readBuffer2[3] = 0; + + /* it looks like time_t here */ + time(&tnow); + memcpy((char *)readBuffer2 + 6, (char *)&tnow, sizeof(time_t)); + + for (r_dt_lh_pos = dt_lh_list.rbegin(); r_dt_lh_pos != dt_lh_list.rend(); r_dt_lh_pos++) + { + /* each entry has 24 bytes */ + + /* start at position 10 to bypass the header */ + strcpy((char *)readBuffer2 + 10 + (24 * j_idx), r_dt_lh_pos->second.c_str()); + p = strchr((char *)r_dt_lh_pos->first.c_str(), '='); + if (p) + { + memcpy((char *)readBuffer2 + 18 + (24 * j_idx), p + 2, 8); + + /* if local or local w/gps */ + if ((p[1] == 'l') || (p[1] == 'g')) + readBuffer2[18 + (24 * j_idx) + 6] = *(p + 1); + + *p = '\0'; + tnow = atol(r_dt_lh_pos->first.c_str()); + *p = '='; + memcpy((char *)readBuffer2 + 26 + (24 * j_idx), &tnow, sizeof(time_t)); + } + else + { + memcpy((char *)readBuffer2 + 18 + (24 * j_idx), "ERROR ", 8); + time(&tnow); + memcpy((char *)readBuffer2 + 26 + (24 * j_idx), &tnow, sizeof(time_t)); + } + + readBuffer2[30 + (24 * j_idx)] = 0; + readBuffer2[31 + (24 * j_idx)] = 0; + readBuffer2[32 + (24 * j_idx)] = 0; + readBuffer2[33 + (24 * j_idx)] = 0; + + j_idx++; + + /* process 39 entries at a time */ + if (j_idx == 39) + { + /* 39 * 24 = 936 + 10 header = 946 */ + readBuffer2[0] = 0xb2; + readBuffer2[1] = 0xc3; + + /* 39 entries */ + readBuffer2[4] = 0x27; + readBuffer2[5] = 0x00; + + sendto(ref_g2_sock,(char *)readBuffer2,946,0, + (struct sockaddr *)&fromDst4, + sizeof(struct sockaddr_in)); + + j_idx = 0; + } + } + + if (j_idx != 0) + { + k_idx = 10 + (j_idx * 24); + memcpy(tmp, (char *)&k_idx, 2); + readBuffer2[0] = tmp[0]; + readBuffer2[1] = tmp[1] | 0xc0; + + memcpy(tmp, (char *)&j_idx, 2); + readBuffer2[4] = tmp[0]; + readBuffer2[5] = tmp[1]; + + sendto(ref_g2_sock,(char *)readBuffer2, k_idx, 0, + (struct sockaddr *)&fromDst4, + sizeof(struct sockaddr_in)); + } + } + } + else + /* linked repeaters request */ + if ((recvlen2 == 4) && + (readBuffer2[0] == 4) && + (readBuffer2[1] == 192) && + (readBuffer2[2] == 5) && + (readBuffer2[3] == 0)) + { + unsigned short i_idx = 0; + unsigned short j_idx = 0; + unsigned short k_idx = 0; + unsigned char tmp[2]; + unsigned short total = 0; + + pos = inbound_list.find(ip); + if (pos != inbound_list.end()) + { + inbound_ptr = (inbound *)pos->second; + // traceit("Remote station %s %s requested linked repeaters list\n", inbound_ptr->call, ip); + + /* header is 8 bytes */ + + /* reply type */ + readBuffer2[2] = 5; + readBuffer2[3] = 1; + + /* we can have up to 3 linked systems */ + total = 3; + memcpy(tmp, (char *)&total, 2); + readBuffer2[6] = tmp[0]; + readBuffer2[7] = tmp[1]; + + for (i = 0, i_idx = 0; i < 3; i++, i_idx++) + { + /* each entry has 20 bytes */ + if (to_remote_g2[i].to_mod != ' ') + { + if (i == 0) + readBuffer2[8 + (20 * j_idx)] = 'A'; + else + if (i == 1) + readBuffer2[8 + (20 * j_idx)] = 'B'; + else + if (i == 2) + readBuffer2[8 + (20 * j_idx)] = 'C'; + + strcpy((char *)readBuffer2 + 9 + (20 * j_idx), to_remote_g2[i].to_call); + readBuffer2[16 + (20 * j_idx)] = to_remote_g2[i].to_mod; + + readBuffer2[17 + (20 * j_idx)] = 0; + readBuffer2[18 + (20 * j_idx)] = 0; + readBuffer2[19 + (20 * j_idx)] = 0; + readBuffer2[20 + (20 * j_idx)] = 0x50; + readBuffer2[21 + (20 * j_idx)] = 0x04; + readBuffer2[22 + (20 * j_idx)] = 0x32; + readBuffer2[23 + (20 * j_idx)] = 0x4d; + readBuffer2[24 + (20 * j_idx)] = 0x9f; + readBuffer2[25 + (20 * j_idx)] = 0xdb; + readBuffer2[26 + (20 * j_idx)] = 0x0e; + readBuffer2[27 + (20 * j_idx)] = 0; + + j_idx++; + + if (j_idx == 39) + { + /* 20 bytes for each user, so 39 * 20 = 780 bytes + 8 bytes header = 788 */ + readBuffer2[0] = 0x14; + readBuffer2[1] = 0xc3; + + k_idx = i_idx - 38; + memcpy(tmp, (char *)&k_idx, 2); + readBuffer2[4] = tmp[0]; + readBuffer2[5] = tmp[1]; + + sendto(ref_g2_sock,(char *)readBuffer2,788,0, + (struct sockaddr *)&fromDst4, + sizeof(struct sockaddr_in)); + + j_idx = 0; + } + } + } + + if (j_idx != 0) + { + k_idx = 8 + (j_idx * 20); + memcpy(tmp, (char *)&k_idx, 2); + readBuffer2[0] = tmp[0]; + readBuffer2[1] = tmp[1] | 0xc0; + + if (i_idx > j_idx) + k_idx = i_idx - j_idx; + else + k_idx = 0; + + memcpy(tmp, (char *)&k_idx, 2); + readBuffer2[4] = tmp[0]; + readBuffer2[5] = tmp[1]; + + sendto(ref_g2_sock,(char *)readBuffer2, 8 + (j_idx * 20), 0, + (struct sockaddr *)&fromDst4, + sizeof(struct sockaddr_in)); + } + } + } + else + /* connected user list request */ + if ((recvlen2 == 4) && + (readBuffer2[0] == 4) && + (readBuffer2[1] == 192) && + (readBuffer2[2] == 6) && + (readBuffer2[3] == 0)) + { + unsigned short i_idx = 0; + unsigned short j_idx = 0; + unsigned short k_idx = 0; + unsigned char tmp[2]; + unsigned short total = 0; + + pos = inbound_list.find(ip); + if (pos != inbound_list.end()) + { + inbound_ptr = (inbound *)pos->second; + // traceit("Remote station %s %s requested connected user list\n", inbound_ptr->call, ip); + + /* header is 8 bytes */ + + /* reply type */ + readBuffer2[2] = 6; + readBuffer2[3] = 0; + + /* total connected users */ + total = inbound_list.size(); + memcpy(tmp, (char *)&total, 2); + readBuffer2[6] = tmp[0]; + readBuffer2[7] = tmp[1]; + + for (pos = inbound_list.begin(), i_idx = 0; pos != inbound_list.end(); pos++, i_idx++) + { + /* each entry has 20 bytes */ + readBuffer2[8 + (20 * j_idx)] = ' '; + inbound_ptr = (inbound *)pos->second; + + readBuffer2[8 + (20 * j_idx)] = inbound_ptr->mod; + strcpy((char *)readBuffer2 + 9 + (20 * j_idx), inbound_ptr->call); + + readBuffer2[17 + (20 * j_idx)] = 0; + /* readBuffer2[18 + (20 * j_idx)] = 0; */ readBuffer2[18 + (20 * j_idx)] = inbound_ptr->client; + readBuffer2[19 + (20 * j_idx)] = 0; + readBuffer2[20 + (20 * j_idx)] = 0x0d; + readBuffer2[21 + (20 * j_idx)] = 0x4d; + readBuffer2[22 + (20 * j_idx)] = 0x37; + readBuffer2[23 + (20 * j_idx)] = 0x4d; + readBuffer2[24 + (20 * j_idx)] = 0x6f; + readBuffer2[25 + (20 * j_idx)] = 0x98; + readBuffer2[26 + (20 * j_idx)] = 0x04; + readBuffer2[27 + (20 * j_idx)] = 0; + + j_idx++; + + if (j_idx == 39) + { + /* 20 bytes for each user, so 39 * 20 = 788 bytes + 8 bytes header = 788 */ + readBuffer2[0] = 0x14; + readBuffer2[1] = 0xc3; + + k_idx = i_idx - 38; + memcpy(tmp, (char *)&k_idx, 2); + readBuffer2[4] = tmp[0]; + readBuffer2[5] = tmp[1]; + + sendto(ref_g2_sock,(char *)readBuffer2,788,0, + (struct sockaddr *)&fromDst4, + sizeof(struct sockaddr_in)); + + j_idx = 0; + } + } + + if (j_idx != 0) + { + k_idx = 8 + (j_idx * 20); + memcpy(tmp, (char *)&k_idx, 2); + readBuffer2[0] = tmp[0]; + readBuffer2[1] = tmp[1] | 0xc0; + + if (i_idx > j_idx) + k_idx = i_idx - j_idx; + else + k_idx = 0; + + memcpy(tmp, (char *)&k_idx, 2); + readBuffer2[4] = tmp[0]; + readBuffer2[5] = tmp[1]; + + sendto(ref_g2_sock,(char *)readBuffer2, 8 + (j_idx * 20), 0, + (struct sockaddr *)&fromDst4, + sizeof(struct sockaddr_in)); + } + } + } + else + /* date request */ + if ((recvlen2 == 4) && + (readBuffer2[0] == 4) && + (readBuffer2[1] == 192) && + (readBuffer2[2] == 8) && + (readBuffer2[3] == 0)) + { + time_t ltime; + struct tm tm; + + pos = inbound_list.find(ip); + if (pos != inbound_list.end()) + { + inbound_ptr = (inbound *)pos->second; + // traceit("Remote station %s %s requested date\n", inbound_ptr->call, ip); + + time(<ime); + localtime_r(<ime,&tm); + + readBuffer2[0] = 34; + readBuffer2[1] = 192; + readBuffer2[2] = 8; + readBuffer2[3] = 0; + readBuffer2[4] = 0xb5; + readBuffer2[5] = 0xae; + readBuffer2[6] = 0x37; + readBuffer2[7] = 0x4d; + snprintf((char *)readBuffer2 + 8, 1024 - 1, + "20%02d/%02d/%02d %02d:%02d:%02d %5.5s", + tm.tm_year % 100, tm.tm_mon+1,tm.tm_mday, + tm.tm_hour,tm.tm_min,tm.tm_sec, + (tzname[0] == NULL)?" ":tzname[0]); + + sendto(ref_g2_sock,(char *)readBuffer2,34,0, + (struct sockaddr *)&fromDst4, + sizeof(struct sockaddr_in)); + } + } + else + /* version request */ + if ((recvlen2 == 4) && + (readBuffer2[0] == 4) && + (readBuffer2[1] == 192) && + (readBuffer2[2] == 3) && + (readBuffer2[3] == 0)) + { + pos = inbound_list.find(ip); + if (pos != inbound_list.end()) + { + inbound_ptr = (inbound *)pos->second; + // traceit("Remote station %s %s requested version\n", inbound_ptr->call, ip); + + readBuffer2[0] = 9; + readBuffer2[1] = 192; + readBuffer2[2] = 3; + readBuffer2[3] = 0; + strncpy((char *)readBuffer2 + 4, VERSION, 4); + readBuffer2[8] = 0; + + sendto(ref_g2_sock,(char *)readBuffer2,9,0, + (struct sockaddr *)&fromDst4, + sizeof(struct sockaddr_in)); + } + } + else + if ((recvlen2 == 5) && + (readBuffer2[0] == 5) && + (readBuffer2[1] == 0) && + (readBuffer2[2] == 24) && + (readBuffer2[3] == 0) && + (readBuffer2[4] == 0)) + { + /* reply with the same DISCONNECT */ + sendto(ref_g2_sock,(char *)readBuffer2,5,0, + (struct sockaddr *)&fromDst4, + sizeof(struct sockaddr_in)); + + for (i = 0; i < 3; i++) + { + if ((fromDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT))) + { + traceit("Call %s disconnected\n", to_remote_g2[i].to_call); + + to_remote_g2[i].to_call[0] = '\0'; + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].from_mod = ' '; + to_remote_g2[i].to_mod = ' '; + to_remote_g2[i].countdown = 0; + to_remote_g2[i].is_connected = false; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + } + } + + pos = inbound_list.find(ip); + if (pos != inbound_list.end()) + { + inbound_ptr = (inbound *)pos->second; + if (memcmp(inbound_ptr->call, "1NFO", 4) != 0) + traceit("Call %s disconnected\n", inbound_ptr->call); + free(pos->second); + pos->second = NULL; + inbound_list.erase(pos); + } + print_status_file(); + } + + for (i = 0; i < 3; i++) + { + if ((fromDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT))) + { + found = true; + if ((recvlen2 == 5) && + (readBuffer2[0] == 5) && + (readBuffer2[1] == 0) && + (readBuffer2[2] == 24) && + (readBuffer2[3] == 0) && + (readBuffer2[4] == 1)) + { + traceit("Connected to call %s\n", to_remote_g2[i].to_call); + queryCommand[0] = 28; + queryCommand[1] = 192; + queryCommand[2] = 4; + queryCommand[3] = 0; + + memcpy(queryCommand + 4, LOGIN_CALL, CALL_SIZE); + for (j = 11; j > 3; j--) + { + if (queryCommand[j] == ' ') + queryCommand[j] = '\0'; + else + break; + } + memset(queryCommand + 12, '\0', 8); + memcpy(queryCommand + 20, "DV019999", 8); + + /*** ATTENTION: I should ONLY send once for each distinct + remote IP, so get out of the loop immediately + ***/ + sendto(ref_g2_sock,(char *)queryCommand,28,0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + + break; + } + } + } + + for (i = 0; i < 3; i++) + { + if ((fromDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT))) + { + found = true; + if ((recvlen2 == 8) && + (readBuffer2[0] == 8) && + (readBuffer2[1] == 192) && + (readBuffer2[2] == 4) && + (readBuffer2[3] == 0)) + { + if ((readBuffer2[4] == 79) && + (readBuffer2[5] == 75) && + (readBuffer2[6] == 82)) + { + if (!to_remote_g2[i].is_connected) + { + to_remote_g2[i].is_connected = true; + to_remote_g2[i].countdown = TIMEOUT; + traceit("Login OK to call %s mod %c\n", + to_remote_g2[i].to_call, to_remote_g2[i].to_mod); + print_status_file(); + + tracing[i].last_time = time(NULL); + + strcpy(linked_remote_system, to_remote_g2[i].to_call); + space_p = strchr(linked_remote_system, ' '); + if (space_p) + *space_p = '\0'; + sprintf(notify_msg, "%c_linked.dat_LINKED_%s_%c", + to_remote_g2[i].from_mod, + linked_remote_system, + to_remote_g2[i].to_mod); + audio_notify(notify_msg); + } + } + else + if ((readBuffer2[4] == 70) && + (readBuffer2[5] == 65) && + (readBuffer2[6] == 73) && + (readBuffer2[7] == 76)) + { + traceit("Login failed to call %s mod %c\n", + to_remote_g2[i].to_call, to_remote_g2[i].to_mod); + + sprintf(notify_msg, "%c_failed_linked.dat_FAILED_TO_LINK", + to_remote_g2[i].from_mod); + audio_notify(notify_msg); + + to_remote_g2[i].to_call[0] = '\0'; + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].from_mod = ' '; + to_remote_g2[i].to_mod = ' '; + to_remote_g2[i].countdown = 0; + to_remote_g2[i].is_connected = false; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + } + else + if ((readBuffer2[4] == 66) && + (readBuffer2[5] == 85) && + (readBuffer2[6] == 83) && + (readBuffer2[7] == 89)) + { + traceit("Busy or unknown status from call %s mod %c\n", + to_remote_g2[i].to_call, to_remote_g2[i].to_mod); + + sprintf(notify_msg, "%c_failed_linked.dat_FAILED_TO_LINK", + to_remote_g2[i].from_mod); + audio_notify(notify_msg); + + to_remote_g2[i].to_call[0] = '\0'; + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].from_mod = ' '; + to_remote_g2[i].to_mod = ' '; + to_remote_g2[i].countdown = 0; + to_remote_g2[i].is_connected = false; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + } + } + } + } + + for (i = 0; i < 3; i++) + { + if ((fromDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT))) + { + found = true; + if ((recvlen2 == 24) && + (readBuffer2[0] == 24) && + (readBuffer2[1] == 192) && + (readBuffer2[2] == 3) && + (readBuffer2[3] == 0)) + { + j = i; + to_remote_g2[i].countdown = TIMEOUT; + } + } + } + + for (i = 0; i < 3; i++) + { + if ((fromDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT))) + { + found = true; + if (recvlen2 == 3) + to_remote_g2[i].countdown = TIMEOUT; + } + } + + /* find out if it is a connected dongle */ + pos = inbound_list.find(ip); + if (pos != inbound_list.end()) + { + inbound_ptr = (inbound *)pos->second; + found = true; + inbound_ptr->countdown = TIMEOUT; + /*** ip is same, do not update port + memcpy((char *)&(inbound_ptr->sin),(char *)&fromDst4, sizeof(struct sockaddr_in)); + ***/ + } + + if (!found) + { + /* + The incoming packet is not in the list of outbound repeater connections. + and it is not a connected dongle. + In this case, this must be an INCOMING dongle request + */ + if ((recvlen2 == 5) && + (readBuffer2[0] == 5) && + (readBuffer2[1] == 0) && + (readBuffer2[2] == 24) && + (readBuffer2[3] == 0) && + (readBuffer2[4] == 1)) + { + if ((inbound_list.size() + 1) > MAX_DONGLES) + traceit("Inbound DONGLE-p connection from %s but over the MAX_DONGLES limit of %d\n", + ip, inbound_list.size()); + else + sendto(ref_g2_sock,(char *)readBuffer2,5,0, + (struct sockaddr *)&fromDst4, + sizeof(fromDst4)); + } + else + if ((recvlen2 == 28) && + (readBuffer2[0] == 28) && + (readBuffer2[1] == 192) && + (readBuffer2[2] == 4) && + (readBuffer2[3] == 0)) + { + /* verify callsign */ + memcpy(call, readBuffer2 + 4, CALL_SIZE); + call[CALL_SIZE] = '\0'; + for (i = 7; i > 0; i--) + { + if (call[i] == '\0') + call[i] = ' '; + else + break; + } + + if (memcmp(call, "1NFO", 4) != 0) + traceit("Inbound DONGLE-p CALL=%s, ip=%s, DV=%.8s\n", + call, ip, readBuffer2 + 20); + + if ((inbound_list.size() + 1) > MAX_DONGLES) + traceit("Inbound DONGLE-p connection from %s but over the MAX_DONGLES limit of %d\n", + ip, inbound_list.size()); + else + if (ONLY_ADMIN_LOGIN && (admin.find(call) == admin.end())) + traceit("Incoming call [%s] from %s not an ADMIN\n", call, ip); + else + if (regexec(&preg, call, 0, NULL, 0) != 0) + { + traceit("Invalid dongle callsign: CALL=%s,ip=%s\n", call, ip); + + readBuffer2[0] = 8; + readBuffer2[4] = 70; + readBuffer2[5] = 65; + readBuffer2[6] = 73; + readBuffer2[7] = 76; + + sendto(ref_g2_sock,(char *)readBuffer2,8,0, + (struct sockaddr *)&fromDst4, + sizeof(fromDst4)); + } + else + { + /* add the dongle to the inbound list */ + inbound_ptr = (inbound *)malloc(sizeof(inbound)); + if (inbound_ptr) + { + inbound_ptr->countdown = TIMEOUT; + memcpy((char *)&(inbound_ptr->sin),(char *)&fromDst4, sizeof(struct sockaddr_in)); + strcpy(inbound_ptr->call, call); + + inbound_ptr->mod = ' '; + + if (memcmp(readBuffer2 + 20, "AP", 2) == 0) + inbound_ptr->client = 'A'; /* dvap */ + else + if (memcmp(readBuffer2 + 20, "DV019999", 8) == 0) + inbound_ptr->client = 'H'; /* spot */ + else + inbound_ptr->client = 'D'; /* dongle */ + + insert_pair = inbound_list.insert(pair(ip, inbound_ptr)); + if (insert_pair.second) + { + if (memcmp(inbound_ptr->call, "1NFO", 4) != 0) + traceit("new CALL=%s, DONGLE-p, ip=%s, users=%d\n", + inbound_ptr->call,ip,inbound_list.size()); + + readBuffer2[0] = 8; + readBuffer2[4] = 79; + readBuffer2[5] = 75; + readBuffer2[6] = 82; + readBuffer2[7] = 87; + + sendto(ref_g2_sock,(char *)readBuffer2,8,0, + (struct sockaddr *)&fromDst4, + sizeof(fromDst4)); + + print_status_file(); + + } + else + { + traceit("failed to add CALL=%s,ip=%s\n",inbound_ptr->call,ip); + free(inbound_ptr); + inbound_ptr = NULL; + + readBuffer2[0] = 8; + readBuffer2[4] = 70; + readBuffer2[5] = 65; + readBuffer2[6] = 73; + readBuffer2[7] = 76; + + sendto(ref_g2_sock,(char *)readBuffer2,8,0, + (struct sockaddr *)&fromDst4, + sizeof(fromDst4)); + } + } + else + { + traceit("malloc() failed for call=%s,ip=%s\n",call,ip); + + readBuffer2[0] = 8; + readBuffer2[4] = 70; + readBuffer2[5] = 65; + readBuffer2[6] = 73; + readBuffer2[7] = 76; + + sendto(ref_g2_sock,(char *)readBuffer2,8,0, + (struct sockaddr *)&fromDst4, + sizeof(fromDst4)); + } + } + } + } + + if ( ((recvlen2 == 58) || + (recvlen2 == 29) || + (recvlen2 == 32)) && + (memcmp(readBuffer2 + 2, "DSVT", 4) == 0) && + ((readBuffer2[6] == 0x10) || + (readBuffer2[6] == 0x20)) && + (readBuffer2[10] == 0x20)) + { + /* Is it one of the donglers or repeaters-reflectors */ + found = false; + for (i = 0; i < 3; i++) + { + if ((fromDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT))) + { + to_remote_g2[i].countdown = TIMEOUT; + found = true; + } + } + if (!found) + { + pos = inbound_list.find(ip); + if (pos != inbound_list.end()) + { + inbound_ptr = (inbound *)pos->second; + inbound_ptr->countdown = TIMEOUT; + found = true; + } + } + + if ((recvlen2 == 58) && found) + { + memset(source_stn, ' ', 9); source_stn[8] = '\0'; + + /* some bad hotspot programs out there using INCORRECT flag */ + if (readBuffer2[17] == 0x40) + readBuffer2[17] = 0x00; + else + if (readBuffer2[17] == 0x48) + readBuffer2[17] = 0x08; + else + if (readBuffer2[17] == 0x60) + readBuffer2[17] = 0x20; + else + if (readBuffer2[17] == 0x68) + readBuffer2[17] = 0x28; + + /* A reflector will send to us its own RPT1 */ + /* A repeater will send to us its own RPT1 */ + /* A dongleR will send to us our RPT1 */ + + /* It is from a repeater-reflector, correct rpt1, rpt2 and re-compute pfcs */ + for (i = 0; i < 3; i++) + { + if ((fromDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT)) && + ( + ((memcmp(readBuffer2 + 20, to_remote_g2[i].to_call, 7) == 0) && + (readBuffer2[27] == to_remote_g2[i].to_mod)) || + ((memcmp(readBuffer2 + 28, to_remote_g2[i].to_call, 7) == 0) && + (readBuffer2[35] == to_remote_g2[i].to_mod)) + )) + { + memcpy(&readBuffer2[20], OWNER, CALL_SIZE); + readBuffer2[27] = to_remote_g2[i].from_mod; + memcpy(&readBuffer2[36], "CQCQCQ ", 8); + + memcpy(source_stn, to_remote_g2[i].to_call, 8); source_stn[7] = to_remote_g2[i].to_mod; + + break; + } + } + + if (i == 3) + { + pos = inbound_list.find(ip); + if (pos != inbound_list.end()) + { + inbound_ptr = (inbound *)pos->second; + memcpy(source_stn, inbound_ptr->call, 8); + } + } + + /* somebody's crazy idea of having a personal callsign in RPT2 */ + /* we must set it to our gateway callsign */ + memcpy(&readBuffer2[28], OWNER, CALL_SIZE); + readBuffer2[35] = 'G'; + calcPFCS(readBuffer2 + 2,56); + + /* At this point, all data have our RPT1 and RPT2 */ + + i = -1; + if (readBuffer2[27] == 'A') + i = 0; + else + if (readBuffer2[27] == 'B') + i = 1; + else + if (readBuffer2[27] == 'C') + i = 2; + + /* are we sure that RPT1 is our system? */ + if ((memcmp(readBuffer2 + 20, OWNER, 7) == 0) && (i >= 0)) + { + /* Last Heard */ + if (memcmp(old_sid[i].sid, readBuffer2 + 14, 2) != 0) + { + if (QSO_DETAILS) + traceit("START from remote g2: streamID=%d,%d, flags=%02x:%02x:%02x, my=%.8s, sfx=%.4s, ur=%.8s, rpt1=%.8s, rpt2=%.8s, %d bytes fromIP=%s, source=%.8s\n", + readBuffer2[14],readBuffer2[15], + readBuffer2[17], readBuffer2[18], readBuffer2[19], + &readBuffer2[44], + &readBuffer2[52], &readBuffer2[36], + &readBuffer2[20], &readBuffer2[28], + recvlen2,inet_ntoa(fromDst4.sin_addr), source_stn); + + // put user into tmp1 + memcpy(tmp1, readBuffer2 + 44, 8); tmp1[8] = '\0'; + + // delete the user if exists + for (dt_lh_pos = dt_lh_list.begin(); dt_lh_pos != dt_lh_list.end(); dt_lh_pos++) + { + if (strcmp((char *)dt_lh_pos->second.c_str(), tmp1) == 0) + { + dt_lh_list.erase(dt_lh_pos); + break; + } + } + /* Limit?, delete oldest user */ + if (dt_lh_list.size() == LH_MAX_SIZE) + { + dt_lh_pos = dt_lh_list.begin(); + dt_lh_list.erase(dt_lh_pos); + } + // add user + time(&tnow); + sprintf(tmp2, "%ld=r%.6s%c%c", tnow, source_stn, source_stn[7], readBuffer2[27]); + dt_lh_list[tmp2] = tmp1; + + memcpy(old_sid[i].sid, readBuffer2 + 14, 2); + } + + /* send the data to the local gateway/repeater */ + sendto(rptr_sock, (char *)readBuffer2 + 2,56,0,(struct sockaddr *)&toLocalg2,sizeof(struct sockaddr_in)); + + /* send the data to the donglers */ + for (pos = inbound_list.begin(); pos != inbound_list.end(); pos++) + { + inbound_ptr = (inbound *)pos->second; + if (fromDst4.sin_addr.s_addr != inbound_ptr->sin.sin_addr.s_addr) + { + sendto(ref_g2_sock, (char *)readBuffer2, 58, 0, + (struct sockaddr *)&(inbound_ptr->sin), + sizeof(struct sockaddr_in)); + } + else + inbound_ptr->mod = readBuffer2[27]; + } + + if ((to_remote_g2[i].toDst4.sin_addr.s_addr != fromDst4.sin_addr.s_addr) && + to_remote_g2[i].is_connected) + { + if ( /*** (memcmp(readBuffer2 + 44, OWNER, 8) != 0) && ***/ /* block repeater announcements */ + (memcmp(readBuffer2 + 36, "CQCQCQ", 6) == 0) && /* CQ calls only */ + ((readBuffer2[17] == 0x00) || /* normal */ + (readBuffer2[17] == 0x08) || /* EMR */ + (readBuffer2[17] == 0x20) || /* BK */ + (readBuffer2[17] == 0x28)) && /* EMR + BK */ + (memcmp(readBuffer2 + 28, OWNER, 7) == 0) && /* rpt2 must be us */ + (readBuffer2[35] == 'G')) + { + to_remote_g2[i].in_streamid[0] = readBuffer2[14]; + to_remote_g2[i].in_streamid[1] = readBuffer2[15]; + + if ((to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT)) || + (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT))) + { + memcpy((char *)readBuffer2 + 20, to_remote_g2[i].to_call, CALL_SIZE); + readBuffer2[27] = to_remote_g2[i].to_mod; + memcpy((char *)readBuffer2 + 28, to_remote_g2[i].to_call, CALL_SIZE); + readBuffer2[35] = 'G'; + calcPFCS(readBuffer2 + 2, 56); + + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT)) + { + /* inform XRF about the source */ + readBuffer2[13] = to_remote_g2[i].from_mod; + + sendto(xrf_g2_sock, (char *)readBuffer2 + 2, 56, 0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + } + else + sendto(ref_g2_sock, (char *)readBuffer2, 58, 0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + } + else + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_DCS_PORT)) + { + memcpy(ref_2_dcs[i].mycall, readBuffer2 + 44, 8); + memcpy(ref_2_dcs[i].sfx, readBuffer2 + 52, 4); + ref_2_dcs[i].dcs_rptr_seq = 0; + } + } + } + } + } + else + if (found) + { + if ((readBuffer2[16] & 0x40) != 0) + { + for (i = 0; i < 3; i++) + { + if (memcmp(old_sid[i].sid, readBuffer2 + 14, 2) == 0) + { + if (QSO_DETAILS) + traceit("END from remote g2: streamID=%d,%d, %d bytes from IP=%s\n", + readBuffer2[14],readBuffer2[15],recvlen2,inet_ntoa(fromDst4.sin_addr)); + + memset(old_sid[i].sid, 0x00, 2); + + break; + } + } + } + + /* send the data to the local gateway/repeater */ + sendto(rptr_sock, (char *)readBuffer2 + 2,27,0,(struct sockaddr *)&toLocalg2,sizeof(struct sockaddr_in)); + + /* send the data to the donglers */ + for (pos = inbound_list.begin(); pos != inbound_list.end(); pos++) + { + inbound_ptr = (inbound *)pos->second; + if (fromDst4.sin_addr.s_addr != inbound_ptr->sin.sin_addr.s_addr) + { + sendto(ref_g2_sock, (char *)readBuffer2, 29, 0, + (struct sockaddr *)&(inbound_ptr->sin), + sizeof(struct sockaddr_in)); + } + } + + for (i = 0; i < 3; i++) + { + if ((to_remote_g2[i].is_connected) && + (to_remote_g2[i].toDst4.sin_addr.s_addr != fromDst4.sin_addr.s_addr) && + (memcmp(to_remote_g2[i].in_streamid, readBuffer2 + 14, 2) == 0)) + { + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT)) + { + /* inform XRF about the source */ + readBuffer2[13] = to_remote_g2[i].from_mod; + + sendto(xrf_g2_sock, (char *)readBuffer2 + 2, 27, 0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + } + else + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT)) + sendto(ref_g2_sock, (char *)readBuffer2, 29, + 0,(struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + else + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_DCS_PORT)) + { + memset(dcs_buf, 0x00, 600); + dcs_buf[0] = dcs_buf[1] = dcs_buf[2] = '0'; + dcs_buf[3] = '1'; + dcs_buf[4] = dcs_buf[5] = dcs_buf[6] = 0x00; + memcpy(dcs_buf + 7, to_remote_g2[i].to_call, 8); + dcs_buf[14] = to_remote_g2[i].to_mod; + memcpy(dcs_buf + 15, OWNER, 8); + dcs_buf[22] = to_remote_g2[i].from_mod; + memcpy(dcs_buf + 23, "CQCQCQ ", 8); + memcpy(dcs_buf + 31, ref_2_dcs[i].mycall, 8); + memcpy(dcs_buf + 39, ref_2_dcs[i].sfx, 4); + dcs_buf[43] = readBuffer2[14]; /* streamid0 */ + dcs_buf[44] = readBuffer2[15]; /* streamid1 */ + dcs_buf[45] = readBuffer2[16]; /* cycle sequence */ + memcpy(dcs_buf + 46, readBuffer2 + 17, 12); + + dcs_buf[58] = (ref_2_dcs[i].dcs_rptr_seq >> 0) & 0xff; + dcs_buf[59] = (ref_2_dcs[i].dcs_rptr_seq >> 8) & 0xff; + dcs_buf[60] = (ref_2_dcs[i].dcs_rptr_seq >> 16) & 0xff; + + ref_2_dcs[i].dcs_rptr_seq ++; + + dcs_buf[61] = 0x01; + dcs_buf[62] = 0x00; + + sendto(dcs_g2_sock, dcs_buf, 100, 0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + } + + if ((readBuffer2[16] & 0x40) != 0) + { + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + } + break; + } + } + } + } + FD_CLR (ref_g2_sock,&fdset); + } + + if (FD_ISSET(dcs_g2_sock, &fdset)) + { + fromlen = sizeof(struct sockaddr_in); + recvlen2 = recvfrom(dcs_g2_sock,(char *)dcs_buf,1000, + 0,(struct sockaddr *)&fromDst4,&fromlen); + + strncpy(ip, inet_ntoa(fromDst4.sin_addr),IP_SIZE); + ip[IP_SIZE] = '\0'; + + /* header, audio */ + if ((dcs_buf[0] == '0') && (dcs_buf[1] == '0') && + (dcs_buf[2] == '0') && (dcs_buf[3] == '1')) + { + if (recvlen2 == 100) + { + memset(source_stn, ' ', 9); source_stn[8] = '\0'; + + /* find out our local module */ + for (i = 0; i < 3; i++) + { + if ((to_remote_g2[i].is_connected) && + (fromDst4.sin_addr.s_addr = to_remote_g2[i].toDst4.sin_addr.s_addr) && + (memcmp(dcs_buf + 7, to_remote_g2[i].to_call, 7) == 0) && + (to_remote_g2[i].to_mod == dcs_buf[14])) + { + memcpy(source_stn, to_remote_g2[i].to_call, 8); source_stn[7] = to_remote_g2[i].to_mod; + break; + } + } + + /* Is it our local module */ + if (i < 3) + { + /* Last Heard */ + if (memcmp(old_sid[i].sid, dcs_buf + 43, 2) != 0) + { + if (QSO_DETAILS) + traceit("START from dcs: streamID=%d,%d, my=%.8s, sfx=%.4s, ur=%.8s, rpt1=%.8s, rpt2=%.8s, %d bytes fromIP=%s, source=%.8s\n", + dcs_buf[43],dcs_buf[44], + &dcs_buf[31], + &dcs_buf[39], &dcs_buf[23], + &dcs_buf[7], &dcs_buf[15], + recvlen2,inet_ntoa(fromDst4.sin_addr), source_stn); + + // put user into tmp1 + memcpy(tmp1, dcs_buf + 31, 8); tmp1[8] = '\0'; + + // delete the user if exists + for (dt_lh_pos = dt_lh_list.begin(); dt_lh_pos != dt_lh_list.end(); dt_lh_pos++) + { + if (strcmp((char *)dt_lh_pos->second.c_str(), tmp1) == 0) + { + dt_lh_list.erase(dt_lh_pos); + break; + } + } + /* Limit?, delete oldest user */ + if (dt_lh_list.size() == LH_MAX_SIZE) + { + dt_lh_pos = dt_lh_list.begin(); + dt_lh_list.erase(dt_lh_pos); + } + // add user + time(&tnow); + sprintf(tmp2, "%ld=r%.6s%c%c", tnow, source_stn, source_stn[7], to_remote_g2[i].from_mod); + dt_lh_list[tmp2] = tmp1; + + memcpy(old_sid[i].sid, dcs_buf + 43, 2); + } + + to_remote_g2[i].countdown = TIMEOUT; + + /* new stream ? */ + if ((to_remote_g2[i].in_streamid[0] != dcs_buf[43]) || + (to_remote_g2[i].in_streamid[1] != dcs_buf[44])) + { + to_remote_g2[i].in_streamid[0] = dcs_buf[43]; + to_remote_g2[i].in_streamid[1] = dcs_buf[44]; + dcs_seq[i] = 0xff; + + /* generate our header */ + + readBuffer2[0] = (unsigned char)(58 & 0xFF); + readBuffer2[1] = (unsigned char)(58 >> 8 & 0x1F); + readBuffer2[1] = (unsigned char)(readBuffer2[1] | 0xFFFFFF80); + memcpy(readBuffer2 + 2, "DSVT", 4); + readBuffer2[6] = 0x10; + readBuffer2[7] = 0x00; + readBuffer2[8] = 0x00; + readBuffer2[9] = 0x00; + readBuffer2[10] = 0x20; + readBuffer2[11] = 0x00; + readBuffer2[12] = 0x01; + if (to_remote_g2[i].from_mod == 'A') + readBuffer2[13] = 0x03; + else + if (to_remote_g2[i].from_mod == 'B') + readBuffer2[13] = 0x01; + else + readBuffer2[13] = 0x02; + readBuffer2[14] = dcs_buf[43]; + readBuffer2[15] = dcs_buf[44]; + readBuffer2[16] = 0x80; + readBuffer2[17] = readBuffer2[18] = readBuffer2[19] = 0x00; + memcpy(readBuffer2 + 20, OWNER, 8); + readBuffer2[27] = to_remote_g2[i].from_mod; + memcpy(readBuffer2 + 28, OWNER, 8); + readBuffer2[35] = 'G'; + memcpy(readBuffer2 + 36, "CQCQCQ ", 8); + memcpy(readBuffer2 + 44, dcs_buf + 31, 8); + memcpy(readBuffer2 + 52, dcs_buf + 39, 4); + calcPFCS(readBuffer2 + 2, 56); + + /* send the header to the local gateway/repeater */ + for (j = 0; j < 5; j++) + sendto(rptr_sock, (char *)readBuffer2 + 2, 56,0, + (struct sockaddr *)&toLocalg2,sizeof(struct sockaddr_in)); + + /* send the data to the donglers */ + for (pos = inbound_list.begin(); pos != inbound_list.end(); pos++) + { + inbound_ptr = (inbound *)pos->second; + for (j = 0; j < 5; j++) + { + sendto(ref_g2_sock, (char *)readBuffer2, 58, 0, + (struct sockaddr *)&(inbound_ptr->sin), + sizeof(struct sockaddr_in)); + } + } + } + + if ((to_remote_g2[i].in_streamid[0] == dcs_buf[43]) && + (to_remote_g2[i].in_streamid[1] == dcs_buf[44]) && + (dcs_seq[i] != dcs_buf[45])) + { + dcs_seq[i] = dcs_buf[45]; + + readBuffer2[0] = (unsigned char)(29 & 0xFF); + readBuffer2[1] = (unsigned char)(29 >> 8 & 0x1F); + readBuffer2[1] = (unsigned char)(readBuffer2[1] | 0xFFFFFF80); + memcpy(readBuffer2 + 2, "DSVT", 4); + readBuffer2[6] = 0x20; + readBuffer2[7] = 0x00; + readBuffer2[8] = 0x00; + readBuffer2[9] = 0x00; + readBuffer2[10] = 0x20; + readBuffer2[11] = 0x00; + readBuffer2[12] = 0x01; + if (to_remote_g2[i].from_mod == 'A') + readBuffer2[13] = 0x03; + else + if (to_remote_g2[i].from_mod == 'B') + readBuffer2[13] = 0x01; + else + readBuffer2[13] = 0x02; + readBuffer2[14] = dcs_buf[43]; + readBuffer2[15] = dcs_buf[44]; + readBuffer2[16] = dcs_buf[45]; + memcpy(readBuffer2 + 17, dcs_buf + 46, 12); + + /* send the data to the local gateway/repeater */ + sendto(rptr_sock, (char *)readBuffer2 + 2, 27,0, + (struct sockaddr *)&toLocalg2,sizeof(struct sockaddr_in)); + + /* send the data to the donglers */ + for (pos = inbound_list.begin(); pos != inbound_list.end(); pos++) + { + inbound_ptr = (inbound *)pos->second; + sendto(ref_g2_sock, (char *)readBuffer2, 29, 0, + (struct sockaddr *)&(inbound_ptr->sin), + sizeof(struct sockaddr_in)); + } + + if ((dcs_buf[45] & 0x40) != 0) + { + memset(old_sid[i].sid, 0x00, 2); + + if (QSO_DETAILS) + traceit("END from dcs: streamID=%d,%d, %d bytes from IP=%s\n", + dcs_buf[43],dcs_buf[44], recvlen2,inet_ntoa(fromDst4.sin_addr)); + + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + dcs_seq[i] = 0xff; + } + } + } + } + } + else + if ((dcs_buf[0] == 'E') && (dcs_buf[1] == 'E') && + (dcs_buf[2] == 'E') && (dcs_buf[3] == 'E')) + ; + else + if (recvlen2 == 35) + ; + else + /* is this a keepalive 22 bytes */ + if (recvlen2 == 22) + { + i = -1; + if (dcs_buf[17] == 'A') + i = 0; + else + if (dcs_buf[17] == 'B') + i = 1; + else + if (dcs_buf[17] == 'C') + i = 2; + + /* It is one of our valid repeaters */ + // DG1HT from OWNER 8 to 7 + if ((i >= 0) && (memcmp(dcs_buf + 9, OWNER, 7) == 0)) + { + /* is that the remote system that we asked to connect to? */ + if ((fromDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[i].toDst4.sin_port == htons(RMT_DCS_PORT)) && + (memcmp(to_remote_g2[i].to_call, dcs_buf, 7) == 0) && + (to_remote_g2[i].to_mod == dcs_buf[7])) + { + if (!to_remote_g2[i].is_connected) + { + tracing[i].last_time = time(NULL); + + to_remote_g2[i].is_connected = true; + traceit("Connected from: %.*s\n", 8, dcs_buf); + print_status_file(); + + strcpy(linked_remote_system, to_remote_g2[i].to_call); + space_p = strchr(linked_remote_system, ' '); + if (space_p) + *space_p = '\0'; + sprintf(notify_msg, "%c_linked.dat_LINKED_%s_%c", + to_remote_g2[i].from_mod, + linked_remote_system, + to_remote_g2[i].to_mod); + audio_notify(notify_msg); + } + to_remote_g2[i].countdown = TIMEOUT; + } + } + } + /* is this a reply to our link/unlink request: 14 bytes */ + else + if (recvlen2 == 14) + { + i = -1; + if (dcs_buf[8] == 'A') + i = 0; + else + if (dcs_buf[8] == 'B') + i = 1; + else + if (dcs_buf[8] == 'C') + i = 2; + + /* It is one of our valid repeaters */ + if ((i >= 0) && (memcmp(dcs_buf, OWNER, 8) == 0)) + { + /* It is from a remote that we contacted */ + if ((fromDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[i].toDst4.sin_port == htons(RMT_DCS_PORT)) && + (to_remote_g2[i].from_mod == dcs_buf[8])) + { + if ((to_remote_g2[i].to_mod == dcs_buf[9]) && + (memcmp(dcs_buf + 10, "ACK", 3) == 0)) + { + to_remote_g2[i].countdown = TIMEOUT; + if (!to_remote_g2[i].is_connected) + { + tracing[i].last_time = time(NULL); + + to_remote_g2[i].is_connected = true; + traceit("Connected from: %.*s\n", 8, to_remote_g2[i].to_call); + print_status_file(); + + strcpy(linked_remote_system, to_remote_g2[i].to_call); + space_p = strchr(linked_remote_system, ' '); + if (space_p) + *space_p = '\0'; + sprintf(notify_msg, "%c_linked.dat_LINKED_%s_%c", + to_remote_g2[i].from_mod, + linked_remote_system, + to_remote_g2[i].to_mod); + audio_notify(notify_msg); + } + } + else + if (memcmp(dcs_buf + 10, "NAK", 3) == 0) + { + traceit("Link module %c to [%s] %c is unlinked\n", + to_remote_g2[i].from_mod, to_remote_g2[i].to_call, + to_remote_g2[i].to_mod); + + sprintf(notify_msg, "%c_failed_linked.dat_UNLINKED", + to_remote_g2[i].from_mod); + audio_notify(notify_msg); + + to_remote_g2[i].to_call[0] = '\0'; + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].from_mod = ' '; + to_remote_g2[i].to_mod = ' '; + to_remote_g2[i].countdown = 0; + to_remote_g2[i].is_connected = false; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + + print_status_file(); + } + } + } + } + FD_CLR (dcs_g2_sock,&fdset); + } + + if (FD_ISSET(rptr_sock, &fdset)) + { + fromlen = sizeof(struct sockaddr_in); + recvlen = recvfrom(rptr_sock,(char *)readBuffer,100, + 0,(struct sockaddr *)&fromRptr,&fromlen); + + if ( ((recvlen == 58) || + (recvlen == 29) || + (recvlen == 32)) && + (readBuffer[6] == 0x73) && + (readBuffer[7] == 0x12) && + ((memcmp(readBuffer,"DSTR", 4) == 0) || (memcmp(readBuffer,"CCS_", 4) == 0)) && + (readBuffer[10] == 0x20) && + (readBuffer[8] == 0x00) && + ((readBuffer[9] == 0x30) || + (readBuffer[9] == 0x13) || + (readBuffer[9] == 0x16)) ) + { + + if (recvlen == 58) + { + if (QSO_DETAILS) + traceit("START from local g2: cntr=%02x %02x, streamID=%d,%d, flags=%02x:%02x:%02x, my=%.8s, sfx=%.4s, ur=%.8s, rpt1=%.8s, rpt2=%.8s, %d bytes fromIP=%s\n", + readBuffer[4], readBuffer[5], + readBuffer[14], readBuffer[15], + readBuffer[17], readBuffer[18], readBuffer[19], + readBuffer + 44, readBuffer + 52, readBuffer + 36, + readBuffer + 28, readBuffer + 20, recvlen, inet_ntoa(fromRptr.sin_addr)); + + /* save mycall */ + memcpy(call, readBuffer + 44, 8); + call[8] = '\0'; + + i = -1; + if (readBuffer[35] == 'A') + i = 0; + else + if (readBuffer[35] == 'B') + i = 1; + else + if (readBuffer[35] == 'C') + i = 2; + + if (i >= 0) + { + memcpy(dtmf_mycall[i], readBuffer + 44, 8); + dtmf_mycall[i][8] = '\0'; + + new_group[i] = true; + GPS_seen[i] = false; + + /* Last Heard */ + + //put user into tmp1 + memcpy(tmp1, readBuffer + 44, 8); tmp1[8] = '\0'; + + // delete the user if exists + for (dt_lh_pos = dt_lh_list.begin(); dt_lh_pos != dt_lh_list.end(); dt_lh_pos++) + { + if (strcmp((char *)dt_lh_pos->second.c_str(), tmp1) == 0) + { + dt_lh_list.erase(dt_lh_pos); + break; + } + } + /* Limit?, delete oldest user */ + if (dt_lh_list.size() == LH_MAX_SIZE) + { + dt_lh_pos = dt_lh_list.begin(); + dt_lh_list.erase(dt_lh_pos); + } + /* add user */ + time(&tnow); + if (memcmp(readBuffer,"CCS_", 4) == 0) + sprintf(tmp2, "%ld=r%.7s%c", tnow, "-->CCS ", readBuffer[35]); + else + sprintf(tmp2, "%ld=l%.8s", tnow, readBuffer + 28); + dt_lh_list[tmp2] = tmp1; + + memcpy(readBuffer, "DSTR", 4); + + tracing[i].streamid[0] = readBuffer[14]; + tracing[i].streamid[1] = readBuffer[15]; + tracing[i].last_time = time(NULL); + } + + if ((memcmp(readBuffer + 36, "CQCQCQ", 6) != 0) && (i >= 0)) + { + if ((memcmp(readBuffer + 36, OWNER, 7) != 0) && + (readBuffer[43] == LINK_CODE) && + (memcmp(readBuffer + 20, OWNER, 7) == 0) && + (readBuffer[27] == 'G') && + ((readBuffer[17] == 0x00) || + (readBuffer[17] == 0x08) || + (readBuffer[17] == 0x20) || + (readBuffer[17] == 0x28))) + { + if (ONLY_LINK_UNLINK && + (link_unlink_user.find(call) == link_unlink_user.end())) + { + traceit("link request denied, unauthorized rf user [%s]\n", call); + } + else + { + memset(temp_repeater, ' ', CALL_SIZE); + memcpy(temp_repeater, readBuffer + 36, CALL_SIZE - 2); + temp_repeater[CALL_SIZE] = '\0'; + + if ((to_remote_g2[i].to_call[0] == '\0') || /* not linked */ + ((to_remote_g2[i].to_call[0] != '\0') && /* waiting for a link reply that may never arrive */ + !to_remote_g2[i].is_connected)) + + g2link(readBuffer[35], temp_repeater, readBuffer[42]); + else + if (to_remote_g2[i].is_connected) + { + strcpy(linked_remote_system, to_remote_g2[i].to_call); + space_p = strchr(linked_remote_system, ' '); + if (space_p) + *space_p = '\0'; + sprintf(notify_msg, "%c_already_linked.dat_LINKED_%s_%c", + to_remote_g2[i].from_mod, + linked_remote_system, + to_remote_g2[i].to_mod); + audio_notify(notify_msg); + } + } + } + else + if ((readBuffer[43] == UNLINK_CODE) && + (readBuffer[36] == ' ')) + { + if (ONLY_LINK_UNLINK && + (link_unlink_user.find(call) == link_unlink_user.end())) + { + traceit("unlink request denied, unauthorized rf user [%s]\n", call); + } + else + { + if (to_remote_g2[i].to_call[0] != '\0') + { + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT)) + { + /* Check to see if any other local bands are linked to that same IP */ + for (j = 0; j < 3; j++) + { + if (j != i) + { + if ((to_remote_g2[j].toDst4.sin_addr.s_addr == to_remote_g2[i].toDst4.sin_addr.s_addr) && + (to_remote_g2[j].toDst4.sin_port == htons(RMT_REF_PORT))) + { + traceit("Info: Local %c is also linked to %s (different module) %c\n", + to_remote_g2[j].from_mod, + to_remote_g2[j].to_call, to_remote_g2[j].to_mod); + break; + } + } + } + + if (j == 3) + { + /* nothing else is linked there, send DISCONNECT */ + queryCommand[0] = 5; + queryCommand[1] = 0; + queryCommand[2] = 24; + queryCommand[3] = 0; + queryCommand[4] = 0; + sendto(ref_g2_sock,(char *)queryCommand,5,0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + } + } + else + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT)) + { + strcpy(unlink_request, OWNER); + unlink_request[8] = to_remote_g2[i].from_mod; + unlink_request[9] = ' '; + unlink_request[10] = '\0'; + + for (j = 0; j < 5; j++) + sendto(xrf_g2_sock,unlink_request, CALL_SIZE + 3,0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + } + else + { + strcpy(cmd_2_dcs, OWNER); + cmd_2_dcs[8] = to_remote_g2[i].from_mod; + cmd_2_dcs[9] = ' '; + cmd_2_dcs[10] = '\0'; + memcpy(cmd_2_dcs + 11, to_remote_g2[i].to_call, 8); + + for (j = 0; j < 5; j++) + sendto(dcs_g2_sock, cmd_2_dcs, 19,0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + } + + traceit("Unlinked from [%s] mod %c\n", + to_remote_g2[i].to_call, to_remote_g2[i].to_mod); + sprintf(notify_msg, "%c_unlinked.dat_UNLINKED", to_remote_g2[i].from_mod); + audio_notify(notify_msg); + + /* now zero out this entry */ + to_remote_g2[i].to_call[0] = '\0'; + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].from_mod = ' '; + to_remote_g2[i].to_mod = ' '; + to_remote_g2[i].countdown = 0; + to_remote_g2[i].is_connected = false; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + + print_status_file(); + } + else + { + sprintf(notify_msg, "%c_already_unlinked.dat_UNLINKED", readBuffer[35]); + audio_notify(notify_msg); + } + } + } + else + if ((readBuffer[43] == INFO_CODE) && + (readBuffer[36] == ' ')) + { + if (to_remote_g2[i].is_connected) + { + strcpy(linked_remote_system, to_remote_g2[i].to_call); + space_p = strchr(linked_remote_system, ' '); + if (space_p) + *space_p = '\0'; + sprintf(notify_msg, "%c_linked.dat_LINKED_%s_%c", + to_remote_g2[i].from_mod, + linked_remote_system, + to_remote_g2[i].to_mod); + audio_notify(notify_msg); + } + else + { + sprintf(notify_msg, "%c_id.dat_%s_NOT_LINKED", readBuffer[35], OWNER); + audio_notify(notify_msg); + } + } + else + if ((readBuffer[43] == EXEC_CODE) && + (readBuffer[36] == ' ') && + (admin.find(call) != admin.end())) // only ADMIN can execute scripts + { + if (readBuffer[42] != ' ') + { + memset(system_cmd, '\0', sizeof(system_cmd)); + snprintf(system_cmd, FILENAME_MAX, "%s/exec_%c.sh %s %c &", + ANNOUNCE_DIR, + readBuffer[42], call, readBuffer[35]); + traceit("Executing %s\n", system_cmd); + system(system_cmd); + } + } + else + if ((readBuffer[42] == DONGLE_CODE) && + (readBuffer[36] == ' ') && + (admin.find(call) != admin.end())) // only ADMIN can block dongle users + { + if (readBuffer[43] == '1') + { + MAX_DONGLES = SAVED_MAX_DONGLES; + traceit("Dongle connections are now allowed\n"); + } + else + if (readBuffer[43] == '0') + { + inbound_list.clear(); + MAX_DONGLES = 0; + traceit("Dongle connections are now disallowed\n"); + } + } + else + if ((readBuffer[43] == FILE_REFRESH_GWYS_CODE) && + (readBuffer[36] == ' ') && + (admin.find(call) != admin.end())) // only ADMIN can reload gwys.txt + { + gwy_list.clear(); + load_gwys(GWYS); + } + } + + /* send data to the donglers */ + if (inbound_list.size() > 0) + { + readBuffer2[0] = (unsigned char)(58 & 0xFF); + readBuffer2[1] = (unsigned char)(58 >> 8 & 0x1F); + readBuffer2[1] = (unsigned char)(readBuffer2[1] | 0xFFFFFF80); + + memcpy(readBuffer2 + 2, "DSVT", 4); + readBuffer2[6] = 0x10; + readBuffer2[7] = 0x00; + readBuffer2[8] = 0x00; + readBuffer2[9] = 0x00; + readBuffer2[10] = readBuffer[10]; + readBuffer2[11] = readBuffer[11]; + readBuffer2[12] = readBuffer[12]; + readBuffer2[13] = readBuffer[13]; + memcpy(readBuffer2 + 14, readBuffer + 14, 44); + memcpy(readBuffer2 + 20, OWNER, CALL_SIZE); + readBuffer2[27] = readBuffer[35]; + memcpy(readBuffer2 + 28, OWNER, CALL_SIZE); + readBuffer2[35] = 'G'; + memcpy(&readBuffer2[36], "CQCQCQ ", 8); + + for (pos = inbound_list.begin(); pos != inbound_list.end(); pos++) + { + inbound_ptr = (inbound *)pos->second; + for (j = 0; j < 5; j++) + sendto(ref_g2_sock, (char *)readBuffer2, 58, + 0,(struct sockaddr *)&(inbound_ptr->sin), + sizeof(struct sockaddr_in)); + } + } + + if (i >= 0) + { + /* do we have to broadcast ? */ + /* make sure the source is linked to xrf */ + if ((to_remote_g2[i].is_connected) && + (memcmp(to_remote_g2[i].to_call, "XRF", 3) == 0) && + /* only CQCQCQ */ + (memcmp(readBuffer + 20, OWNER, 7) == 0) && + (memcmp(readBuffer + 36, "CQCQCQ", 6) == 0) && + (readBuffer[27] == 'G')) + { + brd_from_rptr_idx = 0; + streamid_raw = (readBuffer[14] * 256U) + readBuffer[15]; + + for (j = 0; j < 3; j++) + { + if ((j != i) && + (to_remote_g2[j].is_connected) && + (memcmp(to_remote_g2[j].to_call, to_remote_g2[i].to_call, 8) == 0) && + (to_remote_g2[j].to_mod == to_remote_g2[i].to_mod) && + (to_remote_g2[j].to_mod != 'E')) + { + memcpy(fromrptr_torptr_brd, "DSVT", 4); + fromrptr_torptr_brd[4] = 0x10; + fromrptr_torptr_brd[5] = 0x00; + fromrptr_torptr_brd[6] = 0x00; + fromrptr_torptr_brd[7] = 0x00; + fromrptr_torptr_brd[8] = readBuffer[10]; + fromrptr_torptr_brd[9] = readBuffer[11]; + fromrptr_torptr_brd[10] = readBuffer[12]; + fromrptr_torptr_brd[11] = readBuffer[13]; + memcpy(fromrptr_torptr_brd + 12, readBuffer + 14, 44); + + streamid_raw ++; + if (streamid_raw == 0) + streamid_raw ++; + fromrptr_torptr_brd[12] = streamid_raw / 256U; + fromrptr_torptr_brd[13] = streamid_raw % 256U; + + memcpy(fromrptr_torptr_brd + 18, OWNER, 8); + fromrptr_torptr_brd[25] = to_remote_g2[j].from_mod; + memcpy(fromrptr_torptr_brd + 26, OWNER, 8); + fromrptr_torptr_brd[33] = 'G'; + + memcpy(fromrptr_torptr_brd + 34, "CQCQCQ ", 8); + + calcPFCS(fromrptr_torptr_brd, 56); + + sendto(xrf_g2_sock, (char *)fromrptr_torptr_brd,56,0, + (struct sockaddr *)&toLocalg2,sizeof(struct sockaddr_in)); + + brd_from_rptr.from_rptr_streamid[0] = readBuffer[14]; + brd_from_rptr.from_rptr_streamid[1] = readBuffer[15]; + brd_from_rptr.to_rptr_streamid[brd_from_rptr_idx][0] = fromrptr_torptr_brd[12]; + brd_from_rptr.to_rptr_streamid[brd_from_rptr_idx][1] = fromrptr_torptr_brd[13]; + brd_from_rptr_idx ++; + } + } + } + + if (to_remote_g2[i].is_connected) + { + if ((memcmp(readBuffer + 20, OWNER, 7) == 0) && + (memcmp(readBuffer + 36, "CQCQCQ", 6) == 0) && + (readBuffer[27] == 'G')) + { + to_remote_g2[i].out_streamid[0] = readBuffer[14]; + to_remote_g2[i].out_streamid[1] = readBuffer[15]; + + if ((to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT)) || + (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT))) + { + readBuffer2[0] = (unsigned char)(58 & 0xFF); + readBuffer2[1] = (unsigned char)(58 >> 8 & 0x1F); + readBuffer2[1] = (unsigned char)(readBuffer2[1] | 0xFFFFFF80); + + memcpy(readBuffer2 + 2, "DSVT", 4); + readBuffer2[6] = 0x10; + readBuffer2[7] = 0x00; + readBuffer2[8] = 0x00; + readBuffer2[9] = 0x00; + readBuffer2[10] = readBuffer[10]; + readBuffer2[11] = readBuffer[11]; + readBuffer2[12] = readBuffer[12]; + readBuffer2[13] = readBuffer[13]; + memcpy(readBuffer2 + 14, readBuffer + 14, 44); + memset(readBuffer2 + 20, ' ', CALL_SIZE); + memcpy(readBuffer2 + 20, to_remote_g2[i].to_call, + strlen(to_remote_g2[i].to_call)); + readBuffer2[27] = to_remote_g2[i].to_mod; + memset(readBuffer2 + 28, ' ', CALL_SIZE); + memcpy(readBuffer2 + 28, to_remote_g2[i].to_call, + strlen(to_remote_g2[i].to_call)); + readBuffer2[35] = 'G'; + memcpy(&readBuffer2[36], "CQCQCQ ", 8); + + calcPFCS(readBuffer2 + 2,56); + + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT)) + { + /* inform XRF about the source */ + readBuffer2[13] = to_remote_g2[i].from_mod; + + for (j = 0; j < 5; j++) + sendto(xrf_g2_sock, (char *)readBuffer2 + 2, 56, + 0,(struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + } + else + { + for (j = 0; j < 5; j++) + sendto(ref_g2_sock, (char *)readBuffer2, 58, + 0,(struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + } + } + else + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_DCS_PORT)) + { + memcpy(rptr_2_dcs[i].mycall, readBuffer + 44, 8); + memcpy(rptr_2_dcs[i].sfx, readBuffer + 52, 4); + rptr_2_dcs[i].dcs_rptr_seq = 0; + } + } + } + } + } + else + { + if (inbound_list.size() > 0) + { + readBuffer2[0] = (unsigned char)(29 & 0xFF); + readBuffer2[1] = (unsigned char)(29 >> 8 & 0x1F); + readBuffer2[1] = (unsigned char)(readBuffer2[1] | 0xFFFFFF80); + + memcpy(readBuffer2 + 2, "DSVT", 4); + readBuffer2[6] = 0x20; + readBuffer2[7] = 0x00; + readBuffer2[8] = 0x00; + readBuffer2[9] = 0x00; + readBuffer2[10] = readBuffer[10]; + readBuffer2[11] = readBuffer[11]; + readBuffer2[12] = readBuffer[12]; + readBuffer2[13] = readBuffer[13]; + memcpy(readBuffer2 + 14, readBuffer + 14, 3); + if (recvlen == 29) + memcpy(readBuffer2 + 17, readBuffer + 17, 12); + else + memcpy(readBuffer2 + 17, readBuffer + 20, 12); + + for (pos = inbound_list.begin(); pos != inbound_list.end(); pos++) + { + inbound_ptr = (inbound *)pos->second; + sendto(ref_g2_sock, (char *)readBuffer2, 29, 0, + (struct sockaddr *)&(inbound_ptr->sin), + sizeof(struct sockaddr_in)); + } + } + + for (i = 0; i < 3; i++) + { + if ((to_remote_g2[i].is_connected) && + (memcmp(to_remote_g2[i].out_streamid, readBuffer + 14, 2) == 0)) + { + /* check for broadcast */ + if (memcmp(brd_from_rptr.from_rptr_streamid, readBuffer + 14, 2) == 0) + { + memcpy(fromrptr_torptr_brd, "DSVT", 4); + fromrptr_torptr_brd[4] = 0x10; + fromrptr_torptr_brd[5] = 0x00; + fromrptr_torptr_brd[6] = 0x00; + fromrptr_torptr_brd[7] = 0x00; + fromrptr_torptr_brd[8] = readBuffer[10]; + fromrptr_torptr_brd[9] = readBuffer[11]; + fromrptr_torptr_brd[10] = readBuffer[12]; + fromrptr_torptr_brd[11] = readBuffer[13]; + memcpy(fromrptr_torptr_brd + 12, readBuffer + 14, 3); + + if (recvlen == 29) + memcpy(fromrptr_torptr_brd + 15, readBuffer + 17, 12); + else + memcpy(fromrptr_torptr_brd + 15, readBuffer + 20, 12); + + if ((brd_from_rptr.to_rptr_streamid[0][0] != 0x00) || + (brd_from_rptr.to_rptr_streamid[0][1] != 0x00)) + { + fromrptr_torptr_brd[12] = brd_from_rptr.to_rptr_streamid[0][0]; + fromrptr_torptr_brd[13] = brd_from_rptr.to_rptr_streamid[0][1]; + sendto(xrf_g2_sock, (char *)fromrptr_torptr_brd,27,0, + (struct sockaddr *)&toLocalg2,sizeof(struct sockaddr_in)); + } + + if ((brd_from_rptr.to_rptr_streamid[1][0] != 0x00) || + (brd_from_rptr.to_rptr_streamid[1][1] != 0x00)) + { + fromrptr_torptr_brd[12] = brd_from_rptr.to_rptr_streamid[1][0]; + fromrptr_torptr_brd[13] = brd_from_rptr.to_rptr_streamid[1][1]; + sendto(xrf_g2_sock, (char *)fromrptr_torptr_brd,27,0, + (struct sockaddr *)&toLocalg2,sizeof(struct sockaddr_in)); + } + + if ((readBuffer[16] & 0x40) != 0) + { + brd_from_rptr.from_rptr_streamid[0] = brd_from_rptr.from_rptr_streamid[1] = 0x00; + brd_from_rptr.to_rptr_streamid[0][0] = brd_from_rptr.to_rptr_streamid[0][1] = 0x00; + brd_from_rptr.to_rptr_streamid[1][0] = brd_from_rptr.to_rptr_streamid[1][1] = 0x00; + brd_from_rptr_idx = 0; + } + } + + if ((to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT)) || + (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT))) + { + readBuffer2[0] = (unsigned char)(29 & 0xFF); + readBuffer2[1] = (unsigned char)(29 >> 8 & 0x1F); + readBuffer2[1] = (unsigned char)(readBuffer2[1] | 0xFFFFFF80); + + memcpy(readBuffer2 + 2, "DSVT", 4); + readBuffer2[6] = 0x20; + readBuffer2[7] = 0x00; + readBuffer2[8] = 0x00; + readBuffer2[9] = 0x00; + readBuffer2[10] = readBuffer[10]; + readBuffer2[11] = readBuffer[11]; + readBuffer2[12] = readBuffer[12]; + readBuffer2[13] = readBuffer[13]; + memcpy(readBuffer2 + 14, readBuffer + 14, 3); + if (recvlen == 29) + memcpy(readBuffer2 + 17, readBuffer + 17, 12); + else + memcpy(readBuffer2 + 17, readBuffer + 20, 12); + + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT)) + { + /* inform XRF about the source */ + readBuffer2[13] = to_remote_g2[i].from_mod; + + sendto(xrf_g2_sock, (char *)readBuffer2 + 2, 27, + 0,(struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + } + else + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT)) + sendto(ref_g2_sock, (char *)readBuffer2, 29, + 0,(struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(struct sockaddr_in)); + } + else + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_DCS_PORT)) + { + memset(dcs_buf, 0x00, 600); + dcs_buf[0] = dcs_buf[1] = dcs_buf[2] = '0'; + dcs_buf[3] = '1'; + dcs_buf[4] = dcs_buf[5] = dcs_buf[6] = 0x00; + memcpy(dcs_buf + 7, to_remote_g2[i].to_call, 8); + dcs_buf[14] = to_remote_g2[i].to_mod; + memcpy(dcs_buf + 15, OWNER, 8); + dcs_buf[22] = to_remote_g2[i].from_mod; + memcpy(dcs_buf + 23, "CQCQCQ ", 8); + memcpy(dcs_buf + 31, rptr_2_dcs[i].mycall, 8); + memcpy(dcs_buf + 39, rptr_2_dcs[i].sfx, 4); + dcs_buf[43] = readBuffer[14]; /* streamid0 */ + dcs_buf[44] = readBuffer[15]; /* streamid1 */ + dcs_buf[45] = readBuffer[16]; /* cycle sequence */ + memcpy(dcs_buf + 46, readBuffer + 17, 12); + + dcs_buf[58] = (rptr_2_dcs[i].dcs_rptr_seq >> 0) & 0xff; + dcs_buf[59] = (rptr_2_dcs[i].dcs_rptr_seq >> 8) & 0xff; + dcs_buf[60] = (rptr_2_dcs[i].dcs_rptr_seq >> 16) & 0xff; + + rptr_2_dcs[i].dcs_rptr_seq ++; + + dcs_buf[61] = 0x01; + dcs_buf[62] = 0x00; + + sendto(dcs_g2_sock, dcs_buf, 100, 0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + } + + if ((readBuffer[16] & 0x40) != 0) + { + to_remote_g2[i].out_streamid[0] = 0x00; + to_remote_g2[i].out_streamid[1] = 0x00; + } + break; + } + } + + for (i = 0; i < 3; i++) + { + if (memcmp(tracing[i].streamid, readBuffer + 14, 2) == 0) + { + /* update the last time RF user talked */ + tracing[i].last_time = time(NULL); + + if ((readBuffer[16] & 0x40) != 0) + { + if (QSO_DETAILS) + traceit("END from local g2: cntr=%02x %02x, streamID=%d,%d, %d bytes\n", + readBuffer[4], readBuffer[5], + readBuffer[14],readBuffer[15],recvlen); + + if (RPTR_ACK) + rptr_ack(i); + + memset(dtmf_mycall[i], 0, sizeof(dtmf_mycall[i])); + new_group[i] = true; + GPS_seen[i] = false; + + tracing[i].streamid[0] = 0x00; + tracing[i].streamid[1] = 0x00; + } + else + { + if (!GPS_seen[i]) + { + if (recvlen == 29) + memcpy(tmp_txt, readBuffer + 26, 3); + else + memcpy(tmp_txt, readBuffer + 29, 3); + + if ((tmp_txt[0] != 0x55) || (tmp_txt[1] != 0x2d) || (tmp_txt[2] != 0x16)) + { + if (new_group[i]) + { + tmp_txt[0] = tmp_txt[0] ^ 0x70; + header_type = tmp_txt[0] & 0xf0; + + if ((header_type == 0x50) || /* header */ + (header_type == 0xc0)) /* squelch */ + new_group[i] = false; + else + if (header_type == 0x30) /* GPS or GPS id or APRS */ + { + GPS_seen[i] = true; + new_group[i] = false; + + memcpy(tmp1, dtmf_mycall[i], 8); tmp1[8] = '\0'; + + // delete the user if exists and it is a local RF entry + p_tmp2 = NULL; + for (dt_lh_pos = dt_lh_list.begin(); dt_lh_pos != dt_lh_list.end(); dt_lh_pos++) + { + if (strcmp((char *)dt_lh_pos->second.c_str(), tmp1) == 0) + { + strcpy(tmp2, (char *)dt_lh_pos->first.c_str()); + p_tmp2 = strstr(tmp2, "=l"); + if (p_tmp2) + { + dt_lh_list.erase(dt_lh_pos); + break; + } + } + } + /* we have tmp1 and tmp2, we have the user and it is already been removed */ + /* add the user with gps indicator g */ + if (p_tmp2) + { + *(p_tmp2 + 1) = 'g'; + dt_lh_list[tmp2] = tmp1; + } + } + else + if (header_type == 0x40) /* ABC text */ + new_group[i] = false; + else + new_group[i] = false; + } + else + new_group[i] = true; + } + } + } + break; + } + } + } + } + FD_CLR (rptr_sock,&fdset); + } + } +} + +void audio_notify(char *msg) +{ + if (!ANNOUNCE) + return; + + short int i = 0; + static char notify_msg[3][64]; + + if (*msg == 'A') + i = 0; + else + if (*msg == 'B') + i = 1; + else + if (*msg == 'C') + i = 2; + + strcpy(notify_msg[i], msg); + + int rc = 0; + pthread_t audio_notify_thread; + pthread_attr_t attr; + + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&audio_notify_thread, &attr, audio_notify_run, (void *)(notify_msg[i])); + if (rc != 0) + traceit("failed to start audio_notify thread for mod %c\n", *msg); + pthread_attr_destroy(&attr); + return; +} + +static void *audio_notify_run(void *arg) +{ + char notify_msg[64]; + + strcpy(notify_msg, (char *)arg); + + unsigned short rlen = 0; + size_t nread = 0; + unsigned char dstar_buf[56]; + bool useTEXT = false; + short int TEXT_idx = 0; + char RADIO_ID[21]; + char temp_file[FILENAME_MAX + 1]; + FILE *fp = NULL; + char mod; + char *p = NULL; + u_int16_t streamid_raw = 0; + struct timespec nanos; + unsigned int aseed; + time_t tnow = 0; + struct sigaction act; + + /* example: A_linked.dat_LINKED_TO_XRF005_A */ + /* example: A_unlinked.dat */ + /* example: A_failed_linked.dat */ + + act.sa_handler = sigCatch; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + if (sigaction(SIGTERM, &act, 0) != 0) + { + traceit("sigaction-TERM failed, error=%d\n", errno); + traceit("audio_notify thread exiting...\n"); + pthread_exit(NULL); + } + if (sigaction(SIGINT, &act, 0) != 0) + { + traceit("sigaction-INT failed, error=%d\n", errno); + traceit("audio_notify thread exiting...\n"); + pthread_exit(NULL); + } + + memset(RADIO_ID, ' ', 20); + RADIO_ID[20] = '\0'; + + mod = notify_msg[0]; + + if ((mod != 'A') && (mod != 'B') && (mod != 'C')) + { + traceit("Invalid module %c in %s\n", mod, notify_msg); + pthread_exit(NULL); + } + + p = strstr(notify_msg, ".dat"); + if (!p) + { + traceit("Incorrect filename in %s\n", notify_msg); + pthread_exit(NULL); + } + + if (p[4] == '_') + { + useTEXT = true; + memcpy(RADIO_ID, p + 5, (strlen(p + 5) > 20)?20:strlen(p + 5)); + for (TEXT_idx = 0; TEXT_idx < 20; TEXT_idx++) + { + RADIO_ID[TEXT_idx] = toupper(RADIO_ID[TEXT_idx]); + if (RADIO_ID[TEXT_idx] == '_') + RADIO_ID[TEXT_idx] = ' '; + } + TEXT_idx = 0; + p[4] = '\0'; + } + else + useTEXT = false; + + sleep(DELAY_BEFORE); + + memset(temp_file, '\0', sizeof(temp_file)); + snprintf(temp_file, FILENAME_MAX, "%s/%s", ANNOUNCE_DIR, notify_msg + 2); + traceit("sending File:[%s], mod:[%c], RADIO_ID=[%s]\n", temp_file, mod, RADIO_ID); + + fp = fopen(temp_file, "rb"); + if (!fp) + { + traceit("Failed to open file %s for reading\n", temp_file); + pthread_exit(NULL); + } + + /* stupid DVTOOL + 4 byte num_of_records */ + nread = fread(dstar_buf, 10, 1, fp); + if (nread != 1) + { + traceit("Cant read first 10 bytes from %s\n", temp_file); + fclose(fp); + pthread_exit(NULL); + } + if (memcmp(dstar_buf, "DVTOOL", 6) != 0) + { + traceit("DVTOOL keyword not found in %s\n", temp_file); + fclose(fp); + pthread_exit(NULL); + } + + time(&tnow); + aseed = tnow + pthread_self(); + + while (keep_running) + { + /* 2 byte length */ + nread = fread(&rlen, 2, 1, fp); + if (nread != 1) + break; + + if (rlen == 56) + streamid_raw = (::rand_r(&aseed) % 65535U) + 1U; + else + if (rlen == 27) + ; + else + { + traceit("Not 56-byte and not 27-byte in %s\n", temp_file); + break; + } + + nread = fread(dstar_buf, rlen, 1, fp); + if (nread == 1) + { + if (memcmp(dstar_buf, "DSVT", 4) != 0) + { + traceit("DVST not found in %s\n", temp_file); + break; + } + + if (dstar_buf[8] != 0x20) + { + traceit("Not Voice type in %s\n", temp_file); + break; + } + + if (dstar_buf[4] == 0x10) + ; + else + if (dstar_buf[4] == 0x20) + ; + else + { + traceit("Not a valid record type in %s\n", temp_file); + break; + } + + dstar_buf[12] = streamid_raw / 256U; + dstar_buf[13] = streamid_raw % 256U; + + if (rlen == 56) + { + dstar_buf[15] = 0x01; + + memcpy(dstar_buf + 18, OWNER, CALL_SIZE); + dstar_buf[25] = mod; + + memcpy(dstar_buf + 26, OWNER, CALL_SIZE); + dstar_buf[33] = 'G'; + + memcpy(dstar_buf + 34, "CQCQCQ ", 8); + + memcpy(dstar_buf + 42, OWNER, CALL_SIZE); + dstar_buf[48] = ' '; + dstar_buf[49] = ' '; + + memcpy(dstar_buf + 50, "RPTR", 4); + calcPFCS(dstar_buf, 56); + } + else + { + if (useTEXT) + { + if ((dstar_buf[24] != 0x55) || + (dstar_buf[25] != 0x2d) || + (dstar_buf[26] != 0x16)) + { + if (TEXT_idx == 0) + { + dstar_buf[24] = '@' ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + if (TEXT_idx == 2) + { + dstar_buf[24] = RADIO_ID[TEXT_idx++] ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + if (TEXT_idx == 5) + { + dstar_buf[24] = 'A' ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + if (TEXT_idx == 7) + { + dstar_buf[24] = RADIO_ID[TEXT_idx++] ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + if (TEXT_idx == 10) + { + dstar_buf[24] = 'B' ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + if (TEXT_idx == 12) + { + dstar_buf[24] = RADIO_ID[TEXT_idx++] ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + if (TEXT_idx == 15) + { + dstar_buf[24] = 'C' ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + if (TEXT_idx == 17) + { + dstar_buf[24] = RADIO_ID[TEXT_idx++] ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + { + dstar_buf[24] = 0x70; + dstar_buf[25] = 0x4f; + dstar_buf[26] = 0x93; + } + } + } + } + (void)sendto(rptr_sock, (char *)dstar_buf,rlen,0, + (struct sockaddr *)&toLocalg2,sizeof(struct sockaddr_in)); + } + nanos.tv_sec = 0; + nanos.tv_nsec = DELAY_BETWEEN * 1000000; + nanosleep(&nanos,0); + } + fclose(fp); + traceit("finished sending File to mod:[%c]\n", mod); + pthread_exit(NULL); +} + +int main(int argc, char **argv) +{ + short i, j; + struct sigaction act; + int rc = 0; + char unlink_request[CALL_SIZE + 3]; + inbound_type::iterator pos; + inbound *inbound_ptr; + + char cmd_2_dcs[19]; + + tzset(); + setvbuf(stdout, (char *)NULL, _IOLBF, 0); + + if (argc != 2) + { + traceit("Usage: ./g2_link g2_link.cfg\n"); + return 1; + } + + rc = regcomp(&preg, + "^(([1-9][A-Z])|([A-Z][0-9])|([A-Z][A-Z][0-9]))[0-9A-Z]*[A-Z][ ]*[ A-RT-Z]$", + REG_EXTENDED | REG_NOSUB); + if (rc != 0) + { + traceit("The IRC regular expression is NOT valid\n"); + return 1; + } + + act.sa_handler = sigCatch; + sigemptyset(&act.sa_mask); + act.sa_flags = SA_RESTART; + if (sigaction(SIGTERM, &act, 0) != 0) + { + traceit("sigaction-TERM failed, error=%d\n", errno); + return 1; + } + if (sigaction(SIGINT, &act, 0) != 0) + { + traceit("sigaction-INT failed, error=%d\n", errno); + return 1; + } + + for (i = 0; i < 3; i++) + { + to_remote_g2[i].to_call[0] = '\0'; + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].to_mod = ' '; + to_remote_g2[i].to_mod = ' '; + to_remote_g2[i].countdown = 0; + to_remote_g2[i].is_connected = false; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + to_remote_g2[i].out_streamid[0] = 0x00; + to_remote_g2[i].out_streamid[1] = 0x00; + } + + brd_from_xrf.xrf_streamid[0] = brd_from_xrf.xrf_streamid[1] = 0x00; + brd_from_xrf.rptr_streamid[0][0] = brd_from_xrf.rptr_streamid[0][1] = 0x00; + brd_from_xrf.rptr_streamid[1][0] = brd_from_xrf.rptr_streamid[1][1] = 0x00; + brd_from_xrf_idx = 0; + + brd_from_rptr.from_rptr_streamid[0] = brd_from_rptr.from_rptr_streamid[1] = 0x00; + brd_from_rptr.to_rptr_streamid[0][0] = brd_from_rptr.to_rptr_streamid[0][1] = 0x00; + brd_from_rptr.to_rptr_streamid[1][0] = brd_from_rptr.to_rptr_streamid[1][1] = 0x00; + brd_from_rptr_idx = 0; + + do + { + /* process configuration file */ + if (!read_config(argv[1])) + { + traceit("Failed to process config file %s\n", argv[1]); + break; + } + print_status_file(); + + /* Open DB */ + if (!load_gwys(GWYS)) + break; + + /* create our server */ + if (!srv_open()) + { + traceit("srv_open() failed\n"); + break; + } + + traceit("g2_link %s initialized...entering processing loop\n", VERSION); + runit(); + traceit("Leaving processing loop...\n"); + + } while (false); + + + /* Clear connections */ + queryCommand[0] = 5; + queryCommand[1] = 0; + queryCommand[2] = 24; + queryCommand[3] = 0; + queryCommand[4] = 0; + for (i = 0; i < 3; i++) + { + if (to_remote_g2[i].to_call[0] != '\0') + { + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_REF_PORT)) + sendto(ref_g2_sock,(char *)queryCommand,5,0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + else + if (to_remote_g2[i].toDst4.sin_port == htons(RMT_XRF_PORT)) + { + strcpy(unlink_request, OWNER); + unlink_request[8] = to_remote_g2[i].from_mod; + unlink_request[9] = ' '; + unlink_request[10] = '\0'; + for (j = 0; j < 5; j++) + sendto(xrf_g2_sock,unlink_request, CALL_SIZE + 3,0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + } + else + { + strcpy(cmd_2_dcs, OWNER); + cmd_2_dcs[8] = to_remote_g2[i].from_mod; + cmd_2_dcs[9] = ' '; + cmd_2_dcs[10] = '\0'; + memcpy(cmd_2_dcs + 11, to_remote_g2[i].to_call, 8); + + for (j = 0; j < 5; j++) + sendto(dcs_g2_sock, cmd_2_dcs, 19,0, + (struct sockaddr *)&(to_remote_g2[i].toDst4), + sizeof(to_remote_g2[i].toDst4)); + } + } + to_remote_g2[i].to_call[0] = '\0'; + memset(&(to_remote_g2[i].toDst4),0,sizeof(struct sockaddr_in)); + to_remote_g2[i].from_mod = ' '; + to_remote_g2[i].to_mod = ' '; + to_remote_g2[i].countdown = 0; + to_remote_g2[i].is_connected = false; + to_remote_g2[i].in_streamid[0] = 0x00; + to_remote_g2[i].in_streamid[1] = 0x00; + to_remote_g2[i].out_streamid[0] = 0x00; + to_remote_g2[i].out_streamid[1] = 0x00; + } + + /* tell inbound dongles we are down */ + for (pos = inbound_list.begin(); pos != inbound_list.end(); pos++) + { + inbound_ptr = (inbound *)pos->second; + sendto(ref_g2_sock,(char *)queryCommand,5,0, + (struct sockaddr *)&(inbound_ptr->sin), + sizeof(struct sockaddr_in)); + } + inbound_list.clear(); + + print_status_file(); + srv_close(); + traceit("g2_link exiting\n"); + + return 0; +} + diff --git a/g2link_test.cpp b/g2link_test.cpp new file mode 100644 index 0000000..c51d2d2 --- /dev/null +++ b/g2link_test.cpp @@ -0,0 +1,406 @@ + +/* + * Copyright (C) 2010 by Scott Lawson KI4LKF + * + * 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. + */ + +/* by KI4LKF */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define VERSION "v3.2" + +static int sockDst = -1; +static struct sockaddr_in toDst; +static void dst_close(); +static bool dst_open(char *ip, int port); +static void calcPFCS(unsigned char rawbytes[58]); + +static time_t tNow = 0; +static short streamid_raw = 0; + +/*** +static char silence[12] = +{ + 0x4e,0x8d,0x32,0x88,0x26,0x1a,0x3f,0x61,0xe8, + 0x70,0x4f,0x93 +}; +***/ + +static char silence[12] = +{ + 0xfa,0x87,0x1e,0x32,0x30,0x2f,0xea,0x45,0x66, + 0x70,0x4f,0x93 +}; + + +static unsigned short crc_tabccitt[256] = +{ + 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 +}; + +static void calcPFCS(unsigned char rawbytes[58]) +{ + + unsigned short crc_dstar_ffff = 0xffff; + unsigned short tmp, short_c; + short int i; + + for (i = 17; i < 56 ; i++) + { + short_c = 0x00ff & (unsigned short)rawbytes[i]; + tmp = (crc_dstar_ffff & 0x00ff) ^ short_c; + crc_dstar_ffff = (crc_dstar_ffff >> 8) ^ crc_tabccitt[tmp]; + } + crc_dstar_ffff = ~crc_dstar_ffff; + tmp = crc_dstar_ffff; + + rawbytes[56] = (unsigned char)(crc_dstar_ffff & 0xff); + rawbytes[57] = (unsigned char)((tmp >> 8) & 0xff); + return; +} + +static bool dst_open(char *ip, int port) +{ + int reuse = 1; + + sockDst = socket(PF_INET,SOCK_DGRAM,0); + if (sockDst == -1) + { + printf("Failed to create DSTAR socket\n"); + return false; + } + if (setsockopt(sockDst,SOL_SOCKET,SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) + { + close(sockDst); sockDst = -1; + printf("setsockopt DSTAR REUSE failed\n"); + return false; + } + memset(&toDst,0,sizeof(struct sockaddr_in)); + toDst.sin_family = AF_INET; + toDst.sin_port = htons(port); + toDst.sin_addr.s_addr = inet_addr(ip); + + fcntl(sockDst,F_SETFL,O_NONBLOCK); + return true; +} + +static void dst_close() +{ + if (sockDst != -1) + { + close(sockDst); + sockDst = -1; + } + return; +} + +int main(int argc, char **argv) +{ + unsigned char dstar_buf[58]; + static unsigned short G2_COUNTER = 0; + unsigned long delay; + char RADIO_ID[21]; + short int i; + + if (argc != 10) + { + printf("Usage: g2link_test \n"); + printf("Example: g2link_test 127.0.0.1 19000 \"HELLO\" KJ4NHF B 20 2 KI4LKF XRF005AL\n"); + printf("Where...\n\n"); + printf(" 127.0.0.1 is the G2 INTERNAL IP of the G2 gateway\n"); + printf(" 19000 is the the G2 INTERNAL port of the G2 gateway\n"); + printf(" HELLO is the text message that we will send, no more than 20 characters\n"); + printf(" Note: the text message will be converted to UPPERCASE\n"); + printf(" KJ4NHF is your dstar repeater callsign\n"); + printf(" B is the local repeater module\n"); + printf(" 20 millisecond delay before each packet is sent\n"); + printf(" 2 second delay before the program starts processing your input \n"); + printf(" KI4LKF is the value of mycall\n"); + printf(" XRF005AL is the value of yrcall, in this case this is a Link command\n\n"); + return 0; + } + + if (strlen(argv[4]) > 6) + { + printf("repeaterCallsign can not be more than 6 characters, %s is invalid\n", argv[4]); + return 0; + } + for (i = 0; i < 6; i++) + argv[4][i] = toupper(argv[4][i]); + + + if (strlen(argv[8]) > 8) + { + printf("MYCALL can not be nore than 8 characters, %s is invalid\n", argv[8]); + return 0; + } + for (i = 0; i < 8; i++) + argv[8][i] = toupper(argv[8][i]); + + + if (strlen(argv[9]) > 8) + { + printf("YRCALL can not be nore than 8 characters, %s is invalid\n", argv[9]); + return 0; + } + for (i = 0; i < 8; i++) + argv[9][i] = toupper(argv[9][i]); + + if ((argv[5][0] != 'A') && (argv[5][0] != 'B') && (argv[5][0] != 'C')) + { + printf("module must be one of A B C\n"); + return 0; + } + + delay = atol(argv[6]) * 1000L; + sleep(atoi(argv[7])); + + memset(RADIO_ID, ' ', 20); + RADIO_ID[20] = '\0'; + memcpy(RADIO_ID, argv[3], (strlen(argv[3]) > 20)?20:strlen(argv[3])); + + /*** + for (i = 0; i < 20; i++) + RADIO_ID[i] = toupper(RADIO_ID[i]); + ***/ + + time(&tNow); + srand(tNow + getpid()); + + if (dst_open(argv[1], atoi(argv[2]))) + { + streamid_raw = (short)(::rand() & 0xFFFF); + memcpy(dstar_buf,"DSTR", 4); + dstar_buf[5] = (unsigned char)(G2_COUNTER & 0xff); + dstar_buf[4] = (unsigned char)((G2_COUNTER >> 8) & 0xff); + dstar_buf[6] = 0x73; + dstar_buf[7] = 0x12; + dstar_buf[8] = 0x00; + dstar_buf[9] = 0x30; + dstar_buf[10] = 0x20; + + dstar_buf[11] = 0x00; + dstar_buf[12] = 0x01; + if (argv[5][0] == 'A') + dstar_buf[13] = 0x03; + else + if (argv[5][0] == 'B') + dstar_buf[13] = 0x01; + else + if (argv[5][0] == 'C') + dstar_buf[13] = 0x02; + else + dstar_buf[13] = 0x00; + + dstar_buf[14] = (unsigned char)(streamid_raw & 0xFF); + dstar_buf[15] = (unsigned char)((streamid_raw >> 8) & 0xFF); + dstar_buf[16] = 0x80; + dstar_buf[17] = 0x00; + dstar_buf[18] = 0x00; + dstar_buf[19] = 0x00; + + /* RPT2 */ + memcpy(dstar_buf + 20, argv[4], strlen(argv[4])); + if (strlen(argv[4]) < 6) + memset(dstar_buf + 20 + strlen(argv[4]), ' ', 6 - strlen(argv[4])); + dstar_buf[26] = ' '; + dstar_buf[27] = 'G'; + + /* RPT1 */ + memcpy(dstar_buf + 28, argv[4], strlen(argv[4])); + if (strlen(argv[4]) < 6) + memset(dstar_buf + 28 + strlen(argv[4]), ' ', 6 - strlen(argv[4])); + dstar_buf[34] = ' '; + dstar_buf[35] = argv[5][0]; + + /* YRCALL */ + memcpy(dstar_buf + 36, argv[9], strlen(argv[9])); + if (strlen(argv[9]) < 8) + memset(dstar_buf + 36 + strlen(argv[9]), ' ', 8 - strlen(argv[9])); + + /* MYCALL */ + memcpy(dstar_buf + 44, argv[8], strlen(argv[8])); + if (strlen(argv[8]) < 8) + memset(dstar_buf + 44 + strlen(argv[8]), ' ', 8 - strlen(argv[8])); + + /* suffix */ + memcpy(dstar_buf + 52, " ", 4); + calcPFCS(dstar_buf); + (void)sendto(sockDst,(char *)dstar_buf,58,0,(struct sockaddr *)&toDst,sizeof(toDst)); + G2_COUNTER ++; + usleep(delay); + + dstar_buf[9] = 0x13; + memcpy((char *)dstar_buf + 17, silence, 9); + + /* start sending silence + text */ + + /* SYNC */ + dstar_buf[5] = (unsigned char)(G2_COUNTER & 0xff); + dstar_buf[4] = (unsigned char)((G2_COUNTER >> 8) & 0xff); + dstar_buf[16] = 0x00; + dstar_buf[26] = 0x55; + dstar_buf[27] = 0x2d; + dstar_buf[28] = 0x16; + (void)sendto(sockDst,(char *)dstar_buf,29,0,(struct sockaddr *)&toDst,sizeof(toDst)); + G2_COUNTER ++; + usleep(delay); + + dstar_buf[5] = (unsigned char)(G2_COUNTER & 0xff); + dstar_buf[4] = (unsigned char)((G2_COUNTER >> 8) & 0xff); + dstar_buf[16] = 0x01; + dstar_buf[26] = '@' ^ 0x70; + dstar_buf[27] = RADIO_ID[0] ^ 0x4f; + dstar_buf[28] = RADIO_ID[1] ^ 0x93; + (void)sendto(sockDst,(char *)dstar_buf,29,0,(struct sockaddr *)&toDst,sizeof(toDst)); + G2_COUNTER ++; + usleep(delay); + + dstar_buf[5] = (unsigned char)(G2_COUNTER & 0xff); + dstar_buf[4] = (unsigned char)((G2_COUNTER >> 8) & 0xff); + dstar_buf[16] = 0x02; + dstar_buf[26] = RADIO_ID[2] ^ 0x70; + dstar_buf[27] = RADIO_ID[3] ^ 0x4f; + dstar_buf[28] = RADIO_ID[4] ^ 0x93; + (void)sendto(sockDst,(char *)dstar_buf,29,0,(struct sockaddr *)&toDst,sizeof(toDst)); + G2_COUNTER ++; + usleep(delay); + + dstar_buf[5] = (unsigned char)(G2_COUNTER & 0xff); + dstar_buf[4] = (unsigned char)((G2_COUNTER >> 8) & 0xff); + dstar_buf[16] = 0x03; + dstar_buf[26] = 'A' ^ 0x70; + dstar_buf[27] = RADIO_ID[5] ^ 0x4f; + dstar_buf[28] = RADIO_ID[6] ^ 0x93; + (void)sendto(sockDst,(char *)dstar_buf,29,0,(struct sockaddr *)&toDst,sizeof(toDst)); + G2_COUNTER ++; + usleep(delay); + + dstar_buf[5] = (unsigned char)(G2_COUNTER & 0xff); + dstar_buf[4] = (unsigned char)((G2_COUNTER >> 8) & 0xff); + dstar_buf[16] = 0x04; + dstar_buf[26] = RADIO_ID[7] ^ 0x70; + dstar_buf[27] = RADIO_ID[8] ^ 0x4f; + dstar_buf[28] = RADIO_ID[9] ^ 0x93; + (void)sendto(sockDst,(char *)dstar_buf,29,0,(struct sockaddr *)&toDst,sizeof(toDst)); + G2_COUNTER ++; + usleep(delay); + + dstar_buf[5] = (unsigned char)(G2_COUNTER & 0xff); + dstar_buf[4] = (unsigned char)((G2_COUNTER >> 8) & 0xff); + dstar_buf[16] = 0x05; + dstar_buf[26] = 'B' ^ 0x70; + dstar_buf[27] = RADIO_ID[10] ^ 0x4f; + dstar_buf[28] = RADIO_ID[11] ^ 0x93; + (void)sendto(sockDst,(char *)dstar_buf,29,0,(struct sockaddr *)&toDst,sizeof(toDst)); + G2_COUNTER ++; + usleep(delay); + + dstar_buf[5] = (unsigned char)(G2_COUNTER & 0xff); + dstar_buf[4] = (unsigned char)((G2_COUNTER >> 8) & 0xff); + dstar_buf[16] = 0x06; + dstar_buf[26] = RADIO_ID[12] ^ 0x70; + dstar_buf[27] = RADIO_ID[13] ^ 0x4f; + dstar_buf[28] = RADIO_ID[14] ^ 0x93; + (void)sendto(sockDst,(char *)dstar_buf,29,0,(struct sockaddr *)&toDst,sizeof(toDst)); + G2_COUNTER ++; + usleep(delay); + + dstar_buf[5] = (unsigned char)(G2_COUNTER & 0xff); + dstar_buf[4] = (unsigned char)((G2_COUNTER >> 8) & 0xff); + dstar_buf[16] = 0x07; + dstar_buf[26] = 'C' ^ 0x70; + dstar_buf[27] = RADIO_ID[15] ^ 0x4f; + dstar_buf[28] = RADIO_ID[16] ^ 0x93; + (void)sendto(sockDst,(char *)dstar_buf,29,0,(struct sockaddr *)&toDst,sizeof(toDst)); + G2_COUNTER ++; + usleep(delay); + + dstar_buf[5] = (unsigned char)(G2_COUNTER & 0xff); + dstar_buf[4] = (unsigned char)((G2_COUNTER >> 8) & 0xff); + dstar_buf[16] = 0x08; + dstar_buf[26] = RADIO_ID[17] ^ 0x70; + dstar_buf[27] = RADIO_ID[18] ^ 0x4f; + dstar_buf[28] = RADIO_ID[19] ^ 0x93; + (void)sendto(sockDst,(char *)dstar_buf,29,0,(struct sockaddr *)&toDst,sizeof(toDst)); + G2_COUNTER ++; + usleep(delay); + + dstar_buf[5] = (unsigned char)(G2_COUNTER & 0xff); + dstar_buf[4] = (unsigned char)((G2_COUNTER >> 8) & 0xff); + dstar_buf[16] = 0x09 | 0x40; + + memset((char *)dstar_buf + 17, '\0', 9); + + dstar_buf[26] = 0x70; + dstar_buf[27] = 0x4f; + dstar_buf[28] = 0x93; + (void)sendto(sockDst,(char *)dstar_buf,29,0,(struct sockaddr *)&toDst,sizeof(toDst)); + G2_COUNTER ++; + usleep(delay); + + dst_close(); + } + + printf("g2link_test exiting...\n"); + return 0; +} diff --git a/g2link_test_audio.cpp b/g2link_test_audio.cpp new file mode 100644 index 0000000..f1875b4 --- /dev/null +++ b/g2link_test_audio.cpp @@ -0,0 +1,399 @@ + +/* by KI4LKF */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define VERSION "v3.1" + +static int sockDst = -1; +static struct sockaddr_in toDst; +static void dst_close(); +static bool dst_open(char *ip, int port); +static void calcPFCS(unsigned char rawbytes[58]); + +static FILE *fp = NULL; +static time_t tNow = 0; +static short streamid_raw = 0; + +static unsigned short crc_tabccitt[256] = +{ + 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 +}; + + + +static void calcPFCS(unsigned char rawbytes[58]) +{ + unsigned short crc_dstar_ffff = 0xffff; + unsigned short tmp, short_c; + short int i; + + for (i = 17; i < 56 ; i++) + { + short_c = 0x00ff & (unsigned short)rawbytes[i]; + tmp = (crc_dstar_ffff & 0x00ff) ^ short_c; + crc_dstar_ffff = (crc_dstar_ffff >> 8) ^ crc_tabccitt[tmp]; + } + crc_dstar_ffff = ~crc_dstar_ffff; + tmp = crc_dstar_ffff; + + rawbytes[56] = (unsigned char)(crc_dstar_ffff & 0xff); + rawbytes[57] = (unsigned char)((tmp >> 8) & 0xff); + return; + +} + +static bool dst_open(char *ip, int port) +{ + int reuse = 1; + + sockDst = socket(PF_INET,SOCK_DGRAM,0); + if (sockDst == -1) + { + printf("Failed to create DSTAR socket\n"); + return false; + } + if (setsockopt(sockDst,SOL_SOCKET,SO_REUSEADDR, (char *)&reuse, sizeof(reuse)) == -1) + { + close(sockDst); sockDst = -1; + printf("setsockopt DSTAR REUSE failed\n"); + return false; + } + memset(&toDst,0,sizeof(struct sockaddr_in)); + toDst.sin_family = AF_INET; + toDst.sin_port = htons(port); + toDst.sin_addr.s_addr = inet_addr(ip); + + fcntl(sockDst,F_SETFL,O_NONBLOCK); + return true; +} + +static void dst_close() +{ + if (sockDst != -1) + { + close(sockDst); + sockDst = -1; + } + return; +} + +int main(int argc, char **argv) +{ + unsigned short rlen = 0; + static unsigned short G2_COUNTER = 0; + size_t nread = 0; + unsigned char dstar_buf[56]; + unsigned char rptr_buf[58]; + unsigned long delay; + unsigned short i; + char RADIO_ID[21]; + short int TEXT_idx = 0; + + if (argc != 10) + { + printf("Usage: g2link_test_audio \n"); + printf("Example: g2link_test_audio 127.0.0.1 19000 somefile.dvtool KJ4NHF B 19 2 KI4LKF CQCQCQ\n"); + printf("Where...\n"); + printf(" 127.0.0.1 is the IP address of the local G2\n"); + printf(" 19000 is the port of the INTERNAL G2\n"); + printf(" somefile.dvtool is a dvtool file\n"); + printf(" KJ4NHF is your G2 callsign, dont use KJ4NHF\n"); + printf(" B is one of your modules\n"); + printf(" 19 millisecond delay between each packet\n"); + printf(" 2 second delay before we begin this test\n"); + printf(" mycall is KI4LKF, your personal callsign, do not use KI4LKF\n"); + printf(" yrcall is CQCQCQ\n"); + return 0; + } + + if (strlen(argv[4]) > 6) + { + printf("repeaterCallsign can not be more than 6 characters, %s is invalid\n", argv[4]); + return 0; + } + for (i = 0; i < strlen(argv[4]); i++) + argv[4][i] = toupper(argv[4][i]); + + if ((argv[5][0] != 'A') && (argv[5][0] != 'B') && (argv[5][0] != 'C')) + { + printf("module must be one of A B C\n"); + return 0; + } + + if (strlen(argv[8]) > 8) + { + printf("No more than 8 characters in MYCALL\n"); + return 0; + } + for (i = 0; i < strlen(argv[8]); i++) + argv[8][i] = toupper(argv[8][i]); + + if (strlen(argv[9]) > 8) + { + printf("No more than 8 characters in YRCALL\n"); + return 0; + } + for (i = 0; i < strlen(argv[9]); i++) + argv[9][i] = toupper(argv[9][i]); + + + fp = fopen(argv[3], "rb"); + if (!fp) + { + printf("Failed to open file %s for reading\n", argv[3]); + return 0; + } + + /* stupid DVTOOL + 4 byte num_of_records */ + nread = fread(dstar_buf, 10, 1, fp); + if (nread != 1) + { + printf("Cant read first 10 bytes\n"); + fclose(fp); + return 0; + } + if (memcmp(dstar_buf, "DVTOOL", 6) != 0) + { + printf("DVTOOL not found\n"); + fclose(fp); + return 0; + } + + memset(RADIO_ID, ' ', 20); + RADIO_ID[20] = '\0'; + + memcpy(RADIO_ID, "TEST", 4); + + delay = atol(argv[6]) * 1000L; + sleep(atoi(argv[7])); + + time(&tNow); + srand(tNow + getpid()); + + if (dst_open(argv[1], atoi(argv[2]))) + { + while (true) + { + /* 2 byte length */ + nread = fread(&rlen, 2, 1, fp); + if (nread != 1) + { + printf("End-Of-File\n"); + break; + } + if (rlen == 56) + streamid_raw = (short)(::rand() & 0xFFFF); + else + if (rlen == 27) + ; + else + { + printf("Not 56-byte and not 27-byte\n"); + break; + } + + /* read the packet */ + nread = fread(dstar_buf, rlen, 1, fp); + if (nread == 1) + { + if (memcmp(dstar_buf, "DSVT", 4) != 0) + { + printf("DVST not found\n"); + break; + } + + if (dstar_buf[8] != 0x20) + { + printf("Not Voice type\n"); + break; + } + + if (dstar_buf[4] == 0x10) + ; + else + if (dstar_buf[4] == 0x20) + ; + else + { + printf("Not a valid record type\n"); + break; + } + + if (rlen == 56) + { + memcpy(rptr_buf, "DSTR", 4); + rptr_buf[5] = (unsigned char)(G2_COUNTER & 0xff); + rptr_buf[4] = (unsigned char)((G2_COUNTER >> 8) & 0xff); + rptr_buf[6] = 0x73; + rptr_buf[7] = 0x12; + rptr_buf[8] = 0x00; + rptr_buf[9] = 0x30; + rptr_buf[10] = 0x20; + memcpy(rptr_buf + 11, dstar_buf + 9, 47); + + rptr_buf[14] = (unsigned char)(streamid_raw & 0xFF); + rptr_buf[15] = (unsigned char)((streamid_raw >> 8) & 0xFF); + + memcpy(rptr_buf + 20, argv[4], strlen(argv[4])); + if (strlen(argv[4]) < 6) + memset(rptr_buf + 20 + strlen(argv[4]), ' ', 6 - strlen(argv[4])); + rptr_buf[26] = ' '; + rptr_buf[27] = 'G'; + + memcpy(rptr_buf + 28, argv[4], strlen(argv[4])); + if (strlen(argv[4]) < 6) + memset(rptr_buf + 28 + strlen(argv[4]), ' ', 6 - strlen(argv[4])); + rptr_buf[34] = ' '; + rptr_buf[35] = argv[5][0]; + + /* yrcall */ + memcpy(rptr_buf + 36, argv[9], strlen(argv[9])); + if (strlen(argv[9]) < 8) + memset(rptr_buf + 36 + strlen(argv[9]), ' ', 8 - strlen(argv[9])); + + /* mycall */ + memcpy(rptr_buf + 44, argv[8], strlen(argv[8])); + if (strlen(argv[8]) < 8) + memset(rptr_buf + 44 + strlen(argv[8]), ' ', 8 - strlen(argv[8])); + + memcpy(rptr_buf + 52, "TEST", 4); + + calcPFCS(rptr_buf); + } + else + { + rptr_buf[5] = (unsigned char)(G2_COUNTER & 0xff); + rptr_buf[4] = (unsigned char)((G2_COUNTER >> 8) & 0xff); + rptr_buf[9] = 0x13; + + if ((dstar_buf[24] != 0x55) || + (dstar_buf[25] != 0x2d) || + (dstar_buf[26] != 0x16)) + { + if (TEXT_idx == 0) + { + dstar_buf[24] = '@' ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + if (TEXT_idx == 2) + { + dstar_buf[24] = RADIO_ID[TEXT_idx++] ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + if (TEXT_idx == 5) + { + dstar_buf[24] = 'A' ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + if (TEXT_idx == 7) + { + dstar_buf[24] = RADIO_ID[TEXT_idx++] ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + if (TEXT_idx == 10) + { + dstar_buf[24] = 'B' ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + if (TEXT_idx == 12) + { + dstar_buf[24] = RADIO_ID[TEXT_idx++] ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + if (TEXT_idx == 15) + { + dstar_buf[24] = 'C' ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + if (TEXT_idx == 17) + { + dstar_buf[24] = RADIO_ID[TEXT_idx++] ^ 0x70; + dstar_buf[25] = RADIO_ID[TEXT_idx++] ^ 0x4f; + dstar_buf[26] = RADIO_ID[TEXT_idx++] ^ 0x93; + } + else + { + dstar_buf[24] = 0x70; + dstar_buf[25] = 0x4f; + dstar_buf[26] = 0x93; + } + } + memcpy(rptr_buf + 11, dstar_buf + 9, 18); + rptr_buf[14] = (unsigned char)(streamid_raw & 0xFF); + rptr_buf[15] = (unsigned char)((streamid_raw >> 8) & 0xFF); + } + + sendto(sockDst,(char *)rptr_buf,rlen + 2,0, + (struct sockaddr *)&toDst,sizeof(toDst)); + G2_COUNTER ++; + + } + usleep(delay); + } + dst_close(); + } + fclose(fp); + + printf("g2link_test_audio exiting...\n"); + return 0; +} diff --git a/get_gwy_list.sh b/get_gwy_list.sh new file mode 100755 index 0000000..0738d77 --- /dev/null +++ b/get_gwy_list.sh @@ -0,0 +1,10 @@ +#/bin/bash + +/bin/cp ./gwys.txt ./gwys.txt.bak +/bin/rm -rf ./gwys.txt + +wget http://www.va3uv.com/gwys.txt + +#/sbin/service g2_link restart + +exit 0 diff --git a/golay23.cpp b/golay23.cpp new file mode 100644 index 0000000..fcde541 --- /dev/null +++ b/golay23.cpp @@ -0,0 +1,280 @@ +/* + This file was downloaded from this URL: + + http://www.eccpage.com/golay23.c + + Some source code lines were commented out. + + 2010-12-01 DL1BFF + */ + +/* File: golay23.c + * Title: Encoder/decoder for a binary (23,12,7) Golay code + * Author: Robert Morelos-Zaragoza (robert@spectra.eng.hawaii.edu) + * Date: August 1994 + * + * The binary (23,12,7) Golay code is an example of a perfect code, that is, + * the number of syndromes equals the number of correctable error patterns. + * The minimum distance is 7, so all error patterns of Hamming weight up to + * 3 can be corrected. The total number of these error patterns is: + * + * Number of errors Number of patterns + * ---------------- ------------------ + * 0 1 + * 1 23 + * 2 253 + * 3 1771 + * ---- + * Total number of error patterns = 2048 = 2^{11} = number of syndromes + * -- + * number of redundant bits -------^ + * + * Because of its relatively low length (23), dimension (12) and number of + * redundant bits (11), the binary (23,12,7) Golay code can be encoded and + * decoded simply by using look-up tables. The program below uses a 16K + * encoding table and an 8K decoding table. + * + * For more information, suggestions, or other ideas on implementing error + * correcting codes, please contact me at (I'm temporarily in Japan, but + * below is my U.S. address): + * + * Robert Morelos-Zaragoza + * 770 S. Post Oak Ln. #200 + * Houston, Texas 77056 + * + * email: robert@spectra.eng.hawaii.edu + * + * Homework: Add an overall parity-check bit to get the (24,12,8) + * extended Golay code. + * + * COPYRIGHT NOTICE: This computer program is free for non-commercial purposes. + * You may implement this program for any non-commercial application. You may + * also implement this program for commercial purposes, provided that you + * obtain my written permission. Any modification of this program is covered + * by this copyright. + * + * == Copyright (c) 1994 Robert Morelos-Zaragoza. All rights reserved. == + */ + +#include +#define X22 0x00400000 /* vector representation of X^{22} */ +#define X11 0x00000800 /* vector representation of X^{11} */ +#define MASK12 0xfffff800 /* auxiliary vector for testing */ +#define GENPOL 0x00000c75 /* generator polinomial, g(x) */ + +/* Global variables: + * + * pattern = error pattern, or information, or received vector + * encoding_table[] = encoding table + * decoding_table[] = decoding table + * data = information bits, i(x) + * codeword = code bits = x^{11}i(x) + (x^{11}i(x) mod g(x)) + * numerr = number of errors = Hamming weight of error polynomial e(x) + * position[] = error positions in the vector representation of e(x) + * recd = representation of corrupted received polynomial r(x) = c(x) + e(x) + * decerror = number of decoding errors + * a[] = auxiliary array to generate correctable error patterns + */ + +// long pattern; +// long encoding_table[4096], decoding_table[2048]; +// long data, codeword, recd; +// long position[23] = { 0x00000001, 0x00000002, 0x00000004, 0x00000008, +// 0x00000010, 0x00000020, 0x00000040, 0x00000080, +// 0x00000100, 0x00000200, 0x00000400, 0x00000800, +// 0x00001000, 0x00002000, 0x00004000, 0x00008000, +// 0x00010000, 0x00020000, 0x00040000, 0x00080000, +// 0x00100000, 0x00200000, 0x00400000 }; +// long numerr, errpos[23], decerror = 0; +// int a[4]; + +long arr2int(int *a, int r) +/* + * Convert a binary vector of Hamming weight r, and nonzero positions in + * array a[1]...a[r], to a long integer \sum_{i=1}^r 2^{a[i]-1}. + */ +{ + int i; + long mul, result = 0, temp; + + for (i=1; i<=r; i++) { + mul = 1; + temp = a[i]-1; + while (temp--) + mul = mul << 1; + result += mul; + } + return(result); +} + +void nextcomb(int n, int r, int *a) +/* + * Calculate next r-combination of an n-set. + */ +{ + int i, j; + + a[r]++; + if (a[r] <= n) + return; + j = r - 1; + while (a[j] == n - r + j) + j--; + for (i = r; i >= j; i--) + a[i] = a[j] + i - j + 1; + return; +} + +long get_syndrome(long pattern) +/* + * Compute the syndrome corresponding to the given pattern, i.e., the + * remainder after dividing the pattern (when considering it as the vector + * representation of a polynomial) by the generator polynomial, GENPOL. + * In the program this pattern has several meanings: (1) pattern = infomation + * bits, when constructing the encoding table; (2) pattern = error pattern, + * when constructing the decoding table; and (3) pattern = received vector, to + * obtain its syndrome in decoding. + */ +{ +// long aux = X22, aux2; + long aux = X22; + + if (pattern >= X11) + while (pattern & MASK12) { + while (!(aux & pattern)) + aux = aux >> 1; + pattern ^= (aux/X11) * GENPOL; + } + return(pattern); +} + +// main() +// { +// register int i,j; +// long temp; +// int seed = 133757; +// +// /* +// * --------------------------------------------------------------------- +// * Generate ENCODING TABLE +// * +// * An entry to the table is an information vector, a 32-bit integer, +// * whose 12 least significant positions are the information bits. The +// * resulting value is a codeword in the (23,12,7) Golay code: A 32-bit +// * integer whose 23 least significant bits are coded bits: Of these, the +// * 12 most significant bits are information bits and the 11 least +// * significant bits are redundant bits (systematic encoding). +// * --------------------------------------------------------------------- +// */ +// for (pattern = 0; pattern < 4096; pattern++) { +// temp = pattern << 11; /* multiply information by X^{11} */ +// encoding_table[pattern] = temp + get_syndrome(temp);/* add redundancy */ +// } +// +// /* +// * --------------------------------------------------------------------- +// * Generate DECODING TABLE +// * +// * An entry to the decoding table is a syndrome and the resulting value +// * is the most likely error pattern. First an error pattern is generated. +// * Then its syndrome is calculated and used as a pointer to the table +// * where the error pattern value is stored. +// * --------------------------------------------------------------------- +// * +// * (1) Error patterns of WEIGHT 1 (SINGLE ERRORS) +// */ +// decoding_table[0] = 0; +// decoding_table[1] = 1; +// temp = 1; +// for (i=2; i<= 23; i++) { +// temp *= 2; +// decoding_table[get_syndrome(temp)] = temp; +// } +// /* +// * (2) Error patterns of WEIGHT 2 (DOUBLE ERRORS) +// */ +// a[1] = 1; a[2] = 2; +// temp = arr2int(a,2); +// decoding_table[get_syndrome(temp)] = temp; +// for (i=1; i<253; i++) { +// nextcomb(23,2,a); +// temp = arr2int(a,2); +// decoding_table[get_syndrome(temp)] = temp; +// } +// /* +// * (3) Error patterns of WEIGHT 3 (TRIPLE ERRORS) +// */ +// a[1] = 1; a[2] = 2; a[3] = 3; +// temp = arr2int(a,3); +// decoding_table[get_syndrome(temp)] = temp; +// for (i=1; i<1771; i++) { +// nextcomb(23,3,a); +// temp = arr2int(a,3); +// decoding_table[get_syndrome(temp)] = temp; +// } +// +// /* --------------------------------------------------------------------- +// * Generate DATA +// * --------------------------------------------------------------------- +// */ +// srandom(seed); +// /* +// * data = 12 information bits, an information polynomial i(x) +// */ +// data = random() & 0x00000fff; +// printf("data = %#012x\n", data); +// +// /* +// * --------------------------------------------------------------------- +// * ENCODING +// * --------------------------------------------------------------------- +// */ +// codeword = encoding_table[data]; +// printf("codeword = %#012x\n", codeword); +// +// /* +// * --------------------------------------------------------------------- +// * ERRORS +// * --------------------------------------------------------------------- +// */ +// printf("Enter the number of errors and their positions (0...22): "); +// scanf("%d", &numerr); +// for (i = 0; i < numerr; i++) +// scanf("%d", &errpos[i]); +// +// /* +// * --------------------------------------------------------------------- +// * RECEIVED VECTOR +// * --------------------------------------------------------------------- +// */ +// recd = codeword; +// if (numerr) +// for (i = 0; i < numerr; i++) +// recd ^= position[errpos[i]]; +// printf("received vector = %#012x\n", recd); +// +// /* +// * --------------------------------------------------------------------- +// * DECODING +// * --------------------------------------------------------------------- +// */ +// printf("syndrome = %#012x\n", get_syndrome(recd)); +// printf("error pattern = %#012x\n", decoding_table[get_syndrome(recd)]); +// /* +// * Calculate the syndrome, look up the corresponding error pattern and +// * add it to the received vector. +// */ +// recd ^= decoding_table[get_syndrome(recd)]; +// printf("decoded vector = %#012x\n", recd); +// printf("recovered data = %#012x\n", (recd>>11)); +// printf("original data = %#012x\n", data); +// /* +// * DECODING ERRORS? Only the data portion is compared. Note that this +// * is only possible in a simulation! +// */ +// pattern = (recd ^ codeword) >> 11; +// for (i=0; i<12; i++) +// if (pattern&position[i]) +// decerror++; +// printf("there were %d decoding errors\n", decerror); +// } diff --git a/golay23.h b/golay23.h new file mode 100644 index 0000000..c096e99 --- /dev/null +++ b/golay23.h @@ -0,0 +1,5 @@ + +long get_syndrome(long pattern); +long arr2int(int a[], int r); +void nextcomb(int n, int r, int a[]); + diff --git a/gwys.txt b/gwys.txt new file mode 100644 index 0000000..3a1f1c9 --- /dev/null +++ b/gwys.txt @@ -0,0 +1,99 @@ +REF001 109.69.104.195 20001 +REF002 129.93.2.132 20001 +REF003 203.194.18.195 20001 +REF004 74.204.50.19 20001 +REF005 109.69.104.193 20001 +REF006 78.158.56.61 20001 +REF007 208.111.3.180 20001 +REF008 58.1.235.43 20001 +REF009 204.89.198.18 20001 +REF010 70.233.76.110 20001 +REF011 213.203.177.246 20001 +REF012 209.112.244.26 20001 +REF014 64.250.229.185 20001 +REF015 109.69.104.196 20001 +REF016 204.244.106.103 20001 +REF017 80.69.86.233 20001 +REF018 187.50.254.20 20001 +REF019 208.87.120.144 20001 +REF020 50.199.88.20 20001 +REF023 202.191.108.226 20001 +REF024 69.41.0.15 20001 +REF025 173.10.178.226 20001 +REF026 206.12.104.8 20001 +REF027 194.116.29.72 20001 +REF028 193.190.240.229 20001 +REF029 129.123.7.138 20001 +REF030 64.16.212.14 20001 +REF031 212.112.188.111 20001 +REF032 95.160.171.254 20001 +REF033 208.67.255.202 20001 +REF035 146.129.247.243 20001 +REF036 195.194.238.109 20001 +REF037 208.111.3.181 20001 +REF038 66.6.171.227 20001 +REF039 208.93.191.20 20001 +REF041 129.105.15.195 20001 +REF045 195.251.201.194 20001 +REF046 208.111.3.182 20001 +REF047 157.7.142.13 20001 +REF048 208.88.66.244 20001 +REF050 75.147.26.195 20001 +REF051 50.57.153.17 20001 +REF052 216.207.94.13 20001 +REF053 69.67.29.232 20001 +REF054 216.249.104.190 20001 +REF055 207.251.62.205 20001 +REF058 131.204.255.253 20001 +REF060 50.194.6.1 20001 +REF061 64.150.149.59 20001 +REF062 70.88.145.165 20001 +REF063 205.201.0.4 20001 +XRF000 141.22.15.29 20001 +XRF001 75.76.90.90 20001 +XRF002 91.201.57.233 20001 +XRF003 95.110.226.76 20001 +XRF004 96.36.58.9 20001 +XRF005 216.16.240.236 20001 +XRF006 80.62.20.151 20001 +XRF007 84.232.6.94 20001 +XRF008 95.110.231.219 20001 +XRF009 141.22.15.29 20001 +XRF010 87.106.48.7 20001 +XRF011 87.106.3.249 20001 +XRF012 96.126.101.188 20001 +XRF013 202.191.108.233 20001 +XRF017 87.253.159.114 20001 +XRF018 199.167.129.166 20001 +XRF019 66.30.81.236 20001 +XRF020 204.244.106.101 20001 +XRF021 74.204.50.67 20001 +XRF023 141.75.245.225 20001 +XRF025 63.133.189.2 20001 +XRF026 139.13.100.34 20001 +XRF027 194.116.29.66 20001 +XRF028 193.190.240.228 20001 +XRF031 83.241.141.245 20001 +XRF033 46.226.178.81 20001 +XRF038 66.6.171.228 20001 +XRF044 82.1.185.173 20001 +XRF055 95.110.229.195 20001 +XRF069 91.142.221.75 20001 +XRF073 147.102.7.34 20001 +XRF090 46.37.185.184 20001 +XRF099 76.71.89.114 20001 +XRF119 192.210.137.151 20001 +XRF121 192.254.139.186 20001 +XRF123 213.126.90.100 20001 +XRF310 199.167.193.147 20001 +XRF333 37.187.103.98 20001 +XRF353 94.173.206.53 20001 +XRF444 71.40.84.59 20001 +XRF555 199.167.193.205 20001 +XRF719 199.227.117.121 20001 +XRF727 108.33.72.83 20001 +XRF777 62.167.15.53 20001 +XRF780 96.53.97.22 20001 +XRF858 198.57.255.30 20001 +XRF901 199.167.193.219 20001 +XRF905 199.212.121.20 20001 diff --git a/proc_g2_ircddb_dtmfs.sh b/proc_g2_ircddb_dtmfs.sh new file mode 100755 index 0000000..79a13c8 --- /dev/null +++ b/proc_g2_ircddb_dtmfs.sh @@ -0,0 +1,150 @@ +#!/bin/bash + +# This script finds files in the /tmp directory +# The files have a name like x_mod_DTMF_NOTIFY, where x is one of 0 1 2 +# 0=A module, +# 1=B module, +# 2=C module +# The contents of these files can be as follows: + +# Example: 73 will unlink local module +# Example: #02102 will link local module to XRF021 B +# Example: D00126 will link local module to DCS001 Z +# Example: *01601 will link local module to REF016 A +# Example: 99 will report status of the link + +# We set this to spaces, it will be set later +LUSER=" " + +# The G2 INTERNAL IP/Port +G2_INT_IP=127.0.0.1 +G2_INT_PORT=19000 + +# This is the callsign of your Gateway, set it correctly +G2= + +cd /tmp +echo started at `date` + +while [ 1 ] +do + for i in `ls ?_mod_DTMF_NOTIFY 2>/dev/null` + do + echo found file $i at `date` + x=${i:0:1} + if [ "$x" = "0" ] ; then + LOCAL_BAND=A + elif [ "$x" = "1" ] ; then + LOCAL_BAND=B + elif [ "$x" = "2" ] ; then + LOCAL_BAND=C + else + LOCAL_BAND=garbage + fi + + if [[ "$LOCAL_BAND" == "garbage" ]] + then + echo garbage value for local band + else + CMD=`head -n 1 $i 2>/dev/null` + LUSER=`tail -n 1 $i 2>/dev/null` + echo "... with these contents: " $CMD " " $LUSER + if [ "$CMD" = "73" ] ; then + echo Unlinking local band $LOCAL_BAND requested by $LUSER + /usr/local/bin/g2link_test ${G2_INT_IP} ${G2_INT_PORT} "" $G2 ${LOCAL_BAND} 20 1 "$LUSER" " U" >/dev/null 2>&1 + echo + elif [ "$CMD" = "99" ] ; then + echo Link Status on local band $LOCAL_BAND requested by $LUSER + /usr/local/bin/g2link_test ${G2_INT_IP} ${G2_INT_PORT} "" $G2 ${LOCAL_BAND} 20 1 "$LUSER" " I" >/dev/null 2>&1 + echo + else + LEN=${#CMD} + if [ "$LEN" = "6" ] ; then + PFX=${CMD:0:1} + REMOTE_NODE=${CMD:1:3} + REMOTE_BAND=${CMD:4:2} + + if [ "$REMOTE_BAND" = "01" ] ; then + REMOTE_BAND=A + elif [ "$REMOTE_BAND" = "02" ] ; then + REMOTE_BAND=B + elif [ "$REMOTE_BAND" = "03" ] ; then + REMOTE_BAND=C + elif [ "$REMOTE_BAND" = "04" ] ; then + REMOTE_BAND=D + elif [ "$REMOTE_BAND" = "05" ] ; then + REMOTE_BAND=E + elif [ "$REMOTE_BAND" = "06" ] ; then + REMOTE_BAND=F + elif [ "$REMOTE_BAND" = "07" ] ; then + REMOTE_BAND=G + elif [ "$REMOTE_BAND" = "08" ] ; then + REMOTE_BAND=H + elif [ "$REMOTE_BAND" = "09" ] ; then + REMOTE_BAND=I + elif [ "$REMOTE_BAND" = "10" ] ; then + REMOTE_BAND=J + elif [ "$REMOTE_BAND" = "11" ] ; then + REMOTE_BAND=K + elif [ "$REMOTE_BAND" = "12" ] ; then + REMOTE_BAND=L + elif [ "$REMOTE_BAND" = "13" ] ; then + REMOTE_BAND=M + elif [ "$REMOTE_BAND" = "14" ] ; then + REMOTE_BAND=N + elif [ "$REMOTE_BAND" = "15" ] ; then + REMOTE_BAND=O + elif [ "$REMOTE_BAND" = "16" ] ; then + REMOTE_BAND=P + elif [ "$REMOTE_BAND" = "17" ] ; then + REMOTE_BAND=Q + elif [ "$REMOTE_BAND" = "18" ] ; then + REMOTE_BAND=R + elif [ "$REMOTE_BAND" = "19" ] ; then + REMOTE_BAND=S + elif [ "$REMOTE_BAND" = "20" ] ; then + REMOTE_BAND=T + elif [ "$REMOTE_BAND" = "21" ] ; then + REMOTE_BAND=U + elif [ "$REMOTE_BAND" = "22" ] ; then + REMOTE_BAND=V + elif [ "$REMOTE_BAND" = "23" ] ; then + REMOTE_BAND=W + elif [ "$REMOTE_BAND" = "24" ] ; then + REMOTE_BAND=X + elif [ "$REMOTE_BAND" = "25" ] ; then + REMOTE_BAND=Y + elif [ "$REMOTE_BAND" = "26" ] ; then + REMOTE_BAND=Z + else + REMOTE_BAND=Z + fi + + if [ "$PFX" = "#" ] ; then + RMT=XRF + elif [ "$PFX" = "D" ] ; then + RMT=DCS + elif [ "$PFX" = "*" ] ; then + RMT=REF + else + RMT=garbage + fi + + if [[ "$RMT" == "garbage" ]] + then + echo garbage value in prefix + else + echo linking local band $LOCAL_BAND to remote node ${RMT}${REMOTE_NODE} $REMOTE_BAND requested by $LUSER + /usr/local/bin/g2link_test ${G2_INT_IP} ${G2_INT_PORT} "" $G2 ${LOCAL_BAND} 20 1 "$LUSER" ${RMT}${REMOTE_NODE}${REMOTE_BAND}L >/dev/null 2>&1 + echo + fi + fi + fi + fi + rm -f $i + done + sleep 3 +done + +exit 0 + diff --git a/service.dvap_rptr b/service.dvap_rptr new file mode 100755 index 0000000..d3f5e47 --- /dev/null +++ b/service.dvap_rptr @@ -0,0 +1,60 @@ +#!/bin/sh +# Start/stop the dstar dvap_rptr +# +### BEGIN INIT INFO +# Provides: dvap_rptr +# Required-Start: $null +# Required-Stop: $null +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: dvap_rptr +# Description: dvap_rptr +### END INIT INFO + +. /lib/lsb/init-functions + +DVAP_RPTR_EXE=/usr/local/bin/dvap_rptr +DVAP_RPTR_CFG=/usr/local/etc/dvap_rptr.cfg +DVAP_RPTR_LOG-/var/log/dvap_rptr.log + +start() +{ + status_of_proc dvap_rptr >/dev/null + RETVAL=$? + if [ "$RETVAL" = "0" ] ; then + echo "dvap_rptr already running" + else + echo -n "Starting dvap_rptr" + start-stop-daemon --exec $DVAP_RPTR_EXE -S $DVAP_RPTR_CFG > $DVAP_RPTR_LOG 2>&1 & + log_end_msg $? + fi + +} + +stop() { + start-stop-daemon --exec $DVAP_RPTR_EXE --retry 5 -K +} + +case "$1" in +start) + start + ;; +stop) + stop + ;; +restart) + stop + start + ;; +status) + echo -n "dvap_rptr " + status_of_proc dvap_rptr + ;; + +*) log_action_msg "Usage: /etc/init.d/dvap_rptr {start|stop|restart|status}" + exit 2 + ;; + +esac +exit 0 + diff --git a/service.g2_ircddb b/service.g2_ircddb new file mode 100755 index 0000000..cde8124 --- /dev/null +++ b/service.g2_ircddb @@ -0,0 +1,60 @@ +#!/bin/sh +# Start/stop g2_ircddb +# +### BEGIN INIT INFO +# Provides: g2_ircddb +# Required-Start: $null +# Required-Stop: $null +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: g2_ircddb +# Description: g2_ircddb +### END INIT INFO + +. /lib/lsb/init-functions +IRCDDB_EXE=/usr/local/bin/g2_ircddb +IRCDDB_CFG=/usr/local/etc/g2_ircddb.cfg +IRCDDB_LOG=/var/log/g2_ircddb.log + +start() { + + status_of_proc g2_ircddb >/dev/null + RETVAL=$? + if [ "$RETVAL" = "0" ] ; then + echo "g2_ircddb already running" + else + echo -n "Starting g2_ircddb" + start-stop-daemon --exec $IRCDDB_EXE -S $IRCDDB_CFG > $IRCDDB_LOG 2>&1 & + log_end_msg $? + fi + +} + +stop() { + start-stop-daemon --exec $IRCDDB_EXE --retry 5 -K +} + + +case "$1" in +start) + start + ;; +stop) + stop + ;; +restart) + stop + start + ;; + +status) + echo -n "g2_ircddb " + status_of_proc g2_ircddb + ;; + +*) log_action_msg "Usage: /etc/init.d/g2_ircddb {start|stop|restart|status}" + exit 2 + ;; +esac +exit 0 + diff --git a/service.g2_link b/service.g2_link new file mode 100755 index 0000000..ff2c6e2 --- /dev/null +++ b/service.g2_link @@ -0,0 +1,61 @@ + +#!/bin/sh +# Start/stop g2_link +# +### BEGIN INIT INFO +# Provides: g2_link +# Required-Start: $null +# Required-Stop: $null +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: g2_link +# Description: g2_link +### END INIT INFO + +. /lib/lsb/init-functions + +G2_LINK_EXE=/usr/local/bin/g2_link +G2_LINK_CFG=/usr/local/etc/g2_link.cfg +G2_LINK_LOG=/var/log/g2_link.log + +start() { + + status_of_proc g2_link >/dev/null + RETVAL=$? + if [ "$RETVAL" = "0" ] ; then + echo "g2_link already running" + else + echo -n "Starting g2_link" + start-stop-daemon --exec $G2_LINK_EXE -S $G2_LINK_CFG > $G2_LINK_LOG 2>&1 & + log_end_msg $? + fi + +} + +stop() { + start-stop-daemon --exec $G2_LINK_EXE --retry 5 -K +} + +case "$1" in +start) + start + ;; +stop) + stop + ;; +restart) + stop + start + ;; + +status) + echo -n "g2_link " + status_of_proc g2_link + ;; + +*) log_action_msg "Usage: /etc/init.d/g2_link {start|stop|restart|status}" + exit 2 + ;; +esac +exit 0 + diff --git a/service.proc_g2_ircddb_dtmfs b/service.proc_g2_ircddb_dtmfs new file mode 100755 index 0000000..6ea3be0 --- /dev/null +++ b/service.proc_g2_ircddb_dtmfs @@ -0,0 +1,63 @@ +#!/bin/sh +# proc_g2_ircddb_dtmfs +# + +### BEGIN INIT INFO +# Provides: proc_g2_ircddb_dtmfs +# Required-Start: $syslog $network +# Required-Stop: $syslog $network +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: proc_g2_ircddb_dtmfs +# Description: proc_g2_ircddb_dtmfs +### END INIT INFO + +. /lib/lsb/init-functions + +SHFILE=/usr/local/bin/proc_g2_ircddb_dtmfs.sh +PIDFILE=/usr/local/bin/proc_g2_ircddb_dtmfs.sh_pidfile +LOG=/var/log/proc_g2_ircddb_dtmfs.log + +start() { + + status_of_proc $SHFILE >/dev/null + RETVAL=$? + if [ "$RETVAL" = "0" ] ; then + echo $SHFILE already running + else + echo -n Starting $SHFILE + start-stop-daemon --make-pidfile --pidfile $PIDFILE --exec $SHFILE -S > $LOG 2>&1 & + log_end_msg $? + fi + +} + +stop() { + start-stop-daemon --pidfile $PIDFILE --retry 5 -K + /bin/rm -rf $PIDFILE +} + + +case "$1" in +start) + start + ;; +stop) + stop + ;; +restart) + stop + start + ;; + +status) + echo -n $SHFILE + status_of_proc $SHFILE + ;; + +*) log_action_msg "Usage: /etc/init.d/proc_g2_ircddb_dtmfs {start|stop|restart|status}" + exit 2 + ;; +esac +exit 0 +