commit 3f480a923faa5959330caf0ab04c527067571972 Author: ac2ie Date: Sat Nov 30 18:32:26 2013 -0500 initial commit 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 0000000..ba1910a Binary files /dev/null and b/announce/already_linked.dat differ diff --git a/announce/already_unlinked.dat b/announce/already_unlinked.dat new file mode 100644 index 0000000..88c7035 Binary files /dev/null and b/announce/already_unlinked.dat differ diff --git a/announce/failed_linked.dat b/announce/failed_linked.dat new file mode 100644 index 0000000..878ec34 Binary files /dev/null and b/announce/failed_linked.dat differ diff --git a/announce/id.dat b/announce/id.dat new file mode 100644 index 0000000..b2151dc Binary files /dev/null and b/announce/id.dat differ diff --git a/announce/linked.dat b/announce/linked.dat new file mode 100644 index 0000000..21047f8 Binary files /dev/null and b/announce/linked.dat differ diff --git a/announce/unlinked.dat b/announce/unlinked.dat new file mode 100644 index 0000000..b2151dc Binary files /dev/null and b/announce/unlinked.dat differ 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 +