1 /* Copyright (C) 2002 GFRN systems
2 
3    This program is free software; you can redistribute it and/or
4    modify it under the terms of the GNU General Public License as
5    published by the Free Software Foundation; either version 2 of the
6    License, or (at your option) any later version.
7 
8    This program is distributed in the hope that it will be useful, but
9    WITHOUT ANY WARRANTY; without even the implied warranty of
10    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11    See the GNU General Public License for more details.
12 
13    You should have received a copy of the GNU General Public License
14    along with this program; if not, write to the Free Software
15    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
16    02111-1307, USA.
17 
18    The latest version of this program may be found at
19    http://CQiNet.sourceforge.net
20 
21    $Log: conference.c,v $
22    Revision 1.69  2012/12/09 18:49:48  wb6ymh
23    1. Removed old chan_rptdir style Asterisk support.
24    2. Don't set bDynamicConf in RtpInit. Prevents conference from being
25       deleted when last user logs out.
26    3. Added support for new configuration variable ConfCmdEnable.
27    4. Added -q (quiet) switch support to .users command.
28    5. Corrected memset() argument order n StartCmdHandler.
29    6. Added G.726 support to the .users command.
30 
31    Revision 1.68  2010/11/20 15:45:59  wb6ymh
32    1. Added ability to create an RTP conference separate from the speak freely
33    conference.
34    2. Corrected a bug which disabled the command port (tlbcmd interface) when
35    EchoLink isn't enabled (EchoLinkEnable = 0).
36    3. Modified Send2() to take a boolean rather than a port number to more
37    correctly reflect what's happening internally.
38    4. Added (untested) -n command line switch to the .connect command to create
39    a "nailed up" connection.
40    5. Added -7 command line switch to the .connect command to select the G.726
41    codec (rtp connections only).
42    Revision 1.67  2010/01/17 16:45:29  wb6ymh
43    1. Corrected a bug that caused the user count to get out of sync when an
44    rtcp timeout occured on a permanent connection.
45 
46    2. Corrected a bug which caused a timeout by a conference user to be logged
47    multiple times (once per packet).  Added a log entry when the long winded
48    operator finally unkeys.
49 
50    3. Corrected a bug that caused the user count to get out of sync when an
51    a node was disconnected and then reconnected quickly.
52 
53    4. Cleaned up announcements generated by the connect command.  The node's
54    callsign is now announced rather than it's node number or IP address.
55 
56    Revision 1.66  2009/10/02 16:33:46  wb6ymh
57    1. Removed code added in 0.44 that sent "Bye" packets to stations which were
58       in the disconnecting state when anything is received from them.  IRLP
59       reflectors reflect the user's "Bye" packet which caused an infinite
60       bandwith consuming loop.  Extra "Bye" packets are now only sent in reply
61       to valid RTCP packets.
62    2. Added code to force the compression type for Echolink clients to GSM.
63    3. Removed code that set LastHeard for RTP packets, LastHeard should only
64       be set for RTCP packets.  Ensures we can disconnect from nodes sending
65       an constant audio stream.
66 
67    Revision 1.65  2009/09/15 16:59:30  wb6ymh
68    Added support for -x (mute text) to the .mute and .unmute commands.
69 
70    Revision 1.64  2009/09/14 21:42:27  wb6ymh
71    Corrected compile for thebridge (#ifdef'ed out call to GetConfigVarsPtr in
72    CmdSet).
73 
74    Revision 1.63  2009/09/13 20:06:56  wb6ymh
75    1. Replaced most usages of bInConf with new and better named flag bConnected.
76    2. Modified the way the connection node count is handled so connections that
77       nodes that are in the process of connecting or disconnecting are not
78       included in the total.
79    3. Modified code to send "Bye" packets to stations which are in the
80       disconnecting state when anything is received from them.  This is to
81       cover the case where the initial "Bye" packet is lost.
82    4. Modified conferencing code to prevent doubling warning message from being
83       sent to full duplex stations.
84    5. Modified the StartCmdHandler to take a callsign argument for logging.
85    6. Added code to set bIRLP, bRepeater and bConf based on the first 3
86       characters of the "callsign" for connections running the speak freely
87    7. Added support for the -T switch to the .users command.
88    8. Added support for the -s switch to the .users command.
89    9. Added 'I' user attribute for stations which have been Isolated from the
90       packet level conference.
91    10. Modified CmdSet to call GetConfigVarsPtr when the first attempt to locate
92        a configuration variable fails to support configuration variables
93        that only apply to VoIP ports.
94    11. Add CmdRxLevel.
95    12. Enabled Saving of LastCmdText for all users to ease debug.
96 
97    Revision 1.62  2009/06/30 23:14:49  wb6ymh
98    Added -f switch to the .connect command usage message.
99 
100    Revision 1.61  2009/05/26 19:31:28  wb6ymh
101    Modified SendAudioFromFile to clear ssrc only when absolutely necessary.
102 
103    Revision 1.60  2009/05/22 14:36:04  wb6ymh
104    Set new user's CompressType from conference's CompressType rather than
105    assuming GSM.
106 
107    Revision 1.59  2009/04/04 18:10:16  wb6ymh
108    1. Added code to save the client's "tool" string if present.
109    2. Added -v command line switch to the .users command to display the user's
110       "tool" and (hopefully) Version information.
111 
112    Revision 1.58  2009/03/29 16:29:53  wb6ymh
113    Added support for LogIPAddresses to optionally log station's IP address on
114    connects and disconnects contributed by va3jss.
115 
116    Revision 1.57  2009/02/14 14:59:44  wb6ymh
117    1. Modified RTCP_Rx to ignore welcome files with an extension of .wav.
118    2. Corrected crhas in ConferenceCmd when called with a NULL ClientInfo.  This
119       occurs when EchoLinkEnabled = 0.
120 
121    Revision 1.56  2009/01/04 15:50:10  wb6ymh
122    Added some ifdef's to correct thebridge builds broken by thelinkbox 0.83
123    commits.
124 
125    Revision 1.55  2009/01/03 22:43:31  wb6ymh
126    1. Added APRS-IS support.
127    2. Added entries for CmdPCM and CmdSendAX25Beacon to the command table.
128 
129    Revision 1.54  2008/07/26 18:05:22  wb6ymh
130    1. Modified Asterisk code for compatibility with chan_rtpdir version 0.5.
131       NB: chan_rptdir version 0.2 is no longer supported.
132    2. Added the ability for Asterisk nodes to initiate connections to Echolink
133       nodes by entering the 6 digit EchoLink node number.  Leading zeros must be
134       entered for 4 and 5 digit nodes.
135    3. Added the ability for Asterisk nodes to disconnect the last EchoLink node by
136       connected by entering "000000".
137    4. Added -b, -c and -t arguments to the .users command.  The -b switch
138       suppresses the display of the user's attributes.  The -c switch displays
139       the amount of time each user has been connected.  The -t switch displays
140       the time since the user last transmitted.
141 
142    Revision 1.53  2008/07/23 14:48:45  wb6ymh
143    1. Added support for full duplex operation via the conference.  When full
144    2. Added the ability to dynamically control the Asterisk connection via the
145       the .connect, .disconnect and .kick commands and the AsteriskEnable
146       configuration variable.
147    3. Replaced the AllowADPCM configuration variable with CompressionType.
148    4. Added event hook support for Asterisk connections.
149 
150    Revision 1.52  2008/07/16 22:38:25  wb6ymh
151    1. Added (untested) support for full duplex clients (currently just thelinkbox).
152    2. Added code to enable the stock EchoLink client's DTMF pad when the
153       configuration file variable EnableRemoteDTMF is set to 1.
154    3. Modified logic to set bSendSSRC whenever an rtp packet is received with a
155       nonzero ssrc.  (memory hint: very old EchoLink clients crashed when they
156       received a nonzero ssrc.. they are probably long since dead an buried now,
157       but who really knows?)
158    4. Modified code to prevent 5 bytes of garbage from being written to the end
159       of saved .info files.
160    5. Modified code to prevent station list received from other conferences from
161       being  saved as info and in .info files.
162    6. Added support for Asterisk's new chan_rtpdir driver.
163 
164    Revision 1.51  2008/06/26 20:24:35  wb6ymh
165    1. Modified ConferenceCmd to properly handle a ';' within a quoted string.
166    2. Added .frequency command.
167    3. Added GetCmdOptions.
168 
169    Revision 1.50  2008/06/25 17:33:06  wb6ymh
170    Cleaned up messages generated by .disconnect command.
171 
172    Revision 1.49  2008/06/25 15:23:14  wb6ymh
173    Added support for ".disconnect ." - disconnect the station currently talking.
174 
175    Revision 1.48  2008/06/15 13:34:53  wb6ymh
176    Corrected compile errors under Windoze.
177 
178    Revision 1.47  2008/05/19 13:25:38  wb6ymh
179    Modified ConferenceCmd to make a copy of the command line before processing
180    the command.  Previously commands that generated a lot of output (list)
181    could overwrite the command line causing odd log entries (to say the least).
182 
183    Revision 1.46  2008/05/18 13:58:30  wb6ymh
184    1. Modified RTP_Data to record locally generated chat traffic even when the
185       conference is empty.
186 
187    Revision 1.45  2008/05/14 18:31:23  wb6ymh
188    Added CmdTxOffset, CmdRxFrequency, CmdRxTone, CmdTxTone.
189 
190    Revision 1.44  2008/04/11 18:33:49  wb6ymh
191    1. Added code to send an chat event to the event hook when a text chat message
192       is received from a user.
193 
194    2. Added code to send an sent_chat event to the event hook when a chat message
195       is sent.
196 
197    3. Deleted the obsolete .dtmfgen command.
198 
199    Revision 1.43  2008/03/21 14:47:58  wb6ymh
200    Modified CmdSet to allow assignments without spaces around the '='.
201 
202    Revision 1.42  2008/03/09 17:14:22  wb6ymh
203    1. Modified ConferenceCmd to allow a dot prefix when multiple commands
204      are entered on one line.
205    2. Corrected a bug in ConferenceCmd that caused the first command to be
206       executed multiple times when another command wasn't found or was partially
207       matched.
208 
209    Revision 1.41  2008/03/08 06:52:41  wb6ymh
210    1. audio.h -> linkbox.h.
211    2. Corrected "Callsigns" for command and chat ports when compiled as part
212       of thelinkbox.
213    3. Moved where EndPoint(EVENT_INIT) is called so pCC->Callsign has been
214       initialized.
215    4. Modified RTCP_Rx to always process SDES packets for RTP as well as
216       EchoLink.  Allows nodes to be named by callsign rather than ip addresses
217       in offgrid networks.
218    5. Removed references to the obsolete RxMap and TxMap variables.
219    6. Modified CmdInfo to suppress disable of "Currently displaying" unless
220       ShowStatusInInfo is enabled.
221    7. Modified ConferenceCmd to provide support multiple commands on one line
222       seperated by ';'.
223 
224    Revision 1.40  2008/03/06 14:39:32  wb6ymh
225    Corrected initialization of pPDat in SendAudioFromFile.  Corrects crashs
226    playing recordings for non-EchoLink clients.
227 
228    Revision 1.39  2008/02/29 16:59:22  wb6ymh
229    1. Modified all recording related code to reference pMasterCS rather than
230       pCS.  The recording flag, file handle, etc is only present in the master
231       conference.
232    2. Changed assumed codec type for new SF and RTP connections from ADPCM to
233       GSM.
234    3. Modified code to write *all* packets to recording file.  When a protocol
235       conversion between SF and RTP/Echolink occurs RTP_Data is called once,
236       but multiple packets are generated.
237    4. Added code to display a usage message when the .monitor command is run
238       without arguments.
239    5. Modified the users command to display an 'a' user attribute for ADPCM users.
240 
241    Revision 1.38  2008/02/28 23:08:35  wb6ymh
242    1. Corrected bug in Play4 (without -u option) command introduced in version
243       0.90.  Version 0.90 -> 0.92 were unable to play a file for all users.
244    2. Fairly massive changes to fileplay back to be able to support .play4 -u
245       when the selected user is an RTP or SF client.  ConvertProtocol is now
246       passed a pointer to the ProtoData structure so more than a single
247       protocol conversion may be active at one time.
248 
249    Revision 1.37  2008/02/28 01:02:40  wb6ymh
250    1. Corrected a bug introduced by the last changes to AuthorizedClient
251       which broke EchoLink connections to -L and -R stations.
252 
253    2. Fixed typo in the .connect command which caused duplicate dynamic
254       conferences to be created.
255 
256    Revision 1.36  2008/02/26 18:00:59  wb6ymh
257    1.  Added dmalloc support.
258    2.  Modified GenBye to set the protocol version correctly for SF and RTP.
259    3.  Modified CreateServerClient to prevent sockets for EchoLink ports from
260        being opened unless EchoLink is enabled.
261    4.  Corrected bugs created in CreateServerClient when SFBind2IP was added.
262    5.  Modified code to initialize command line client using SF conference when
263        EchoLink is disabled.
264    6.  Modified RTP_Data to only forward audio packet to clients running the same
265        codec as client speaking.
266    7.  Removed newline from playbackcomplete event.
267    8.  Clear bPlayWhenFree flag in PlayBackComplete.  Fixes crash which occured
268        when a client disconnected while a fileplay back was pending.
269    10. Modified the .connect command to default to the port specified by the
270        SF_Port configuration variable rather than 2074 for SF and RTP connections.
271    11. Corrected a bug in the .connect command that prevented a connection to
272        a node by IP address from being reestablished immediately after it had
273        been disconnected.
274    12. Modified Send2 to avoid using EchoLink sockets when Echolink is not enabled.
275    13. Modified GetPacketType to return PKT_TYPE_IGNORE for SF and RTP cleints
276        with unknown codecs rather than kicking the connection.
277    14. Corrected a bug in AuthorizedClient that prevented -R or -L stations from
278        being .allowed to connect via RTP or SF.
279    15. Modified AuthorizedClient to prevent callsign from RTCP packet from being
280        replaced with callsign from ACL unless needed. (Prevent loss of -R or -L
281        from the callsign)
282    16. Added ConferenceCleanup.
283 
284    Revision 1.35  2008/02/09 17:23:57  wb6ymh
285    Add conditional compile around CallBacksRegistered, SigChilds and
286    ChildExits to fix Windoze build.
287 
288    Revision 1.34  2008/02/09 17:05:43  wb6ymh
289    Removed left over debug printf in RTP_Data.
290 
291    Revision 1.33  2008/02/09 17:03:12  wb6ymh
292    1. Added support for a new configuration variable ShowStatusInInfo to
293       enable EchoIRLP nodes to be configured to automatically show the name of
294       the current connection in the info field on the EchoLink directory servers.
295 
296    2. Added Echolink firewall mitigation code courtesy of Johnathan K1RFD.
297       This code is invoked by the .connect command to assist with connections to
298       version 2.0 and above EchoLink nodes located behind unconfigured firewalls.
299 
300    3. Added support for SFBind2IP to allow Speak Freely conferences to be bound
301       to a different IP address than EchoLink conferences.
302 
303    4. Added support for sysop and admin private chats.
304 
305    5. Modified the behavour for handling unknown EchoLink clients: ignore
306       unknown EchoLink users but to send a "bye" packets to banned users.
307 
308    Revision 1.32  2008/01/13 17:09:18  wb6ymh
309    1. Replaced hardcoded phoney EchoLink number that is used when thelinkbox
310       is not logged into the EchoLink network with a crc of the user's callsign.
311       Fixes EchoLink and RTP connections between two nodes when neither node
312       are logged into EchoLink.
313 
314    2. Modified the Play4 command so file playback is also sent to the currently
315       selected port.
316 
317    Revision 1.31  2007/12/27 17:59:25  wb6ymh
318    1. Added "port" command.
319    2. Added code send the "NAME" RTCP field to SF and RTP clients.  The
320       default value is ConferenceCall unless overridden by the new configuration
321       variable FullName.  The CNAME field is now set to "CALLSIGN" for
322       compatibility, previously it was set to ConferenceCall.
323 
324    Revision 1.30  2007/12/14 23:13:59  wb6ymh
325    1. Added avl tree of "conferences" indexed by audio port number.
326    2. Modified DeleteConf to clear pAudio->p and pControl->p before calling
327       DeleteClient.  Otherwise the conference is free'ed three times.
328    3. Modified DeleteConf to only destroy local ConfTrees.
329    4. Eliminiated ControlPort member of ConfServer. According to the RFC
330       the audio port must be an even port and the control port must be the
331       next port.
332    5. Modified code to use the audio port number from the "conference" instead
333       of using hard coded values.
334    6. Modified RTP_Data to ignore closing connections that return
335       PKT_TYPE_UNSUPPROTED as the protocol type.
336    7. Modified RTCP_Handler to delete dynamic conferences when the user count
337       becomes zero.
338    8. Modified the .set command to call the new AccessFunc to get a display
339       string for variables with a AccessFunc.
340    9. Added -m command line parameter to the .connect command to establish
341       connections in monitor mode.
342    10. Added -p command line parameter to the connect command to allow
343        connections to arbitrary port numbers.
344    11. Modified the .connect command to create a dynamic conference on the fly
345        to handle clients at nonstandard port numbers.
346    12. Modified the .connect command to initialize the new client's RxMap
347        and TxMap variables from the new VoipOutMap configuration variable
348        rather than the current port number.
349    13. Modified Send2 to use pCS->pControl->Socket or pCS->pAudio->Socket
350        rather than a bit if tree conditioned on hard coded port numbers.
351 
352    Revision 1.29  2007/12/14 22:28:32  wb6ymh
353    Modified code so data is always sent from the appropriate port. Previously
354    IRLP data (after conversion to EchoLink format) was sent to the EchoLink
355    ports, but from the IRLP ports.  Normally this is not a problem, but if
356    a user's firewall is using dynamic rules created by outbound packets then
357    the these packets would be lost.  Effect is some EchoLink clients can not
358    hear IRLP stations via integrated IRLP/EchoLink conferences.
359 
360    Revision 1.28  2007/12/01 00:59:09  wb6ymh
361    1. Fixed bug in CmdSet, some pVars->var_ptr's weren't replaced with
362       var_ptr.  Caused crashes when displaying configuration variables
363       that are now class members.
364 
365    Revision 1.27  2007/11/26 14:51:15  wb6ymh
366    1. Modified CmdSet to handle configuration variables that are now class
367       members.
368    2. Added code to CmdConnect to set new RxMap and TxMap variables from
369       CurrentPort (thelinkbox builds).
370    3. Added link and unlink commands to commandtable.
371 
372    Revision 1.26  2007/07/02 13:55:07  wb6ymh
373    Modified the logic in GenSDES to suppress the conference signature when
374    running as a user node and the number of external connections is one.
375 
376    Revision 1.25  2007/06/29 15:51:02  wb6ymh
377    Modified RTCP_Rx to send a "bye" packet to Banned stations.
378    Previously banned stations were just ignored. This caused problems when
379    a conference was banned because thebridge is designed to continue to attempt
380    to connect forever unless stopped manually.  Sending a "bye" packet stops
381    the connection attempt (and prevent log files from filling with
382    "Ignoring unauthorized user" messages).
383 
384    Revision 1.24  2007/06/27 21:04:51  wb6ymh
385    1.  Added hooks for tbdQt by Scott KI4LKF.
386    2.  Added lookup command contributed by Scott KI4LKF.
387    3.  Moved PacketType enum to conference.h.
388    4.  Added set command.
389    5.  Added -r and -a arguments to the connect command.
390    6.  Added an "last" option to the disconnect command.
391    7.  Added a separate interface for the EchoLink text mode via tbdchat.
392    8.  Modified GenSDES to only send the conference signature when thebridge is
393        configured as a conference server.
394    9.  Renamed the "quit" command to "quickexit" to prevent accidents.
395    10. Modified GetPacketType to allow duplicate commands to be entered via local
396        chat or command ports.
397 
398    Revision 1.23  2006/08/05 23:30:04  wb6ymh
399    Corrected warnings and errors with GCC 4.x.x.
400 
401    Revision 1.22  2005/01/08 23:57:53  wb6ymh
402    Give priority to repeater attribute over conference attribute in
403    autolurking code.
404 
405    Revision 1.21  2004/11/29 01:10:00  wb6ymh
406    1. Added text messaging support to the command interface. The new ".chat"
407       command is used to enable and disable chat mode for compatibility with
408       existing scripts.
409    2. Added AVRS support for reporting the repeater or link location, frequency,
410       status and other parameters to EchoLink's server.
411    3. Added .refresh command.
412    4. Added a "status" option to the .busy command.
413    5. Modified RTP_Data to keep it from dumping Speak Freely clients on that
414       we started when the a packet with the wrong compression format is received.
415       This prevents problems staying connected to reflectors with misconfigured
416       clients.
417    6. Added patch from KF7FLY to improve the ability of EchoIRLP to track refused
418       outbound connections.
419    7. Removed code that attempted to deal with an IP address changes for
420       logged in clients.  The code had bugs and was just about impossible to
421       test.  If your ISP randomly forces you to change your IP address it's
422       time to find a new ISP!  This code caused thebridge to crash if a logged
423       in user's IP address did change.
424    8. Modified .connect command to add ability to make outbound connections
425       to Speak Freely clients.
426    9. Modified AuthorizedClient() so it doesn't attempt to connect to the
427       EchoLink servers when LoginInterval is zero.
428    10.Corrected bug in statistics gathering for the RxMBytesAllConf variable.
429 
430    Revision 1.20  2004/05/29 17:18:04  wb6ymh
431    1. Added a -p option to the .mute and .unmute commands to allow a station to be
432       muted or unmuted more persistently.  Normally when a station is explicitly
433       muted or unmuted the action applies current connection only.  If the station
434       disconnects and then reconnects he will return to the default mute state for
435       his station class.  The -p option causes the stations mute state to presist
436       across connections and disconnections as long as the station's callsign
437       remains in the directory cache.  This feature may be useful in certain
438       unusual circumstances when the a station class is muted, but there are
439       certain exceptions.
440 
441    2. Corrected an infinite loop that occured when a "play4 -u ..." command was
442       issued when a file was already playing for the specified user.
443 
444    3. Corrected a bug that caused the command ".mute -u" to mute conferences as
445       well as PC users.
446 
447    4. Removed the code added in 0.70 that prevented the conference from
448       connecting to itself.  This feature caused problems for the EchoIRLP project.
449 
450    5. Corrected bug which caused the play4 pesudo user to timeout when the
451       BlabOffTimer was enabled.
452 
453    6. Added code to update the IP address of persistent clients when the client's
454       IP address changes in the directory.
455 
456    Revision 1.19  2003/09/09 21:03:12  wb6ymh
457    1. Changed the automute and autolurk logic to accomodate new EchoLink clients
458       which can be repeaters or links *as well* as conferences.  The conference
459       attribute is now the highest priority.  If conferences are muted, but not
460       links and a user is both a conference and a link he will now be muted.
461 
462    2. Added a "-e" flag to the .mute and .unmute flags to allow EchoLink
463       conferences to be treated separately from tbd conferences.  The original
464       -c flag now effects tbd conferences only.
465 
466    3. Corrected infinite loop that occurred on some platforms when the command
467       ".mute -?" (or any other undefined flag) was entered.
468 
469    4. Corrected a crash that occurred when an user attempted to connect to a
470       conference when it was busy or at the configured maximum number of users.
471 
472    Revision 1.18  2003/09/08 03:50:09  wb6ymh
473    Moved auto mute code to *after* GetCall() in RTCP_RX(), helps to check
474    flags after they are set!
475 
476    Revision 1.17  2003/09/07 15:12:49  wb6ymh
477    1.  Added calls to EventHook() when users connect, disconnect, issued
478        undefined commands or when file playback completes.
479    2.  Modified CmdListAdd() so the bulletin list order does not change when
480        thebridge was restarted.
481    3.  Revised the way the callsign of the station talking was sent from one
482        conference to the next.  Earlier versions used a (malformed) private
483        extension "txt" in the SDES info, now we just dynamically change the
484        CNAME field.  This is simpler and more importantly compatible with newer
485        EchoLink clients that support multiconferencing.
486    4.  Added CRC based duplicate text message checking.  The previous scheme
487        was only partially effective for simple closed loops.
488    5.  Added local command line interface to command processor.
489    6.  Corrected a bug that caused Bind2IP to fail randomly on FreeBSD.
490    7.  Added a new configuration variable SF_ReplyPort that may be used to
491        configure thebridge to talk to a Speak Freely or RTP client on the same
492        host.
493    8.  Reworked SendSDES2Conf() -> SendSDES2All() now handles all protocols plus
494        now obsolete SDES w/ "txt" extension when needed.
495    9.  Replaced all ERROR_CODE2MSG with Err2String().
496    10. Modified code to pass local users SSRC on unmodified.  Previously local
497        user's SSRC was changed to the SSRC of the conference for audio loop
498        detection.
499    11. Added a ".play4" command that allows a recorded file to be played everyone
500        or a specified user.
501    12. Added -r, -c, -u, -s, -t, and -a options to the .mute and .unmute commands
502        to mute and unmute groups of stations with one command.  New stations
503        connecting to the conference will be muted automatically when a group
504        mute for their station type is in effect.
505    13. Added ".busy" command to put the conference into a busy state.
506    14. Added support for WelcomeDelay that specifies a delay between when the
507        user connects and when the welcome file starts playing.
508    15. Modified RTCP handler to fully process all packets received.  Previously
509        the user's callsign and name was extracted when the user first connected
510        and never looked at again.  This prevented the dynamic information
511        generated by the new EchoLink client from being displayed from the name
512        field.
513    16. Added support for the "StartupCmd" configuration file variable that
514        provides the ability to execute commands automatically at startup.
515    17. Modified snprintf usage to handle more flavors correctly. (The C99
516        standard defined the behaviour differently than earlier implementations.)d
517    18. Modified GenSDES() to parse "<conference name> (talker)" strings to pass
518        on only the talker to prevent the line from growing at each hop.
519    19. Added returned codes to all commands for command line interface.
520    20. Added carriage returns at the end of all strings generated by commands for
521        the command line interface.
522    21. Added a ".message" command.
523    22. Added the ability to .connect to a node by node number. Numeric arguments
524        that do not contain dots are assumed to be node IDs.
525    23. Added an option to disconnect all users to the ".disconnect" command.
526    24. Added support for configuration variable HelpFile used to replace
527        the builtin help text with a customized version.
528    25. Added code to clear the waiting for response to .quote'ed command state
529        after 30 seconds. Previously this state was not cleared resulting in
530        sysops receiving station lists from connected EchoLink conferences.
531    26. Added the conference name to responses to commands issued with the .quote
532        command so that responses from multiple conferences can be differentiated.
533    27. Added code to prevent the conference from connecting to itself.
534    28. Added code to automatically disconnect stations that send too many
535        duplicate text messages.
536 
537    Revision 1.16  2003/05/28 18:07:49  wb6ymh
538    1. Corrected belch filter bug that was introduced by the addition of the
539       timeout timer feature in 0.51.
540    2. Corrected bug in RTP_Rx() which would could cause memory corruption if
541       recvfrom() returned an error.  Thanks to KD5MU for discovering and
542       reporting this problem by code inspection.
543 
544    Revision 1.15  2003/04/30 21:31:29  wb6ymh
545    1.  TxBytesAllConf, etc to track overall statistics of all conferences and
546        protocols.  Modified LogStats() to display overall statistics.
547    2.  Added support for Bind2IP configuration variable to CreateServerClient().
548    3.  Added filename argument to SaveBadPacket() so packets with different
549        types of errors are saved to different files.
550    4.  Modified RTP_Rx() to add a terminating NULL after string returned by
551        version queries.
552    5.  Added a timeout timer controlled by the BlabOffTimer configuration
553        variable.
554    6.  Added ".monitor" command.
555    7.  Added code to optionally put specified types of stations into autolurk
556        mode upon login.
557    8.  Added code to return stations that had been lurking to lurk mode after
558        AutoLurkTimeout seconds of inactivity.
559    9.  Modified RTCP_RX() to send station list before playing welcome file.
560    10. Corrected a bug in the ACL handling that prevented .allow'ing a EchoLink
561        client by IP address.
562    11. Fixed a bug which sent a verify command to a iLink directory server.
563 
564    Revision 1.14  2003/01/01 19:08:04  wb6ymh
565    Added commands .crash, .quit, and .shutdown.
566 
567    Revision 1.13  2002/12/21 18:47:28  wb6ymh
568    1.  Added the ability to display a new users information (brag) sheet during
569        his first transmission.
570    2.  Added the ability to save user's brag sheets to disk.
571    3.  Added new user .about command to display a specified station's brag sheet
572        on demand.
573    4.  Added support for an test only conference (*ECHOTEST*).
574    5.  Added new hourly log entry showing current bandwidths, amount of traffic
575        sent since boot and uptime.
576    6.  Added code to delete files created by the .test command.
577    7.  Modified the ".mute ." command to mute stations on linked conferences.
578    8.  Added admin .rehash command to reload configuration file remotely.
579    9.  Added reason argument to PlayBackComplete().
580    10. Added admin .quote command to allow commands to be sent to linked
581        conferences.
582    11. Enhanced audio file playback code to pauses between individual
583        tranmissions in the recording with configurable timing.
584    12. Corrected premature playback termination when thebridge is run on (very?)
585        fast computers.
586    13. Implemented EnableDiskCommands configuration file variable as documented,
587        previously it was ignored.
588 
589    Revision 1.12  2002/11/05 05:26:29  wb6ymh
590    Added protection against PktHdr.Len < 0 in SendAudioFromFile.
591 
592    Revision 1.11  2002/11/02 19:04:26  wb6ymh
593    1. Removed BannedStations, SaveBanedList(), LoadBannedList(), and
594       FreeBannedList(). These have been replaced with new ACL routines.
595    2. Added routines to convert between Speak Freely, RTP and EchoLink protocols.
596    3. Added support for a Speak Freely and RTP conference.
597    4. Added code to check connecting clients against ACL and EchoLink directory
598       servers.
599    5. Added code to suppress the first BelchTime milliseconds of transmissions
600       from -L, -R stations and conferences.
601    6. Removed code that transmitted a bye packet to banned stations.  It's
602       better to just ignore the unwelcomed so we don't catch the attention
603       of port scanners.
604    7. Restored full validity checking for RTCP packets.
605    8. Added '*' first character test for detecting conference clients.
606    9. Added the ability for sysops to set the lurking mode of other stations
607       to the .lurk and .delurk commands.
608    10.Added the ability for sysops to suppress the forwarding of chat text
609       to themselfs.
610    11.Added .allow, .belchfilter, and .dns commands.
611    12.Modified the code to allow the help command to be entered without
612       a leading dot.
613    13.Added code to suppress looping of text messages when a conference
614       loop is formed.  BUG: a message introduced by a conference that is
615       not in the loop (a conference on a fork) will still loop.  This
616       will be fixed in a later version.
617    14.Muted stations are now displayed in the station list as muted.
618 
619    Revision 1.10  2002/09/29 16:58:34  wb6ymh
620    1. Added enable and disable options to the .lurk command (sysops only).
621    2. Added .sysop command.
622    3. Added support for PauseTime variable and .pausetime command.
623    4. Added .users command.
624    5. Added code to send a  message to all sysops when a .connect request is
625       refused by the target station.
626    6. Added conference callsign to the beginning of all warning messages so that
627       older versions of the EchoLink client will display it. (Apparently older
628       EchoLink clients suppress text messages which begin with users callsign,
629       newer version require the '>' character as well).
630    7. Added ability to list muted stations to the .mute command.
631    8. Added bMuted flag to conference client structure so text messages from
632       muted stations can be forwarded.  bSWL is no longer used for muting.
633    9. Corrected bug in GenBye that overwrote the src field with the text
634       message.
635    10. Modified TimeLapse() to handle overflows properly.  This bug was causing
636        the bandwidth calculations to be bypassed on occasion.
637    11. Modified code to prevent packets from being forward to stations that
638        don't have the bInConf flag set.
639    12. Added '.' as a shorthand for the station that is talking to the .mute
640        command.
641 
642    Revision 1.9  2002/09/19 22:48:56  wb6ymh
643    1. Modified GetCall() to protect against (invalid) RTCP_SDES blocks that don't
644       terminate with a RTCP_SDES_END item.  This was causing page faults in the
645       Windows version, but didn't appear to effect the *nix versions.
646    2. Modified RTP_Rx() to only forward audio packet to clients with bInConf set.
647 
648    Revision 1.8  2002/09/15 15:03:48  wb6ymh
649    1.  Added new administrator commands .connect and .disconnect.
650    2.  Added additional statistics to the .stats command.
651    3.  Added support for the Echolink client's "Request version Information"
652        packet.
653    4.  Added configuration file variables to set the contents of the banner area.
654    5.  Added configruation file variables to allow current and maximum user
655        counts to added to the location string and conference ID line.
656    6.  Added new user command .stop to allow bulletin playback to be terminated
657        without disconnecting.
658    7.  Added playback time to bulletin list for each entry.
659    8.  Added the callsign of the station that has the floor to the doubling
660        warning.
661    9.  Added timestamps to the recording file at the beginning of each audio
662        segment.
663    10. Made the file format of recordings platform independent by converting all
664        values to network order.
665    11. Changed the code to send SDES packets periodically rather than only in
666        response to a received SDES packet.
667    12. Changed the way the station list works (yet again).  When a station
668        transmits he is now moved to the top of the station list and remains
669        there after he stops talking.
670    13. Added the configuration file varialbe to enable/disable the display of
671        the "<SB>" string.
672    14. Added code to remove any leading path from filename for record commands.
673    15. Correct a bug which would cause a crash if a user disconnected while
674        listening to a recorded bulletin.
675    16. Moved code that checks for RTCP timeouts to the RTCP routine (!).
676        Previously RTCP timeouts were only checked when an RTP packet was
677        received.  This caused stations to appear to remain logged in for hours
678        when the conference was idle.
679 
680    Revision 1.7  2002/09/02 15:20:35  wb6ymh
681    Changed "logged in for " time in logs to short (d/hh:mm:ss) format.
682 
683    Revision 1.6  2002/09/01 00:12:32  wb6ymh
684    1.  Added new administrator commands .admin, .admins, .record, .list, .mute,
685       .kick, .ban, and .info.
686    2.  Added new user commands .list, .play, .stats and .debug.
687    3.  Modified the station list so that new stations enter at the top of the list.
688    4.  Modified the station list so the station talking is always displayed.
689    5.  Added total number of connected stations to top of station list.
690    6.  Added code to send a warning message to stations that double.
691    7.  Modified all log entries to use %m for error message display when available.
692    8.  Added amount to time client was connected to log entries.
693    9.  Added code to track the number of open sockets.
694    10. Modified GetCall() to compress mutiple spaces between callsign and user's
695        name to a single space.
696    11. Move transmit indicator from to left hand side of station list.
697    12. Added (optional) welcome message at bottom of station list.
698 
699    Revision 1.5  2002/08/18 16:42:06  wb6ymh
700    Automatically exit lurking mode when a lurker talks.
701    Corrected a bug that allowed two stations to talk at once when they doubled.
702 
703    Revision 1.4  2002/08/15 05:36:51  wb6ymh
704    Fix bug that allows timed out talker to keep transmit token forever.
705    Added version command.
706    Added "?" command (alias for help command).
707 
708    Revision 1.3  2002/08/12 17:08:35  wb6ymh
709    Removed some left over debug info from log.
710    Removed \r from end of commands before logging them.
711    Added abilty to record and playback audio from files.
712    Added .help and .test commands.
713 
714    Revision 1.2  2002/08/10 20:52:06  wb6ymh
715    disable dumping of avl trees
716 
717    Revision 1.1.1.1  2002/08/10 20:33:41  wb6ymh
718    initial import
719 
720 */
721 
722 #include "common.h"
723 
724 #ifndef _WIN32
725    // FreeBSD, Linux, etc..
726    #include <stdio.h>
727    #include <stdlib.h>
728    #include <sys/types.h>
729    #ifdef TIME_WITH_SYS_TIME
730       #include <sys/time.h>
731       #include <time.h>
732    #else
733       #ifdef HAVE_SYS_TIME_H
734          #include <sys/time.h>
735       #else
736          #include <time.h>
737       #endif
738    #endif
739    #include <unistd.h>
740    #include <sys/socket.h>
741    #include <netinet/in.h>
742    #include <arpa/inet.h>
743    #include <errno.h>
744    #include <string.h>
745 #ifdef HAVE_FCNTL_H
746    #include <fcntl.h>
747 #endif
748    #include <netdb.h>
749    #include <ctype.h>
750    #ifdef HAVE_SYS_TIMEB_H
751       #include <sys/timeb.h>
752    #endif
753    #include <sys/stat.h>
754    #include <stdarg.h>
755 #else
756    // Windoze
757    #include <stdio.h>
758    #include <time.h>
759    #include <io.h>
760    #include <winsock2.h>
761    #include <sys\timeb.h>
762    #include <ctype.h>
763    #include <sys\stat.h>
764 #endif
765 
766 // Use a non-case sensitive compare function if one is available
767 #ifdef HAVE_STRICMP
768    #define STRCMP stricmp
769 #else
770 #ifdef HAVE_STRCASECMP
771    #define STRCMP strcasecmp
772 #else
773    #define STRCMP strcmp
774 #endif
775 #endif
776 
777 #include "avl.h"
778 #include "main.h"
779 #include "configvars.h"
780 #include "dirclient.h"
781 #include "rtp.h"
782 #include "users.h"
783 #include "conference.h"
784 #include "ilink.h"
785 #include "sf.h"
786 #include "hostfile.h"
787 #include <zlib.h>
788 #include "tbd.h"
789 
790 #if defined _WIN32GUI || defined _X11GUI
791 #include "tbdthread_common.h"
792 #endif
793 
794 #include "eventhook.h"
795 
796 #ifdef   LINK_BOX
797    #include "linkbox.h"
798    #define TBDCMD    "tlbcmd"
799    #define TBDCHAT   "tlbchat"
800 #else
801    #define EndPointInit() 0
802    #define EndPoint(x,y,z)
803    #define TBDCMD    "tbdcmd"
804    #define TBDCHAT   "tbdchat"
805 #endif
806 
807 #ifdef USE_DMALLOC
808 #include "dmalloc.h"
809 #endif
810 
811 #define  HEADER_OVERHEAD   28    // 20 byte IP header + 8 byte UDP header
812 #define  INFO_EXTENSION    ".info"
813 
814 #define  FDUPLEX_TEXT   "\003dpx1"
815 
816 /* Macros for min/max. */
817 #ifndef MIN
818 #define  MIN(a,b) (((a)<(b))?(a):(b))
819 #endif
820 
821 #ifndef MAX
822 #define  MAX(a,b) (((a)>(b))?(a):(b))
823 #endif
824 
825 
826 typedef struct Bulletin_TAG {
827    struct Bulletin_TAG *Link;
828    char *Filename;
829    char *Description;
830    char *RunTime;
831 } Bulletin;
832 
833 Bulletin *BulletinList;
834 Bulletin *BulletinListTail;
835 
836 ConfClient *ClientTalking = NULL;
837 ConfClient *LastTalker = NULL;
838 
839 const char *CallSignString   = "CALLSIGN";
840 
841 char StatusMsg[512];
842 int StatusMsgLen = 0;
843 
844 time_t NextLoginTime;
845 time_t NextStationListTime;
846 time_t NextAVRSTime;
847 time_t LasttAprsIsTime;
848 
849 int ConferenceClients;
850 int PeakClients;
851 time_t PeakClientTime;
852 int ClientConnects;
853 int EchoLinkIPCompareFailures;
854 int EchoAuthenticationFailures;
855 int AuthenticationFailures;
856 
857 ConfClient *StationList;
858 
859 int BadRTCPPacketCount = 0;
860 int bLurkDisabled = FALSE;
861 ConfServer *piLinkConf = NULL;
862 
863 int Rcode;
864 int LastRcode;
865 int ZeroSSRC;
866 char *LastCmdText;
867 char *LastCmd;
868 char ConnectedStatus[MAX_QTH_LEN+1];
869 
870 int bConferenceBusy = FALSE;
871 
872 // overall statistics of all conferences
873 int TxBytesAllConf = 0;
874 int TxMBytesAllConf = 0;
875 int RxBytesAllConf = 0;
876 int RxMBytesAllConf = 0;
877 
878 char *StationDisconnected = NULL;
879 
880 
881 ProtoData gProtoData[NUM_PROTOCOLS];
882 
883 ConfClient CmdLineCC;
884 ClientInfo *CmdClient = NULL;
885 ConfClient ChatCC;
886 ClientInfo *ChatClient = NULL;
887 int bCmdLineChatMode;
888 char *CmdArg;
889 int bLogCmd;
890 
891 struct avl_table *Conferences;   // sorted by Port number
892 
893 void ConvertRTP2iLink(ProtoData *pPDat,char *inBuf,int inLen,int bNewStream);
894 void ConvertiLink2RTP(ProtoData *pPDat,char *inBuf,int inLen,int bNewStream);
895 void ConvertSF2iLink(ProtoData *pPDat,char *inBuf,int inLen,int bNewStream);
896 void ConvertiLink2SF(ProtoData *pPDat,char *inBuf,int inLen,int bNewStream);
897 void ConvertiLink2Ast(ProtoData *pPDat,char *inBuf,int inLen,int bNewStream);
898 void OpenAudioFile(ConfClient *pCC,char *Filename, char *Mode);
899 void StartPlayback(ClientInfo *p,ConfClient *pCC);
900 int FileRecord(ClientInfo *p,ConfClient *pCC,EventType Event);
901 int DuplicateTextMsg(ClientInfo *p,ConfClient *pCC);
902 
903 typedef void (*ConversionF)(ProtoData *pPDat,char *inBuf,int inLen,int bNewStream);
904 
905 ConversionF ProtoConvert[NUM_PROTOCOLS][NUM_PROTOCOLS] =
906 {
907    {NULL,            // From Speak Freely to Speak Freely
908    NULL,             // From Speak Freely to RTP
909    ConvertSF2iLink}, // From Speak Freely to ILink
910 
911    {NULL,            // From RTP to Speak Freely
912    NULL,             // From RTP to RTP
913    ConvertRTP2iLink},// From RTP to ILink
914 
915    {ConvertiLink2SF, // From iLink to SF
916    ConvertiLink2RTP, // From ilink to RTP
917    NULL},            // From iLink to iLink
918 };
919 
920 #define DUP_TRACKING_TIMEOUT  10 // 10 seconds
921 #define MAX_DUP_TRACKING      32
922 typedef struct {
923    unsigned short crc;
924    time_t   TimeStamp;
925    int      bLogged:1;
926    char     FirstFrom[MAX_CALL_LEN+1];
927 } DupTrackingEntry;
928 
929 DupTrackingEntry DupTracking[MAX_DUP_TRACKING];
930 
931 int ConfCompare(const void *avl_a,const void *avl_b,void *avl_param);
932 int CClientCompare(const void *avl_a,const void *avl_b,void *avl_param);
933 ConfClient *CreateNewConfClient(void);
934 void RemoveFromStationList(ConfClient *pCC);
935 void DeleteCClient(ConfClient *pCC);
936 char CheckRTCP(ConfServer *pCS);
937 int GetCall(ConfServer *pCS,ConfClient *pCC,rtcp_t *p,int len);
938 char *GetLocalIPAdr(void);
939 int GenBye(ConfClient *pCC,char *Temp,char *Reason);
940 void SendStationList(ConfServer *pCS);
941 void SetTimeoutRTCP(ClientInfo *p);
942 void ConferenceCmd(ClientInfo *p,ConfClient *pCC,char *cmd);
943 int TimeLapse(struct timeval *p);
944 void CmdHelp(ClientInfo *p,ConfClient *pCC1,char *Arg);
945 int FilePlayBack(ClientInfo *p);
946 void SendBuf2(ClientInfo *p,ConfClient *pCC,int bControLPort);
947 void CalcBW(ConfServer *pCS,int bClear);
948 void CmdVersion(ClientInfo *p,ConfClient *pCC1,char *Arg);
949 void SendToAllSysops(ClientInfo *p,ConfServer *pCS);
950 void SendSDES(ConfServer *pCS,ConfClient *pCC);
951 void ConvertProtocol(ProtoData *pProtoDat,Protocol InputProto,
952                      Protocol OutputProto,int bNewStream);
953 PacketType GetPacketType(ClientInfo *p,ConfClient *pCC);
954 int FromUs(ClientInfo *p,ConfClient *pCC);
955 int AuthorizedClient(ClientInfo *p,ConfClient *pCC);
956 void SaveBadPacket(ClientInfo *p,char *Filename);
957 void SendFirewallOpenRequest(ConfServer *pCS, ConfClient *pCC);
958 char *FirstChatChar(char *Buf);
959 void SendChatEvent(char *Type,char *Buf);
960 
CreateServerClient(ConfServer * pCS,int Port,int bLocal,ClientInfo ** pRet)961 int CreateServerClient(ConfServer *pCS,int Port,int bLocal,ClientInfo **pRet)
962 {
963    IPAdrUnion MyAdr;
964    ClientInfo *pClient;
965    int On = 1;
966    int Err = 0;   // assume the best
967    int bEchoLinkPort = (Port == ILINK_RTP_PORT || Port == ILINK_RTCP_PORT);
968 
969    for( ; ; ) {
970       pClient = CreateNewClient();
971       if(pClient == NULL) {
972          LOG_ERROR(("CreateServerClient(): CreateNewClient failed.\n"));
973          Err = ERR_MALLOC;
974          break;
975       }
976 
977       memset(&MyAdr,0,sizeof(MyAdr));
978       if(bLocal) {
979       // listen to localhost port only
980          MyAdr.ADDR = inet_addr("127.0.0.1");
981       }
982       else if(Bind2IP != NULL && (SFBind2IP == NULL || bEchoLinkPort)) {
983           MyAdr.ADDR = inet_addr(Bind2IP);
984           if(MyAdr.ADDR == INADDR_NONE) {
985              LOG_ERROR(("CreateServerClient(): failed to convert \"%s\" to IP "
986                         "address.\n",Bind2IP));
987              Err = ERR_BIND_IP;
988              break;
989           }
990           else {
991               LOG_ERROR(("Port %d bound to %s.\n",Port,Bind2IP));
992           }
993       }
994       else if(SFBind2IP != NULL && !bEchoLinkPort) {
995          MyAdr.ADDR = inet_addr(SFBind2IP);
996          if(MyAdr.ADDR == INADDR_NONE) {
997             LOG_ERROR(("CreateServerClient(): failed to convert \"%s\" to IP "
998                        "address.\n",Bind2IP));
999             Err = ERR_BIND_IP;
1000             break;
1001          }
1002          else {
1003              LOG_ERROR(("Binding port %d to %s.\n",Port,SFBind2IP));
1004          }
1005       }
1006       else {
1007          MyAdr.ADDR = INADDR_ANY;
1008       }
1009 
1010       SET_WAIT4_RD(pClient);
1011       pClient->p = pCS;
1012       pClient->BufSize = CONF_BUF_SIZE;
1013       pClient->Buf = malloc(pClient->BufSize);
1014 
1015       pClient->Socket = socket(AF_INET,SOCK_DGRAM,0);
1016       if(pClient->Socket == SOCKET_ERROR) {
1017          Err = ERROR_CODE;
1018          LOG_ERROR(("CreateServerClient(): Socket() failed, %s",
1019                     Err2String(Err)));
1020          break;
1021       }
1022 
1023       OpenSockets++;
1024       MyAdr.i.sin_family = AF_INET;
1025       MyAdr.PORT = htons((unsigned short) Port);
1026       pClient->HisAdr = MyAdr;
1027       avl_insert(ClientTree,pClient);
1028       pClient->bInClientTree = TRUE;
1029 
1030       if(setsockopt(pClient->Socket,SOL_SOCKET,SO_REUSEADDR,(char *) &On,
1031                     sizeof(On)) == -1)
1032       {
1033          Err = ERROR_CODE;
1034          LOG_ERROR(("CreateServerClient(): setsockopt() failed %s",
1035                     Err2String(ERROR_CODE)));
1036          break;
1037       }
1038 
1039       if(bind(pClient->Socket,&MyAdr.s,sizeof(MyAdr)) == SOCKET_ERROR) {
1040          Err = ERROR_CODE;
1041          LOG_ERROR(("CreateServerClient(): bind() failed for %s:%d %s",
1042                     inet_ntoa(MyAdr.i.sin_addr),Port,Err2String(ERROR_CODE)));
1043       }
1044       break;
1045    }
1046 
1047    if(Err == 0) {
1048       *pRet = pClient;
1049    }
1050    else if(pClient != NULL) {
1051       DeleteClient(pClient);
1052    }
1053 
1054    return Err;
1055 }
1056 
1057 
DestroyCC(void * avl_item,void * avl_param)1058 void DestroyCC(void *avl_item, void *avl_param)
1059 {
1060    ConfClient *pCC = (ConfClient *) avl_item;
1061 
1062    DeleteCClient(pCC);
1063 }
1064 
DeleteConf(ConfServer * pCS)1065 void DeleteConf(ConfServer *pCS)
1066 {
1067    LOG_NORM(("Deleting conference for port %d.\n",pCS->AudioPort));
1068    if(avl_delete(Conferences,pCS) == NULL) {
1069       LOG_ERROR(("DeleteConf: avl_delete() failed to find client.\n"));
1070    }
1071 
1072    if(pCS->pAudio != NULL) {
1073       pCS->pAudio->p = NULL;
1074       DeleteClient(pCS->pAudio);
1075    }
1076 
1077    if(pCS->pControl != NULL) {
1078       pCS->pControl->p = NULL;
1079       DeleteClient(pCS->pControl);
1080    }
1081 
1082    if(pCS->fp != NULL) {
1083       fclose(pCS->fp);
1084    }
1085 
1086    if(!pCS->bSlaveConf && pCS->ConfTree != NULL) {
1087       avl_destroy(pCS->ConfTree,NULL);
1088    }
1089 
1090    free(pCS);
1091 }
1092 
CreateConference(int AudioPort,ConfServer * pMainConf,ConfServer ** pRet)1093 int CreateConference(
1094    int AudioPort,
1095    ConfServer *pMainConf,
1096    ConfServer **pRet)
1097 {
1098    ConfServer *pCS;
1099    int Err = 0;   // assume good things
1100 
1101    for(; ; ) {
1102       if((pCS = (ConfServer *) malloc(sizeof(ConfServer))) == NULL) {
1103          LOG_ERROR(("CreateConference(): malloc failed.\n"));
1104          Err = ERR_MALLOC;
1105          break;
1106       }
1107       memset(pCS,0,sizeof(ConfServer));
1108       avl_insert(Conferences,pCS);
1109       pCS->AudioPort = AudioPort;
1110 
1111       if(pMainConf == NULL) {
1112          pCS->ConfTree = avl_create(CClientCompare,pCS,NULL);
1113          pCS->pLastAudioIn = &pCS->LastAudioIn;
1114          pCS->pMasterCS = pCS;
1115          pCS->CompressionType = CompressionType;
1116       }
1117       else {
1118          pCS->bSlaveConf = TRUE;
1119          pCS->pMasterCS = pMainConf;
1120          pCS->ConfTree = pMainConf->ConfTree;
1121          pCS->pLastAudioIn = &pMainConf->LastAudioIn;
1122          pCS->CompressionType = pMainConf->CompressionType;
1123       }
1124 
1125       if(pCS->ConfTree == NULL) {
1126          LOG_ERROR(("CreateConference(): avl_create failed.\n"));
1127          Err = ERR_AVL_CREATE;
1128          break;
1129       }
1130 
1131       if(bEchoLinkEnabled || AudioPort != ILINK_RTP_PORT) {
1132       // Don't open ILINK_RTP_PORT unless EchoLink mode is enabled
1133          if((Err = CreateServerClient(pCS,AudioPort,FALSE,&pCS->pAudio)) != 0) {
1134             break;
1135          }
1136 
1137          Err = CreateServerClient(pCS,AudioPort+1,FALSE,&pCS->pControl);
1138       }
1139       break;
1140    }
1141 
1142    if(Err == 0) {
1143       *pRet = pCS;
1144       if(AudioPort == ILINK_RTP_PORT) {
1145          pCS->biLinkConf = TRUE;
1146       }
1147    }
1148    else if(pCS != NULL) {
1149       DeleteConf(pCS);
1150    }
1151 
1152    return Err;
1153 }
1154 
1155 
1156 // Rtp_port = 0   // disable
1157 // Rtp_port = <evenport>
1158 // Rtp_port = <evenport>-<oddport> for a range
RtpInit()1159 int RtpInit()
1160 {
1161    int Ret = ERR_CONFIG_FILE; // assume the worse
1162    char *cp;
1163    int FirstPort;
1164    int LastPort;
1165    int i;
1166    int Err;
1167    ConfServer *pCS;
1168    ConfServer ConfLookup;
1169 
1170    do {
1171       if((cp = strchr(RTP_Port,'-')) != NULL) {
1172       // Range
1173          *cp++ = 0;
1174       }
1175 
1176       if(sscanf(RTP_Port,"%d",&FirstPort) != 1) {
1177          LOG_ERROR(("Error:  Invalid RTP_Port \"%s\"\n",RTP_Port));
1178          break;
1179       }
1180       else if(FirstPort & 1) {
1181          LOG_ERROR(("Error: First RTP port must be even\n"));
1182          break;
1183       }
1184 
1185       if(cp == NULL) {
1186       // Single port
1187          LastPort = FirstPort + 1;
1188       }
1189       else {
1190       // Range
1191          if(sscanf(cp,"%d",&LastPort) != 1) {
1192             *cp = '-';
1193             LOG_ERROR(("Error: Invalid RTP_Port \"%s\"\n",RTP_Port));
1194             break;
1195          }
1196          else if(!(LastPort & 1)) {
1197             LOG_ERROR(("Error: Last RTP port must be odd\n"));
1198             break;
1199          }
1200          else if(LastPort <= FirstPort) {
1201             LOG_ERROR(("Error: The last port must larger the first port\n"));
1202             break;
1203          }
1204       }
1205 
1206       for(i = FirstPort; i < LastPort; i += 2) {
1207          ConfLookup.AudioPort = i;
1208 
1209          if((pCS = (ConfServer *) avl_find(Conferences,&ConfLookup)) == NULL) {
1210          // need to create the conference
1211             if(LinkConferences) {
1212                Err = CreateConference(i,piLinkConf,&pCS);
1213             }
1214             else {
1215                Err = CreateConference(i,NULL,&pCS);
1216             }
1217 
1218             if(Err != 0) {
1219                LOG_ERROR(("Error: Couldn't open port (%d)\r",i));
1220                break;
1221             }
1222             else {
1223                LOG_ERROR(("Created RTP conference for port %d\n",i));
1224                pCS->bRTPConf = TRUE;
1225                pCS->pAudio->State = RTP_Handler;
1226                pCS->pControl->State = RTCP_Handler;
1227                pCS->TimeNextSDES = TimeNow.tv_sec + SDES_Interval;
1228 
1229                if(piLinkConf->pAudio == NULL) {
1230                   piLinkConf->pAudio = pCS->pAudio;
1231                   piLinkConf->pControl= pCS->pControl;
1232                }
1233             }
1234          }
1235 
1236          Ret = 0;
1237       }
1238    } while(FALSE);
1239 
1240    return Ret;
1241 }
1242 
1243 // Init iLink/EchoLink conference
ConferenceInit()1244 int ConferenceInit()
1245 {
1246    time_t Time = time(NULL);
1247    int Err = 0;   // Assume good things
1248    ClientInfo *pClient;
1249 
1250    do {
1251       Conferences = avl_create(ConfCompare,NULL,NULL);
1252 
1253       Err = CreateConference(ILINK_RTP_PORT,NULL,&piLinkConf);
1254 
1255       if(Err != 0 || piLinkConf == NULL) {
1256          break;
1257       }
1258 
1259       if(RTP_Port != NULL) {
1260          Err = RtpInit();
1261       }
1262 
1263 #ifdef   LINK_BOX
1264       if((Err = EndPointInit()) != 0) {
1265          break;
1266       }
1267 #endif
1268 
1269       if(CmdPort != 0) {
1270          if((Err = CreateServerClient(piLinkConf,CmdPort,TRUE,&pClient)) != 0) {
1271             break;
1272          }
1273          CmdClient = pClient;
1274          pClient->State = CmdLine_Handler;
1275          CmdLineCC.pCS = piLinkConf;
1276          CmdLineCC.bCmdLine = TRUE;
1277          CmdLineCC.bSysop = TRUE;
1278          CmdLineCC.bAdmin = TRUE;
1279          CmdLineCC.Callsign = TBDCMD;
1280          CmdLineCC.CallPlus = CmdLineCC.Callsign;
1281          CmdLineCC.Proto = PROTO_ILINK;
1282       }
1283 
1284       if(bEchoLinkEnabled) {
1285       // EchoLink enabled
1286          piLinkConf->pAudio->State = RTP_Handler;
1287          piLinkConf->pControl->State = RTCP_Handler;
1288          if(LoginInterval > 0) {
1289          // EchoLink directory server access enabled
1290             NextLoginTime = Time + 60; // wait a minute before the second login
1291             if(StationListInterval > 0) {
1292                NextStationListTime = Time + StationListInterval;
1293             }
1294             else {
1295                NextStationListTime = 0;
1296             }
1297             if(AvrsEnable) {
1298                NextAVRSTime = Time;
1299             }
1300             piLinkConf->TimeNextSDES = TimeNow.tv_sec + SDES_Interval;
1301             if(StationListInterval > 0) {
1302             // Get initial station list
1303                ServerRequest(SERV_REQ_LOGIN_AND_LIST,0,NULL);
1304             }
1305             else {
1306             // Do initial login
1307                ServerRequest(SERV_REQ_LOGIN,0,NULL);
1308             }
1309             SetTimeoutRTCP(piLinkConf->pControl);
1310          }
1311 
1312          if(ChatPort!= 0) {
1313             if((Err = CreateServerClient(piLinkConf,ChatPort,TRUE,&pClient)) != 0) {
1314                break;
1315             }
1316             ChatClient = pClient;
1317             pClient->State = CmdLine_Handler;
1318             ChatCC.pCS = piLinkConf;
1319             ChatCC.bCmdLine = TRUE;
1320             ChatCC.bSysop = TRUE;
1321             ChatCC.bAdmin = TRUE;
1322             ChatCC.Callsign = TBDCHAT;
1323             ChatCC.CallPlus = ChatCC.Callsign;
1324             ChatCC.Proto = PROTO_ILINK;
1325          }
1326       }
1327 
1328       if(LoginInterval == 0) {
1329       // We'll never login, set a phoney NodeID so SF will run
1330          OurNodeID = crc32(0,(Bytef *) ConferenceCall,strlen(ConferenceCall));
1331          NextLoginTime = 0;
1332          NextStationListTime = 0;
1333       }
1334 
1335    } while(FALSE);
1336 
1337    return Err;
1338 }
1339 
1340 
1341 // Init Speakfreely conference
SFConfInit()1342 int SFConfInit()
1343 {
1344    ConfServer *pCS;
1345    int Err = 0;   // assume the best
1346 
1347    if(LinkConferences) {
1348       Err = CreateConference(SF_Port,piLinkConf,&pCS);
1349    }
1350    else {
1351       Err = CreateConference(SF_Port,NULL,&pCS);
1352    }
1353 
1354    if(Err == 0) {
1355       pCS->AudioPort = SF_ReplyPort;
1356       pCS->bSFConf = TRUE;
1357 
1358       pCS->TimeNextSDES = TimeNow.tv_sec + SDES_Interval;
1359 
1360       pCS->pAudio->State = RTP_Handler;
1361       pCS->pControl->State = RTCP_Handler;
1362    }
1363    return Err;
1364 }
1365 
SendSDES2All(ClientInfo * p,ConfServer * pCS)1366 void SendSDES2All(ClientInfo *p,ConfServer *pCS)
1367 {
1368    ConfClient *pCC = NULL;
1369    struct avl_traverser avl_trans;
1370    char *pSDES;
1371    int Len;
1372    int i;
1373    int bNeedPass[NUM_PROTOCOLS+1];
1374    int Protocol = PROTO_SF;
1375    int bTxtExtension = FALSE;
1376 
1377    memset(bNeedPass,0,sizeof(bNeedPass));
1378 
1379    if(pCS->biLinkConf) {
1380       bNeedPass[PROTO_ILINK] = TRUE;
1381    }
1382    else if(pCS->bSFConf) {
1383       bNeedPass[PROTO_SF] = TRUE;
1384    }
1385    else if(pCS->bRTPConf) {
1386       bNeedPass[PROTO_RTP] = TRUE;
1387    }
1388 
1389 // Loop thru clients N+1 times, once for each protocol
1390 // plus once for old versions of thebridge that used the private "txt"
1391 // extension to pass the callsign of the station talking
1392 
1393    for(i = 0; i < NUM_PROTOCOLS+1; i++, Protocol++) {
1394       if(!bNeedPass[i]) {
1395       // Don't need this pass, skip it
1396          continue;
1397       }
1398 
1399       if(i == NUM_PROTOCOLS) {
1400       // Ilink protocols /w private "txt" extension used by
1401       // old versions of thebridge
1402          Protocol = PROTO_ILINK;
1403          bTxtExtension = TRUE;
1404       }
1405 
1406       GenSDES(Protocol+1,bTxtExtension);
1407 
1408       pSDES = gProtoData[Protocol].OurSDES;
1409       Len = gProtoData[Protocol].SDESLen;
1410 
1411       if(Len > 0) {
1412          pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
1413       }
1414 
1415       while(pCC != NULL) {
1416          if(!pCC->bKicked && pCC->Proto == Protocol) {
1417             if(Protocol == PROTO_ILINK) {
1418                if(i == PROTO_ILINK) {
1419                   if(pCC->bTxtExtension) {
1420                   // Client needs txt extension, send in later pass
1421                      bNeedPass[NUM_PROTOCOLS] = TRUE;
1422                   }
1423                   else {
1424                   // send SDES w/o txt extension
1425                      Send2(pCC,pSDES,Len,TRUE);
1426                   }
1427                }
1428                else if(pCC->bTxtExtension) {
1429                // send SDES w txt extension
1430                   Send2(pCC,pSDES,Len,TRUE);
1431                }
1432             }
1433             else {
1434             // send Speak freely / RTP SDES
1435                Send2(pCC,pSDES,Len,TRUE);
1436             }
1437          }
1438          pCC = (ConfClient *) avl_t_next(&avl_trans);
1439       }
1440    }
1441    pCS->TimeNextSDES = TimeNow.tv_sec + SDES_Interval;
1442 }
1443 
DisconnectCC(ClientInfo * p,ConfClient * pCC,char * Reason)1444 void DisconnectCC(ClientInfo *p,ConfClient *pCC,char *Reason)
1445 {
1446    p->Count = GenBye(pCC,p->Buf,Reason);
1447    pCC->bKicked = TRUE;
1448    pCC->bSWL = TRUE;
1449    pCC->bPermanent = FALSE;
1450    pCC->bInConf = FALSE;
1451    pCC->bDisconnected = TRUE;
1452    if(pCC->bConnected) {
1453       pCC->bConnected = FALSE;
1454       ConferenceClients--;
1455    }
1456    SendBuf2(p,pCC,TRUE);
1457 }
1458 
FwdTextCmdLine(ClientInfo * p,char * Buf,ConfClient * pCC)1459 void FwdTextCmdLine(ClientInfo *p,char *Buf,ConfClient *pCC)
1460 {
1461    char *cp;
1462    char TempBuf[CONF_BUF_SIZE];
1463    int Count;
1464 
1465    if(pCC->HisAdr.PORT != 0) {
1466    // Strip Text header from message and add TBD_CHAT_TEXT result code
1467    // to the beginning of the packet
1468 
1469       Count = snprintf(TempBuf,sizeof(TempBuf),"%d\r%s",TBD_CHAT_TEXT,
1470                        &Buf[sizeof(NDATA)]);
1471 
1472       if(Count == -1 || Count >= sizeof(TempBuf)) {
1473       // Pre-C99 style snprintf returns -1 on buffer overflow.
1474       // C99 style snprintf returns the number of characters that would
1475       // have been written on buffer overflow.
1476       // In either case the output was truncated and p->Count is bogus.
1477 
1478          Count = sizeof(TempBuf) - 1;
1479          Buf[Count] = 0;
1480       }
1481 
1482       // convert \r end of line convention into *nix standard \n
1483 
1484       cp = TempBuf;
1485       while((cp = strchr(cp,'\r')) != NULL) {
1486          *cp = '\n';
1487       }
1488 
1489       sendto(p->Socket,TempBuf,Count+1,0,&pCC->HisAdr.s,sizeof(IPAdrUnion));
1490    }
1491 }
1492 
RTP_Data(ClientInfo * p,ConfServer * pCS,ConfClient * pLookup)1493 void RTP_Data(ClientInfo *p,ConfServer *pCS,ConfClient *pLookup)
1494 {
1495    ConfClient *pCC = NULL;
1496    ConfClient *pSource = NULL;
1497    ConfClient *pNextCC = NULL;
1498    struct avl_traverser avl_trans;
1499    char *cp;
1500    char *cp1;
1501    rtp_hdr_t *pRTP = (rtp_hdr_t *) p->Buf;
1502    int bConfSrc = FALSE;
1503    u_int32 OrigSSRC = 0;
1504    int bNewTalker = FALSE;
1505    int AudioDeltaT;
1506    int bFwdPacket = FALSE;
1507    int bFwdFullDpxOnly = FALSE;
1508    int i,j;
1509    int Count;
1510    char *Buf;
1511    PacketType Type;
1512    int bHaveiLinkData = FALSE;
1513    Protocol InputProto;
1514    int RxLen = p->Count;
1515    int bUserChat  = TRUE;
1516    int bSysopChat = TRUE;
1517    u_int8 CompressionType;
1518    ConfServer *pMasterCS = pCS->pMasterCS;
1519 
1520    if(pLookup->bFilePlayer || pLookup->bCmdLine) {
1521       pSource = pCC = pLookup;
1522    }
1523    else if((pSource = pCC = avl_find(pCS->ConfTree,pLookup)) == NULL) {
1524       LOG_NORM(("RTP_Data: Failed to find client %s\n",
1525                 inet_ntoa(pLookup->HisAdr.i.sin_addr)));
1526    }
1527 
1528    if(pCC != NULL) {
1529       pCC->RxBytes += RxLen + HEADER_OVERHEAD;
1530       InputProto = pCC->Proto;
1531       CompressionType = pCC->CompressionType;
1532 
1533       for(i = 0; i < NUM_PROTOCOLS; i++) {
1534          gProtoData[i].bDataValid = FALSE;
1535          for(j = 0; j < MAX_CONV_PACKETS; j++) {
1536             gProtoData[i].DataLen[j] = 0;
1537          }
1538       }
1539 
1540       if(InputProto == PROTO_ILINK) {
1541          bHaveiLinkData = TRUE;
1542       }
1543 
1544       gProtoData[InputProto].Data[0] = p->Buf;
1545       gProtoData[InputProto].DataLen[0] = RxLen;
1546       gProtoData[InputProto].bDataValid = TRUE;
1547       gProtoData[InputProto].Type = Type = GetPacketType(p,pCC);
1548       bConfSrc = pCC->bConf;
1549 
1550       if(pCC->bSWL || Type == PKT_TYPE_IGNORE) {
1551       // Ignore packet, client is an SWL or the packet is an unknown or
1552       // unhandled type
1553       }
1554       else if(pCC->State != NULL) {
1555          pCC->State(p,pCC,EVENT_RTP_RX);
1556       }
1557       else if(Type == PKT_TYPE_CMD && InputProto == PROTO_ILINK) {
1558       // Command
1559          if(STRCMP(p->Buf,"//ver.") == 0) {
1560          // Version request
1561             p->Count = 0;  // init for BufPrintf
1562             BufPrintf(p,"%c" NDATA"%s ",ILINK_DATA_PACKET,ConferenceCall);
1563             CmdVersion(p,pCC,NULL);
1564             p->Count++;    // include terminating null
1565             Send2(pCC,p->Buf,p->Count,FALSE);
1566          }
1567          else if(ConfCmdEnable) {
1568             cp = &p->Buf[strlen(NDATA)+1];   // Point to first character of data
1569             if((cp1 = strchr(cp,'>')) != NULL) {
1570                if((cp = strchr(&cp1[2],'\r')) != NULL) {
1571                   *cp = 0;
1572                }
1573                ConferenceCmd(p,pCC,&cp1[2]);
1574             // Don't forward commands to other conference users
1575                bFwdPacket = FALSE;
1576             }
1577          }
1578       }
1579       else if(Type == PKT_TYPE_DATA) {
1580          if(!bHaveiLinkData) {
1581             ConvertProtocol(gProtoData,InputProto,PROTO_ILINK,bNewTalker);
1582             if(gProtoData[PROTO_ILINK].DataLen[0] > 0) {
1583                bHaveiLinkData = TRUE;
1584             }
1585          }
1586 
1587          if(bHaveiLinkData && !pCC->bMuteChat) {
1588             bFwdPacket = TRUE;
1589             if(pCC->bSysop) {
1590                char *Temp = FirstChatChar(gProtoData[PROTO_ILINK].Data[0]);
1591                if(Temp != NULL) {
1592                   if(*Temp == ';') {
1593                   // First character is a ';' for forward this message to
1594                   // Admins and sysop's only
1595                      bUserChat = FALSE;
1596                   }
1597                   else if(*Temp == ',') {
1598                   // First character is a ',' for forward this message to
1599                   // Admins only
1600                      bUserChat  = FALSE;
1601                      bSysopChat = FALSE;
1602                   }
1603                }
1604             }
1605 
1606             Buf = gProtoData[PROTO_ILINK].Data[0];
1607             SendChatEvent(pCC->bCmdLine ? "sent_chat" : "chat",Buf);
1608             if(!pCC->bCmdLine) {
1609                 if(bCmdLineChatMode) {
1610                    FwdTextCmdLine(p,Buf,&CmdLineCC);
1611                 }
1612                 if(ChatPort != 0) {
1613                    FwdTextCmdLine(p,Buf,&ChatCC);
1614                 }
1615             }
1616          }
1617       }
1618       else if(Type == PKT_TYPE_AUDIO && !pCC->bMuted && !FromUs(p,pCC)) {
1619       // Audio packet, not originally from us and client is not muted
1620          pCC->LastAudioIn = TimeNow;
1621          if(!pCC->bTalking) {
1622             pCC->FirstAudioIn = TimeNow;
1623             pCC->bTalking = TRUE;
1624          }
1625 
1626          if(ClientTalking == NULL && !pCC->bTimedOut && pCC->bInConf) {
1627          // A new station is trying to talk
1628 
1629             if(pCC->bSysop || pCC == LastTalker) {
1630             // Ok to talk now.
1631                bNewTalker = TRUE;
1632             }
1633             else {
1634             // First apply the minimum pause time
1635                AudioDeltaT = TimeLapse(pCS->pLastAudioIn);
1636                if(PauseTime == 0 || AudioDeltaT >= PauseTime) {
1637                // Ok to talk now.
1638                   bNewTalker = TRUE;
1639                }
1640                else if(InputProto == PROTO_ILINK && !pCC->bPauseWarningSent &&
1641                        !pCC->bConf)
1642                {
1643                   pCC->bPauseWarningSent = TRUE;
1644                   DPRINTF(("RTP_Rx(): Sending pause warning\n"));
1645                   p->Count = 0;
1646                   BufPrintf(p,"%c" NDATA "%s>%s please pause for at least %d.%d"
1647                             " seconds between transmissions!\r",
1648                             ILINK_DATA_PACKET,ConferenceCall,pCC->Callsign,
1649                             PauseTime/1000,PauseTime % 1000);
1650                   p->Count++;    // include terminating null
1651                   SendBuf2(p,pCC,FALSE);
1652                }
1653 
1654             // Now apply the belch filter
1655                if(BelchTime > 0 && bNewTalker && pCC->bBelcher) {
1656                // A potential belcher, see if he's talked long
1657                // enough that it's not a belch.
1658                   AudioDeltaT = TimeLapse(&pCC->FirstAudioIn);
1659                   if(AudioDeltaT < BelchTime) {
1660                   // Not yet your grossness
1661                      bNewTalker = FALSE;
1662                   }
1663                }
1664             }
1665 
1666             if(bNewTalker) {
1667             // A new station is talking
1668                CalcBW(pCS,TRUE);
1669                SetTimeout(pCS->pAudio,ConfAudioTimeout);
1670                LastTalker = ClientTalking = pCC;
1671                pCS->bSendStationList = TRUE;
1672                pCC->bLurking = FALSE;
1673 
1674             // Move him to the top of the station list
1675                RemoveFromStationList(pCC);
1676                pCC->Link = StationList;
1677                StationList = pCC;
1678 
1679                if(pCS->bLinkedConfs) {
1680                // We have linked conferences listening, send them an
1681                // SDES before the audio packet so they can tell who's talking
1682                   SendSDES2All(p,pCS);
1683                }
1684             }
1685          }
1686 
1687          if(pCC == ClientTalking ||
1688             (FullDuplex && (pCC->bFullDuplex || pCC->Proto == PROTO_RTP) &&
1689              pCC->bInConf))
1690          {
1691             *pCS->pLastAudioIn = TimeNow;
1692             bFwdPacket = TRUE;
1693             if(pCC != ClientTalking) {
1694             // Not the station with the talk token, only forward his packets
1695             // to other full duplex stations
1696                bFwdFullDpxOnly = TRUE;
1697             }
1698             if(BlabOffTimer != 0) {
1699                if((TimeNow.tv_sec - pCC->FirstAudioIn.tv_sec) > BlabOffTimer) {
1700                // long winded so and so !
1701                   bFwdPacket = FALSE;
1702                   if(!pCC->bTimedOut) {
1703                      p->Count = 0;  // init for BufPrintf
1704                      LOG_WARN(("%s timed out.\n",pCC->Callsign));
1705                      BufPrintf(p,"%c" NDATA "%s>%s you have timed out!\r"
1706                                "The timeout timer on this conference is set to "
1707                                "%d seconds.\r",
1708                                ILINK_DATA_PACKET,ConferenceCall,pCC->Callsign,
1709                                BlabOffTimer);
1710                      p->Count++;    // include terminating null
1711                      SendBuf2(p,pCC,FALSE);
1712                      pCC->bTimedOut = TRUE;
1713                      ClientTalking = NULL;   // let someone else talk!
1714                      pCS->bSendStationList = TRUE;
1715                   }
1716                }
1717             }
1718          }
1719          else if(ClientTalking != NULL && !pCC->bDoublingWarningSent &&
1720                  InputProto == PROTO_ILINK && !pCC->bConf &&
1721                  !pCC->bFilePlayer && !pCC->bFullDuplex)
1722          {  // Station is doubling !
1723             pCC->bDoublingWarningSent = TRUE;
1724             p->Count = 0;  // init for BufPrintf
1725             BufPrintf(p,"%c" NDATA "%s>%s you are DOUBLING with %s!\r",
1726                       ILINK_DATA_PACKET,ConferenceCall,pCC->Callsign,
1727                       ClientTalking->Callsign);
1728             p->Count++;    // include terminating null
1729             SendBuf2(p,pCC,FALSE);
1730          }
1731       }
1732 
1733       if(bFwdPacket) {
1734          pNextCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
1735       }
1736 
1737       if(pNextCC != NULL && pMasterCS->bRecording && !bHaveiLinkData &&
1738          CompressionType == RTP_PT_GSM)
1739       {
1740          ConvertProtocol(gProtoData,InputProto,PROTO_ILINK,bNewTalker);
1741          if(gProtoData[PROTO_ILINK].DataLen[0] > 0) {
1742             bHaveiLinkData = TRUE;
1743          }
1744       }
1745 
1746       if(pNextCC != NULL && pMasterCS->bRecording && bNewTalker &&
1747          bHaveiLinkData)
1748       {
1749          FilePacketHdr PktHdr;
1750 
1751       // timestamp this entry, we have a new talker
1752          PktHdr.Flags = htonl(FILE_FLAG_TIME_STAMP);
1753          PktHdr.Len = htonl(TimeNow.tv_sec);
1754 
1755          if(fwrite(&PktHdr,sizeof(PktHdr),1,pMasterCS->fp) != 1) {
1756          // Write error
1757             LOG_ERROR(("RTP_Rx(): fwrite failed, %s",Err2String(errno)));
1758             fclose(pMasterCS->fp);
1759             pMasterCS->fp = NULL;
1760             pMasterCS->bRecording = FALSE;
1761          }
1762       }
1763 
1764       if(bFwdPacket && pMasterCS->bRecording && bHaveiLinkData) {
1765          FilePacketHdr PktHdr;
1766 
1767          for(i = 0; i < MAX_CONV_PACKETS; i++) {
1768             if((Count = gProtoData[PROTO_ILINK].DataLen[i]) == 0) {
1769                break;
1770             }
1771             Buf = gProtoData[PROTO_ILINK].Data[i];
1772             Count = gProtoData[PROTO_ILINK].DataLen[i];
1773 
1774             PktHdr.Len = htonl(Count);
1775             PktHdr.Flags = 0;
1776             if(Type == PKT_TYPE_DATA) {
1777                PktHdr.Flags |= FILE_FLAG_DATA_PKT;
1778                if(ClientTalking == NULL) {
1779                   PktHdr.Flags |= FILE_FLAG_RATE_LIMIT;
1780                }
1781             }
1782             else {
1783                PktHdr.Flags |= FILE_FLAG_RATE_LIMIT;
1784             }
1785             PktHdr.Flags = htonl(PktHdr.Flags);
1786 
1787             if(fwrite(&PktHdr,sizeof(PktHdr),1,pMasterCS->fp) != 1 ||
1788                fwrite(Buf,Count,1,pMasterCS->fp) != 1)
1789             {  // Write error
1790                LOG_ERROR(("RTP_Rx(): fwrite failed, %s",Err2String(errno)));
1791                fclose(pMasterCS->fp);
1792                pMasterCS->fp = NULL;
1793                pMasterCS->bRecording = FALSE;
1794             }
1795          }
1796       }
1797 
1798       if(!pCC->bCmdLine) {
1799          if(Type == PKT_TYPE_DATA) {
1800             if(!ConfTextEnable) {
1801             // Text conferencing isn't enabled, don't forward packet
1802                pNextCC = NULL;
1803             }
1804          }
1805          else if(!ConfEnable) {
1806          // Conferencing isn't enabled, only forward packets from the local client
1807             pNextCC = NULL;
1808          }
1809       }
1810 
1811       // Forward packet to clients
1812       while((pCC = pNextCC) != NULL) {
1813          pNextCC = (ConfClient *) avl_t_next(&avl_trans);
1814          if(!pCC->bInConf || pCC->bMonitor || pCC == pSource) {
1815          // This guy isn't in the conference at the moment or is
1816          // in monitor mode, or is the guy that's talking, ignore him
1817             continue;
1818          }
1819 
1820          if(Type == PKT_TYPE_AUDIO) {
1821             if(pCC->CompressionType != CompressionType) {
1822             // Audio packet of a different compression type than this client,
1823             // Don't confuse him.
1824                DLOG(DLOG_CODEC_TYPE,
1825                     ("Not forwarding CType 0x%x, HisCtype: 0x%x to %s.\n",
1826                      CompressionType,pCC->CompressionType,pCC->Callsign));
1827                continue;
1828             }
1829             if(bFwdFullDpxOnly && !pCC->bFullDuplex) {
1830             // only fowarding to full duplex clients and this guy isn't
1831                continue;
1832             }
1833          }
1834 
1835          if(!gProtoData[pCC->Proto].bDataValid) {
1836             if(!gProtoData[PROTO_ILINK].bDataValid) {
1837                ConvertProtocol(gProtoData,InputProto,PROTO_ILINK,bNewTalker);
1838             }
1839             ConvertProtocol(gProtoData,PROTO_ILINK,pCC->Proto,bNewTalker);
1840          }
1841 
1842          for(i = 0; i < MAX_CONV_PACKETS; i++) {
1843             if((Count = gProtoData[pCC->Proto].DataLen[i]) == 0) {
1844                break;
1845             }
1846             Buf = gProtoData[pCC->Proto].Data[i];
1847 
1848             switch(pCC->Proto) {
1849             case PROTO_ILINK:
1850                if(Type == PKT_TYPE_DATA && !pCC->bNoChat) {
1851                // Sending Data packet and the user hasn't muted chat
1852                   if(bUserChat || (bSysopChat && pCC->bSysop) || pCC->bAdmin) {
1853                   // forward chat text
1854                      Send2(pCC,Buf,Count,FALSE);
1855                   }
1856                }
1857                else if(Type == PKT_TYPE_AUDIO) {
1858                   OrigSSRC = pRTP->ssrc;
1859                   if(pCC->bSendSSRC) {
1860                   // Sending audio from another conference. If the sssrc is
1861                   // zero then set our ssrc ID, otherwise just pass the
1862                   // packet on unmodified.
1863                      if(pRTP->ssrc == 0) {
1864                         ZeroSSRC++;
1865                         pRTP->ssrc = OurNodeID;
1866                      }
1867                   }
1868                   else {
1869                   // Sending audio to user that may crash if they see a
1870                   // non-zero ssrc, make sure ssrc is zero
1871                      pRTP->ssrc = 0;
1872                   }
1873 
1874                   Send2(pCC,Buf,Count,FALSE);
1875 
1876                // restore the original ssrc in case it was modified above
1877                   pRTP->ssrc = OrigSSRC;
1878                }
1879                break;
1880 
1881             case PROTO_SF:
1882                if(Type == PKT_TYPE_AUDIO) {
1883                // Audio packet and current user is not talking
1884                   Send2(pCC,Buf,Count,FALSE);
1885                }
1886                break;
1887 
1888             case PROTO_RTP:
1889                if(Type == PKT_TYPE_AUDIO) {
1890                // Audio packet and current user is not talking
1891                   Send2(pCC,Buf,Count,FALSE);
1892                }
1893                break;
1894             }
1895          }
1896       }
1897 
1898       if(Type == PKT_TYPE_AUDIO || Type == PKT_TYPE_OTHER_AUDIO) {
1899          EndPoint(p,pSource,EVENT_RTP_RX);
1900       }
1901    }
1902 }
1903 
RTP_Rx(ClientInfo * p,ConfServer * pCS)1904 void RTP_Rx(ClientInfo *p,ConfServer *pCS)
1905 {
1906    ConfClient  Lookup;
1907    int RxLen;
1908    socklen_t Adrlen = sizeof(Lookup.HisAdr);
1909 
1910    memset(&Lookup,0,sizeof(Lookup));
1911 
1912    RxLen = recvfrom(p->Socket,p->Buf,p->BufSize-1,0,&Lookup.HisAdr.s,&Adrlen);
1913    Lookup.HisAdr.PORT = pCS->AudioPort;
1914 
1915    if(RxLen != SOCKET_ERROR) {
1916       p->Buf[RxLen] = 0;
1917       p->Count = RxLen;
1918       pCS->RxBytes += RxLen + HEADER_OVERHEAD;
1919       RxBytesAllConf += RxLen + HEADER_OVERHEAD;
1920       pCS->RxCount++;
1921    }
1922 
1923    if(RxLen == SOCKET_ERROR) {
1924       pCS->RxErrs++;
1925       DPRINTF(("RTP_Handler(): recvfrom returned %u.\n",ERROR_CODE));
1926    }
1927    else {
1928       RxBytesAllConf += RxLen + HEADER_OVERHEAD;
1929       RTP_Data(p,pCS,&Lookup);
1930    }
1931 }
1932 
RTP_Handler(ClientInfo * p)1933 int RTP_Handler(ClientInfo *p)
1934 {
1935    int bClientsWereTalking = ClientTalking != NULL;
1936    int bClientsTalking = FALSE;
1937    int bConfTalkerFound = FALSE;
1938    int bConfFound = FALSE;
1939    int AudioDeltaT;
1940    struct avl_traverser avl_trans;
1941    ConfServer *pCS = (ConfServer *) p->p;
1942    ConfClient *pCC = avl_t_first(&avl_trans,pCS->ConfTree);
1943 
1944    GetTimeNow();
1945 
1946 // Check RTP timeouts
1947    while(pCC != NULL) {
1948       if(pCC->bConf) {
1949          bConfFound = TRUE;
1950       }
1951 
1952       if(pCC->bTalking) {
1953          AudioDeltaT = TimeLapse(&pCC->LastAudioIn);
1954 
1955          if(AudioDeltaT > ConfAudioTimeout) {
1956          // This guy has stopped talking
1957             EndPoint(p,pCC,EVENT_RTP_TO);
1958             if(pCC->bTimedOut) {
1959             // remove "Timed out" from users entry in station list
1960                pCS->bSendStationList = TRUE;
1961                LOG_WARN(("timed out user %s unkeyed.\n",pCC->Callsign));
1962             }
1963             pCC->bTalking = FALSE;
1964             pCC->bTimedOut = FALSE;
1965 
1966             if(pCC->State != NULL) {
1967                pCC->State(p,pCC,EVENT_RTP_TO);
1968             }
1969          }
1970          else {
1971          // This guy is still talking
1972             bClientsTalking = TRUE;
1973             if(pCC == ClientTalking && !pCC->bKicked) {
1974                bConfTalkerFound = TRUE;
1975             }
1976          }
1977       }
1978       pCC = (ConfClient *) avl_t_next(&avl_trans);
1979    }
1980 
1981    pCS->bLinkedConfs = bConfFound;
1982 
1983    if(!bConfTalkerFound && ClientTalking != NULL &&
1984       ClientTalking != pCS->pFilePlayer)
1985    {  // The conference is free now
1986       CalcBW(pCS,TRUE);
1987       ClientTalking = NULL;
1988       pCS->bSendStationList = TRUE;
1989       if(pCS->bPlayWhenFree) {
1990       // Waiting to play an file
1991          pCS->bPlayWhenFree = FALSE;
1992          StartPlayback(pCS->pAudio,pCS->pFilePlayer);
1993       }
1994    }
1995 
1996    if(bClientsTalking) {
1997    // Set a timeout
1998       SetTimeout(p,ConfAudioTimeout);
1999    }
2000    else {
2001    // No one's talking no need for a timeout
2002       p->bTimeWait = FALSE;
2003    }
2004 
2005    if(p->bReadReady) {
2006       RTP_Rx(p,pCS);
2007    }
2008 
2009    if(bClientsWereTalking && ClientTalking == NULL && pCS->bLinkedConfs) {
2010    // We have linked conferences listening, send them an
2011    // SDES update
2012       SendSDES2All(p,pCS);
2013    }
2014 
2015    if(pCS->bSendStationList) {
2016       SendStationList(pCS);
2017       pCS->bSendStationList = FALSE;
2018    }
2019 
2020    return FALSE;
2021 }
2022 
AutoLurk(ConfClient * pCC)2023 int AutoLurk(ConfClient *pCC)
2024 {
2025    int bAutoLurk = FALSE;
2026 
2027    if(pCC->bRepeater) {
2028       if(DefaultAutoLurk & AUTOLURK_REPEATERS) {
2029          bAutoLurk = TRUE;
2030       }
2031    }
2032    else if(pCC->bLink) {
2033       if(DefaultAutoLurk & AUTOLURK_LINKS) {
2034          bAutoLurk = TRUE;
2035       }
2036    }
2037    else if(pCC->bConf) {
2038       if(DefaultAutoLurk & AUTOLURK_CONFERENCES) {
2039          bAutoLurk = TRUE;
2040       }
2041    }
2042    else if(DefaultAutoLurk & AUTOLURK_HEADSETS) {
2043       bAutoLurk = TRUE;
2044    }
2045 
2046    return bAutoLurk;
2047 }
2048 
IncrementClientCount(ConfServer * pCS,ConfClient * pCC)2049 void IncrementClientCount(ConfServer *pCS,ConfClient *pCC)
2050 {
2051    ConferenceClients++;
2052    pCS->bSendStationList = TRUE;
2053 
2054    if(ConferenceClients > PeakClients) {
2055       PeakClients = ConferenceClients;
2056       PeakClientTime = TimeNow.tv_sec;
2057    }
2058 }
2059 
AddClient2Conf(ConfServer * pCS,ConfClient * pCC)2060 void AddClient2Conf(ConfServer *pCS,ConfClient *pCC)
2061 {
2062    gProtoData[pCC->Proto].Clients++;
2063    pCC->LoginTime = TimeNow.tv_sec;
2064    avl_insert(pCS->ConfTree,pCC);
2065    pCS->Users++;
2066 // put our new guy on the top of the station list
2067    pCC->Link = StationList;
2068    StationList = pCC;
2069    if(pCC->bConnected) {
2070       IncrementClientCount(pCS,pCC);
2071    }
2072 
2073    if(pCS->ConfTree->avl_count == 1) {
2074    // reset the RTCP timer, the conference was empty before
2075    // so it wasn't running
2076       pCS->TimeNextSDES = TimeNow.tv_sec + SDES_Interval;
2077       SetTimeoutRTCP(pCS->pControl);
2078 
2079       if(AvrsEnable) {
2080       // Force an AVRS update
2081          NextAVRSTime = TimeNow.tv_sec;
2082       }
2083    }
2084 }
2085 
2086 
RTCP_Rx(ClientInfo * p)2087 int RTCP_Rx(ClientInfo *p)
2088 {
2089    int RxLen;
2090    int Ret = FALSE;
2091    ConfClient  Lookup;
2092    char *end;
2093    ConfClient *pCC;
2094    int bNewClient = FALSE;
2095    int Authorized = 0;
2096    ConfServer *pCS = (ConfServer *) p->p;
2097    socklen_t Adrlen = sizeof(Lookup.HisAdr);
2098    union {
2099       rtcp_common_t *p;
2100       rtcp_t *pRTCP;
2101       char *cp;
2102    } u;
2103 
2104    union {
2105       rtcp_sdes_item_t *p;
2106       char *cp;
2107    } Item;
2108    char RTCP_Ver;
2109 
2110    u.cp = p->Buf;
2111 
2112    RxLen = recvfrom(p->Socket,p->Buf,p->BufSize,0,&Lookup.HisAdr.s,&Adrlen);
2113    p->Count = RxLen;
2114    Lookup.HisAdr.PORT = pCS->AudioPort;
2115 
2116    if(RxLen == SOCKET_ERROR) {
2117       pCS->RxErrs++;
2118       DPRINTF(("RTCP_Rx(): recvfrom returned %u.\n",ERROR_CODE));
2119       return FALSE;
2120    }
2121    else {
2122       pCS->RxCount++;
2123       pCS->RxBytes += RxLen + HEADER_OVERHEAD;
2124       RxBytesAllConf += RxLen + HEADER_OVERHEAD;
2125    }
2126 
2127    if((RTCP_Ver = CheckRTCP(pCS)) == 0) {
2128    // Bogus packet, ignore it
2129       LOG_WARN(("RTCP_Rx(): ignoring bogus packet from %s.\n",
2130                 inet_ntoa(Lookup.HisAdr.i.sin_addr)));
2131       SaveBadPacket(p,"badrtcp.bin");
2132       return FALSE;
2133    }
2134 
2135    if((pCC = avl_find(pCS->ConfTree,&Lookup)) == NULL) {
2136    // New conference client
2137       bNewClient = TRUE;
2138       pCC = CreateNewConfClient();
2139       pCC->pCS = pCS;
2140       pCC->Proto = RTCP_Ver - 1;
2141       pCC->HisAdr = Lookup.HisAdr;
2142 
2143       pCC->RxBytes += RxLen + HEADER_OVERHEAD;
2144       RxBytesAllConf += RxLen + HEADER_OVERHEAD;
2145 
2146       if(!GetCall(pCS,pCC,u.pRTCP,RxLen)) {
2147       // Not an SDES (a bye for some we don't have connection with?), ignore it
2148          free(pCC);
2149          return FALSE;
2150       }
2151 
2152       if(pCC->bConf) {
2153       // Conference
2154          if(pCC->bTBD) {
2155          // thebridge
2156             pCC->bMuted = pCS->bMuteTBD;
2157          }
2158          else {
2159          // EchoLink or other type of conference
2160             pCC->bMuted = pCS->bMuteConf;
2161          }
2162       }
2163       else if(pCC->bRepeater || pCC->bLink) {
2164       // RF user
2165          pCC->bMuted = pCS->bMuteRF;
2166       }
2167       else {
2168       // PC User
2169          pCC->bMuted = pCS->bMuteUsers;
2170       }
2171 
2172       pCC->bMuteChat = pCS->bMuteChat;
2173 
2174       if(pCC->Proto == PROTO_ILINK && pCC->Callsign == NULL) {
2175          LOG_ERROR(("failed to get callsign from iLink client %s.\n",
2176                     inet_ntoa(pCC->HisAdr.i.sin_addr)));
2177          SaveBadPacket(p,"nocall.bin");
2178       }
2179 
2180       pCC->bAutoLurk = AutoLurk(pCC);
2181 
2182    // LastAudioIn to the current time (a lie) so new stations don't
2183    // disappear immediately when DefaultAutoLurk is active.
2184       pCC->LastAudioIn = TimeNow;
2185 
2186       if(pCC->Callsign == NULL) {
2187       // Didn't get a callsign, default to the IP address for now
2188          pCC->CallPlus = strdup(inet_ntoa(pCC->HisAdr.i.sin_addr));
2189          pCC->Callsign = strdup(pCC->CallPlus);
2190       }
2191 
2192       Authorized = AuthorizedClient(p,pCC);
2193 
2194       if(bConferenceBusy ||
2195          ConferenceClients >= MaxConferenceClients ||
2196          Authorized != 1)
2197       {  // Sorry Jack
2198          if(Authorized == 1) {
2199             LOG_NORM(("%s failed to logon, conference %s.\n",CallLog(pCC),
2200                       bConferenceBusy ? "busy" : "full"));
2201 
2202             if(bConferenceBusy) {
2203                p->Count = GenBye(pCC,p->Buf,"Sorry the conference is busy");
2204             }
2205             else {
2206                p->Count = GenBye(pCC,p->Buf,"Sorry the conference is full");
2207             }
2208          }
2209          else if(Authorized == 0) {
2210          // Sorry Jack, you're not welcome here
2211             LOG_NORM(("Disconnecting unauthorized user %s.\n",CallLog(pCC)));
2212             p->Count = GenBye(pCC,p->Buf,"Bye Bye");
2213          }
2214          else {
2215             LOG_NORM(("Ignoring unknown user %s (%s).\n",pCC->Callsign,
2216                       inet_ntoa(pCC->HisAdr.i.sin_addr)));
2217          }
2218 
2219          if(Authorized == 0) {
2220             SendBuf2(p,pCC,TRUE);
2221          }
2222          free(pCC->Callsign);
2223          free(pCC->CallPlus);
2224          if(pCC->Password != NULL) {
2225             free(pCC->Password);
2226          }
2227          free(pCC);
2228          pCC = NULL;
2229       }
2230       else {
2231       // New user accepted
2232          pCC->bConnected = TRUE;
2233          AddClient2Conf(pCS,pCC);
2234          EndPoint(NULL,pCC,EVENT_INIT);
2235 
2236       // NB: *ASSUME* the client is running the same codec as the conference
2237       // There's no way to tell if this is actually the case until he sends
2238       // an audio packet.  This may be wrong, but currently thebridge only
2239       // supports welcome messages in GSM format.  If a welcome message is
2240       // played in GSM format for some clients (thelinkbox) they will *SWITCH*
2241       // to GSM when they receive it.  Meaning if you want an ADPCM conference
2242       // you should *NOT* configure the conference to send a welcome message.
2243       //
2244       // A possible solution would be for the client to sent a few empty
2245       // audio packets on connect to convey the audio type but AFAIK no
2246       // clients do that.
2247 
2248          pCC->CompressionType = CompressionType;
2249          switch(pCC->Proto) {
2250             case PROTO_SF:
2251                LOG_NORM(("SF client %s logged in, current users: %d.\n",
2252                          CallLog(pCC),ConferenceClients));
2253                EventHook("connected speakfreely %s %d",pCC->Callsign,
2254                          ConferenceClients);
2255                break;
2256 
2257             case PROTO_RTP:
2258                LOG_NORM(("RTP client %s logged in, current users: %d.\n",
2259                          CallLog(pCC),ConferenceClients));
2260                EventHook("connected rtp %s %d",pCC->Callsign,ConferenceClients);
2261                break;
2262 
2263             case PROTO_ILINK:
2264             // EchoLink only supports GSM
2265                pCC->CompressionType = RTP_PT_GSM;
2266                LOG_NORM(("%s%s logged in, current users: %d.\n",
2267                          CallLog(pCC),pCC->bConf ? " conference" : "",
2268                          ConferenceClients));
2269                EventHook("connected echolink %s %d",pCC->Callsign,
2270                          ConferenceClients);
2271                break;
2272          }
2273 
2274          if(WelcomeFile != NULL && strstr(WelcomeFile,".wav") == NULL) {
2275             if(WelcomeDelay != 0) {
2276                if(ClientTalking == NULL) {
2277                   pCC->bWelcomePending = TRUE;
2278                }
2279             }
2280             else {
2281             // Play the welcome file for him.
2282 
2283             // NB: Send the station list now, otherwise the new user won't
2284             // get a copy because the welcome file will be playing
2285 
2286                SendStationList(pCS);
2287                pCS->bSendStationList = FALSE;
2288 
2289                OpenAudioFile(pCC,WelcomeFile,MODE_RD_BIN);
2290                if(pCC->bFileIOActive) {
2291                   StartPlayback(p,pCC);
2292                }
2293             }
2294          }
2295          else if(AudioTestConf && FileRecord(NULL,pCC,EVENT_INIT)) {
2296             pCC->bInConf = FALSE;
2297             pCC->State = FileRecord;
2298          }
2299       }
2300    }
2301    else {
2302       pCC->RxBytes += RxLen + HEADER_OVERHEAD;
2303       RxBytesAllConf += RxLen + HEADER_OVERHEAD;
2304    }
2305 
2306    if(pCC != NULL && pCC->bKicked && pCC->bDisconnected) {
2307    // maybe he didn't get the news
2308       DisconnectCC(p,pCC,"Goodbye");
2309    }
2310 
2311    if(pCC != NULL && !pCC->bKicked) {
2312       pCC->LastHeard = TimeNow.tv_sec;
2313 
2314       if(pCC->State != NULL) {
2315          pCC->State(p,pCC,EVENT_RTCP_RX);
2316       }
2317 
2318       Item.p = NULL;
2319       end = u.cp + RxLen;
2320 
2321       while(u.cp < end) {
2322          D3PRINTF(("RTCP_Rx(): processing pt %d.\n",u.p->pt));
2323          switch(u.p->pt) {
2324             case RTCP_SR:
2325                break;
2326 
2327             case RTCP_RR:
2328                break;
2329 
2330             case RTCP_SDES:
2331             // Answer with our SDES if he's a new client
2332                if(bNewClient) {
2333                   SendSDES(pCS,pCC);
2334                }
2335 
2336                GetCall(pCS,pCC,u.pRTCP,RxLen);
2337 
2338                if(pCC->bPermanent && !pCC->bConnected) {
2339                // A new connection to a permanent connection
2340                   pCC->bInConf = TRUE;
2341                   pCC->bConnected = TRUE;
2342                   IncrementClientCount(pCS,pCC);
2343 
2344                   pCC->LoginTime = TimeNow.tv_sec;
2345                   LOG_NORM(("%s%s connected, current users: %d.\n",
2346                             CallLog(pCC),pCC->bConf ? " conference" : "",
2347                             ConferenceClients));
2348                   EndPoint(NULL,pCC,EVENT_INIT);
2349                   EventHook("connected outbound %s %d",pCC->Callsign,
2350                             ConferenceClients);
2351                   pCS->bSendStationList = TRUE;
2352                }
2353                break;
2354 
2355             case RTCP_BYE:
2356                if(pCC->bPermanent && !pCC->bConnected) {
2357                // Must be a .connect command that failed. Send the results
2358                // to all admins & sysops
2359                   p->Count = 0;  // init for BufPrintf
2360                   BufPrintf(p,"%c" NDATA,ILINK_DATA_PACKET);
2361                   BufPrintf(p,"%s refused connection request.\r",pCC->Callsign);
2362                   SendToAllSysops(p,pCC->pCS);
2363                   LOG_NORM(("%s refused connection request.\n",CallLog(pCC)));
2364                   EventHook("disconnected refused %s %d",pCC->Callsign,
2365                             ConferenceClients-1);
2366                }
2367                else {
2368                   LOG_NORM(("%s logged out, on %s, current users: %d.\n",
2369                             CallLog(pCC),TimeLapse2String(pCC->LoginTime,TRUE),
2370                             ConferenceClients-1));
2371                   EventHook("disconnected bye %s %d",pCC->Callsign,
2372                             ConferenceClients-1);
2373                }
2374                DeleteCClient(pCC);
2375             // Update the station list
2376                pCS->bSendStationList = TRUE;
2377                return Ret;
2378 
2379             case RTCP_APP:
2380                break;
2381          }
2382          u.cp = u.cp + sizeof(rtcp_common_t) + ntohs(u.p->length) * 4;
2383       }
2384    }
2385 
2386    return Ret;
2387 }
2388 
SetTimeoutRTCP(ClientInfo * p)2389 void SetTimeoutRTCP(ClientInfo *p)
2390 {
2391    ConfServer *pCS = (ConfServer *) p->p;
2392    int Timeout = 0x7fffffff;
2393 
2394    if(pCS->ConfTree->avl_count > 0) {
2395       Timeout = MIN(Timeout,pCS->TimeNextSDES);
2396    }
2397 
2398    if(NextLoginTime != 0) {
2399       Timeout = MIN(Timeout,NextLoginTime);
2400    }
2401 
2402    if(NextStationListTime != 0) {
2403       Timeout = MIN(Timeout,NextStationListTime);
2404    }
2405 
2406    if(NextAVRSTime != 0) {
2407       Timeout = MIN(Timeout,NextAVRSTime);
2408    }
2409 
2410    if(Timeout != 0x7fffffff) {
2411       SetTimeout(p,(Timeout - TimeNow.tv_sec) * 1000);
2412    }
2413    else {
2414       p->bTimeWait = FALSE;
2415    }
2416 }
2417 
LogStats(ConfServer * pCS)2418 void LogStats(ConfServer *pCS)
2419 {
2420    u_int TxBytes = TxBytesAllConf;
2421    u_int TxMBytes = TxMBytesAllConf;
2422    u_int RxBytes = RxBytesAllConf;
2423    u_int RxMBytes = RxMBytesAllConf;
2424 
2425    CalcBW(pCS,FALSE);
2426 
2427    if(TxBytes > 1000000) {
2428       TxMBytes += TxBytes / 1000000;
2429       TxBytes %= 1000000;
2430    }
2431 
2432    if(RxBytes > 1000000) {
2433       RxMBytes += RxBytes / 1000000;
2434       RxBytes %= 1000000;
2435    }
2436 
2437    LOG_NORM(("Users:%d BW T:%d R:%d T:%d.%02dMB R:%d.%02dMB Up:%s\n",
2438              ConferenceClients,pCS->TxBandWidth,pCS->RxBandWidth,
2439              TxMBytes,TxBytes*100/1000000,RxMBytes,RxBytes*100/1000000,
2440              TimeLapse2String(BootTime,TRUE)));
2441 }
2442 
StartCmdHandler(char * Cmd,char * Callsign)2443 void StartCmdHandler(char *Cmd,char *Callsign)
2444 {
2445    ConfServer *pCS = piLinkConf;
2446    ClientInfo *p = pCS->pAudio;
2447    ConfClient cc;
2448    char *cp, *cp1;
2449 
2450    if(pCS != NULL) {
2451       p = pCS->pAudio;
2452 
2453       memset(&cc,0,sizeof(cc));
2454 
2455       cc.bCmdLine = TRUE;
2456       cc.bSysop = TRUE;
2457       cc.bAdmin = TRUE;
2458       cc.Callsign = Callsign;
2459       cc.CallPlus = cc.Callsign;
2460       cc.pCS = pCS;
2461 
2462       cp = Cmd;
2463       if(*cp == '.') {
2464          cp++;
2465       }
2466 
2467       ConferenceCmd(p,&cc,cp);
2468       cp1 = &p->Buf[sizeof(NDATA)];
2469       if(strlen(cp1) > 0) {
2470       // log the startup command response
2471          while((cp = strchr(cp1,'\r')) != NULL) {
2472             *cp++ = 0;
2473             LOG_NORM(("StartupCmd: %s\n",cp1));
2474             cp1 = cp;
2475          }
2476 
2477          if(strlen(cp1) > 0) {
2478             LOG_NORM(("StartupCmd: %s\n",cp1));
2479          }
2480       }
2481    }
2482 }
2483 
CmdLine_Handler(ClientInfo * pClient)2484 int CmdLine_Handler(ClientInfo *pClient)
2485 {
2486    ConfServer *pCS = (ConfServer *) pClient->p;
2487    ClientInfo *p = pCS->pAudio;
2488    socklen_t Adrlen = sizeof(IPAdrUnion);
2489    int Ret = FALSE;
2490    int bIsCmd = FALSE;
2491    int RxLen = SOCKET_ERROR;
2492    char  Cmd[CONF_BUF_SIZE];
2493    char  *cp;
2494    ConfClient *pCC;
2495    unsigned short OurPort = ntohs(pClient->HisAdr.PORT);
2496 
2497    for( ; ; ) {
2498       if(OurPort == ChatPort) {
2499          pCC = &ChatCC;
2500       }
2501       else if(OurPort == CmdPort) {
2502          pCC = &CmdLineCC;
2503       }
2504       else {
2505          LOG_ERROR(("CmdLine_Handler(): Error: unknown port number %d\n",
2506                     OurPort));
2507          break;
2508       }
2509 
2510       if(pClient->bReadReady) {
2511          RxLen = recvfrom(pClient->Socket,Cmd,sizeof(Cmd)-1,0,&pCC->HisAdr.s,
2512                           &Adrlen);
2513       }
2514 
2515       if(RxLen == SOCKET_ERROR) {
2516          LOG_ERROR(("CmdLine_Handler(): recvfrom failed %s\n",
2517                     Err2String(errno)));
2518          break;
2519       }
2520 
2521       if(ntohs(pCC->HisAdr.PORT) == OurPort) {
2522          LOG_ERROR(("CmdLine_Handler(): I'm talking to myself again!\n"));
2523          break;
2524       }
2525 
2526       if(ChatCC.HisAdr.PORT == CmdLineCC.HisAdr.PORT) {
2527       // We're either running tbdcmd or tbdchat but it appears that we're
2528       // running both from the same port.  This can happen if we exit
2529       // one program and start the other and it happens to be assigned the
2530       // same port number.  Clear the port on the stale client
2531          if(OurPort == ChatPort) {
2532             CmdLineCC.HisAdr.PORT = 0;
2533          }
2534          else {
2535             ChatCC.HisAdr.PORT = 0;
2536          }
2537       }
2538 
2539       Cmd[RxLen] = 0;
2540       if(bCmdLineChatMode || OurPort == ChatPort) {
2541          if(Cmd[0] == '.' && Cmd[1] == '.') {
2542             bIsCmd = TRUE;
2543          }
2544       }
2545       else {
2546          bIsCmd = TRUE;
2547       }
2548 
2549       if(bIsCmd) {
2550          cp = Cmd;
2551          while(*cp == '.') {
2552             cp++;
2553          }
2554          ConferenceCmd(p,pCC,cp);
2555       // Strip Text header from result and add result code to the beginning
2556       // of the response
2557          cp = strdup(&p->Buf[sizeof(NDATA)]);
2558          p->Count = snprintf(p->Buf,p->BufSize,"%d\r%s",Rcode,cp);
2559          free(cp);
2560 
2561          if(p->Count == -1 || p->Count >= p->BufSize) {
2562          // Pre-C99 style snprintf returns -1 on buffer overflow.
2563          // C99 style snprintf returns the number of characters that would
2564          // have been written on buffer overflow.
2565          // In either case the output was truncated and p->Count is bogus.
2566 
2567             p->Count = p->BufSize - 1;
2568             p->Buf[p->Count] = 0;
2569          }
2570 
2571          // convert \r end of line convention into *nix standard \n
2572 
2573          cp = p->Buf;
2574          while((cp = strchr(cp,'\r')) != NULL) {
2575             *cp = '\n';
2576          }
2577       }
2578       else {
2579       // Format data into an EchoLink text data packet
2580          p->Count = 0;  // init for BufPrintf
2581          BufPrintf(p,"%c" NDATA "%s>%s\r",ILINK_DATA_PACKET,
2582                    ConferenceCall,Cmd);
2583          p->Count++;    // include terminating null
2584          if(p->Count+4 <= p->BufSize) {
2585          // Add SSRC to end of text
2586               memcpy(&p->Buf[p->Count],&OurNodeID,4);
2587               p->Count += 4;
2588          }
2589          RTP_Data(p,pCS,pCC);
2590          p->Count = snprintf(p->Buf,p->BufSize,"%d\n",TBD_CHAT_TEXT_SENT);
2591       }
2592       sendto(pClient->Socket,p->Buf,p->Count+1,0,&pCC->HisAdr.s,
2593              sizeof(IPAdrUnion));
2594       break;
2595    }
2596 
2597    return Ret;
2598 }
2599 
RTCP_Handler(ClientInfo * p)2600 int RTCP_Handler(ClientInfo *p)
2601 {
2602    ConfClient *pCC;
2603    ConfClient *pNextCC;
2604    ConfClient *pOldestCC = NULL;
2605    time_t      OldestConnectTime = 0x7fffffff;
2606    struct avl_traverser avl_trans;
2607    ConfServer *pCS = (ConfServer *) p->p;
2608    int Ret = FALSE;
2609    static int LastStatsHr = -1;
2610    time_t timenow = (time_t) TimeNow.tv_sec;
2611    struct tm *tm = localtime(&timenow);
2612 
2613    if(p->bReadReady) {
2614       Ret |= RTCP_Rx(p);
2615    }
2616 
2617    if(LastStatsHr != tm->tm_hour) {
2618    // Generate hourly stats while active
2619       if(LastStatsHr != -1) {
2620          LogStats(pCS);
2621       }
2622       LastStatsHr = tm->tm_hour;
2623    }
2624 
2625 // Check RTCP timeouts
2626    pNextCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
2627    while((pCC = pNextCC) != NULL) {
2628       pNextCC = (ConfClient *) avl_t_next(&avl_trans);
2629       if((TimeNow.tv_sec - pCC->LastHeard) > ConfMemberTimeout) {
2630          int Clients = ConferenceClients;
2631       // RTCP timeout
2632          if(pCC->bPermanent) {
2633             if(pCC->bConnected) {
2634                LOG_NORM(("RTCP timeout on permanent client %s. On %s.\n",
2635                          CallLog(pCC),TimeLapse2String(pCC->LoginTime,TRUE)));
2636                EndPoint(p,pCC,EVENT_RTCP_TO);
2637                EventHook("disconnected rtcp_timeout %s %d",pCC->Callsign,
2638                          Clients);
2639                if(pCC->bConnected) {
2640                   pCC->bConnected = FALSE;
2641                   ConferenceClients--;
2642                   Clients--;
2643                }
2644                pCC->bInConf = FALSE;
2645                pCS->bSendStationList = TRUE;
2646             }
2647          }
2648          else {
2649             if(!pCC->bConnected) {
2650                LOG_NORM(("Dropping %s. On %s.\n",CallLog(pCC),
2651                          TimeLapse2String(pCC->LoginTime,TRUE)));
2652             }
2653             else {
2654                LOG_NORM(("Dropping %s, RTCP timeout. On %s.\n",CallLog(pCC),
2655                          TimeLapse2String(pCC->LoginTime,TRUE)));
2656                Clients--;
2657             }
2658             EndPoint(p,pCC,EVENT_RTCP_TO);
2659             if(pCC->State != NULL) {
2660                pCC->State(p,pCC,EVENT_RTCP_TO);
2661             }
2662             pCS->bSendStationList = TRUE;
2663             EventHook("disconnected rtcp_timeout %s %d",pCC->Callsign,Clients);
2664             DeleteCClient(pCC);
2665          }
2666          continue;
2667       }
2668 
2669       if(pCC->bAutoLurk && !pCC->bLurking &&
2670          (TimeNow.tv_sec - pCC->LastAudioIn.tv_sec) > AutoLurkTimeout) {
2671          pCC->bLurking = TRUE;
2672          pCS->bSendStationList = TRUE;
2673          if(!AutoLurk(pCC)) {
2674          // Only sent back to lurking message to stations that selected
2675          // lurk mode manually
2676             p->Count = 0;  // init for BufPrintf
2677             BufPrintf(p,"%c" NDATA "Back to lurking...\r",ILINK_DATA_PACKET);
2678             p->Count++; // include terminating null
2679             Send2(pCC,p->Buf,p->Count,FALSE);
2680          }
2681       }
2682 
2683       if(pCC->bWelcomePending &&
2684          (TimeNow.tv_sec - pCC->LoginTime) > WelcomeDelay)
2685       {  // Time to play welcome
2686          pCC->bWelcomePending = FALSE;
2687          OpenAudioFile(pCC,WelcomeFile,MODE_RD_BIN);
2688          if(pCC->bFileIOActive) {
2689             StartPlayback(p,pCC);
2690          }
2691       }
2692 
2693       if(pCC->LoginTime < OldestConnectTime &&
2694          strcmp(pCC->Callsign,ConferenceCall) != 0)
2695       {
2696          OldestConnectTime = pCC->LoginTime;
2697          pOldestCC = pCC;
2698       }
2699    }
2700 
2701    if(p->bTimeOut) {
2702       if(LoginInterval > 0 && NextLoginTime <= TimeNow.tv_sec) {
2703       // Time to login again
2704          NextLoginTime = TimeNow.tv_sec + LoginInterval;
2705          if(NextStationListTime != 0 && NextStationListTime <= TimeNow.tv_sec) {
2706          // Also time to refresh the station list
2707             NextStationListTime = TimeNow.tv_sec + StationListInterval;
2708             DPRINTF(("RTCP_Handler(): refreshing login and station list.\n"));
2709             ServerRequest(SERV_REQ_LOGIN_AND_LIST,0,NULL);
2710          }
2711          else {
2712             DPRINTF(("RTCP_Handler(): refreshing login.\n"));
2713             ServerRequest(SERV_REQ_LOGIN,0,NULL);
2714          }
2715       }
2716       else if(LoginInterval > 0 && NextStationListTime > 0 &&
2717               NextStationListTime <= TimeNow.tv_sec)
2718       {
2719       // Time to refresh our station list
2720          NextStationListTime = TimeNow.tv_sec + StationListInterval;
2721          DPRINTF(("RTCP_Handler(): refreshing station list.\n"));
2722          ServerRequest(SERV_REQ_STATION_LIST,0,NULL);
2723       }
2724 
2725       if(pCS->TimeNextSDES <= TimeNow.tv_sec) {
2726       // Time to send a SDES to all of our clients
2727          D3PRINTF(("Sending SDES to clients\n"));
2728          SendSDES2All(p,pCS);
2729       }
2730 
2731       if(pCS->bPlayWhenIdle &&
2732          TimeNow.tv_sec - pCS->LastAudioIn.tv_sec > IdleTimeout)
2733       {
2734          pCS->bPlayWhenIdle = FALSE;
2735          StartPlayback(pCS->pAudio,pCS->pFilePlayer);
2736       }
2737 
2738       if(AvrsEnable && pCS->biLinkConf && NextAVRSTime <= TimeNow.tv_sec) {
2739       // Time to send an AVRS update
2740 
2741          NextAVRSTime = TimeNow.tv_sec + AvrsInterval;
2742          if(PullerStatusBusy) {
2743          // Busy
2744             SendAvrsUpdate(AVRS_BUSY,NULL);
2745          }
2746          else if(pOldestCC != NULL) {
2747          // Connected to someone
2748             SendAvrsUpdate(AVRS_CONNECTED,pOldestCC->Callsign);
2749          }
2750          else {
2751          // Just hanging out
2752             SendAvrsUpdate(AVRS_ON,NULL);
2753          }
2754       }
2755 
2756 #ifdef   LINK_BOX
2757       if(AprsIsEnable && LasttAprsIsTime + AprsIsInterval <= TimeNow.tv_sec) {
2758          LasttAprsIsTime = TimeNow.tv_sec;
2759          if(PullerStatusBusy) {
2760          // Busy
2761             Send2Aprsd(AVRS_BUSY,NULL);
2762          }
2763          else if(pOldestCC != NULL) {
2764          // Connected to someone
2765             Send2Aprsd(AVRS_CONNECTED,pOldestCC->Callsign);
2766          }
2767          else {
2768          // Just hanging out
2769             Send2Aprsd(AVRS_ON,NULL);
2770          }
2771       }
2772 #endif
2773    }
2774    SetTimeoutRTCP(p);
2775 
2776    if(pCS->bSendStationList) {
2777       SendStationList(pCS);
2778       pCS->bSendStationList = FALSE;
2779    }
2780 
2781    if(pCS->bDynamicConf && pCS->Users == 0) {
2782       DeleteConf(pCS);
2783    }
2784 
2785    return Ret;
2786 }
2787 
2788 // Conference compare by port number
ConfCompare(const void * avl_a,const void * avl_b,void * avl_param)2789 int ConfCompare(const void *avl_a,const void *avl_b,void *avl_param)
2790 {
2791    ConfServer *p_a = (ConfServer *) avl_a;
2792    ConfServer *p_b = (ConfServer *) avl_b;
2793 
2794    return p_a->AudioPort - p_b->AudioPort;
2795 }
2796 
2797 // Client compare by IP address : port number
CClientCompare(const void * avl_a,const void * avl_b,void * avl_param)2798 int CClientCompare(const void *avl_a,const void *avl_b,void *avl_param)
2799 {
2800    ConfClient *p_a = (ConfClient *) avl_a;
2801    ConfClient *p_b = (ConfClient *) avl_b;
2802 
2803    if(p_a->HisAdr.ADDR == p_b->HisAdr.ADDR) {
2804       return p_a->HisAdr.PORT - p_b->HisAdr.PORT;
2805    }
2806 
2807    if(p_a->HisAdr.ADDR > p_b->HisAdr.ADDR) {
2808       return 1;
2809    }
2810    else {
2811       return -1;
2812    }
2813 }
2814 
CreateNewConfClient()2815 ConfClient *CreateNewConfClient()
2816 {
2817    ConfClient *pCC = (ConfClient *) malloc(sizeof(ConfClient));
2818 
2819    if(pCC != NULL) {
2820       memset(pCC,0,sizeof(ConfClient));
2821       pCC->SN = ++ClientConnects;
2822       pCC->bInConf = TRUE;
2823    }
2824    else {
2825       LOG_ERROR(("CreateNewConfClient(): malloc failed.\n"));
2826    }
2827 
2828    return pCC;
2829 }
2830 
RemoveFromStationList(ConfClient * pCC)2831 void RemoveFromStationList(ConfClient *pCC)
2832 {
2833    ConfClient *pCCLast = (ConfClient *) &StationList;
2834    ConfClient *pCC1 = StationList;
2835 
2836    // Remove him from the station list
2837 
2838    while(pCC1 != NULL) {
2839       if(pCC1 == pCC) {
2840          pCCLast->Link = pCC->Link;
2841          break;
2842       }
2843       pCCLast = pCC1;
2844       pCC1 = pCC1->Link;
2845    }
2846 
2847    if(pCC1 == NULL && !pCC->bFilePlayer) {
2848       LOG_ERROR(("RemoveFromStationList: station %s not found in station list.\n",
2849                  inet_ntoa(pCC->HisAdr.i.sin_addr)));
2850    }
2851 }
2852 
FileCleanup(ConfClient * pCC)2853 void FileCleanup(ConfClient *pCC)
2854 {
2855    ClientFileIO *pFIO = (ClientFileIO *) pCC->p;
2856 
2857    if(pFIO->fp != NULL) {
2858       fclose(pFIO->fp);
2859    }
2860 
2861    if(pFIO->pClient != NULL) {
2862       pFIO->pClient->Socket = INVALID_SOCKET;
2863       DeleteClient(pFIO->pClient);
2864    }
2865 
2866    if(pFIO->bDeleteOnClose) {
2867       unlink(pFIO->Filename);
2868    }
2869 
2870    if(pFIO->Filename != NULL) {
2871       free(pFIO->Filename);
2872    }
2873 
2874    if(pFIO->pPDat != NULL) {
2875       free(pFIO->pPDat);
2876    }
2877 
2878    free(pFIO);
2879    pCC->p = NULL;
2880    pCC->bFileIOActive = FALSE;
2881 }
2882 
DeleteCClient(ConfClient * pCC)2883 void DeleteCClient(ConfClient *pCC)
2884 {
2885    ConfServer *pCS = pCC->pCS;
2886    size_t Nodes = pCS->ConfTree->avl_count;
2887 
2888    EndPoint(NULL,pCC,EVENT_DELETE);
2889 
2890    if(pCC == ClientTalking) {
2891       ClientTalking = NULL;
2892    }
2893 
2894    if(!pCC->bFilePlayer) {
2895       if(pCC->bConnected){
2896          ConferenceClients--;
2897       }
2898       gProtoData[pCC->Proto].Clients--;
2899       if(ConferenceClients == 0 && AvrsEnable) {
2900       // Force an AVRS update
2901          NextAVRSTime = TimeNow.tv_sec;
2902       }
2903    }
2904    RemoveFromStationList(pCC);
2905 
2906    if(!pCC->bFilePlayer && avl_delete(pCS->ConfTree,pCC) == NULL) {
2907       LOG_ERROR(("DeleteCClient: avl_delete() failed to find client.\n"));
2908    }
2909    else {
2910       if(!pCC->bFilePlayer && (Nodes - 1) != pCS->ConfTree->avl_count) {
2911          LOG_NORM(("DeleteCClient: %d nodes before delete, %d after\n",Nodes,
2912                    pCS->ConfTree->avl_count));
2913       }
2914 
2915       if(pCC->bFileIOActive) {
2916          FileCleanup(pCC);
2917       }
2918 
2919       if(pCC->CallPlus != NULL) {
2920          free(pCC->CallPlus);
2921       }
2922 
2923       if(pCC->Callsign != NULL) {
2924          free(pCC->Callsign);
2925       }
2926 
2927       if(pCC->Info != NULL) {
2928          free(pCC->Info);
2929       }
2930 
2931       if(pCC->Password != NULL) {
2932          free(pCC->Password);
2933       }
2934 
2935       if(pCC->Tool != NULL) {
2936          free(pCC->Tool);
2937       }
2938 
2939       pCC->pCS->Users--;
2940       free(pCC);
2941    }
2942 }
2943 
2944 // Sanity check RTCP packet
2945 // An Speak Freely packet has RTCP_Ver set to 1 in the first chunk,
2946 // but set to 2 in the remaining chunks.
2947 //
2948 // An standard RTP packet has RTCP_Ver set to 2 in all chunks
2949 //
2950 // An iLink packet has RTCP_Ver set to 3 in all chunks
2951 //
2952 // Return RTCP version or 0 if invalid
CheckRTCP(ConfServer * pCS)2953 char CheckRTCP(ConfServer *pCS)
2954 {
2955    char *end;
2956    union {
2957       rtcp_common_t *p;
2958       char *cp;
2959    } u;
2960    rtcp_common_t *p = (rtcp_common_t *) pCS->pControl->Buf;
2961    char Ret = 0;
2962 
2963    u.p = p;
2964    end = u.cp + pCS->pControl->Count;
2965 
2966 // Sanity check: Version correct, no padding in first packet,
2967 // first item is either RTCP_SR or RTCP_RR
2968 
2969    while(u.cp < end) {
2970       if(u.p == p && (p->p || (p->pt != RTCP_SR && p->pt != RTCP_RR)) ){
2971       // Invalid RTCP packet
2972          LOG_WARN(("CheckRTCP(): First item invalid\n"));
2973          break;
2974       }
2975 
2976       if(u.p == p && pCS->bSFConf && u.p->version == SF_RTP_VERSION) {
2977       // First chunk
2978          Ret = SF_RTP_VERSION;
2979       }
2980       else if((pCS->bSFConf || pCS->bRTPConf) && u.p->version == RTP_VERSION) {
2981          if(Ret == 0) {
2982             Ret = RTP_VERSION;
2983          }
2984       }
2985       else if(pCS->biLinkConf && u.p->version == ILINK_RTP_VERSION) {
2986          Ret = ILINK_RTP_VERSION;
2987       }
2988       else {
2989       // Invalid RTCP packet
2990          LOG_WARN(("CheckRTCP(): Invalid RTCP version %d\n",u.p->version));
2991          Ret = 0;
2992          break;
2993       }
2994       u.cp = u.cp + sizeof(rtcp_common_t) + ntohs(u.p->length) * 4;
2995    }
2996 
2997    if(u.cp != end) {
2998       Ret = 0;
2999       LOG_WARN(("CheckRTCP(): End of packet mismatch\n"));
3000    }
3001 
3002    if(Ret == 0) {
3003       BadRTCPPacketCount++;
3004    }
3005    return Ret;
3006 }
3007 
GetCall(ConfServer * pCS,ConfClient * pCC,rtcp_t * p,int len)3008 int GetCall(ConfServer *pCS,ConfClient *pCC,rtcp_t *p,int len)
3009 {
3010    char *end;
3011    char *Temp;
3012    char *Callsign = NULL;
3013    char *cp;
3014    char *TextData = NULL;
3015    union {
3016       rtcp_common_t *p;
3017       rtcp_t *pRTCP;
3018       char *cp;
3019    } u;
3020 
3021    union {
3022       rtcp_sdes_item_t *p;
3023       char *cp;
3024    } Item;
3025    int  bFoundCallsign = FALSE;
3026    int  x;
3027    int  Ret = FALSE;
3028    int  EchoLinkRevMajor;
3029    int  EchoLinkRevMinor;
3030    int  EchoLinkBuild;
3031 
3032    Item.p = NULL;
3033    u.pRTCP = p;
3034    end = u.cp + len;
3035 
3036    while(u.cp < end) {
3037       if(u.p->pt == RTCP_SDES) {
3038       // SDES packet
3039          Item.p = u.pRTCP->r.sdes.item;
3040          Ret = TRUE;
3041          break;
3042       }
3043       u.cp = u.cp + sizeof(rtcp_common_t) + ntohs(u.p->length) * 4;
3044    }
3045 
3046    while(Item.p != NULL && Item.p->type != RTCP_SDES_END) {
3047       switch(Item.p->type) {
3048          case RTCP_SDES_CNAME:   // CNAME is the only required SDES field
3049             if(Item.p->length == 8 && strncmp(Item.p->data,"CALLSIGN",8) == 0) {
3050                bFoundCallsign = TRUE;
3051             }
3052             break;
3053 
3054          case RTCP_SDES_NAME:
3055             if(Item.p->length > 0) {
3056                if(Callsign != NULL) {
3057                // Two NAME fields !
3058                   free(Callsign);
3059                }
3060 
3061                Temp = malloc(Item.p->length+1);
3062 
3063                if(Temp != NULL) {
3064                   memcpy(Temp,Item.p->data,Item.p->length);
3065                   Temp[Item.p->length] = 0;
3066                   if((cp = strchr(Temp,' ')) != NULL) {
3067                   // compress spaces between call and Name
3068                      *cp++ = 0;
3069                      while(*cp == ' ') {
3070                         cp++;
3071                      }
3072 
3073                      if(strstr(cp,"conf") != NULL || strstr(cp,"CONF") != NULL) {
3074                      // if "conf" appears in the name field it's a conference.
3075                         pCC->bConf = TRUE;
3076                      }
3077 
3078                      Callsign = malloc(strlen(Temp)+strlen(cp)+2);
3079                      if(Callsign != NULL) {
3080                         strcpy(Callsign,Temp);
3081                         strcat(Callsign," ");
3082                         strcat(Callsign,cp);
3083                      }
3084                      free(Temp);
3085                   }
3086                   else {
3087                   // no spaces to compress
3088                      Callsign = Temp;
3089                   }
3090                }
3091             }
3092             break;
3093 
3094          case RTCP_SDES_TOOL:
3095             x = strlen(PACKAGE);
3096             if(Item.p->length > 0 && pCC->Tool == NULL) {
3097             // Save the Client's tool name
3098                if((pCC->Tool = malloc(Item.p->length+1)) != NULL) {
3099                   memcpy(pCC->Tool,Item.p->data,Item.p->length);
3100                   pCC->Tool[Item.p->length] = 0;
3101                }
3102             }
3103 
3104             if(Item.p->length >= x && strncmp(Item.p->data,PACKAGE,x) == 0) {
3105             // One of our guys, set the flags
3106                pCC->bConf = pCC->bTBD = pCC->bSendSSRC = TRUE;
3107             }
3108             else if(Item.p->length > 6 && Item.p->data[0] == 'E') {
3109             // Possible EchoLink version
3110                Temp = malloc(Item.p->length+1);
3111 
3112                if(Temp != NULL) {
3113                   memcpy(Temp,Item.p->data,Item.p->length);
3114                   Temp[Item.p->length] = 0;
3115                   if(sscanf(&Temp[1],"%d.%d.%d",&EchoLinkRevMajor,
3116                             &EchoLinkRevMinor,&EchoLinkBuild) == 3)
3117                   { // Yup, it's a (recent) EchoLink client
3118                      pCC->bSendSSRC = TRUE;
3119                   }
3120                   free(Temp);
3121                }
3122             }
3123             break;
3124 
3125          case RTCP_SDES_PHONE:   // We use the phone number field as a password
3126             if(RTP_Pass != NULL) {
3127                if(pCC->Password != NULL) {
3128                   free(pCC->Password);
3129                }
3130 
3131                pCC->Password = (char *) malloc(Item.p->length+1);
3132 
3133                if(pCC->Password != NULL) {
3134                   memcpy(pCC->Password,Item.p->data,Item.p->length);
3135                   pCC->Password[Item.p->length] = 0;
3136                }
3137             }
3138             break;
3139 
3140          case RTCP_SDES_PRIV:
3141             if(Item.p->length >= 3 && strncmp(Item.p->data,"txt",3) == 0) {
3142             // Our 'talker" or current station
3143                pCC->bTxtExtension = TRUE;
3144                if(TextData != NULL) {
3145                   free(TextData);
3146                   TextData = NULL;
3147                }
3148 
3149                if(Item.p->length > 3) {
3150                   TextData = (char *) malloc(Item.p->length-3+1);
3151 
3152                   if(TextData != NULL) {
3153                      memcpy(TextData,&Item.p->data[3],Item.p->length-3);
3154                      TextData[Item.p->length-3] = 0;
3155                   }
3156                }
3157             }
3158             if(Item.p->length == 5 && strncmp(Item.p->data,FDUPLEX_TEXT,5) == 0) {
3159                pCC->bFullDuplex = TRUE;
3160                pCC->bSendSSRC = TRUE;
3161             }
3162             break;
3163 
3164          default:
3165             break;
3166       }
3167       Item.cp = Item.cp + sizeof(rtcp_sdes_item_t) + Item.p->length - 1;
3168       if(Item.cp >= end) {
3169       // bogus packet RTCP_SDES packet
3170          BadRTCPPacketCount++;
3171          break;
3172       }
3173    }
3174 
3175    if(!bFoundCallsign) {
3176       if(Callsign != NULL) {
3177          free(Callsign);
3178          Callsign = NULL;
3179       }
3180    // Only accept a password if the callsign is also specified
3181       if(pCC->Password != NULL) {
3182          free(pCC->Password);
3183          pCC->Password = NULL;
3184       }
3185    }
3186 
3187    if(Callsign != NULL && pCC->CallPlus != NULL) {
3188       if(strcmp(Callsign,pCC->CallPlus) != 0) {
3189       // the callsign/name field has changed
3190          if(pCC == ClientTalking) {
3191          // This guy is currently talking, force a station list update.
3192             pCS->bSendStationList = TRUE;
3193          }
3194       }
3195       else if(TextData == NULL) {
3196       // Nothing has changed, nothing to do...
3197          if(Callsign != NULL) {
3198             free(Callsign);
3199             Callsign = NULL;
3200          }
3201          if(pCC->Password != NULL) {
3202             free(pCC->Password);
3203             pCC->Password = NULL;
3204          }
3205       }
3206    }
3207 
3208    if(Callsign != NULL) {
3209       if(Callsign[0] == '*') {
3210          pCC->bConf = TRUE;
3211       }
3212 
3213       if((cp = strchr(Callsign,' ')) != NULL) {
3214       // Remove the name from the callsign/name string
3215          *cp = 0;
3216       }
3217 
3218       if(!pCC->bCallsignOverride) {
3219          if(pCC->Callsign != NULL) {
3220             free(pCC->Callsign);
3221          }
3222          pCC->Callsign = strdup(Callsign);
3223 
3224          if(pCC->CallPlus != NULL) {
3225             free(pCC->CallPlus);
3226          }
3227 
3228          if(TextData != NULL) {
3229          // "txt" data was present in SDES, use it
3230             pCC->CallPlus = malloc(strlen(Callsign)+strlen(TextData) + 2);
3231             if(pCC->CallPlus != NULL) {
3232                strcpy(pCC->CallPlus,Callsign);
3233                strcat(pCC->CallPlus," ");
3234                strcat(pCC->CallPlus,TextData);
3235             }
3236          }
3237          else {
3238             if(cp != NULL) {
3239             // Restore the name to the end of the callsign
3240                *cp = ' ';
3241             }
3242             if(pCC->bConf && (cp = strchr(Callsign,')')) != NULL) {
3243             // Remove "CONF" from end of the string
3244                cp[1] = 0;
3245             }
3246             pCC->CallPlus = Callsign;
3247          }
3248       }
3249 
3250       if(strstr(pCC->Callsign,"-L")) {
3251          pCC->bLink = TRUE;
3252       }
3253       else if(strstr(pCC->Callsign,"-R")) {
3254          pCC->bRepeater = TRUE;
3255       }
3256       else if(pCC->Proto == PROTO_SF) {
3257          if(strncmp(pCC->Callsign,"stn",3) == 0) {
3258             pCC->bRepeater = TRUE;
3259             pCC->bIRLP = TRUE;
3260          }
3261          else if(strncmp(pCC->Callsign,"exp",3) == 0) {
3262             pCC->bRepeater = TRUE;
3263             pCC->bIRLP = TRUE;
3264          }
3265          else if(strncmp(pCC->Callsign,"ref",3) == 0) {
3266             pCC->bConf = TRUE;
3267             pCC->bIRLP = TRUE;
3268          }
3269       }
3270 
3271       if(pCC->bLink || pCC->bRepeater || pCC->bConf) {
3272       // a Potential belcher
3273          pCC->bBelcher = TRUE;
3274       }
3275    }
3276 
3277    if(TextData != NULL) {
3278       free(TextData);
3279    }
3280 
3281    return Ret;
3282 }
3283 
GetUserCountString()3284 char *GetUserCountString()
3285 {
3286    static char Temp[80];
3287 
3288    Temp[0] = 0;
3289    if(UserCountEnable) {
3290       if(MaxCountEnable) {
3291          snprintf(Temp,sizeof(Temp)," [%d/%d]",ConferenceClients,
3292                   MaxConferenceClients);
3293       }
3294       else {
3295          snprintf(Temp,sizeof(Temp)," [%d]",ConferenceClients);
3296       }
3297    }
3298 
3299    return Temp;
3300 }
3301 
3302 
GenStationList(ClientInfo * p)3303 void GenStationList(ClientInfo *p)
3304 {
3305    ConfClient *pCC = StationList;
3306 
3307    while(pCC != NULL) {
3308       if(pCC->bConnected && (!pCC->bLurking || bLurkDisabled)) {
3309          if(pCC == ClientTalking) {
3310             BufPrintf(p,"->%s\r",pCC->CallPlus);
3311          }
3312          else if(pCC->bMuted) {
3313             BufPrintf(p,"%s (Muted)\r",pCC->Callsign);
3314          }
3315          else if(pCC->bTimedOut) {
3316             BufPrintf(p,"%s (Timed Out)\r",pCC->Callsign);
3317          }
3318          else if(pCC->bMonitor && strstr(pCC->CallPlus,"(Confer") != NULL) {
3319             BufPrintf(p,"%s (Receive only)\r",pCC->Callsign);
3320          }
3321          else {
3322             BufPrintf(p,"%s\r",pCC->CallPlus);
3323          }
3324       }
3325       pCC = pCC->Link;
3326    }
3327 }
3328 
SendStationList(ConfServer * pCS)3329 void SendStationList(ConfServer *pCS)
3330 {
3331    struct avl_traverser avl_trans;
3332    ConfClient *pCC;
3333    ClientInfo *p = pCS->pAudio;
3334    int bSendingInfo = FALSE;
3335 
3336    p->Count = 0;  // init for BufPrintf
3337    BufPrintf(p,"%c" NDATA,ILINK_DATA_PACKET);
3338 
3339    if(ShowStationInfo &&
3340       ClientTalking != NULL &&
3341       ClientTalking->Info != NULL &&
3342       !ClientTalking->bSentInfo)
3343    {  // Show new client's info
3344       ClientTalking->bSentInfo = TRUE;
3345       bSendingInfo = TRUE;
3346       BufPrintf(p,"\rAbout %s:%s",ClientTalking->Callsign,ClientTalking->Info);
3347    }
3348    else {
3349       BufPrintf(p,"CONF %s%s",ConferenceID,GetUserCountString());
3350       if(SB_Enable && ClientTalking == NULL) {
3351          BufPrintf(p," <SB>");
3352       }
3353 
3354       BufPrintf(p,"\r\r");
3355       GenStationList(p);
3356 
3357       if(Banner != NULL) {
3358          BufPrintf(p,"\r%s\r",Banner);
3359       }
3360    }
3361 
3362    if(p->Count >= sizeof(StatusMsg)) {
3363       p->Count = sizeof(StatusMsg) - 1;
3364    }
3365    p->Buf[p->Count++] = 0;
3366    StatusMsgLen = p->Count;
3367    memcpy(StatusMsg,p->Buf,StatusMsgLen);
3368 
3369 #if defined _WIN32GUI || defined _X11GUI
3370    UpdateGui(GUIUPDATETYPE_STN,StatusMsg,StatusMsgLen);
3371 #endif
3372 
3373    pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
3374 
3375    while(pCC != NULL) {
3376       if(ClientTalking == NULL) {
3377       // Reset everyone's warning flags
3378          pCC->bDoublingWarningSent = FALSE;
3379          pCC->bPauseWarningSent = FALSE;
3380       }
3381       else {
3382          // Reset everyone's welcome pending flags
3383          pCC->bWelcomePending = FALSE;
3384       }
3385 
3386       if(pCC->bInConf && !pCC->bConf && pCC->Proto == PROTO_ILINK) {
3387          Send2(pCC,StatusMsg,StatusMsgLen,FALSE);
3388       }
3389       pCC = (ConfClient *) avl_t_next(&avl_trans);
3390    }
3391 }
3392 
GenSDES(int rtp_version,int bTxtExtension)3393 void GenSDES(int rtp_version,int bTxtExtension)
3394 {
3395    char PadCount;
3396    char *pFirstData;
3397    char CharSave = 0;
3398    time_t timenow = (time_t) TimeNow.tv_sec;
3399    struct tm *tm = localtime(&timenow);
3400    char *cp, *cp1, *cp2;
3401    ConfClient *pCC;
3402    char *Talker = NULL;
3403    int bConferencing = ConfEnable;
3404    union {
3405       char *cp;
3406       rtcp_t *p;
3407    } u;
3408 
3409    union {
3410       char *cp;
3411       rtcp_sdes_item_t *p;
3412    } Item;
3413    int i = rtp_version - 1;
3414 
3415 
3416    if(rtp_version != ILINK_RTP_VERSION && OurNodeID == 0) {
3417    // Don't generate an SDES for SF or RTP until we have a node number
3418       return;
3419    }
3420 
3421    gProtoData[i].bTxtExtension = bTxtExtension;
3422    u.cp = gProtoData[i].OurSDES;
3423 
3424    u.p->common.version = rtp_version;
3425    u.p->common.p = 0;
3426    u.p->common.count = 0;
3427    u.p->common.pt = RTCP_RR;
3428    u.p->common.length = htons(1);
3429    u.p->r.rr.ssrc = OurNodeID;
3430 
3431    u.cp += sizeof(u.p->common) + sizeof(u.p->r.rr.ssrc);
3432 
3433    if(rtp_version == SF_RTP_VERSION) {
3434    // Only the first RTCP packet is version 1 on Speek freely, the
3435    // remaining packets report the standard version
3436       rtp_version = RTP_VERSION;
3437    }
3438 
3439    u.p->common.version = rtp_version;
3440    u.p->common.p = 1;
3441    u.p->common.count = 1;
3442    u.p->common.pt = RTCP_SDES;
3443 
3444    u.p->r.sdes.src = OurNodeID;
3445    pFirstData = (char *) &u.p->r.sdes.src;
3446 
3447    Item.p = &u.p->r.sdes.item[0];
3448    Item.p->type = RTCP_SDES_CNAME;
3449    Item.p->length = (u_int8) strlen(CallSignString);
3450    memcpy(Item.p->data,CallSignString,Item.p->length);
3451 
3452    Item.cp = Item.cp + sizeof(rtcp_sdes_item_t) + Item.p->length - 1;
3453    Item.p->type = RTCP_SDES_NAME;
3454 
3455    if(rtp_version == ILINK_RTP_VERSION) {
3456 
3457       if((pCC = ClientTalking) != NULL) {
3458          cp1 = NULL;
3459          cp = strchr(pCC->CallPlus,' ');
3460 
3461          if(cp != NULL && cp[1] == '(') {
3462             while((cp = strchr(&cp[1],'(')) != NULL) {
3463                cp1 = cp;
3464             }
3465          }
3466 
3467          if(cp1 != NULL && (cp2 = strchr(cp1,')')) != NULL) {
3468          // CallPlus plus contains (<stuff>), just pass along the <stuff>
3469             CharSave = *cp2;
3470             *cp2 = 0;
3471             Talker = strdup(&cp1[1]);
3472             *cp2 = CharSave;
3473          }
3474          else {
3475             if((cp = strchr(ClientTalking->CallPlus,' ')) != NULL) {
3476                while(*cp == ' ') {
3477                   cp++;
3478                }
3479                while(isalnum(*cp)) {
3480                   cp++;
3481                }
3482 
3483             // truncate name at after first non-alphanumeric character
3484                CharSave = *cp;
3485                *cp = 0;
3486             }
3487             Talker = strdup(ClientTalking->CallPlus);
3488 
3489          // restore the original character
3490             if(cp != NULL) {
3491                *cp = CharSave;
3492             }
3493          }
3494       }
3495 
3496       if(SF_Port != SF_ReplyPort) {
3497       // Configured for EchoIRLP
3498          if(ConferenceClients < 3) {
3499          // EchoIRLP has an internal connection so 2 connections isn't
3500          // really a conference
3501             bConferencing = FALSE;
3502          }
3503       }
3504 #ifdef   LINK_BOX
3505       else {
3506          if(ConferenceClients < 2) {
3507             bConferencing = FALSE;
3508          }
3509       }
3510 #endif
3511 
3512       if(!bConferencing) {
3513          if(UserName != NULL) {
3514             sprintf(Item.p->data,"%s %s",ConferenceCall,UserName);
3515          }
3516          else {
3517             strcpy(Item.p->data,ConferenceCall);
3518          }
3519       }
3520       else if(Talker != NULL) {
3521          sprintf(Item.p->data,"%s (%s) CONF",ConferenceCall,Talker);
3522       }
3523       else {
3524          sprintf(Item.p->data,"%s (Conference %s) CONF",ConferenceCall,
3525                  GetUserCountString());
3526       }
3527 
3528       Item.p->length = (u_int8) strlen(Item.p->data);
3529 
3530       Item.cp = Item.cp + sizeof(rtcp_sdes_item_t) + Item.p->length - 1;
3531       Item.p->type = RTCP_SDES_EMAIL;
3532       Item.p->length = (u_int8) strlen(CallSignString);
3533       memcpy(Item.p->data,CallSignString,Item.p->length);
3534 
3535       Item.cp = Item.cp + sizeof(rtcp_sdes_item_t) + Item.p->length - 1;
3536       Item.p->type = RTCP_SDES_PHONE;
3537       sprintf(Item.p->data,"%02d:%02d",tm->tm_hour,tm->tm_min);
3538       Item.p->length = (u_int8) strlen(Item.p->data);
3539    }
3540    else {
3541    // RTP / Speak freely
3542       if(FullName != NULL) {
3543          strcpy(Item.p->data,FullName);
3544       }
3545       else if(UserName != NULL) {
3546          sprintf(Item.p->data,"%s %s",ConferenceCall,UserName);
3547       }
3548       else {
3549          strcpy(Item.p->data,ConferenceCall);
3550       }
3551       Item.p->length = (u_int8) strlen(Item.p->data);
3552    }
3553 
3554 // Set tool item so another conference can identify us as thebridge
3555    Item.cp = Item.cp + sizeof(rtcp_sdes_item_t) + Item.p->length - 1;
3556    Item.p->type = RTCP_SDES_TOOL;
3557    sprintf(Item.p->data,PACKAGE " V " VERSION);
3558 
3559    Item.p->length = (u_int8) strlen(Item.p->data);
3560 
3561    if(rtp_version == ILINK_RTP_VERSION && bTxtExtension && ConfEnable) {
3562    // Set
3563       Item.cp = Item.cp + sizeof(rtcp_sdes_item_t) + Item.p->length - 1;
3564       Item.p->type = RTCP_SDES_PRIV;
3565 
3566       if(Talker != NULL) {
3567          sprintf(Item.p->data,"txt(%s)",Talker);
3568       }
3569       else {
3570          sprintf(Item.p->data,"txt(Conference%s)",GetUserCountString());
3571       }
3572       Item.p->length = (u_int8) strlen(Item.p->data);
3573    }
3574 
3575 // enable remote DTMF pad
3576    if(EnableRemoteDTMF) {
3577       Item.cp = Item.cp + sizeof(rtcp_sdes_item_t) + Item.p->length - 1;
3578       Item.p->type = RTCP_SDES_PRIV;
3579       Item.p->length = 3;
3580       memcpy(Item.p->data,"\001D1",3);
3581    }
3582 
3583    if(FullDuplex) {
3584    // We're capable of running full duplex
3585       Item.cp = Item.cp + sizeof(rtcp_sdes_item_t) + Item.p->length - 1;
3586       Item.p->type = RTCP_SDES_PRIV;
3587       Item.p->length = 5;
3588       memcpy(Item.p->data,"\003dpx1",5);
3589    }
3590 
3591    Item.cp = Item.cp + sizeof(rtcp_sdes_item_t) + Item.p->length - 1;
3592    Item.p->type = RTCP_SDES_END ;
3593    Item.p->length = 0;
3594    Item.cp = Item.cp + sizeof(rtcp_sdes_item_t) + Item.p->length - 1;
3595 
3596    PadCount = 4 - ((Item.cp - pFirstData) % 4);
3597 
3598    if(PadCount > 0 && PadCount < 4) {
3599    // Padding is needed
3600       u.p->common.p = 1;
3601       if(PadCount > 1) {
3602          memset(Item.cp,0,PadCount-1);
3603       }
3604       Item.cp[PadCount-1] = PadCount;
3605       Item.cp += PadCount;
3606    }
3607    else {
3608    // No padding needed
3609       u.p->common.p = 0;
3610    }
3611    u.p->common.length = htons((u_short) ((Item.cp - pFirstData)/4));
3612    gProtoData[i].SDESLen = Item.cp - gProtoData[i].OurSDES;
3613 
3614    if(Talker != NULL) {
3615       free(Talker);
3616    }
3617 }
3618 
GenBye(ConfClient * pCC,char * Temp,char * Reason)3619 int GenBye(ConfClient *pCC,char *Temp,char *Reason)
3620 {
3621    char PadCount;
3622    char *pFirstData;
3623    char *cp;
3624 
3625    union {
3626       char *cp;
3627       rtcp_t *p;
3628    } u;
3629 
3630    u.cp = Temp;
3631 
3632    u.p->common.version = pCC->Proto + 1;
3633    u.p->common.p = 0;
3634    u.p->common.count = 0;
3635    u.p->common.pt = RTCP_RR;
3636    u.p->common.length = htons(1);
3637    u.p->r.rr.ssrc = 0;
3638 
3639    u.cp += sizeof(u.p->common) + sizeof(u.p->r.rr.ssrc);
3640 
3641 // NB: SF has version 1 as the in the first chunk, but version 2 in
3642 // the remaining chunks.
3643    u.p->common.version = pCC->Proto == PROTO_ILINK ? 3 : 2;
3644    u.p->common.count = 1;
3645    u.p->common.pt = RTCP_BYE;
3646 
3647    u.p->r.bye.src[0] = OurNodeID;
3648    pFirstData = (char *) &u.p->r.bye.src;
3649    cp = pFirstData + sizeof(u.p->r.bye.src);
3650 
3651    strcpy(&cp[1],Reason);
3652 
3653    *cp = (char) strlen(Reason);
3654    cp += *cp + 1;
3655 
3656    PadCount = 4 - ((cp - pFirstData) % 4);
3657 
3658    if(PadCount > 0 && PadCount < 4) {
3659    // Padding is needed
3660       u.p->common.p = 1;
3661       if(PadCount > 1) {
3662          memset(cp,0,PadCount-1);
3663       }
3664       cp[PadCount-1] = PadCount;
3665       cp += PadCount;
3666    }
3667    else {
3668    // No padding needed
3669       u.p->common.p = 0;
3670    }
3671    u.p->common.length = htons((u_short) ((cp - pFirstData)/4));
3672    return cp - Temp;
3673 }
3674 
3675 // Convert string to upper case
Convert2Upper(char * cp)3676 void Convert2Upper(char *cp)
3677 {
3678    while(*cp) {
3679       *cp = toupper(*cp);
3680       cp++;
3681    }
3682 }
3683 
SetLurkMode(ClientInfo * p,char * Arg,int bEnable)3684 void SetLurkMode(ClientInfo *p,char *Arg,int bEnable)
3685 {
3686    ConfClient *pCC;
3687    struct avl_traverser avl_trans;
3688    ConfServer *pCS = (ConfServer *) p->p;
3689 
3690 // Try to find a station matching the specified callsign
3691    pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
3692    while(pCC != NULL) {
3693       if(STRCMP(Arg,pCC->Callsign) == 0) {
3694          if((pCC->bLurking && !bEnable) || (!pCC->bLurking && bEnable)) {
3695             pCC->bLurking = bEnable;
3696             pCC->bAutoLurk = bEnable;
3697             pCS->bSendStationList = TRUE;
3698          }
3699          break;
3700       }
3701       pCC = (ConfClient *) avl_t_next(&avl_trans);
3702    }
3703 
3704    if(pCC == NULL) {
3705       BufPrintf(p,"Say what?\r");
3706    }
3707 }
3708 
CmdLurk(ClientInfo * p,ConfClient * pCC,char * Arg)3709 void CmdLurk(ClientInfo *p,ConfClient *pCC,char *Arg)
3710 {
3711    ConfServer *pCS = (ConfServer *) p->p;
3712    int bLurkModeSet = FALSE;
3713 
3714    if(pCC->bSysop && *Arg) {
3715       if(STRCMP(Arg,"enable") == 0) {
3716          bLurkDisabled = FALSE;
3717          BufPrintf(p,"Lurking enabled\r");
3718       }
3719       else if(STRCMP(Arg,"disable") == 0) {
3720          bLurkDisabled = TRUE;
3721          BufPrintf(p,"Lurking disabled\r");
3722       }
3723       else {
3724          SetLurkMode(p,Arg,TRUE);
3725       }
3726       bLurkModeSet = TRUE;
3727    }
3728 
3729    if(!bLurkModeSet) {
3730       if(bLurkDisabled) {
3731          BufPrintf(p,"Sorry, the administrator of this conference has "
3732                    "disabled the .lurk command\r");
3733       }
3734       else if(!pCC->bLurking) {
3735          pCC->bAutoLurk = TRUE;
3736          pCC->bLurking = TRUE;
3737          pCS->bSendStationList = TRUE;
3738       }
3739    }
3740 }
3741 
CmdDelurk(ClientInfo * p,ConfClient * pCC,char * Arg)3742 void CmdDelurk(ClientInfo *p,ConfClient *pCC,char *Arg)
3743 {
3744    ConfServer *pCS = (ConfServer *) p->p;
3745 
3746    if(pCC->bSysop && *Arg) {
3747       SetLurkMode(p,Arg,FALSE);
3748    }
3749    else {
3750       pCC->bLurking = FALSE;
3751       pCC->bAutoLurk = FALSE;
3752       if(pCC->bLurking) {
3753          pCS->bSendStationList = TRUE;
3754       }
3755    }
3756 }
3757 
CmdLurkers(ClientInfo * p,ConfClient * pCC1,char * Arg)3758 void CmdLurkers(ClientInfo *p,ConfClient *pCC1,char *Arg)
3759 {
3760    ConfClient *pCC;
3761    struct avl_traverser avl_trans;
3762    int Lurkers = 0;
3763    ConfServer *pCS = (ConfServer *) p->p;
3764 
3765 
3766    pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
3767    while(pCC != NULL) {
3768       if(pCC->bInConf && pCC->bLurking) {
3769          if(Lurkers == 0) {
3770             BufPrintf(p,"Lurkers:\r");
3771          }
3772          BufPrintf(p,"%s\r",pCC->CallPlus);
3773          Lurkers++;
3774       }
3775       pCC = (ConfClient *) avl_t_next(&avl_trans);
3776    }
3777 
3778    if(Lurkers == 0) {
3779       BufPrintf(p,"I see no lurkers here, try down below\r");
3780    }
3781 }
3782 
CmdUpTime(ClientInfo * p,ConfClient * pCC1,char * Arg)3783 void CmdUpTime(ClientInfo *p,ConfClient *pCC1,char *Arg)
3784 {
3785    BufPrintf(p,"Uptime: %s\r",TimeLapse2String(BootTime,FALSE));
3786 }
3787 
CmdShutdown(ClientInfo * p,ConfClient * pCC1,char * Arg)3788 void CmdShutdown(ClientInfo *p,ConfClient *pCC1,char *Arg)
3789 {
3790    BufPrintf(p,"Shutting down\r");
3791    bShutdown = TRUE;
3792 }
3793 
CmdStats(ClientInfo * p,ConfClient * pCC1,char * Arg)3794 void CmdStats(ClientInfo *p,ConfClient *pCC1,char *Arg)
3795 {
3796    ConfServer *pCS = (ConfServer *) p->p;
3797    u_int TxBytes = pCS->TxBytes;
3798    u_int TxMBytes = pCS->TxMBytes;
3799    u_int RxBytes = pCS->RxBytes;
3800    u_int RxMBytes = pCS->RxMBytes;
3801 
3802 
3803    CalcBW(pCS,FALSE);
3804    CmdUpTime(p,pCC1,Arg);
3805 
3806 
3807    if(TxBytes > 1000000) {
3808       TxMBytes += TxBytes / 1000000;
3809       TxBytes %= 1000000;
3810    }
3811 
3812    if(RxBytes > 1000000) {
3813       RxMBytes += RxBytes / 1000000;
3814       RxBytes %= 1000000;
3815    }
3816 
3817    BufPrintf(p,"Tx packets: %d, ",pCS->TxCount);
3818    BufPrintf(p,"%d.%02d Mbytes\r",TxMBytes,TxBytes*100/1000000);
3819    BufPrintf(p,"Rx packets: %d, ",pCS->RxCount);
3820    BufPrintf(p,"%d.%02d Mbytes\r",RxMBytes,RxBytes*100/1000000);
3821    BufPrintf(p,"Connections: %d\r",ClientConnects);
3822 
3823    BufPrintf(p,"Users: %d of %d Max\r",ConferenceClients,MaxConferenceClients);
3824    BufPrintf(p,"Bandwidth Tx: %d Kbps, ",pCS->TxBandWidth);
3825    BufPrintf(p,"Rx: %d Kbps\r",pCS->RxBandWidth);
3826 
3827    BufPrintf(p,"Peak users: %d, %s\r",PeakClients,TimeT2String(PeakClientTime));
3828    BufPrintf(p,"Peak Tx bandwidth: %d Kbps @ %s\r",pCS->PeakTxBandWidth,
3829              TimeT2String(pCS->PeakTxBandWidthTime));
3830    BufPrintf(p,"Peak Rx bandwidth: %d Kbps @ %s\r",pCS->PeakRxBandWidth,
3831              TimeT2String(pCS->PeakRxBandWidthTime));
3832 }
3833 
CmdVersion(ClientInfo * p,ConfClient * pCC1,char * Arg)3834 void CmdVersion(ClientInfo *p,ConfClient *pCC1,char *Arg)
3835 {
3836    BufPrintf(p,PACKAGE " V " VERSION " on " OUR_HOST "\r");
3837 }
3838 
3839 #ifndef _WIN32
3840 extern int CallBacksRegistered;
3841 extern int SigChilds;
3842 extern int ChildExits;
3843 #endif
3844 
CmdDebug(ClientInfo * p,ConfClient * pCC,char * Arg)3845 void CmdDebug(ClientInfo *p,ConfClient *pCC,char *Arg)
3846 {
3847    int i;
3848    ConfServer *pCS = (ConfServer *) p->p;
3849 
3850 #ifndef _WIN32
3851    BufPrintf(p,"CallBacksRegistered: %d\r",CallBacksRegistered);
3852    BufPrintf(p,"SigChilds: %d\r",SigChilds);
3853    BufPrintf(p,"ChildExits: %d\r",ChildExits);
3854 #endif
3855    BufPrintf(p,"OpenSockets: %d\r",OpenSockets);
3856    BufPrintf(p,"ClientTree entries: %d\r",ClientTree->avl_count);
3857    BufPrintf(p,"ConfTree entries: %d\r",pCS->ConfTree->avl_count);
3858    BufPrintf(p,"DuplicateClientsDeleted: %d\r",DuplicateClientsDeleted);
3859    BufPrintf(p,"Rx errors: %d\r",pCS->RxErrs);
3860    BufPrintf(p,"Tx errors: %d\r",pCS->TxErrs);
3861    BufPrintf(p,"Bad RTCP packets Rxed: %d\r",BadRTCPPacketCount);
3862    BufPrintf(p,"Node #: %d\r",ntohl(OurNodeID));
3863    BufPrintf(p,"EchoLink Auth Failures: %d\r",EchoAuthenticationFailures);
3864    BufPrintf(p,"EchoLink Auth IP compare failures: %d\r",EchoLinkIPCompareFailures);
3865    BufPrintf(p,"Other Auth Failures: %d\r",AuthenticationFailures);
3866    BufPrintf(p,"Active Dir entries: %d\r",ActiveDirEntries);
3867    BufPrintf(p,"Inactive Dir entries: %d\r",InactiveDirEntries);
3868    BufPrintf(p,"BelchTime: %d\r",BelchTime);
3869    BufPrintf(p,"PauseTime: %d\r",PauseTime);
3870    BufPrintf(p,"BlabOffTimer: %d\r",BlabOffTimer);
3871    BufPrintf(p,"ZeroSSRC: %d\r",ZeroSSRC);
3872    BufPrintf(p,"EventProcessRunning: %d\r",EventProcessRunning);
3873    BufPrintf(p,"DupsReceived: %d\r",pCS->DupsReceived);
3874    BufPrintf(p,"DupDisconnects: %d\r",pCS->DupDisconnects);
3875 
3876    BufPrintf(p,"Server: Requested, Succeed, Failed\r");
3877    for(i = 0; i < NUM_DIRECTORY_SERVERS; i++) {
3878       if(DirServerHost[i] == NULL || ServerStats[i].Requests == 0) {
3879          break;
3880       }
3881       BufPrintf(p,"%s: %d, %d, %d\r",DirServerHost[i],
3882                 ServerStats[i].Requests,ServerStats[i].Success,
3883                 ServerStats[i].Failure);
3884    }
3885 }
3886 
CmdAbout(ClientInfo * p,ConfClient * pCC1,char * Arg)3887 void CmdAbout(ClientInfo *p,ConfClient *pCC1,char *Arg)
3888 {
3889    ConfClient *pCC;
3890    struct avl_traverser avl_trans;
3891    ConfServer *pCS = (ConfServer *) p->p;
3892 
3893    pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
3894    while(pCC != NULL) {
3895       if(STRCMP(Arg,pCC->Callsign) == 0) {
3896          if(pCC->Info != NULL) {
3897             BufPrintf(p,"\rAbout %s:%s",pCC->Callsign,pCC->Info);
3898          }
3899          else {
3900             BufPrintf(p,"Sorry I don't know anything about %s.\r",Arg);
3901             Rcode = TBD_STATION_NO_INFO;
3902          }
3903          break;
3904       }
3905       pCC = (ConfClient *) avl_t_next(&avl_trans);
3906    }
3907 
3908    if(pCC == NULL) {
3909       BufPrintf(p,"%s not found\r",Arg);
3910       Rcode = TBD_STATION_NOT_FOUND;
3911    }
3912 }
3913 
CmdAdmin(ClientInfo * p,ConfClient * pCC,char * Arg)3914 void CmdAdmin(ClientInfo *p,ConfClient *pCC,char *Arg)
3915 {
3916    if(AdminPass != NULL && STRCMP(AdminPass,Arg) == 0) {
3917       pCC->bAdmin = TRUE;
3918       pCC->bSysop = TRUE;
3919       pCC->bMuted = FALSE;
3920       BufPrintf(p,"Your wish is my command.\r");
3921       LOG_NORM(("Admin %s logged in\n",pCC->Callsign));
3922    }
3923 }
3924 
CmdSysop(ClientInfo * p,ConfClient * pCC,char * Arg)3925 void CmdSysop(ClientInfo *p,ConfClient *pCC,char *Arg)
3926 {
3927    if(SysopPass != NULL && strcmp(SysopPass,Arg) == 0) {
3928       pCC->bSysop = TRUE;
3929       pCC->bMuted = FALSE;
3930       BufPrintf(p,"By your command.\r");
3931       LOG_NORM(("Sysop %s logged in\n",pCC->Callsign));
3932    }
3933 }
3934 
CmdAdmins(ClientInfo * p,ConfClient * pCC1,char * Arg)3935 void CmdAdmins(ClientInfo *p,ConfClient *pCC1,char *Arg)
3936 {
3937    ConfClient *pCC;
3938    struct avl_traverser avl_trans;
3939    ConfServer *pCS = (ConfServer *) p->p;
3940 
3941    BufPrintf(p,"Admins:\r");
3942    pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
3943    while(pCC != NULL) {
3944       if(pCC->bAdmin) {
3945          BufPrintf(p,"%s\r",pCC->CallPlus);
3946       }
3947       pCC = (ConfClient *) avl_t_next(&avl_trans);
3948    }
3949    BufPrintf(p,"Sysops:\r");
3950    pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
3951    while(pCC != NULL) {
3952       if(!pCC->bAdmin && pCC->bSysop) {
3953          BufPrintf(p,"%s\r",pCC->CallPlus);
3954       }
3955       pCC = (ConfClient *) avl_t_next(&avl_trans);
3956    }
3957 }
3958 
SortCClientsByTalkTime(const void * p,const void * p1)3959 int SortCClientsByTalkTime(const void *p, const void *p1)
3960 {
3961    return (*(ConfClient **)p1)->LastAudioIn.tv_sec -
3962       (*(ConfClient **)p)->LastAudioIn.tv_sec;
3963 }
3964 
SortCClientsBySN(const void * p,const void * p1)3965 int SortCClientsBySN(const void *p, const void *p1)
3966 {
3967    return (*(ConfClient **)p)->SN - (*(ConfClient **)p1)->SN;
3968 }
3969 
CmdUsers(ClientInfo * p,ConfClient * pCC1,char * Args)3970 void CmdUsers(ClientInfo *p,ConfClient *pCC1,char *Args)
3971 {
3972    struct avl_traverser avl_trans;
3973    size_t i = 0;
3974    ConfServer *pCS = (ConfServer *) p->p;
3975    int CClientsSize = pCS->ConfTree->avl_count * sizeof(ConfClient *);
3976    ConfClient **CClients = NULL;
3977    ConfClient *pCC;
3978    char Line[80];
3979    char *cp;
3980    char *Arg = Args;
3981    int Option = 0;
3982    int bDisplayConnectTime = FALSE;
3983    int bDisplayTalkTime = FALSE;
3984    int bDisplayAttributes = TRUE;
3985    int bDisplayTool = FALSE;
3986    int bStationList = FALSE;
3987    int bSortByTalkTime = FALSE;
3988    int BytesLeft;
3989    int Len;
3990 
3991    while((Option = GetCmdOptions(&Arg,"bcqstTv")) != -1) {
3992       switch(Option) {
3993          case 'b':   // bare
3994             bDisplayAttributes = FALSE;
3995             break;
3996 
3997          case 'c':   // display connection time
3998             bDisplayConnectTime = TRUE;
3999             break;
4000 
4001          case 'q':   // Quiet - for CGI polling, suppress logging.
4002             bLogCmd = FALSE;
4003             break;
4004 
4005          case 't':   // display time since last transmission
4006             bDisplayTalkTime = TRUE;
4007             break;
4008 
4009          case 'T':   // sort by time since last transmission
4010             bSortByTalkTime = TRUE;
4011             break;
4012 
4013          case 'v':   // display client's "tool" and version number
4014             bDisplayTool = TRUE;
4015             break;
4016 
4017          case 's':   // display station list
4018             bStationList = TRUE;
4019             break;
4020 
4021          case '?':   // invalid switch
4022             BufPrintf(p,"Usage:\r");
4023             BufPrintf(p,"users [-b] [-c] [-t] [-T] [-v] [?]\r");
4024             BufPrintf(p,"  -b - bare (suppress user attributes)\r");
4025             BufPrintf(p,"  -c - display time user has been connected\r");
4026             BufPrintf(p,"  -q - Quiet (don't log command)\r");
4027             BufPrintf(p,"  -t - display time since user last transmitted\r");
4028             BufPrintf(p,"  -T - sort by time since last transmission\r");
4029             BufPrintf(p,"  -s - display in station list format\r");
4030             BufPrintf(p,"  -v - display version number of user's client\r");
4031             BufPrintf(p,"  ?  - display User attribute characters\r");
4032             break;
4033       }
4034    }
4035 
4036    do {
4037       if(*Arg == '?') {
4038          BufPrintf(p,"User attributes:\r");
4039          BufPrintf(p,"0 Speak Freely protocol\r");
4040          BufPrintf(p,"1 RTP protocol\r");
4041          BufPrintf(p,"7 G.726 codec\r");
4042          BufPrintf(p,"A Admin\r");
4043          BufPrintf(p,"a ADCPM codec\r");
4044          BufPrintf(p,"B theBridge conference\r");
4045          BufPrintf(p,"C other conference\r");
4046          BufPrintf(p,"c Chat text suppressed\r");
4047          BufPrintf(p,"F playing File\r");
4048          BufPrintf(p,"f Full duplex\r");
4049          BufPrintf(p,"I Isolated (not In conference)\r");
4050          BufPrintf(p,"K Kicked\r");
4051          BufPrintf(p,"L Lurker\r");
4052          BufPrintf(p,"m audio Muted\r");
4053          BufPrintf(p,"M audio and text Muted\r");
4054          BufPrintf(p,"n Nailed up connection\r");
4055          BufPrintf(p,"P Permanent connection\r");
4056          BufPrintf(p,"R Receive only (monitored)\r");
4057          BufPrintf(p,"S Sysop\r");
4058          BufPrintf(p,"t Text muted\r");
4059          BufPrintf(p,"T Talking\r");
4060          BufPrintf(p,"u uLaw codec\r");
4061          BufPrintf(p,"x inactive\r");
4062          break;
4063       }
4064 
4065       if(bStationList) {
4066          GenStationList(p);
4067       }
4068       else {
4069          if((CClients = malloc(CClientsSize)) == NULL) {
4070             LOG_ERROR(("CmdUsers: malloc failed\n"));
4071             break;
4072          }
4073          pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
4074          while(pCC != NULL) {
4075             CClients[i++] = pCC;
4076             pCC = (ConfClient *) avl_t_next(&avl_trans);
4077          }
4078 
4079          qsort(CClients,pCS->ConfTree->avl_count,sizeof(ConfClient *),
4080                bSortByTalkTime? SortCClientsByTalkTime : SortCClientsBySN);
4081 
4082          for(i = 0; i < pCS->ConfTree->avl_count; i++) {
4083             pCC = CClients[i];
4084             cp = Line;
4085             *cp = 0;
4086 
4087             if(bDisplayAttributes) {
4088                if(pCC->bAdmin) {
4089                   *cp++ = 'A';
4090                }
4091                else if(pCC->bSysop) {
4092                   *cp++ = 'S';
4093                }
4094 
4095                if(!pCC->bInConf) {
4096                   *cp++ = 'I';
4097                }
4098 
4099                if(pCC->bLurking) {
4100                   *cp++ = 'L';
4101                }
4102                else if(pCC->bAutoLurk) {
4103                   *cp++ = 'l';
4104                }
4105 
4106                if(pCC->bMuted) {
4107                   *cp++ = 'm';
4108                }
4109 
4110                if(pCC->bSWL) {
4111                   *cp++ = 'M';
4112                }
4113 
4114                if(pCC->bKicked) {
4115                   *cp++ = 'K';
4116                }
4117 
4118                if(pCC->bTBD) {
4119                   *cp++ = 'B';
4120                }
4121                else if(pCC->bConf) {
4122                   *cp++ = 'C';
4123                }
4124 
4125                if(pCC->bFileIOActive) {
4126                   *cp++ = 'F';
4127                }
4128                else if(!pCC->bConnected) {
4129                   *cp++ = 'x';
4130                }
4131 
4132                if(pCC->bPermanent) {
4133                   *cp++ = 'P';
4134                }
4135 
4136                if(pCC->bTalking) {
4137                   *cp++ = 'T';
4138                }
4139 
4140                if(pCC->bMuteChat) {
4141                   *cp++ = 't';
4142                }
4143 
4144                else if(pCC->Proto != PROTO_ILINK) {
4145                   *cp++ = (char) ('0' + pCC->Proto);
4146                }
4147 
4148                if(pCC->bNoChat) {
4149                   *cp++ = 'c';
4150                }
4151 
4152                if(pCC->bMonitor) {
4153                   *cp++ = 'R';
4154                }
4155 
4156                if(pCC->bTxtExtension) {
4157                   *cp++ = '!';
4158                }
4159 
4160                if(pCC->CompressionType == RTP_PT_DVI4_8K) {
4161                   *cp++ = 'a';
4162                }
4163 
4164                if(pCC->CompressionType == RTP_PT_PCMU) {
4165                   *cp++ = 'u';
4166                }
4167 
4168                if(pCC->CompressionType == RTP_PT_G726) {
4169                   *cp++ = '7';
4170                }
4171 
4172                if(pCC->bFullDuplex) {
4173                   *cp++ = 'f';
4174                }
4175 
4176                if(pCC->bNailed) {
4177                   *cp++ = 'n';
4178                }
4179             }
4180             else if(!pCC->bConnected) {
4181                continue;
4182             }
4183 
4184             if(bDisplayConnectTime) {
4185                BytesLeft = sizeof(Line)-(cp-Line);
4186                Len = snprintf(cp,BytesLeft,"%s%s%s",
4187                               (bDisplayAttributes && Line[0] != 0) ? ", " : "",
4188                               bDisplayAttributes ? "connected: " : "\t",
4189                               TimeLapse2String(pCC->LoginTime,TRUE));
4190                if(Len != -1 && Len < BytesLeft) {
4191                   cp += Len;
4192                }
4193             }
4194 
4195             if(bDisplayTalkTime) {
4196                BytesLeft = sizeof(Line)-(cp-Line);
4197                if(pCC->FirstAudioIn.tv_sec != 0) {
4198                   Len = snprintf(cp,BytesLeft,"%s%s%s",
4199                                  (bDisplayAttributes && Line[0] != 0) ? ", " : "",
4200                                  bDisplayAttributes ? "last tx: " : "\t",
4201                                  TimeLapse2String(pCC->LastAudioIn.tv_sec,TRUE));
4202                   if(Len != -1 && Len < BytesLeft) {
4203                      cp += Len;
4204                   }
4205                }
4206             }
4207 
4208             if(bDisplayTool && pCC->Tool != NULL) {
4209                BytesLeft = sizeof(Line)-(cp-Line);
4210                Len = snprintf(cp,BytesLeft,"%s%s%s",
4211                               (bDisplayAttributes && Line[0] != 0) ? ", " : "",
4212                               bDisplayAttributes ? "ver: " : "\t",
4213                               pCC->Tool);
4214                if(Len != -1 && Len < BytesLeft) {
4215                   cp += Len;
4216                }
4217             }
4218             *cp = 0;
4219 
4220             BufPrintf(p,"%d. %s %s\r",i+1,pCC->Callsign,Line);
4221          }
4222          free(CClients);
4223       } while(FALSE);
4224    } while(FALSE);
4225 }
4226 
CmdMessage(ClientInfo * p,ConfClient * pCC1,char * Arg)4227 void CmdMessage(ClientInfo *p,ConfClient *pCC1,char *Arg)
4228 {
4229    ConfServer *pCS = (ConfServer *) p->p;
4230    struct avl_traverser avl_trans;
4231    ConfClient *pCC = avl_t_first(&avl_trans,pCS->ConfTree);
4232 
4233    BufPrintf(p,"%s\r",Arg);
4234    p->Count++;
4235 
4236    while(pCC != NULL) {
4237       SendBuf2(p,pCC,FALSE);
4238       pCC = (ConfClient *) avl_t_next(&avl_trans);
4239    }
4240    SendChatEvent("sent_chat",p->Buf);
4241 
4242    p->Count = 0;
4243    BufPrintf(p,"%c" NDATA "Message sent\r",ILINK_DATA_PACKET);
4244 }
4245 
CmdMonitor(ClientInfo * p,ConfClient * pCC1,char * Arg)4246 void CmdMonitor(ClientInfo *p,ConfClient *pCC1,char *Arg)
4247 {
4248    ConfClient *pCC;
4249    struct avl_traverser avl_trans;
4250    ConfServer *pCS = (ConfServer *) p->p;
4251    int bMonitor = TRUE;
4252 
4253    if(strstr(Arg,"disable") == Arg) {
4254       bMonitor = FALSE;
4255       while(*Arg && *Arg != ' ') {
4256          Arg++;
4257       }
4258 
4259       while(*Arg && *Arg == ' ') {
4260          Arg++;
4261       }
4262    }
4263 
4264    pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
4265    while(pCC != NULL) {
4266       if(STRCMP(Arg,pCC->Callsign) == 0) {
4267          pCC->bMonitor = bMonitor;
4268          if(bMonitor) {
4269             BufPrintf(p,"Monitoring %s\r",Arg);
4270          }
4271          else {
4272             BufPrintf(p,"transmit enabled to %s\r",Arg);
4273          }
4274          break;
4275       }
4276       pCC = (ConfClient *) avl_t_next(&avl_trans);
4277    }
4278 
4279    if(*Arg == 0) {
4280    // No argument
4281       BufPrintf(p,"Usage:\r");
4282       BufPrintf(p,"monitor <call>\r");
4283       BufPrintf(p,"monitor disable <call>\r");
4284    }
4285    else if(pCC == NULL) {
4286       BufPrintf(p,"%s not found\r",Arg);
4287       Rcode = TBD_STATION_NOT_FOUND;
4288    }
4289 }
4290 
4291 //  "lookup"  <callsign>
CmdLookUp(ClientInfo * p,ConfClient * pCC,char * Arg)4292 void CmdLookUp(ClientInfo *p,ConfClient *pCC,char *Arg)
4293 {
4294    UserInfo UserLookup;
4295    UserInfo *pUser = NULL;
4296 
4297    char *Callsign = NULL;
4298    char *cp = Arg;
4299    char  *WhiteSpace = "\t\n ";
4300 
4301    if((Callsign = strtok(cp,WhiteSpace)) == NULL) {
4302       BufPrintf(p,"Usage: lookup <callsign>\r");
4303       Rcode = TBD_ERR_INVALID_ARG;
4304    }
4305 
4306    if(Rcode == 0) {
4307       Convert2Upper(Callsign);
4308       UserLookup.Callsign = Callsign;
4309       if((pUser = avl_find(UserTree,&UserLookup)) != NULL) {
4310          BufPrintf(p,"Callsign:%s \r"
4311                    "NodeID:%d \r"
4312                    "Qth:%s \r"
4313                    "%s \r",
4314                    pUser->Callsign,
4315                    pUser->NodeID,
4316                    pUser->Qth,
4317                    pUser->bBusy?"Busy":"Not Busy");
4318       }
4319       else {
4320          BufPrintf(p,"Station \"%s\" not found.\r",Callsign);
4321          Rcode = TBD_STATION_NOT_FOUND;
4322       }
4323    }
4324 }
4325 
4326 // "mute" | "unmute" [-u][-c][-t][-a][-p] [.] [-x][<callsign> ...] [chat]
CmdMuteCommon(ClientInfo * p,ConfClient * pCC1,char * Arg,int bMute)4327 void CmdMuteCommon(ClientInfo *p,ConfClient *pCC1,char *Arg,int bMute)
4328 {
4329    #define MAX_MUTE_CALLS  32
4330    ConfClient *pCC = NULL;
4331    struct avl_traverser avl_trans;
4332    ConfServer *pCS = (ConfServer *) p->p;
4333    int bDoConf = FALSE;
4334    int bDoTBD = FALSE;
4335    int bDoUsers = FALSE;
4336    int bDoSyops = FALSE;
4337    int bDoRF = FALSE;
4338    int bDoTalker = FALSE;
4339    int bDidTalker = FALSE;
4340    int bDoList = FALSE;
4341    int bDidit = FALSE;
4342    int bDoUsage = FALSE;
4343    int bDidChat = FALSE;
4344    int bMuteChat = FALSE;
4345    int bPersistent = FALSE;
4346    int PersistentCalls = 0;
4347    int bByCall = FALSE;
4348    int bDoIt;
4349    int NumCalls = 0;
4350    int CallsFound = 0;
4351    char *Calls[MAX_MUTE_CALLS];
4352    int CallFound[MAX_MUTE_CALLS];
4353    int OptionsFound = 0;
4354    char *Sep = "";
4355    char *SetSep;
4356    char *MsgStart = &p->Buf[p->Count];
4357    int i;
4358    UserInfo UserLookup;
4359    UserInfo *pUser;
4360 
4361    memset(CallFound,0,sizeof(CallFound));
4362 
4363    if(*Arg == 0) {
4364    // No argument
4365       bDoList = TRUE;
4366    }
4367    else while(*Arg) {
4368       while(*Arg == ' ') {
4369       // Skip spaces between arguments
4370          Arg++;
4371       }
4372 
4373       if(*Arg == '-') {
4374       // switch
4375          Arg++;
4376          OptionsFound++;
4377          while(*Arg && *Arg != ' ') {
4378             switch(tolower(*Arg)) {
4379                case 'a':
4380                   bDoTBD = bDoConf = bDoUsers = bDoSyops = bDoRF = TRUE;
4381                   bDoTalker = TRUE;
4382                   pCS->bMuteTBD = pCS->bMuteRF = pCS->bMuteConf = bMute;
4383                   pCS->bMuteUsers = bMute;
4384                   bDidit = TRUE;
4385                   break;
4386 
4387                case 'c':
4388                   pCS->bMuteTBD = bMute;
4389                   bDoTBD = TRUE;
4390                   bDidit = TRUE;
4391                   break;
4392 
4393                case 'e':
4394                   pCS->bMuteConf = bMute;
4395                   bDoConf = TRUE;
4396                   bDidit = TRUE;
4397                   break;
4398 
4399                case 'p':
4400                   bPersistent = TRUE;
4401                   break;
4402 
4403                case 'r':
4404                   pCS->bMuteRF = bMute;
4405                   bDoRF = TRUE;
4406                   bDidit = TRUE;
4407                   break;
4408 
4409                case 's':
4410                   bDoSyops = TRUE;
4411                   break;
4412 
4413                case 'u':
4414                   pCS->bMuteUsers = bMute;
4415                   bDoUsers = TRUE;
4416                   bDidit = TRUE;
4417                   break;
4418 
4419                case 'x':   // inbound Chat
4420                   bMuteChat = TRUE;
4421                   break;
4422 
4423                case 't':
4424                case '.':
4425                   if(bMute) {
4426                      bDoTalker = TRUE;
4427                      break;
4428                   }
4429                // intentional fall thru to default case if unmute command
4430 
4431                default:
4432                // Unknown switch
4433                   BufPrintf(p,"Error: unknown option \"%c\".\r",*Arg);
4434                // intentional fall thru to 'h' case
4435 
4436                case 'h':
4437                   Arg = " ";
4438                   bDoUsage = TRUE;
4439                   break;
4440             }
4441             Arg++;
4442          }
4443       }
4444       else if(*Arg == '?') {
4445          bDoUsage = TRUE;
4446          break;
4447       }
4448       else if(strncmp(Arg,"chat",4) == 0) {
4449       // Mute/UnMute chat command
4450          OptionsFound++;
4451          pCC1->bNoChat = bMute;
4452          bDidit = TRUE;
4453          bDidChat = TRUE;
4454          Arg += 4;
4455       }
4456       else if(*Arg == '.') {
4457          if(bMute) {
4458             OptionsFound++;
4459             bDoTalker = TRUE;
4460             Arg++;
4461          }
4462          else {
4463          // No '.' option to the unmute command
4464             bDoUsage = TRUE;
4465             break;
4466          }
4467       }
4468       else {
4469       // Assume it's a callsign
4470          OptionsFound++;
4471          Calls[NumCalls++] = Arg;
4472       // Skip to end of callsign
4473          while(*Arg && *Arg != ' ') {
4474             Arg++;
4475          }
4476          if(*Arg) {
4477             *Arg++ = 0;
4478          }
4479          if(NumCalls == MAX_MUTE_CALLS) {
4480          // Callsign list full (!)
4481             break;
4482          }
4483       }
4484    }
4485 
4486    pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
4487    while(!bDoUsage && pCC != NULL) {
4488       bDoIt = bByCall = FALSE;
4489       if(bDoList && bMute) {
4490       // No argument to mute command - list muted stations
4491          if(pCC->bMuted) {
4492             bDidit = TRUE;
4493             BufPrintf(p,"%s\r",pCC->Callsign);
4494          }
4495       }
4496 
4497       if(bDoTalker && pCC == ClientTalking && !bDoConf) {
4498       // Mute station current talking
4499          if(ClientTalking->bConf) {
4500          // The guy talking is on another conference, send a .mute
4501          // command to that conference
4502             BufPrintf(p,"%s>%s\r",ConferenceCall,".mute %s.",
4503                       bMuteChat ? "-x " : "");
4504             Send2(ClientTalking,p->Buf,p->Count,FALSE);
4505             p->Count = 0;  // init for BufPrintf
4506             BufPrintf(p,"%c" NDATA,ILINK_DATA_PACKET);
4507             BufPrintf(p,"Mute command sent to %s\r",ClientTalking->Callsign);
4508             bDidit = TRUE; // message sent
4509          }
4510          else if(pCC1 == ClientTalking) {
4511          // Don't allow someone mute himself by accident
4512             if(!pCC1->bConf) {
4513                BufPrintf(p,"Just stop talking to mute yourself!\r");
4514             }
4515          }
4516          else {
4517             bDidTalker = bDoIt = TRUE;
4518             ClientTalking = NULL;
4519          }
4520       }
4521 
4522       if((!pCC->bSysop || bDoSyops || !bMute) &&
4523          (pCC != pCC1 || !bMute) &&
4524          (pCC != ClientTalking || bDoTalker) &&
4525          (
4526             (bDoTBD && pCC->bTBD) ||
4527             (bDoConf && pCC->bConf && !pCC->bTBD) ||
4528             (bDoUsers && !(pCC->bLink || pCC->bRepeater || pCC->bConf)) ||
4529             (bDoSyops && pCC->bSysop) ||
4530             (bDoRF && (pCC->bLink || pCC->bRepeater)) ||
4531             (bDoTalker && pCC == ClientTalking)
4532          ))
4533       {
4534          bDoIt = TRUE;
4535       }
4536 
4537       for(i = 0; i < NumCalls; i++) {
4538          if(STRCMP(Calls[i],pCC->Callsign) == 0) {
4539             CallFound[i] = TRUE;
4540             bDoIt = TRUE;
4541             bByCall = TRUE;
4542             CallsFound++;
4543             break;
4544          }
4545       }
4546 
4547       if(bDoIt) {
4548          bDidit = TRUE;
4549          if(bMuteChat) {
4550             pCC->bMuteChat = bMute;
4551          }
4552          else {
4553             pCC->bMuted = bMute;
4554          }
4555       }
4556       pCC = (ConfClient *) avl_t_next(&avl_trans);
4557    }
4558 
4559    if(NumCalls > 0 && bPersistent) {
4560    // Persistent request
4561       for(i = 0; i < NumCalls; i++) {
4562          UserLookup.Callsign = Calls[i];
4563          if((pUser = avl_find(UserTree,&UserLookup)) != NULL) {
4564          // user is in directory, save his mute flag
4565             if(bMuteChat) {
4566                pUser->bChatMuted = bMute;
4567                pUser->bChatUnMuted = !bMute;
4568             }
4569             else {
4570                pUser->bMuted = bMute;
4571                pUser->bUnMuted = !bMute;
4572             }
4573          }
4574          else {
4575             LOG_WARN(("CmdMuteCommon(): %s not in directory.\n",
4576                       pCC->Callsign));
4577          }
4578       }
4579    }
4580 
4581    if(bDoUsage) {
4582       Rcode = TBD_ERR_INVALID_CMD;
4583       if(bMute) {
4584          BufPrintf(p,"Usage: mute -[aceprstuX] [<call> ...] [.] [chat]\r");
4585       }
4586       else {
4587          BufPrintf(p,"Usage: unmute -[aceprsuX] [<call> ...] [chat]\r");
4588       }
4589       BufPrintf(p,
4590                 "  -a: All users\r"
4591                 "  -c: tbd conferences\r"
4592                 "  -e: Echolink conferences\r"
4593                 "  -p: Persistent\r"
4594                 "  -r: RF users (-R and -L stations)\r"
4595                 "  -s: Sysops and Admins\r"
4596                 "  -t: Station talking\r"
4597                 "  -u: PC users\r"
4598                 "  -x: chat text from user\r"
4599                 "<call>: specific user\r");
4600    }
4601    else if(!bDidit && bDoTalker && OptionsFound == 1 && ClientTalking == NULL) {
4602    // Simple attempt to mute current talker and no one is talking.
4603       Rcode = TBD_ERR_NO_TALKER;
4604       if(!pCC1->bConf) {
4605       // Command received from a local sysop, not via a quoted command
4606       // Forward the command
4607          BufPrintf(p,"Error: no one is talking.\r");
4608       }
4609    }
4610    else if(bDoList) {
4611    // Now list persistently muted/unmuted text
4612       pUser = (UserInfo *) avl_t_first(&avl_trans,UserTree);
4613       while(pUser != NULL) {
4614          if((bMute && pUser->bChatMuted) || (!bMute && pUser->bChatUnMuted)) {
4615             if(PersistentCalls++ == 0) {
4616                BufPrintf(p,"Stations with persistently %s text:\r",
4617                          bMute ? "muted" : "unmuted");
4618             }
4619             BufPrintf(p,"%s\r",pUser->Callsign);
4620          }
4621          pUser = (UserInfo *) avl_t_next(&avl_trans);
4622       }
4623 
4624    // Now list persistently muted/unmuted stations
4625       PersistentCalls = 0;
4626       pUser = (UserInfo *) avl_t_first(&avl_trans,UserTree);
4627       while(pUser != NULL) {
4628          if((bMute && pUser->bMuted) || (!bMute && pUser->bUnMuted)) {
4629             if(PersistentCalls++ == 0) {
4630                BufPrintf(p,"Stations persistently %s:\r",
4631                          bMute ? "muted" : "unmuted");
4632             }
4633             BufPrintf(p,"%s\r",pUser->Callsign);
4634          }
4635          pUser = (UserInfo *) avl_t_next(&avl_trans);
4636       }
4637 
4638       if(!bDidit) {
4639          BufPrintf(p,"No stations are currently muted.\r",Arg);
4640       }
4641 
4642       if(PersistentCalls == 0 && !bMute) {
4643          BufPrintf(p,"No stations are unmuted persistently.\r",Arg);
4644       }
4645 
4646       if(pCS->bMuteConf || pCS->bMuteUsers || pCS->bMuteRF || pCS->bMuteTBD) {
4647          BufPrintf(p,"New connections from ");
4648          SetSep = " and ";
4649          if(pCS->bMuteConf) {
4650             BufPrintf(p,"Echolink conferences");
4651             Sep = SetSep;
4652          }
4653 
4654          if(pCS->bMuteTBD) {
4655             BufPrintf(p,"%stbd conferences",Sep);
4656             Sep = SetSep;
4657          }
4658 
4659          if(pCS->bMuteUsers) {
4660             BufPrintf(p,"%sPC users",Sep);
4661             Sep = SetSep;
4662          }
4663 
4664          if(pCS->bMuteRF) {
4665             BufPrintf(p,"%sRF users",Sep);
4666             Sep = SetSep;
4667          }
4668          BufPrintf(p," will be muted.\r");
4669       }
4670 
4671       if(pCS->bMuteChat) {
4672          BufPrintf(p,"Chat text from new connections will be muted.\r");
4673       }
4674 
4675       if(pCC1->bNoChat) {
4676          BufPrintf(p,"Your text chat is muted.\r");
4677       }
4678    }
4679    else {
4680       if(bDidit) {
4681        // Did something
4682          SetSep = ", ";
4683       }
4684       else {
4685       // Didn't do anything
4686          BufPrintf(p,"No ");
4687          SetSep = " or ";
4688       }
4689 
4690       if(bDoConf) {
4691          BufPrintf(p,"EchoLink conferences");
4692          Sep = SetSep;
4693       }
4694 
4695       if(bDoTBD) {
4696          BufPrintf(p,"%stbd conferences",Sep);
4697          Sep = SetSep;
4698       }
4699 
4700       if(bDoUsers) {
4701          BufPrintf(p,"%sPC users",Sep);
4702          Sep = SetSep;
4703       }
4704 
4705       if(bDoSyops) {
4706          BufPrintf(p,"%ssysops",Sep);
4707          Sep = SetSep;
4708       }
4709 
4710       if(bDoRF) {
4711          BufPrintf(p,"%sRF users",Sep);
4712          Sep = SetSep;
4713       }
4714 
4715       if(bDoTalker && (bDidTalker || !bDidit)) {
4716          BufPrintf(p,"%sstation talking",Sep);
4717          Sep = SetSep;
4718       }
4719 
4720       if(bDidChat) {
4721          BufPrintf(p,"%schat text",Sep);
4722          Sep = SetSep;
4723       }
4724 
4725       for(i = 0; i < NumCalls; i++) {
4726          if((bDidit && CallFound[i]) || (!bDidit && !CallFound[i])) {
4727             BufPrintf(p,"%s%s",Sep,Calls[i]);
4728             Sep = SetSep;
4729          }
4730       }
4731 
4732       if(bDidit) {
4733          BufPrintf(p,"%s%s%s",
4734                    bMuteChat ? " text" : "",
4735                    bPersistent ? " persistently" : "",
4736                    bMute ? " muted" : " unmuted");
4737          LOG_NORM(("%s by %s\n",MsgStart,pCC1->Callsign));
4738          BufPrintf(p,".\r");
4739 
4740          if(NumCalls != CallsFound) {
4741             Sep = "";
4742             for(i = 0; i < NumCalls; i++) {
4743                if(!CallFound[i]) {
4744                   BufPrintf(p,"%s%s",Sep,Calls[i]);
4745                   Sep = ", ";
4746                }
4747             }
4748             BufPrintf(p," not found.\r");
4749             Rcode = TBD_STATION_NOT_FOUND;
4750          }
4751       }
4752       else {
4753          BufPrintf(p," found.\r");
4754          Rcode = TBD_STATION_NOT_FOUND;
4755       }
4756    }
4757    #undef MAX_MUTE_CALLS
4758 }
4759 
CmdUnMute(ClientInfo * p,ConfClient * pCC1,char * Arg)4760 void CmdUnMute(ClientInfo *p,ConfClient *pCC1,char *Arg)
4761 {
4762    CmdMuteCommon(p,pCC1,Arg,FALSE);
4763 }
4764 
CmdMute(ClientInfo * p,ConfClient * pCC1,char * Arg)4765 void CmdMute(ClientInfo *p,ConfClient *pCC1,char *Arg)
4766 {
4767    CmdMuteCommon(p,pCC1,Arg,TRUE);
4768 }
4769 
StopRecording(ClientInfo * p,const char * Arg)4770 void StopRecording(ClientInfo *p,const char *Arg)
4771 {
4772    FilePacketHdr PktHdr;
4773    ConfServer *pCS = ((ConfServer *) p->p)->pMasterCS;
4774 
4775    if(pCS->fp != NULL) {
4776    // Write end of stream record
4777       PktHdr.Len = 0;
4778       PktHdr.Flags = 0;
4779 
4780       if(fwrite(&PktHdr,sizeof(PktHdr),1,pCS->fp) != 1) {
4781       // Write error
4782          LOG_ERROR(("StopRecording(): fwrite failed, %s",Err2String(errno)));
4783       }
4784 
4785       fclose(pCS->fp);
4786       pCS->fp = NULL;
4787       pCS->bRecording = FALSE;
4788       BufPrintf(p,"Recording completed.\r");
4789    }
4790    else if(STRCMP(Arg,"stop") == 0) {
4791       BufPrintf(p,"Can't stop, we're not recording.\r");
4792    }
4793 }
4794 
4795 // Arg = filename
CmdRecord(ClientInfo * p,ConfClient * pCC1,char * Arg)4796 void CmdRecord(ClientInfo *p,ConfClient *pCC1,char *Arg)
4797 {
4798    char *cp;
4799    ConfServer *pCS = ((ConfServer *) p->p)->pMasterCS;
4800 
4801    StopRecording(p,Arg);
4802 
4803 // Point Arg to the a bare filename to prevent "accidents"
4804    if((cp = strrchr(Arg,'\\')) != NULL) {
4805       Arg = cp + 1;
4806    }
4807 
4808    if((cp = strrchr(Arg,'/')) != NULL) {
4809       Arg = cp + 1;
4810    }
4811 
4812    if(STRCMP(Arg,"stop") != 0) {
4813       if((pCS->fp = FOPEN(Arg,MODE_RDWR_BIN)) == NULL) {
4814          pCS->bRecording = FALSE;
4815          BufPrintf(p,"Error: Couldn't open %s.",Arg);
4816          Rcode = TBD_ERR_FILE_OPEN;
4817       }
4818       else {
4819          pCS->bRecording = TRUE;
4820          BufPrintf(p,"Recording to %s.\r",Arg);
4821       }
4822    }
4823 }
4824 
CmdRefresh(ClientInfo * p,ConfClient * pCC1,char * Arg)4825 void CmdRefresh(ClientInfo *p,ConfClient *pCC1,char *Arg)
4826 {
4827    NextStationListTime = TimeNow.tv_sec + StationListInterval;
4828    ServerRequest(SERV_REQ_LOGIN_AND_LIST,0,NULL);
4829 }
4830 
CmdRehash(ClientInfo * p,ConfClient * pCC1,char * Arg)4831 void CmdRehash(ClientInfo *p,ConfClient *pCC1,char *Arg)
4832 {
4833    bRefreshConfig = TRUE;
4834    BufPrintf(p,"Reloading configuration file.\r");
4835 }
4836 
CmdSet(ClientInfo * p,ConfClient * pCC1,char * Arg)4837 void CmdSet(ClientInfo *p,ConfClient *pCC1,char *Arg)
4838 {
4839    struct config_entry *pVars = ConfigVars;
4840    char *cp;
4841    int x;
4842    float f;
4843    int bSetVariable = FALSE;
4844    void *var_ptr;
4845    char Line[80];
4846    int bFirstLoop = TRUE;
4847    int Err;
4848 
4849    do {
4850       if(*Arg == 0) {
4851          Rcode = TBD_ERR_INVALID_CMD;
4852          BufPrintf(p,"Usage:\n");
4853          BufPrintf(p,"set variable\r");
4854          BufPrintf(p,"set variable = value\r");
4855          break;
4856       }
4857 
4858       if(strchr(Arg,'=') != NULL) {
4859          Err = ParseConfigLine(Arg,pVars,MANUAL_SET);
4860 #ifdef   LINK_BOX
4861          if(Err != 0 && (pVars = GetConfigVarsPtr()) != ConfigVars) {
4862          // Try again
4863             Err = ParseConfigLine(Arg,pVars,MANUAL_SET);
4864             bFirstLoop = FALSE;
4865          }
4866 #endif
4867          if(Err != 0) {
4868             Rcode = TBD_ERR_INVALID_ARG;
4869             BufPrintf(p,"Error: Unable to set variable.\r");
4870             break;
4871          }
4872          else {
4873             bSetVariable = TRUE;
4874          }
4875       }
4876 
4877       if(Rcode == TBD_OK) {
4878       // display the current value
4879          if((cp = strchr(Arg,' ')) != NULL) {
4880          // Remove trailing spaces
4881             *cp = 0;
4882          }
4883          if((cp = strchr(Arg,'=')) != NULL) {
4884          // Remove '='
4885             *cp = 0;
4886          }
4887 
4888          while(pVars->var_name[0] != 0) {
4889             if(!STRCMP(Arg,pVars->var_name)) {
4890             // found our guy
4891                if(pVars->Flags & CON_FLG_BASED_VAR) {
4892                   if((var_ptr = ConfigGetVarPtr(pVars)) == NULL) {
4893                      BufPrintf(p,"Error: Unable to set variable.\r");
4894                      break;
4895                   }
4896                }
4897                else {
4898                   var_ptr = pVars->var_ptr;
4899                }
4900 
4901                switch(pVars->var_type[1]) {
4902                   case 's':   // String value
4903                   // string value, make sure it's set
4904                      cp = *((char **) var_ptr);
4905                      if(cp == NULL) {
4906                         BufPrintf(p,"variable %s is not set\r",pVars->var_name);
4907                      }
4908                      else {
4909                         BufPrintf(p,"%s = \"%s\"\r",pVars->var_name,cp);
4910                      }
4911                      break;
4912 
4913                   case 'd':   // int
4914                   case 'x':
4915                      x = *((int *) var_ptr);
4916                      BufPrintf(p,"%s = ",pVars->var_name);
4917                      BufPrintf(p,pVars->var_type,x);
4918                      BufPrintf(p,"\r");
4919                      break;
4920 
4921                   case 'f':   // float
4922                      f = *((float *) var_ptr);
4923                      BufPrintf(p,"%s = ",pVars->var_name);
4924                      BufPrintf(p,pVars->var_type,f);
4925                      BufPrintf(p,"\r");
4926                      break;
4927 
4928                   case 'F':
4929                   // value is set via function call.
4930                      Line[0] = 0;
4931                      pVars->AccessFunc(pVars,Line,AF_DISPLAY_VAR,sizeof(Line));
4932                      if(Line[0] == 0) {
4933 
4934                         BufPrintf(p,"Sorry, variable %s can not be displayed\r",
4935                                   pVars->var_name);
4936                      }
4937                      else {
4938                         BufPrintf(p,"%s = %s\r",pVars->var_name,Line);
4939                      }
4940                      break;
4941 
4942                   default:
4943                      if(!bSetVariable) {
4944                         BufPrintf(p,"Sorry, variable %s can not be displayed\r",
4945                                   pVars->var_name);
4946                      }
4947                      break;
4948                }
4949                break;
4950             }
4951             pVars++;
4952          }
4953 
4954          if(pVars->var_name[0] != 0) {
4955          // Found it
4956             break;
4957          }
4958 
4959 #ifdef   LINK_BOX
4960          if((pVars = GetConfigVarsPtr()) == ConfigVars)
4961 #endif
4962             bFirstLoop = FALSE;
4963 
4964          if(!bFirstLoop) {
4965             BufPrintf(p,"variable %s not found\r",Arg);
4966             Rcode = TBD_ERR_INVALID_ARG;
4967             break;
4968          }
4969          bFirstLoop = FALSE;
4970       }
4971    } while(TRUE);
4972 }
4973 
CmdShowIP(ClientInfo * p,ConfClient * pCC1,char * Arg)4974 void CmdShowIP(ClientInfo *p,ConfClient *pCC1,char *Arg)
4975 {
4976    struct avl_traverser avl_trans;
4977    ConfServer *pCS = (ConfServer *) p->p;
4978    ConfClient *pCC;
4979    char TypeChar;
4980 
4981    pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
4982    while(pCC != NULL) {
4983       switch(pCC->Proto) {
4984          case PROTO_SF:
4985             TypeChar = 'S';
4986             break;
4987 
4988          case PROTO_RTP:
4989             TypeChar = 'R';
4990             break;
4991 
4992          case PROTO_ILINK:
4993             TypeChar = 'E';
4994             break;
4995 
4996          default:
4997             TypeChar = '?';
4998             break;
4999       }
5000       BufPrintf(p,"%s\t%s\t%c\r",pCC->Callsign,
5001                 inet_ntoa(pCC->HisAdr.i.sin_addr),TypeChar);
5002       pCC = (ConfClient *) avl_t_next(&avl_trans);
5003    }
5004    BufPrintf(p,"\r");
5005 }
5006 
CmdDNS(ClientInfo * p,ConfClient * pCC1,char * Arg)5007 void CmdDNS(ClientInfo *p,ConfClient *pCC1,char *Arg)
5008 {
5009    if(STRCMP(Arg,"refresh") == 0) {
5010       UpdateDNSCache(TRUE);
5011       BufPrintf(p,"Refreshing DNS cache\r");
5012    }
5013    else if(STRCMP(Arg,"list") == 0) {
5014       DumpDNSCache(p);
5015    }
5016    else {
5017       BufPrintf(p,"Usage:\r"
5018                   "dns refresh\r"
5019                   "dns list\r");
5020       Rcode = TBD_ERR_INVALID_CMD;
5021    }
5022 }
5023 
CmdKick(ClientInfo * p,ConfClient * pCC1,char * Arg)5024 void CmdKick(ClientInfo *p,ConfClient *pCC1,char *Arg)
5025 {
5026    ConfClient *pCC;
5027    struct avl_traverser avl_trans;
5028    ConfServer *pCS = (ConfServer *) p->p;
5029 
5030    pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
5031    while(pCC != NULL) {
5032 
5033       if(STRCMP(Arg,pCC->Callsign) == 0) {
5034          LOG_NORM(("%s kicked by %s\n",pCC->Callsign,pCC1->Callsign));
5035          BufPrintf(p,"%s kicked\r",Arg);
5036          pCC->bKicked = TRUE;
5037          if(pCC->bConnected) {
5038             pCC->bConnected = FALSE;
5039             ConferenceClients--;
5040          }
5041          pCC->bSWL = TRUE;
5042          break;
5043       }
5044       pCC = (ConfClient *) avl_t_next(&avl_trans);
5045    }
5046 
5047    if(pCC == NULL) {
5048       BufPrintf(p,"%s not found\r",Arg);
5049       Rcode = TBD_STATION_NOT_FOUND;
5050    }
5051 }
5052 
ACLAdd(ClientInfo * p,ConfClient * pCC1,char * Arg,int bAllowed)5053 void ACLAdd(ClientInfo *p,ConfClient *pCC1,char *Arg,int bAllowed)
5054 {
5055    ACL_User ACL_New;
5056    char *CmdString = bAllowed ? "allowed" : "banned";
5057    char  *WhiteSpace = "\t\n ";
5058    int Count = p->Count;
5059 
5060    if(!bAllowed) {
5061    // Kick the guy if he's on currently
5062       CmdKick(p,pCC1,Arg);
5063       p->Count = Count; // remove message generated by kick command
5064    }
5065 
5066    memset(&ACL_New,0,sizeof(ACL_New));
5067    ACL_New.bAuthorized = bAllowed;
5068 
5069    ACL_New.Callsign = strtok(Arg,WhiteSpace);
5070    Convert2Upper(ACL_New.Callsign);
5071 
5072    ACL_New.HostName = strtok(NULL,WhiteSpace);
5073 
5074    if(ACL_New.HostName == NULL) {
5075       ACL_New.HostName = "-";
5076    }
5077    else {
5078       ACL_New.Password = strtok(NULL,WhiteSpace);
5079    }
5080 
5081    if(ACL_New.Password == NULL) {
5082       ACL_New.Password = "-";
5083    }
5084    else {
5085       // Take the rest of the string
5086       ACL_New.CallPlus = strtok(NULL,"");
5087       if(ACL_New.CallPlus != NULL) {
5088          // Skip leading white space
5089          while(*ACL_New.CallPlus && isspace(*ACL_New.CallPlus)) {
5090             ACL_New.CallPlus++;
5091          }
5092       }
5093    }
5094 
5095    if(!ACLAddUser(&ACL_New)) {
5096       BufPrintf(p,"Error unable to resolve hostname \"%s\".",
5097                 ACL_New.HostName);
5098    }
5099    else {
5100       SaveACL();
5101       BufPrintf(p,"%s %s\r",Arg,CmdString);
5102       LOG_NORM(("%s %s by %s\n",ACL_New.Callsign,CmdString,pCC1->Callsign));
5103    }
5104 }
5105 
ACLDelete(ClientInfo * p,ConfClient * pCC1,char * Arg,int bAllowed)5106 void ACLDelete(ClientInfo *p,ConfClient *pCC1,char *Arg,int bAllowed)
5107 {
5108    ACL_User *pACL = NULL;
5109    ACL_User ACL_Lookup;
5110    char *cp;
5111    char *CmdString = bAllowed ? "allowed" : "banned";
5112 
5113 // Remove possible -L or -R from the argument, we ban the base call
5114 
5115    if((cp = strchr(Arg,'-')) != NULL) {
5116       *cp = 0;
5117    }
5118 
5119    ACL_Lookup.Callsign = Arg;
5120    Convert2Upper(ACL_Lookup.Callsign);
5121 
5122    if((pACL = avl_find(ACL_Call_Tree,&ACL_Lookup)) != NULL) {
5123       if((bAllowed && pACL->bAuthorized) || (!bAllowed && !pACL->bAuthorized)) {
5124          DeleteACLUser(pACL);
5125          BufPrintf(p,"%s removed from the %s list.\r",Arg,CmdString);
5126          SaveACL();
5127       }
5128       else {
5129          pACL = NULL;
5130       }
5131    }
5132 
5133    if(pACL == NULL) {
5134       BufPrintf(p,"%s was not found on the %s list.\r",Arg,CmdString);
5135    }
5136 }
5137 
ACLList(ClientInfo * p,ConfClient * pCC1,const char * Arg,int bAllowed)5138 void ACLList(ClientInfo *p,ConfClient *pCC1,const char *Arg,int bAllowed)
5139 {
5140    ACL_User *pACL = NULL;
5141    struct avl_traverser avl_trans;
5142    int Count = 0;
5143    char *CmdString = bAllowed ? "allowed" : "banned";
5144    ACL_User *pNextACL;
5145 
5146    pNextACL = (ACL_User *) avl_t_first(&avl_trans,ACL_Call_Tree);
5147    while((pACL = pNextACL) != NULL) {
5148    // NB: Get the next user now in case we deleted the current user
5149       pNextACL = (ACL_User *) avl_t_next(&avl_trans);
5150       if((bAllowed && pACL->bAuthorized) || (!bAllowed && !pACL->bAuthorized)) {
5151          if(Count++ == 0) {
5152             BufPrintf(p,"%s users:\r",CmdString);
5153          }
5154          BufPrintf(p,"%s %s\r",pACL->CallPlus,pACL->HostName);
5155       }
5156    }
5157 
5158    if(Count == 0) {
5159       BufPrintf(p,"The %s list is empty.\r",CmdString);
5160    }
5161 }
5162 
5163 // [allow | deny] add <callsign> [<hostname> [<user name>]]
CmdACL(ClientInfo * p,ConfClient * pCC1,char * Arg,int bAllowed)5164 void CmdACL(ClientInfo *p,ConfClient *pCC1,char *Arg,int bAllowed)
5165 {
5166    char *cp;
5167 
5168    if((cp = strchr(Arg,' ')) != NULL) {
5169       *cp++ = 0;
5170       while(*cp && *cp == ' ') {
5171          cp++;
5172       }
5173    }
5174 
5175    if(STRCMP(Arg,"add") == 0) {
5176       if(cp != NULL && *cp) {
5177          ACLAdd(p,pCC1,cp,bAllowed);
5178       }
5179       else {
5180          BufPrintf(p,"Add who ?\r");
5181          Rcode = TBD_ERR_ARG_COUNT;
5182       }
5183    }
5184    else if(STRCMP(Arg,"delete") == 0) {
5185       if(cp != NULL && *cp) {
5186          ACLDelete(p,pCC1,cp,bAllowed);
5187       }
5188       else {
5189          BufPrintf(p,"Delete who ?\r");
5190          Rcode = TBD_ERR_ARG_COUNT;
5191       }
5192    }
5193    else if(STRCMP(Arg,"list") == 0) {
5194       ACLList(p,pCC1,Arg,bAllowed);
5195    }
5196    else {
5197       Rcode = TBD_ERR_INVALID_CMD;
5198       if(bAllowed) {
5199          BufPrintf(p,"Usage:\r"
5200                      "allow add <callsign> [<hostname/IP Address> [password [<User's name>]]]\r"
5201                      "allow delete <callsign>\r"
5202                      "allow list\r");
5203       }
5204       else {
5205          BufPrintf(p,"Usage:\r"
5206                      "ban add <callsign> [<hostname/IP Address>]\r"
5207                      "ban delete <callsign>\r"
5208                      "ban list\r");
5209       }
5210    }
5211 }
5212 
CmdAllow(ClientInfo * p,ConfClient * pCC1,char * Arg)5213 void CmdAllow(ClientInfo *p,ConfClient *pCC1,char *Arg)
5214 {
5215    CmdACL(p,pCC1,Arg,TRUE);
5216 }
5217 
CmdBan(ClientInfo * p,ConfClient * pCC1,char * Arg)5218 void CmdBan(ClientInfo *p,ConfClient *pCC1,char *Arg)
5219 {
5220    CmdACL(p,pCC1,Arg,FALSE);
5221 }
5222 
5223 
SaveBulletinList()5224 void SaveBulletinList()
5225 {
5226    FILE *fp;
5227    Bulletin *pBulletin = BulletinList;
5228 
5229    if((fp = fopen("bulletin.lst","w")) != NULL) {
5230       while(pBulletin != NULL) {
5231          fprintf(fp,"%s\t%s\n",pBulletin->Filename,pBulletin->Description);
5232          pBulletin = pBulletin->Link;
5233       }
5234       fclose(fp);
5235    }
5236 }
5237 
FreeBulletinList()5238 void FreeBulletinList()
5239 {
5240    Bulletin *pBulletin;
5241 
5242    while(BulletinList != NULL) {
5243       pBulletin = BulletinList;
5244       BulletinList = pBulletin->Link;
5245       free(pBulletin->Filename);
5246       free(pBulletin->Description);
5247       free(pBulletin->RunTime);
5248       free(pBulletin);
5249    }
5250 }
5251 
CmdListAdd(char * Filename,char * Desc)5252 int CmdListAdd(char *Filename,char *Desc)
5253 {
5254    struct stat FileStats;
5255    int AudioBlocks;
5256    int RunningTime;
5257    Bulletin *pBulletin;
5258    int Ret = FALSE;
5259 
5260    // check that the file exists and calculate approximate length
5261    if(stat(Filename,&FileStats) == 0) {
5262    // Each audio packet encodes 80 milliseconds of audio.  It consists
5263    // of a file header plus a RTP header plus 4 GSM 6.10 encoded audio frames.
5264    // Each GSM frame contains 33 bytes.  This calculation is approximate
5265    // since it ignores text messages and timestamps, but it should be close
5266    // enough for our purposes.
5267 
5268       AudioBlocks = FileStats.st_size /
5269                         ((4 * 33) +
5270                          sizeof(rtp_hdr_t) -
5271                          sizeof(u_int32) /* optional csrc field not sent */ +
5272                          sizeof(FilePacketHdr));
5273 
5274       RunningTime = AudioBlocks * 2 / 25; // 12.5 packets/second of audio
5275 
5276       pBulletin = (Bulletin *) malloc(sizeof(Bulletin));
5277       if(pBulletin != NULL) {
5278          pBulletin->Filename = strdup(Filename);
5279          pBulletin->Description = strdup(Desc);
5280          pBulletin->RunTime = strdup(DeltaTime2String(0,RunningTime,TRUE));
5281          pBulletin->Link = NULL;
5282          if(BulletinList == NULL) {
5283             BulletinList = pBulletin;
5284          }
5285          else {
5286             BulletinListTail->Link = pBulletin;
5287          }
5288          BulletinListTail = pBulletin;
5289 
5290          Ret = TRUE;
5291       }
5292    }
5293 
5294    return Ret;
5295 }
5296 
LoadBulletinList()5297 void LoadBulletinList()
5298 {
5299    FILE *fp;
5300    char  Line[80];
5301    char *cp;
5302 
5303    FreeBulletinList();
5304    if((fp = fopen("bulletin.lst","r")) != NULL) {
5305       while(fgets(Line,sizeof(Line),fp) != NULL) {
5306          if((cp = strchr(Line,'\n')) != NULL) {
5307             *cp = 0;
5308          }
5309 
5310          if((cp = strchr(Line,'\t')) != NULL) {
5311             *cp++ = 0;
5312             CmdListAdd(Line,cp);
5313          }
5314       }
5315       fclose(fp);
5316    }
5317 }
5318 
CmdBulletinDelete(ClientInfo * p,ConfClient * pCC,char * Filename)5319 void CmdBulletinDelete(ClientInfo *p,ConfClient *pCC,char *Filename)
5320 {
5321    Bulletin *pLast = (Bulletin *) &BulletinList;
5322    Bulletin *pBulletin = BulletinList;
5323 
5324    while(pBulletin != NULL) {
5325       if(STRCMP(pBulletin->Filename,Filename) == 0) {
5326          BufPrintf(p,"%s deleted\r",Filename);
5327          pLast->Link = pBulletin->Link;
5328          free(pBulletin->Filename);
5329          free(pBulletin->Description);
5330          free(pBulletin);
5331          SaveBulletinList();
5332          break;
5333       }
5334       pLast = pBulletin;
5335       pBulletin = pBulletin->Link;
5336    }
5337 
5338    if(pBulletin == NULL) {
5339       BufPrintf(p,"%s not found\r",Filename);
5340    }
5341 }
5342 
CmdXyzzy(ClientInfo * p,ConfClient * pCC,char * Arg)5343 void CmdXyzzy(ClientInfo *p,ConfClient *pCC,char *Arg)
5344 {
5345    BufPrintf(p,"Nothing happens\r");
5346 }
5347 
CmdList(ClientInfo * p,ConfClient * pCC,char * Arg)5348 void CmdList(ClientInfo *p,ConfClient *pCC,char *Arg)
5349 {
5350    char *cp;
5351    char *cp1 = NULL;
5352    int i;
5353    Bulletin *pBulletin = BulletinList;
5354 
5355    if((cp = strchr(Arg,' ')) != NULL) {
5356       *cp++ = 0;
5357       while(*cp && *cp == ' ') {
5358          cp++;
5359       }
5360 
5361       if((cp1 = strchr(cp,' ')) != NULL) {
5362          *cp1++ = 0;
5363          while(*cp1 && *cp1 == ' ') {
5364             cp1++;
5365          }
5366       }
5367    }
5368 
5369    if(pCC->bAdmin && cp != NULL && *cp) {
5370       if(STRCMP(Arg,"add") == 0) {
5371          if(cp != NULL && cp1 != NULL) {
5372             if(CmdListAdd(cp,cp1)) {
5373                SaveBulletinList();
5374                BufPrintf(p,"Added: %s as %s\r",cp,cp1);
5375             }
5376             else {
5377                BufPrintf(p,"File \"%s\" not found.\r",cp);
5378             }
5379          }
5380          else {
5381             BufPrintf(p,"usage: list add <filename> <description>\r");
5382             Rcode = TBD_ERR_INVALID_CMD;
5383          }
5384       }
5385       else if(STRCMP(Arg,"delete") == 0) {
5386          if(cp != NULL) {
5387             CmdBulletinDelete(p,pCC,cp);
5388          }
5389          else {
5390             BufPrintf(p,"usage: list delete <filename>\r");
5391             Rcode = TBD_ERR_INVALID_CMD;
5392          }
5393       }
5394       else {
5395          BufPrintf(p,"Usage:\r"
5396                      "list add <filename> <description>\r"
5397                      "list delete <filename>\r");
5398          Rcode = TBD_ERR_INVALID_CMD;
5399       }
5400    }
5401    else {
5402    // Normal client or just bare list command
5403 
5404       i = 1;
5405       if(pBulletin == NULL) {
5406          BufPrintf(p,"Sorry, no bulletins are available at this time.\r");
5407       }
5408       else {
5409          BufPrintf(p,"Bulletins:\r");
5410          while(pBulletin != NULL) {
5411             if(pCC->bAdmin) {
5412                BufPrintf(p,"%d. (%s) %s (%s)\r",i++,pBulletin->Filename,
5413                          pBulletin->Description,pBulletin->RunTime);
5414             }
5415             else {
5416                BufPrintf(p,"%d. %s (%s)\r",i++,pBulletin->Description,
5417                          pBulletin->RunTime);
5418             }
5419             pBulletin = pBulletin->Link;
5420          }
5421       }
5422    }
5423 }
5424 
PlayBackComplete(ConfClient * pCC,char * Reason)5425 void PlayBackComplete(ConfClient *pCC,char *Reason)
5426 {
5427    ClientFileIO *pFIO = (ClientFileIO *) pCC->p;
5428    ConfServer *pCS = pCC->pCS;
5429 
5430    if(pFIO != NULL) {
5431       EventHook("playbackcomplete %s %s",pCC->Callsign,Reason);
5432       LOG_NORM(("PlayBackComplete() for %s, %s.\n",pCC->Callsign,Reason));
5433 
5434       FileCleanup(pCC);
5435    }
5436    else {
5437       LOG_NORM(("PlayBackComplete() Error, pCC->p == NULL for %s, %s.\n",
5438                 pCC->Callsign,Reason));
5439    }
5440 
5441    if(AudioTestConf && FileRecord(NULL,pCC,EVENT_INIT)) {
5442       pCC->bInConf = FALSE;
5443       pCC->State = FileRecord;
5444    }
5445    else if(pCC->bFilePlayer) {
5446       pCC->pCS->bPlayWhenFree = FALSE;
5447       pCC->pCS->pFilePlayer = NULL;
5448       if(ClientTalking == pCC) {
5449          ClientTalking = NULL;
5450       }
5451       else {
5452          LOG_ERROR(("PlayBackComplete(): ClientTalking is not us.\n"));
5453       }
5454       DeleteCClient(pCC);
5455       SendStationList(pCS);
5456       pCS->bSendStationList = FALSE;
5457    }
5458    else {
5459       pCC->bInConf = TRUE;
5460       pCC->State = NULL;
5461    }
5462 }
5463 
StartPlayback(ClientInfo * p,ConfClient * pCC)5464 void StartPlayback(ClientInfo *p,ConfClient *pCC)
5465 {
5466    ClientInfo *pPB;
5467    ClientFileIO *pFIO = (ClientFileIO *) pCC->p;
5468 
5469 // Create a mainloop client to playback the file
5470    pPB = CreateNewClient();
5471    if(pPB == NULL) {
5472       LOG_ERROR(("FileRecord(): CreateNewClient failed.\n"));
5473       PlayBackComplete(pCC,"CreateNewClient() failed");
5474    }
5475    else {
5476    // Wait a second and then start playing it back
5477       pFIO->TimeFirstTx.tv_sec = 0;
5478       pFIO->pClient = pPB;
5479       pPB->p = pFIO;
5480       pPB->HisAdr = pCC->HisAdr;
5481       pPB->Socket = p->Socket;
5482       pPB->State = FilePlayBack;
5483       pPB->BufSize = CONF_BUF_SIZE;
5484       pPB->Buf = malloc(pPB->BufSize);
5485       if(pCC->bFilePlayer) {
5486          SetTimeout(pPB,1);
5487       }
5488       else {
5489          SetTimeout(pPB,1000);
5490       }
5491       avl_insert(ClientTree,pPB);
5492       pPB->bInClientTree = TRUE;
5493    }
5494 }
5495 
OpenAudioFile(ConfClient * pCC,char * Filename,char * Mode)5496 void OpenAudioFile(ConfClient *pCC,char *Filename, char *Mode)
5497 {
5498    ClientFileIO *pFIO;
5499    FILE *fp;
5500 
5501    if(pCC->bFileIOActive) {
5502    // A file is currently playing for this client, cancel it
5503       PlayBackComplete(pCC,"opening a new file");
5504    }
5505 
5506    if((fp = FOPEN(Filename,Mode)) != NULL) {
5507       pFIO = (ClientFileIO *) (pCC->p = malloc(sizeof(ClientFileIO)));
5508       if(pFIO != NULL) {
5509          pCC->bFileIOActive = TRUE;
5510          pCC->bInConf = FALSE;
5511          memset(pFIO,0,sizeof(*pFIO));
5512          pFIO->fp = fp;
5513          pFIO->pCC = pCC;
5514          pFIO->Filename = strdup(Filename);
5515          pFIO->Timeout = 80;   // Assume a perfect delay at first
5516       }
5517       else {
5518          LOG_ERROR(("OpenAudioFile(): malloc failed.\n"));
5519       }
5520    }
5521    else {
5522       LOG_ERROR(("OpenAudioFile: Unable to open \"%s\", %s.\n",
5523                  Filename,Err2String(errno)));
5524    }
5525 }
5526 
5527 
5528 // play4 [-f] [-i] [-u callsign] filename [displayed name]
5529 // -f = force file to play now even if someone is talking
5530 // -i = play when conference becomes idle (IdleTimeout seconds of inactivity)
5531 // -u = play for specified user only
CmdPlayFor(ClientInfo * p,ConfClient * pCC1,char * Arg)5532 void CmdPlayFor(ClientInfo *p,ConfClient *pCC1,char *Arg)
5533 {
5534    int bForced = FALSE;
5535    int bWhenIdle = FALSE;
5536    int bIdle   = FALSE;
5537    int bFileFoundErr = FALSE;
5538    ConfClient *pCC = NULL;
5539    ConfServer *pCS = (ConfServer *) p->p;
5540    char *cp = Arg;
5541    char *cp1;
5542    char *Filename = NULL;
5543    char CharSave;
5544    struct avl_traverser avl_trans;
5545 
5546    while(*cp == '-') {
5547       switch(cp[1]) {
5548          case 'f':
5549             bForced = TRUE;
5550             break;
5551 
5552          case 'i':
5553             bWhenIdle = TRUE;
5554             break;
5555 
5556          case 'u':
5557             cp += 2;
5558             while(*cp && *cp == ' ') {
5559                cp++;
5560             }
5561             cp1 = cp;
5562             while(*cp1 && *cp1 != ' ') {
5563                cp1++;
5564             }
5565             CharSave = *cp1;
5566             *cp1 = 0;
5567 
5568             pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
5569             while(pCC!= NULL) {
5570                if(STRCMP(cp,pCC->Callsign) == 0) {
5571                   break;
5572                }
5573                pCC = (ConfClient *) avl_t_next(&avl_trans);
5574             }
5575             *cp1 = CharSave;
5576             cp = cp1 - 2;
5577 
5578             if(pCC == NULL) {
5579                BufPrintf(p,"%s not found\r",cp);
5580                Rcode = TBD_STATION_NOT_FOUND;
5581                cp = NULL;
5582             }
5583             break;
5584 
5585          default:
5586             BufPrintf(p,"Error: Unknown option '%c'\r",cp[1]);
5587             cp = NULL;
5588             Rcode = TBD_ERR_INVALID_ARG;
5589             break;
5590       }
5591 
5592       if(cp == NULL) {
5593          break;
5594       }
5595       else {
5596          cp += 2;
5597          while(*cp && *cp == ' ') {
5598             cp++;
5599          }
5600       }
5601    }
5602 
5603    if(cp != NULL) {
5604    // No error yet
5605       Filename = cp;
5606       if((cp = strchr(cp,' ')) != NULL) {
5607          while(*cp == ' ') {
5608             *cp++ = 0;
5609          }
5610       }
5611 
5612       if(pCC != NULL) {
5613       // Playing for a specified user
5614          OpenAudioFile(pCC,Filename,MODE_RD_BIN);
5615          if(pCC->bFileIOActive) {
5616          // Playing for one station
5617             StartPlayback(p,pCC);
5618             BufPrintf(p,"Playing %s.\r",Filename);
5619          }
5620          else {
5621             bFileFoundErr = TRUE;
5622          }
5623       }
5624       else {
5625       // Playing for everyone
5626          pCC = CreateNewConfClient();
5627          pCC->bFilePlayer = TRUE;
5628          pCC->pCS = pCS;
5629          pCC->Proto = PROTO_ILINK;
5630          pCC->CompressionType = RTP_PT_GSM;
5631 
5632          OpenAudioFile(pCC,Filename,MODE_RD_BIN);
5633          if(pCC->bFileIOActive) {
5634             pCC->bInConf = TRUE;
5635             if(cp == NULL) {
5636                cp = "QST";
5637             }
5638             pCC->CallPlus = strdup(cp);
5639             pCC->Callsign = strdup(cp);
5640             pCC->HisAdr.ADDR = inet_addr("127.0.0.1");
5641             if(pCS->pFilePlayer != NULL) {
5642                PlayBackComplete(pCS->pFilePlayer,"starting new playback 1");
5643             }
5644             pCS->pFilePlayer = pCC;
5645 
5646             if(ClientTalking == NULL) {
5647                if(TimeNow.tv_sec - pCS->LastAudioIn.tv_sec > IdleTimeout) {
5648                   bIdle = TRUE;
5649                }
5650             }
5651 
5652             EndPoint(p,pCC,EVENT_INIT);
5653             if(bForced || (!bWhenIdle && ClientTalking == NULL) ||
5654                (bWhenIdle && bIdle))
5655             {
5656                ClientTalking = pCC;
5657                pCC->FirstAudioIn = TimeNow;
5658                StartPlayback(p,pCC);
5659                BufPrintf(p,"Playing %s.\r",Filename);
5660             }
5661             else if(bWhenIdle) {
5662                pCS->bPlayWhenIdle = TRUE;
5663                BufPrintf(p,"%s set to play when the conference becomes idle.\r",
5664                          Filename);
5665             }
5666             else {
5667                pCS->bPlayWhenFree = TRUE;
5668                BufPrintf(p,"%s set to play when the conference becomes free.\r",
5669                          Filename);
5670             }
5671          }
5672          else {
5673             bFileFoundErr = TRUE;
5674          }
5675       }
5676    }
5677 
5678    if(bFileFoundErr) {
5679       BufPrintf(p,"%s not found.\r",Filename);
5680       Rcode = TBD_ERR_FILE_OPEN;
5681    }
5682 }
5683 
CmdPlay(ClientInfo * p,ConfClient * pCC,char * Arg)5684 void CmdPlay(ClientInfo *p,ConfClient *pCC,char *Arg)
5685 {
5686    int Selection;
5687    Bulletin *pBulletin = BulletinList;
5688 
5689    if(pCC->bFileIOActive) {
5690       PlayBackComplete(pCC,"starting new playback");
5691    }
5692 
5693    if(sscanf(Arg,"%d",&Selection) == 1) {
5694       while(Selection != 1 && pBulletin != NULL) {
5695          Selection--;
5696          pBulletin = pBulletin->Link;
5697       }
5698 
5699       if(Selection == 1 && pBulletin != NULL) {
5700          OpenAudioFile(pCC,pBulletin->Filename,MODE_RD_BIN);
5701          if(pCC->bFileIOActive) {
5702             StartPlayback(p,pCC);
5703          }
5704       }
5705    }
5706 
5707    if(pCC->bFileIOActive) {
5708       BufPrintf(p,"Playing bulletin %s.\r"
5709                 "Enter .stop to end, or just disconnect.\r",Arg);
5710    }
5711    else {
5712       BufPrintf(p,"Usage: .play <bulletin number>\r"
5713                   " hint: use the list command to find the bulletins.\r");
5714    }
5715 }
5716 
CmdQuit(ClientInfo * p,ConfClient * pCC,char * Arg)5717 void CmdQuit(ClientInfo *p,ConfClient *pCC,char *Arg)
5718 {
5719    BufPrintf(p,"Exiting\r");
5720    bRunning = FALSE;
5721 }
5722 
CmdQuote(ClientInfo * p,ConfClient * pCC1,char * Arg)5723 void CmdQuote(ClientInfo *p,ConfClient *pCC1,char *Arg)
5724 {
5725    ConfClient *pCC;
5726    struct avl_traverser avl_trans;
5727    ConfServer *pCS = (ConfServer *) p->p;
5728 
5729    BufPrintf(p,"%s>%s\r",ConferenceCall,Arg);
5730 
5731    pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
5732    while(pCC != NULL) {
5733       if(pCC->bTBD || pCC->bSysop) {
5734          Send2(pCC,p->Buf,p->Count,FALSE);
5735       }
5736       pCC = (ConfClient *) avl_t_next(&avl_trans);
5737    }
5738    pCC1->bSentQuotedCmd = TRUE;
5739    pCC1->FirstAudioIn = TimeNow;
5740 
5741    p->Count = 0;  // reinit for BufPrintf
5742    BufPrintf(p,"%c" NDATA,ILINK_DATA_PACKET);
5743 }
5744 
CmdStop(ClientInfo * p,ConfClient * pCC,char * Arg)5745 void CmdStop(ClientInfo *p,ConfClient *pCC,char *Arg)
5746 {
5747    ConfServer *pCS = (ConfServer *) p->p;
5748 
5749    if(pCC->bFileIOActive) {
5750       BufPrintf(p,"Playback stopped.\r");
5751       PlayBackComplete(pCC,"stop command");
5752    }
5753    else if(pCS->pFilePlayer != NULL &&
5754            ((ClientFileIO *) pCS->pFilePlayer->p)->pClient != NULL)
5755    {
5756       PlayBackComplete(pCS->pFilePlayer,"stop command");
5757    }
5758    else {
5759       BufPrintf(p,"Error: Nothing is playing.\r");
5760    }
5761 }
5762 
SendAudioFromFile(ClientInfo * p,ClientFileIO * pFIO,int NumPackets)5763 int SendAudioFromFile(ClientInfo *p,ClientFileIO *pFIO,int NumPackets)
5764 {
5765    FilePacketHdr PktHdr;
5766    int Ret = 0;
5767    time_t TimeStamp;
5768    int PlayBackPause = 0;
5769    ConfClient *pCC = pFIO->pCC;
5770    ConfServer *pCS = pCC->pCS;
5771    int TotalDelta = 0;
5772    rtp_hdr_t *pRTP;
5773 
5774    if(MaxPlayWithoutPause > 0 && pFIO->TimeFirstTx.tv_sec != 0) {
5775       TotalDelta = ((TimeNow.tv_sec - pFIO->TimeFirstTx.tv_sec) * 1000) +
5776                    ((TimeNow.tv_usec - pFIO->TimeFirstTx.tv_usec) / 1000);
5777 
5778       if(TotalDelta > (MaxPlayWithoutPause * 1000)) {
5779       // time to pause for a while
5780          if(MinPlayBackPause > 0) {
5781             PlayBackPause = MinPlayBackPause;
5782          }
5783          else {
5784             PlayBackPause = 5;
5785          }
5786          Ret = 2;
5787       }
5788    }
5789 
5790    while(Ret != 2 && NumPackets--) {
5791       if(fread(&PktHdr,sizeof(PktHdr),1,pFIO->fp) != 1) {
5792          if(feof(pFIO->fp)) {
5793             LOG_ERROR(("SendAudioFromFile(): unexpected end of file\n"));
5794          }
5795          else {
5796             LOG_ERROR(("SendAudioFromFile(): fread failed, %s",
5797                        Err2String(errno)));
5798          }
5799          D3PRINTF(("Reading header\n"));
5800          break;
5801       }
5802 
5803       PktHdr.Flags = ntohl(PktHdr.Flags);
5804       PktHdr.Len = ntohl(PktHdr.Len);
5805 
5806       D3PRINTF(("SendAudioFromFile(): PktHdr.Len = %d, PktHdr.Flags = 0x%x\n",
5807                 PktHdr.Len,PktHdr.Flags));
5808 
5809       if(PktHdr.Flags & FILE_FLAG_TIME_STAMP) {
5810       // A timestamp header, pause playback
5811          TimeStamp = (time_t) PktHdr.Len;
5812          if(pFIO->FirstTimeStamp == 0) {
5813             pFIO->FirstTimeStamp = TimeStamp;
5814          }
5815 
5816          if(pFIO->LastTimeStamp != 0) {
5817             PlayBackPause = TimeStamp - pFIO->LastTimeStamp;
5818             if(PlayBackPause > MaxPlayBackPause) {
5819                PlayBackPause = MaxPlayBackPause;
5820             }
5821             else if(PlayBackPause < MinPlayBackPause) {
5822                PlayBackPause = MinPlayBackPause;
5823             }
5824          }
5825          else {
5826             PlayBackPause = MinPlayBackPause;
5827          }
5828          pFIO->LastTimeStamp = TimeStamp;
5829          Ret = 2;
5830          break;
5831       }
5832 
5833       if(PktHdr.Len > CONF_BUF_SIZE || PktHdr.Len < 0) {
5834          LOG_ERROR(("SendAudioFromFile(): Len > CONF_BUF_SIZE (%d).\n",
5835                     PktHdr.Len));
5836          break;
5837       }
5838 
5839       if(PktHdr.Len == 0) {
5840          D2PRINTF(("SendAudioFromFile(): playback complete.\n"));
5841          break;
5842       }
5843 
5844       if(fread(p->Buf,PktHdr.Len,1,pFIO->fp) != 1) {
5845          if(feof(pFIO->fp)) {
5846             LOG_ERROR(("SendAudioFromFile(): unexpected end of file\n"));
5847          }
5848          else {
5849             LOG_ERROR(("SendAudioFromFile(): fread failed, %s",
5850                        Err2String(errno)));
5851          }
5852          break;
5853       }
5854       pRTP = (rtp_hdr_t *) p->Buf;
5855 
5856       if(!pCC->bFilePlayer) {
5857          if(pCC->Proto != PROTO_ILINK) {
5858             ProtoData *pPDat = pFIO->pPDat;
5859             int Len;
5860 
5861          // playing for a non-EchoLink client
5862             if(pPDat == NULL) {
5863                if((pPDat = malloc(sizeof(ProtoData)*NUM_PROTOCOLS)) == NULL) {
5864                   LOG_ERROR(("SendAudioFromFile: malloc failed.\n"));
5865                }
5866                else {
5867                   pFIO->pPDat = pPDat;
5868                   memset(pPDat,0,sizeof(ProtoData)*NUM_PROTOCOLS);
5869                   pPDat->Callsign = pCC->Callsign;
5870                   pPDat[PROTO_ILINK].Type = PKT_TYPE_AUDIO;
5871                   pPDat[PROTO_ILINK].bDataValid = TRUE;
5872                   pPDat[PROTO_ILINK].Data[0] = p->Buf;
5873                }
5874             }
5875 
5876             if(pPDat != NULL && !(PktHdr.Flags & FILE_FLAG_DATA_PKT)) {
5877                pPDat[pCC->Proto].DataLen[0] = 0;
5878                pPDat[pCC->Proto].bDataValid = FALSE;
5879                pPDat[PROTO_ILINK].DataLen[0] = PktHdr.Len;
5880                ConvertProtocol(pPDat,PROTO_ILINK,pCC->Proto,FALSE);
5881 
5882                if((Len = pPDat[pCC->Proto].DataLen[0]) != 0) {
5883                   Send2(pCC,pPDat[pCC->Proto].Data[0],Len,FALSE);
5884                }
5885             }
5886          }
5887          else {
5888             if(!(PktHdr.Flags & FILE_FLAG_DATA_PKT) && !pCC->bSendSSRC) {
5889             // It's an audio packet make sure the ssrc is zero, this
5890             // client can't deal with a non-zero ssrc !
5891                pRTP->ssrc = 0;
5892             }
5893             Send2(pCC,p->Buf,PktHdr.Len,FALSE);
5894          }
5895       }
5896       else {
5897          p->Count = PktHdr.Len;
5898          RTP_Data(p,pCS,pCC);
5899          if(pCS->bSendStationList) {
5900             pCS->bSendStationList = FALSE;
5901             SendStationList(pCS);
5902          }
5903       }
5904 
5905       if(!(PktHdr.Flags & FILE_FLAG_RATE_LIMIT)) {
5906       // Don't count this packet against rate limit
5907          NumPackets++;
5908       }
5909       else {
5910          pFIO->x++;
5911       }
5912       Ret = 1;
5913    }
5914 
5915    if(Ret == 1) {
5916       SetTimeout(p,pFIO->Timeout);
5917       pFIO->TimeLastTx = TimeNow;
5918       D3PRINTF(("SendAudioFromFile(): Next timeout in %d milliseconds.\n",
5919                 pFIO->Timeout));
5920    }
5921    else if(Ret == 2) {
5922       EndPoint(p,pFIO->pCC,EVENT_RTP_TO);
5923       pFIO->TimeFirstTx.tv_sec = 0;
5924       pFIO->x = 0;
5925       SetTimeout(p,PlayBackPause*1000);
5926       if(ClientTalking == pCC) {
5927          ClientTalking = NULL;
5928          pCS->bSendStationList = TRUE;
5929       }
5930       D3PRINTF(("SendAudioFromFile(): Pausing %d seconds.\n",PlayBackPause));
5931    }
5932    return Ret;
5933 }
5934 
5935 
FilePlayBack(ClientInfo * p)5936 int FilePlayBack(ClientInfo *p)
5937 {
5938    int TotalDelta = 0;
5939    int ThisDelta;
5940    int NewDelay;
5941    int Packets2Send = 0;
5942    ClientFileIO *pFIO = (ClientFileIO *) p->p;
5943    ConfClient *pCC = pFIO->pCC;
5944 
5945    if(pFIO->TimeFirstTx.tv_sec != 0) {
5946       TotalDelta = ((TimeNow.tv_sec - pFIO->TimeFirstTx.tv_sec) * 1000) +
5947                    ((TimeNow.tv_usec - pFIO->TimeFirstTx.tv_usec) / 1000);
5948 
5949    // send one packet every 80 milliseconds
5950    // pFIO->x = packets sent so far
5951       Packets2Send = (TotalDelta / 80) - pFIO->x;
5952 
5953    // Adjust the next Timeout
5954       ThisDelta = ((TimeNow.tv_sec - pFIO->TimeLastTx.tv_sec) * 1000) +
5955                   ((TimeNow.tv_usec - pFIO->TimeLastTx.tv_usec) / 1000);
5956 
5957       D3PRINTF(("FilePlayBack(): Last delta %d milliseconds.\n",ThisDelta));
5958 
5959       pFIO->Timeout -= ((ThisDelta - 80)/2);
5960       if(pFIO->Timeout <= 0) {
5961       // Hmmm not keeping up... but we can't make time go backwards!
5962          pFIO->Timeout = 1;
5963          if(!pFIO->bTooSlowLogged) {
5964             pFIO->bTooSlowLogged = TRUE;
5965             LOG_ERROR(("FilePlayBack(): Desired timeout reached zero after %d"
5966                        " packets.\n",pFIO->x));
5967          }
5968       }
5969    }
5970    else {
5971    // First transmission
5972       Packets2Send = 1;
5973       pFIO->TimeFirstTx = TimeNow;
5974    }
5975 
5976    if(Packets2Send <= 0) {
5977       Packets2Send = 0;
5978       NewDelay = ((pFIO->x + 1) * 80) - TotalDelta;
5979       if(NewDelay <= 0) {
5980       // Hmmm again !
5981          NewDelay = 1;
5982       }
5983 
5984       D3PRINTF(("FilePlayBack(): Too soon to send, waiting %d milliseconds.\n",
5985                 NewDelay));
5986       SetTimeout(p,NewDelay);
5987    }
5988    else {
5989       D3PRINTF(("FilePlayBack(): Send %d packets this time.\n",Packets2Send));
5990       if(!SendAudioFromFile(p,pFIO,Packets2Send)) {
5991          PlayBackComplete(pCC,"playback complete");
5992       }
5993    }
5994 
5995    return FALSE;
5996 }
5997 
5998 
FileRecord(ClientInfo * p,ConfClient * pCC,EventType Event)5999 int FileRecord(ClientInfo *p,ConfClient *pCC,EventType Event)
6000 {
6001    char *Filename;
6002    FilePacketHdr PktHdr;
6003    ClientFileIO *pFIO = (ClientFileIO *) pCC->p;
6004    int Ret = TRUE;
6005 
6006    switch(Event) {
6007       case EVENT_INIT:
6008       // Open the file
6009          Filename = malloc(strlen(pCC->Callsign)+6);
6010          if(Filename != NULL) {
6011             strcpy(Filename,pCC->Callsign);
6012             strcat(Filename,".test");
6013             D2PRINTF(("FileRecord(): Opening file \"%s\".\n",Filename));
6014             OpenAudioFile(pCC,Filename,MODE_RDWR_BIN);
6015             free(Filename);
6016             pFIO = (ClientFileIO *) pCC->p;
6017             if(pFIO != NULL) {
6018                pFIO->bDeleteOnClose = TRUE;
6019             }
6020          }
6021 
6022          if(!pCC->bFileIOActive) {
6023          // file open failed
6024             LOG_ERROR(("FileRecord(): fopen for \"%s\" failed, %s",Filename,
6025                        Err2String(errno)));
6026             Ret = FALSE;
6027          }
6028          break;
6029 
6030       case EVENT_RTP_RX:
6031          if(pFIO->pClient == NULL && p->Buf[0] != ILINK_DATA_PACKET) {
6032             if(!pCC->bTalking) {
6033                D2PRINTF(("FileRecord(): First RTP data received.\n"));
6034                pCC->bTalking = TRUE;
6035             }
6036             pCC->LastAudioIn = TimeNow;
6037             PktHdr.Len = htonl(p->Count);
6038             PktHdr.Flags = htonl(FILE_FLAG_RATE_LIMIT);
6039 
6040             if(fwrite(&PktHdr,sizeof(PktHdr),1,pFIO->fp) != 1 ||
6041                fwrite(p->Buf,p->Count,1,pFIO->fp) != 1)
6042             {  // Write error
6043                LOG_ERROR(("FileRecord(): fwrite failed, %s",Err2String(errno)));
6044                PlayBackComplete(pCC,"write error 1");
6045             }
6046          }
6047          break;
6048 
6049       case EVENT_RTP_TO:
6050       // timeout, recording is complete, play it back
6051          D2PRINTF(("FileRecord(): RTP timeout, rewinding file.\n"));
6052          PktHdr.Len = 0;
6053          PktHdr.Flags = 0;
6054          if(fwrite(&PktHdr,sizeof(PktHdr),1,pFIO->fp) != 1) {
6055          // Write error
6056             LOG_ERROR(("FileRecord(): fwrite of eof header failed, %s",
6057                        Err2String(errno)));
6058             PlayBackComplete(pCC,"write error 2");
6059          }
6060          else {
6061             fseek(pFIO->fp,0,SEEK_SET);
6062             StartPlayback(p,pCC);
6063          }
6064          break;
6065 
6066       case EVENT_RTCP_TO:
6067       // He's gone
6068          PlayBackComplete(pCC,"rtcp timeout");
6069          break;
6070 
6071       default:
6072          break;
6073    }
6074 
6075    return Ret;
6076 }
6077 
CmdLevelTest(ClientInfo * p,ConfClient * pCC,char * Arg)6078 void CmdLevelTest(ClientInfo *p,ConfClient *pCC,char *Arg)
6079 {
6080    if(!AudioTestConf && FileRecord(p,pCC,EVENT_INIT)) {
6081       pCC->bInConf = FALSE;
6082       pCC->State = FileRecord;
6083       BufPrintf(p,"Your next transmission will be recorded and played back.\r");
6084    }
6085 }
6086 
6087 // .connect [-a] [-p <port#>] [-f] [-m] [-n] [-s | -r] [-7] <callign/ip adr> [description]
6088 // options:
6089 //    -a - use ADPCM codec (RTP and Speak Freely protocols only)
6090 //    -f - full duplex
6091 //    -m - monitor only
6092 //    -n - nailed up connection (not disconnected by disconnect all)
6093 //    -p - use specified port
6094 //    -r - use RTP protocol
6095 //    -s - use Speak Freely protocol
6096 //    -u - use uLaw codec (RTP and Speak Freely protocols only)
6097 //    -7 - use G.726 codec (RTP protocols only)
CmdConnect(ClientInfo * p,ConfClient * pCC2,char * Arg)6098 void CmdConnect(ClientInfo *p,ConfClient *pCC2,char *Arg)
6099 {
6100    UserInfo OtherConf;
6101    ConfClient  Lookup;
6102    UserInfo *pUser;
6103    struct hostent *pTemp;
6104    ConfClient *pCC = NULL;
6105    ConfClient *pCC1;
6106    struct avl_traverser avl_trans;
6107    ConfServer *pCS = (ConfServer *) p->p;
6108    ConfServer *pHisCS = pCS;
6109    ConfServer *pDynamicCS = NULL;
6110    ConfServer ConfLookup;
6111    int NodeID;
6112    int Codecs = 0;
6113    int bSpeakFreely = FALSE;
6114    int bMonitor = FALSE;
6115    int bNailed = FALSE;
6116    int bRTP = FALSE;
6117    int bADPCM = FALSE;
6118    int bULaw = FALSE;
6119    int bG726 = FALSE;
6120    int bFullDuplex = FALSE;
6121    int Port = SF_ReplyPort;
6122    int Err;
6123    int bEchoLinkNode = FALSE;
6124    char *cp = Arg;
6125    char *DispCall = NULL;
6126    char *Callsign = NULL;
6127    char *WhiteSpace = "\t\n ";
6128    char Option;
6129 
6130    while(*cp == '-') {
6131       cp++;
6132       switch(Option = *cp++) {
6133          case 'p':
6134             if(sscanf(cp,"%d",&Port) != 1) {
6135                BufPrintf(p,"Error: Invalid port number\r");
6136                cp = NULL;
6137                Rcode = TBD_ERR_INVALID_ARG;
6138                break;
6139             }
6140             else if(Port & 1) {
6141                BufPrintf(p,"Error: Invalid port number, must be even\r");
6142                cp = NULL;
6143                Rcode = TBD_ERR_INVALID_ARG;
6144                break;
6145             }
6146             else {
6147             // Skip past port number
6148                while(*cp == ' ') {
6149                   cp++;
6150                }
6151 
6152                while(*cp && *cp != ' ') {
6153                   cp++;
6154                }
6155             }
6156             break;
6157 
6158          case 's':
6159             bSpeakFreely = TRUE;
6160             bRTP = FALSE;
6161             break;
6162 
6163          case 'r':
6164             bRTP = TRUE;
6165             bSpeakFreely = FALSE;
6166             break;
6167 
6168          case 'm':
6169             bMonitor = TRUE;
6170             break;
6171 
6172          case 'n':
6173             bNailed = TRUE;
6174             break;
6175 
6176 #ifdef   LINK_BOX
6177          case 'a':
6178             bADPCM = TRUE;
6179             Codecs++;
6180             break;
6181 
6182          case 'u':
6183             bULaw = TRUE;
6184             Codecs++;
6185             break;
6186 
6187          case '7':
6188             bG726 = TRUE;
6189             Codecs++;
6190             break;
6191 #endif
6192          case 'f':
6193             bFullDuplex = TRUE;
6194             break;
6195 
6196          default:
6197             BufPrintf(p,"Error: Unknown option '%c'\r",Option);
6198             cp = NULL;
6199             Rcode = TBD_ERR_INVALID_ARG;
6200             break;
6201       }
6202 
6203       if(cp == NULL) {
6204          break;
6205       }
6206       else {
6207          while(*cp && *cp == ' ') {
6208             cp++;
6209          }
6210       }
6211    }
6212 
6213    if(Rcode == 0) do {
6214       if(bADPCM && !bSpeakFreely && !bRTP) {
6215       // No ADPCM on Echolink
6216          BufPrintf(p,"Error: ADPCM codec is not supported by EchoLink.\r");
6217          Rcode = TBD_ERR_INVALID_ARG;
6218          break;
6219       }
6220 
6221       if(bULaw && !bSpeakFreely && !bRTP) {
6222       // No uLaw on Echolink
6223          BufPrintf(p,"Error: uLaw codec is not supported by EchoLink.\r");
6224          Rcode = TBD_ERR_INVALID_ARG;
6225          break;
6226       }
6227 
6228       if(bG726 && !bRTP) {
6229       // No G726 on SF or Echolink
6230          BufPrintf(p,"Error: G.726 codec is not supported by EchoLink or "
6231                    "Speak Freely.\r");
6232          Rcode = TBD_ERR_INVALID_ARG;
6233          break;
6234       }
6235 
6236       if(Codecs > 1) {
6237          BufPrintf(p,"Error: pick just one, uLaw, ADPCM or G.726.\r");
6238          Rcode = TBD_ERR_INVALID_ARG;
6239          break;
6240       }
6241 
6242       if((Callsign= strtok(cp,WhiteSpace)) == NULL) {
6243          BufPrintf(p,"Usage:\r");
6244          BufPrintf(p,"connect [-a] [-u] [-7] [-p <port#>] [-m] [-s | -r] [-f] "
6245                    "<callign/ip adr> [description]\r");
6246          Rcode = TBD_ERR_INVALID_ARG;
6247          break;
6248       }
6249 
6250       if(bSpeakFreely || bRTP || bG726) {
6251       // Look for a "conference" using this port number
6252          ConfLookup.AudioPort = Port;
6253          if((pHisCS = avl_find(Conferences,&ConfLookup)) == NULL) {
6254          // need to create a dynamic conference
6255             Err = CreateConference(Port,piLinkConf,&pDynamicCS);
6256             if(Err != 0) {
6257                BufPrintf(p,"Error: Couldn't open port (%d)\r",Err);
6258                Rcode = TBD_ERR_FILE_OPEN;
6259                break;
6260             }
6261             else {
6262                LOG_NORM(("Conference for port %d created\n",Port));
6263                pDynamicCS->bDynamicConf = TRUE;
6264                if(bSpeakFreely) {
6265                   pDynamicCS->bSFConf = TRUE;
6266                }
6267                else {
6268                   pDynamicCS->bRTPConf = TRUE;
6269                }
6270                pDynamicCS->pAudio->State = RTP_Handler;
6271                pDynamicCS->pControl->State = RTCP_Handler;
6272                pDynamicCS->TimeNextSDES = TimeNow.tv_sec + SDES_Interval;
6273                pHisCS = pDynamicCS;
6274             }
6275          }
6276       }
6277 
6278 
6279       DispCall = strtok(NULL,"");
6280       Convert2Upper(Callsign);
6281       OtherConf.Callsign = Callsign;
6282       pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
6283       while(pCC != NULL) {
6284          if(STRCMP(Arg,pCC->Callsign) == 0) {
6285             if(pCC->bKicked) {
6286                pCC->bKicked = FALSE;
6287                pCC->bSWL = FALSE;
6288                pCC->bInConf = TRUE;
6289                pCC->bConnected = TRUE;
6290                pCC->bPermanent = TRUE;
6291                IncrementClientCount(pCS,pCC);
6292                BufPrintf(p,"Reconnecting \"%s\".\r",Arg);
6293             }
6294             else if(pCC->bPermanent) {
6295                BufPrintf(p,"Already connected to \"%s\".\r",Arg);
6296                Rcode = TBD_ERR_CONNECTED;
6297                pCC = NULL;
6298             }
6299             else {
6300                pCC->bPermanent = TRUE;
6301                BufPrintf(p,"Connection to \"%s\" set to permanent.\r",Arg);
6302             }
6303             break;
6304          }
6305          pCC = (ConfClient *) avl_t_next(&avl_trans);
6306       }
6307 
6308       if(Rcode != 0 || pCC != NULL) {
6309          break;
6310       }
6311 
6312    // Didn't find an existing conference client, create a new one
6313       if((pCC = CreateNewConfClient()) == NULL) {
6314          break;
6315       }
6316 
6317       pCC->pCS = pHisCS;
6318       pCC->HisAdr.ADDR = INADDR_NONE;
6319 //    pCC->HisAdr.PORT = htons((uint16_t) Port);
6320       pCC->HisAdr.i.sin_family = AF_INET;
6321       pCC->bNailed = bNailed;
6322       if(bSpeakFreely) {
6323          pCC->Proto = PROTO_SF;
6324       }
6325       else if(bRTP) {
6326          pCC->Proto = PROTO_RTP;
6327       }
6328       else {
6329          pCC->Proto = PROTO_ILINK;
6330       }
6331 
6332       if(bFullDuplex) {
6333          pCC->bFullDuplex = TRUE;
6334       }
6335 
6336       if(bADPCM) {
6337          pCC->CompressionType = RTP_PT_DVI4_8K;
6338       }
6339       else if(bULaw) {
6340          pCC->CompressionType = RTP_PT_PCMU;
6341       }
6342       else if(bG726) {
6343          pCC->CompressionType = RTP_PT_G726;
6344       }
6345       else {
6346          pCC->CompressionType = RTP_PT_GSM;
6347       }
6348 
6349       if(pDynamicCS != NULL) {
6350          pDynamicCS->CompressionType = pCC->CompressionType;
6351       }
6352       pCC->bMonitor = bMonitor;
6353 
6354       if(DispCall != NULL) {
6355          pCC->bCallsignOverride = TRUE;
6356          pCC->CallPlus = strdup(DispCall);
6357          if((cp = strchr(DispCall,' ')) != NULL) {
6358          // truncate string after callsign
6359             *cp = 0;
6360          }
6361          pCC->Callsign = strdup(DispCall);
6362       }
6363       else {
6364          pCC->Callsign = strdup(Callsign);
6365          pCC->CallPlus = strdup(Callsign);
6366       }
6367       pCC->bPermanent = TRUE;
6368       pCC->bInConf = FALSE;   // Not until we get a response from him
6369 
6370       if((pUser = avl_find(UserTree,&OtherConf)) != NULL) {
6371          pCC->HisAdr.ADDR = pUser->HisAdr.ADDR;
6372          bEchoLinkNode = TRUE;
6373       }
6374       else if(strchr(Callsign,'.') == NULL &&
6375               sscanf(Callsign,"%d",&NodeID) == 1)
6376       {  // Numeric argument that doesn't contain a '.',
6377          // looking up argument as a EchoLink node ID
6378          pUser = (UserInfo *) avl_t_first(&avl_trans,UserTree);
6379          while(pUser != NULL) {
6380             if(pUser->NodeID == NodeID) {
6381                free(pCC->Callsign);
6382                free(pCC->CallPlus);
6383                pCC->Callsign = strdup(pUser->Callsign);
6384                pCC->CallPlus = strdup(pUser->Callsign);
6385                pCC->HisAdr.ADDR = pUser->HisAdr.ADDR;
6386                bEchoLinkNode = TRUE;
6387                break;
6388             }
6389             pUser = (UserInfo *) avl_t_next(&avl_trans);
6390          }
6391       }
6392       else {
6393       // Didn't find a currently logged in station by that call
6394       // or Node ID, try an IP address
6395 
6396          pCC->HisAdr.ADDR = inet_addr(Callsign);
6397 
6398          if(pCC->HisAdr.ADDR == INADDR_NONE) {
6399          // Try looking up the argument as a hostname
6400             pTemp = GetHostByName(Callsign);
6401             if(pTemp != NULL) {
6402                pCC->HisAdr.ADDR = IP_FROM_HOSTENT(pTemp,0);
6403             }
6404          }
6405       }
6406 
6407       if(pCC->HisAdr.ADDR != INADDR_NONE) {
6408          pCC->HisAdr.PORT = pHisCS->AudioPort;
6409          Lookup.HisAdr = pCC->HisAdr;
6410 
6411          if((pCC1 = avl_find(pCS->ConfTree,&Lookup)) == NULL) {
6412             AddClient2Conf(pHisCS,pCC);
6413             SendSDES(pHisCS,pCC);
6414             if(bEchoLinkNode) {
6415             // K1RFD 1/30/08
6416                SendFirewallOpenRequest(pHisCS,pCC);
6417             }
6418             BufPrintf(p,"Connecting to \"%s\".\r",pCC->Callsign);
6419          }
6420          else {
6421             pCC->HisAdr.ADDR = INADDR_NONE;
6422             if(pCC1->bKicked) {
6423                pCC1->bKicked = FALSE;
6424                pCC1->bSWL = FALSE;
6425                pCC1->bInConf = TRUE;
6426                pCC1->bPermanent = TRUE;
6427                pCC1->bConnected = TRUE;
6428                IncrementClientCount(pCS,pCC);
6429                BufPrintf(p,"Reconnecting \"%s\".\r",pCC->Callsign);
6430             }
6431             else if(pCC1->bPermanent) {
6432                BufPrintf(p,"Already connected to \"%s\".\r",pCC->Callsign);
6433                Rcode = TBD_ERR_CONNECTED;
6434             }
6435             else {
6436                pCC1->bPermanent = TRUE;
6437                BufPrintf(p,"Connection to \"%s\" set to permanent.\r",
6438                          pCC->Callsign);
6439             }
6440          }
6441       }
6442       else {
6443          BufPrintf(p,"Station \"%s\" not found.\r",Callsign);
6444          Rcode = TBD_STATION_NOT_FOUND;
6445       }
6446 
6447       if(pCC->HisAdr.ADDR == INADDR_NONE) {
6448          free(pCC->Callsign);
6449          free(pCC->CallPlus);
6450          free(pCC);
6451       }
6452    } while(FALSE);
6453 
6454    if(pDynamicCS != NULL) {
6455       if(Rcode != 0) {
6456       // Clean up dynamically created conference on failure
6457          DeleteConf(pDynamicCS);
6458       }
6459    }
6460 }
6461 
CmdDisconnect(ClientInfo * p,ConfClient * pCC1,char * Arg)6462 void CmdDisconnect(ClientInfo *p,ConfClient *pCC1,char *Arg)
6463 {
6464    ConfClient *pCC;
6465    struct avl_traverser avl_trans;
6466    ConfServer *pCS = (ConfServer *) p->p;
6467    int bStationFound = FALSE;
6468    char *Call = Arg;
6469    int bAllStations = FALSE;
6470 
6471    if(STRCMP(Arg,"all") == 0) {
6472       bAllStations = TRUE;
6473    }
6474    else if(STRCMP(Arg,"last") == 0) {
6475    // Find the last station that connected
6476       int LargestSN = 0;
6477       ConfClient *pCC1 = NULL;
6478 
6479       pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
6480       while(pCC != NULL) {
6481          if(pCC->SN > LargestSN) {
6482             LargestSN = pCC->SN;
6483             pCC1 = pCC;
6484          }
6485          pCC = (ConfClient *) avl_t_next(&avl_trans);
6486       }
6487 
6488       if(pCC1 != NULL) {
6489       // Set arg to the callsign of the selected station
6490          Call = pCC1->Callsign;
6491       }
6492    }
6493 
6494    pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
6495    while(pCC != NULL) {
6496       if(STRCMP(Call,pCC->Callsign) == 0 ||
6497          (bAllStations && !pCC->bNailed) ||
6498          (*Arg == '.' && pCC == ClientTalking))
6499       {
6500          bStationFound = TRUE;
6501          LOG_NORM(("%s disconnected by %s\n",CallLog(pCC),pCC1->Callsign));
6502          DisconnectCC(p,pCC,"Bye bye !");
6503 
6504          pCS->bSendStationList = TRUE;
6505          if(StationDisconnected != NULL) {
6506             free(StationDisconnected);
6507          }
6508          StationDisconnected = strdup(pCC->Callsign);
6509 
6510          if(!bAllStations) {
6511             break;
6512          }
6513       }
6514       pCC = (ConfClient *) avl_t_next(&avl_trans);
6515    }
6516 
6517 // We clobbered the buffer, reinitialize it for our response
6518    p->Count = 0;  // init for BufPrintf
6519    BufPrintf(p,"%c" NDATA,ILINK_DATA_PACKET);
6520 
6521    if(!bStationFound) {
6522       if(*Arg == '.') {
6523          BufPrintf(p,"No one is talking\r");
6524       }
6525       else if(bAllStations) {
6526          BufPrintf(p,"No stations found\r");
6527       }
6528       else {
6529          BufPrintf(p,"%s not found\r",Arg);
6530       }
6531       Rcode = TBD_STATION_NOT_FOUND;
6532    }
6533    else if(bAllStations) {
6534       BufPrintf(p,"All stations disconnected\r");
6535    }
6536    else {
6537       BufPrintf(p,"%s disconnected\r",StationDisconnected);
6538    }
6539 }
6540 
CmdInfo(ClientInfo * p,ConfClient * pCC,char * Arg)6541 void CmdInfo(ClientInfo *p,ConfClient *pCC,char *Arg)
6542 {
6543    char *Temp = strdup(ConnectedStatus);
6544    UpdateConnectedStatus();
6545    if(*Arg) {
6546       if(ConferenceQth != NULL) {
6547          free(ConferenceQth);
6548       }
6549       ConferenceQth = strdup(Arg);
6550    }
6551 
6552    if(LoginInterval > 0 && (*Arg || strcmp(ConnectedStatus,Temp) != 0)) {
6553    // Send update to directory server
6554       NextLoginTime = TimeNow.tv_sec + LoginInterval;
6555       ServerRequest(SERV_REQ_LOGIN,0,NULL);
6556    }
6557    free(Temp);
6558 
6559    BufPrintf(p,"Current location string: %s\r",ConferenceQth);
6560    if(ShowStatusInInfo && ConnectedStatus[0] != 0) {
6561       BufPrintf(p,"Currently displaying: %s\r",ConnectedStatus);
6562    }
6563 }
6564 
CmdPauseTime(ClientInfo * p,ConfClient * pCC,char * Arg)6565 void CmdPauseTime(ClientInfo *p,ConfClient *pCC,char *Arg)
6566 {
6567    if(*Arg) {
6568       if(sscanf(Arg,"%d",&PauseTime) != 1) {
6569          BufPrintf(p,"Error: \"%s\" is not a number.\r",Arg);
6570          Rcode = TBD_ERR_INVALID_ARG;
6571       }
6572    }
6573    BufPrintf(p,"The minimum pause time is %d milliseconds\r",PauseTime);
6574 }
6575 
CmdBelchfilter(ClientInfo * p,ConfClient * pCC,char * Arg)6576 void CmdBelchfilter(ClientInfo *p,ConfClient *pCC,char *Arg)
6577 {
6578    if(*Arg) {
6579       if(sscanf(Arg,"%d",&BelchTime) != 1) {
6580          BufPrintf(p,"Error: \"%s\" is not a number.\r",Arg);
6581       }
6582    }
6583    BufPrintf(p,"The belchfilter is current set to %d milliseconds\r",BelchTime);
6584 }
6585 
6586 // Busy [on] [off] [status]
CmdBusy(ClientInfo * p,ConfClient * pCC,char * Arg)6587 void CmdBusy(ClientInfo *p,ConfClient *pCC,char *Arg)
6588 {
6589    int bBusyStateChanged = FALSE;
6590 
6591    if(*Arg) {
6592       if(STRCMP(Arg,"on") == 0) {
6593          bBusyStateChanged = !PullerStatusBusy;
6594          bConferenceBusy = TRUE;
6595          PullerStatusBusy = TRUE;
6596       }
6597       else if(STRCMP(Arg,"off") == 0) {
6598          bBusyStateChanged = PullerStatusBusy;
6599          bConferenceBusy = FALSE;
6600          PullerStatusBusy = FALSE;
6601       }
6602       else if(STRCMP(Arg,"status") == 0) {
6603          bBusyStateChanged = !PullerStatusBusy;
6604          bConferenceBusy = FALSE;
6605          PullerStatusBusy = TRUE;
6606       }
6607       else {
6608          BufPrintf(p,"Error: Invalid argument \"%s\"\r",Arg);
6609          BufPrintf(p,"Usage: Busy [on] [off] [status]\r");
6610       }
6611    }
6612    if(bBusyStateChanged) {
6613       if(LoginInterval > 0) {
6614          NextAVRSTime = TimeNow.tv_sec;   // Force an AVRS update
6615          NextLoginTime = TimeNow.tv_sec + LoginInterval;
6616          ServerRequest(SERV_REQ_LOGIN,0,NULL);
6617       }
6618    }
6619    if(bConferenceBusy != PullerStatusBusy) {
6620       BufPrintf(p,"The indicated conference state is %s.\r",
6621                 PullerStatusBusy ? "busy" : "available");
6622    }
6623    else {
6624       BufPrintf(p,"The conference is %s.\r",
6625                 PullerStatusBusy ? "busy" : "available");
6626    }
6627 }
6628 
CmdChat(ClientInfo * p,ConfClient * pCC,char * Arg)6629 void CmdChat(ClientInfo *p,ConfClient *pCC,char *Arg)
6630 {
6631    if(*Arg) {
6632       if(STRCMP(Arg,"on") == 0) {
6633          bCmdLineChatMode = TRUE;
6634       }
6635       else if(STRCMP(Arg,"off") == 0) {
6636          bCmdLineChatMode = FALSE;
6637       }
6638       else {
6639          BufPrintf(p,"Error: Invalid argument \"%s\"\r",Arg);
6640          BufPrintf(p,"Usage: Chat [on] [off]\r");
6641       }
6642    }
6643    BufPrintf(p,"Chat mode is %s.\r",bCmdLineChatMode ? "on" : "off");
6644 }
6645 
CmdCrash(ClientInfo * p,ConfClient * pCC,char * Arg)6646 void CmdCrash(ClientInfo *p,ConfClient *pCC,char *Arg)
6647 {
6648    DumpMe();
6649 }
6650 
6651 
CmdHelpNoDot(ClientInfo * p,ConfClient * pCC,char * Arg)6652 void CmdHelpNoDot(ClientInfo *p,ConfClient *pCC,char *Arg)
6653 {
6654    CmdHelp(p,pCC,Arg);
6655 
6656    BufPrintf(p,"PLEASE NOTE: All commands MUST start with a PERIOD or DOT.\r");
6657    BufPrintf(p,"The HELP command you just entered is the only exception.\r");
6658 }
6659 
6660 // CmdLine
6661 // 0 = command doesn't match
6662 // 1 = command matches
6663 // 2 = exact command match
MatchCmd(const char * CmdLine,const char * Command)6664 int MatchCmd(const char *CmdLine, const char *Command)
6665 {
6666    while(*CmdLine == ' ') {
6667       CmdLine++;
6668    }
6669 
6670    if(!*CmdLine) {
6671       return 0;
6672    }
6673 
6674    while(*CmdLine && *CmdLine != ' ' && *CmdLine != '\r' && *CmdLine != '\n') {
6675       if(tolower(*CmdLine) != tolower(*Command)) {
6676          return FALSE;
6677       }
6678       CmdLine++;
6679       Command++;
6680    }
6681    if(*Command == 0) {
6682    // Exact match
6683       return 2;
6684    }
6685    return 1;
6686 }
6687 
6688 struct COMMAND_TABLE {
6689    char *CmdString;
6690    char *HelpString;
6691    void (*CmdHandler)(ClientInfo *p,ConfClient *pCC,char *Arg);
6692    int   Flags;
6693       #define  CMD_FLAG_NOLIST      1  // suppress listing in help commands
6694       #define  CMD_FLAG_ADMIN       2  // commands available to admins only
6695       #define  CMD_FLAG_NEED_DISK   4  // commands that access the disk
6696       #define  CMD_FLAG_SYSOP       8  // commands available to sysops & admins
6697       #define  CMD_FLAG_LURK     0x10  // lurking command
6698       #define  CMD_FLAG_NOSCRIPT 0x20  // command not available to scripting
6699       #define  CMD_FLAG_SCRIPT   0x40  // command only available to scripting
6700       #define  CMD_FLAG_RFONLY   0x80  // command only available to rf users
6701 };
6702 
6703 #define CMD_ADMIN_DISK  (CMD_FLAG_ADMIN | CMD_FLAG_NEED_DISK)
6704 
6705 struct COMMAND_TABLE commandtable[] = {
6706    { "about", "Show information about specified station", CmdAbout, 0},
6707    { "admin", "", CmdAdmin, CMD_FLAG_NOLIST},
6708    { "admins", "List logged in admins", CmdAdmins, CMD_FLAG_SYSOP},
6709    { "allow", "Allow a user to use the conference", CmdAllow, CMD_FLAG_ADMIN},
6710    { "ban", "Ban a user from the conference", CmdBan, CMD_FLAG_ADMIN},
6711    { "belchfilter", "Set minimum TX time for recognition", CmdBelchfilter,
6712       CMD_FLAG_SYSOP},
6713    { "busy", "List conference as busy", CmdBusy, CMD_FLAG_SYSOP},
6714    { "chat", "enable or disable chat traffic", CmdChat, CMD_FLAG_SCRIPT},
6715    { "crash", "", CmdCrash, CMD_FLAG_ADMIN | CMD_FLAG_NOLIST },
6716    { "connect", "Connect to another conference", CmdConnect, CMD_FLAG_SYSOP},
6717    { "debug", "", CmdDebug, CMD_FLAG_NOLIST },
6718 #ifdef   LINK_BOX
6719    { "dtmfdecode", "", CmdDtmfDecode, CMD_FLAG_NOLIST },
6720 #endif
6721    { "dns", "DNS [refresh] [list]", CmdDNS, CMD_FLAG_ADMIN},
6722    { "disconnect", "Disconnect from another conference", CmdDisconnect,
6723       CMD_FLAG_SYSOP},
6724    { "delurk", "Leave monitor mode", CmdDelurk, CMD_FLAG_LURK },
6725    { "help", "", CmdHelp, CMD_FLAG_NOLIST},
6726    { "elp", "", CmdHelpNoDot, CMD_FLAG_NOLIST},
6727 #ifdef   LINK_BOX
6728    { "id", "", CmdForceID, CMD_FLAG_NOLIST | CMD_FLAG_ADMIN},
6729    { "frequency", "set RF ports operating parameters", CmdFrequency,
6730       CMD_FLAG_ADMIN | CMD_FLAG_NOLIST},
6731 #endif
6732    { "info", "Set directory server location string", CmdInfo, CMD_FLAG_ADMIN},
6733    { "kick", "kick a user off conference", CmdKick, CMD_FLAG_ADMIN},
6734    { "list", "List available prerecorded nets and bulletins", CmdList,
6735       CMD_FLAG_NEED_DISK },
6736 #ifdef   LINK_BOX
6737    { "link", "Link on port to another", CmdLink, CMD_FLAG_NOLIST | CMD_FLAG_ADMIN},
6738 #endif
6739    { "lookup","Lookup a user by Callsign and display Node,Qth", CmdLookUp,0},
6740    { "lurk", "Enter monitor mode", CmdLurk, CMD_FLAG_LURK },
6741    { "lurkers", "", CmdLurkers, CMD_FLAG_NOLIST},
6742    { "message", "send text message to all users", CmdMessage,
6743       CMD_FLAG_SYSOP | CMD_FLAG_SCRIPT},
6744    { "monitor", "don't send to specified station", CmdMonitor, CMD_FLAG_SYSOP},
6745    { "mute", "put a user into receive only mode", CmdMute, CMD_FLAG_SYSOP},
6746 #ifdef   LINK_BOX
6747    { "script", "", CmdScript, CMD_FLAG_RFONLY | CMD_FLAG_NOLIST},
6748 #endif
6749    { "sysop", "", CmdSysop, CMD_FLAG_NOLIST},
6750    { "pausetime", "Set minimum pause time", CmdPauseTime, CMD_FLAG_SYSOP},
6751 
6752 #ifdef   LINK_BOX
6753    { "pcm", "raw pcm record/playback for debuggging", CmdPCM,
6754       CMD_FLAG_ADMIN | CMD_FLAG_NOLIST},
6755 #endif
6756 
6757    { "play", "play are prerecorded net or bulletin", CmdPlay,
6758       CMD_FLAG_NEED_DISK | CMD_FLAG_NOSCRIPT},
6759    { "play4", "play file for specified user(s)", CmdPlayFor,
6760       CMD_FLAG_SYSOP | CMD_FLAG_NEED_DISK },
6761 #ifdef   LINK_BOX
6762    { "port", "",CmdSetPort, CMD_FLAG_ADMIN | CMD_FLAG_NOLIST },
6763 #endif
6764    { "quickexit", "", CmdQuit, CMD_FLAG_ADMIN | CMD_FLAG_NOLIST },
6765    { "quote", "send a command to linked conferences", CmdQuote, CMD_FLAG_SYSOP},
6766    { "record", "record traffic for later playback", CmdRecord, CMD_ADMIN_DISK},
6767    { "refresh", "refresh station list", CmdRefresh, CMD_FLAG_ADMIN},
6768    { "rehash", "re-read the configuration file", CmdRehash, CMD_FLAG_ADMIN},
6769 #ifdef   LINK_BOX
6770    { "txoffset", "set transmitter offset", CmdTxOffset,
6771       CMD_FLAG_ADMIN | CMD_FLAG_NOLIST},
6772    { "txpower", "set transmitter power", CmdTxPower,
6773       CMD_FLAG_ADMIN | CMD_FLAG_NOLIST},
6774    { "rxfrequency", "set ports receive frequency", CmdRxFrequency,
6775       CMD_FLAG_ADMIN | CMD_FLAG_NOLIST},
6776    { "rxtone", "set CTCSS or DCS decoder tone", CmdRxTone,
6777       CMD_FLAG_ADMIN | CMD_FLAG_NOLIST},
6778    { "rxlevel", "display receiver levels", CmdRxLevel,
6779       CMD_FLAG_ADMIN | CMD_FLAG_NOLIST},
6780 #endif
6781    { "set", "set a configuration variable", CmdSet, CMD_FLAG_ADMIN},
6782    { "showip", "show IP address of current users", CmdShowIP, CMD_FLAG_ADMIN },
6783    { "shutdown", "", CmdShutdown, CMD_FLAG_ADMIN | CMD_FLAG_NOLIST},
6784 #ifdef   LINK_BOX
6785    { "shutup", "kill queued announcements", CmdShutup,
6786       CMD_FLAG_ADMIN | CMD_FLAG_NOLIST},
6787    { "say", "Speak a phrase to RF port", CmdSay,
6788       CMD_FLAG_ADMIN | CMD_FLAG_NOLIST},
6789    { "sayresult", "Say results of previous command", CmdSayResult,
6790       CMD_FLAG_RFONLY | CMD_FLAG_NOLIST},
6791    { "saystatus", "Announce system status", CmdSayStatus,
6792       CMD_FLAG_RFONLY | CMD_FLAG_NOLIST},
6793    { "sendbeacon", "send AX25 beacon", CmdSendAx25Beacon,
6794       CMD_FLAG_RFONLY | CMD_FLAG_NOLIST},
6795 #endif
6796    { "stats", "Display statistics", CmdStats, 0},
6797 #ifdef   LINK_BOX
6798    { "sweep", "Generate test tone", CmdSweep, CMD_FLAG_ADMIN | CMD_FLAG_NOLIST},
6799 #endif
6800    { "stop", "end playback of recorded bulletin", CmdStop,
6801       CMD_FLAG_NEED_DISK | CMD_FLAG_NOSCRIPT},
6802 #ifdef   LINK_BOX
6803    { "tonegen", "Generate telemeter tones on for current port", CmdToneGen,
6804       CMD_FLAG_ADMIN | CMD_FLAG_NOLIST},
6805    { "txtone", "set CTCSS or DCS encode tone", CmdTxTone,
6806       CMD_FLAG_ADMIN | CMD_FLAG_NOLIST},
6807 #endif
6808    { "test", "Record and playback test transmission", CmdLevelTest,
6809       CMD_FLAG_NEED_DISK | CMD_FLAG_NOSCRIPT},
6810    { "unmute", "restore a user to transceive mode", CmdUnMute, CMD_FLAG_SYSOP},
6811    { "users", "show user list", CmdUsers, CMD_FLAG_SYSOP},
6812 
6813 #ifdef   LINK_BOX
6814    { "unlink", "Unlink ports", CmdUnLink, CMD_FLAG_NOLIST | CMD_FLAG_ADMIN},
6815 #endif
6816    { "uptime", "Display system uptime", CmdUpTime, 0},
6817    { "version", "Display version info", CmdVersion, 0},
6818    { "xyzzy", "", CmdXyzzy, CMD_FLAG_NOLIST},
6819    { "?", "", CmdHelp, CMD_FLAG_NOLIST},
6820    { NULL}
6821 };
6822 
CmdHelp(ClientInfo * p,ConfClient * pCC,char * Arg)6823 void CmdHelp(ClientInfo *p,ConfClient *pCC,char *Arg)
6824 {
6825    struct COMMAND_TABLE *pTbl = commandtable;
6826 
6827    if(CustomHelp != NULL) {
6828       BufPrintf(p,"%s",CustomHelp);
6829    }
6830    else {
6831       BufPrintf(p,"Commands:\r");
6832 
6833       pTbl = commandtable;
6834       while(pTbl->CmdString != NULL) {
6835          if(!(pTbl->Flags & CMD_FLAG_NOLIST) &&
6836             (!(pTbl->Flags & CMD_FLAG_ADMIN) || pCC->bAdmin) &&
6837             (!(pTbl->Flags & CMD_FLAG_SYSOP) || pCC->bSysop) &&
6838             (!(pTbl->Flags & CMD_FLAG_SCRIPT) || pCC->bCmdLine) &&
6839             (!(pTbl->Flags & CMD_FLAG_NOSCRIPT) || !pCC->bCmdLine) &&
6840             (!(pTbl->Flags & CMD_FLAG_NEED_DISK) || EnableDiskCommands) &&
6841             (!(pTbl->Flags & CMD_FLAG_LURK) || !bLurkDisabled))
6842          {
6843             BufPrintf(p,".%s:\r   %s\r",pTbl->CmdString,pTbl->HelpString);
6844          }
6845          pTbl++;
6846       }
6847    }
6848 }
6849 
6850 // Process conference command
ConferenceCmd(ClientInfo * p,ConfClient * pCC,char * Cmd)6851 void ConferenceCmd(ClientInfo *p,ConfClient *pCC,char *Cmd)
6852 {
6853    int i = -1 ;
6854    int Result = -1;
6855    int InitialCount = -1;
6856    int CurrentCmd = -1;
6857    int CmdsMatched = -1;
6858    int bFirstLoop = TRUE;
6859    char *cp = NULL;
6860    char *Arg = NULL;
6861    struct COMMAND_TABLE *pTbl = commandtable;
6862    char *CmdLine = strdup(Cmd);
6863    char *NextCommand = CmdLine;
6864    char *Command = NULL;
6865    int QuoteCount = 0;
6866    ClientInfo DummyClient;
6867 
6868    DummyClient.Buf = NULL;
6869    Rcode = TBD_OK;
6870 
6871    for( ; ; ) {
6872       bLogCmd = TRUE;   // assume we'll log this command
6873       if(bFirstLoop) {
6874          bFirstLoop = FALSE;
6875          if(p == NULL) {
6876          // no ClientInfo, use a the dummy
6877             DummyClient.BufSize = CONF_BUF_SIZE;
6878             if((DummyClient.Buf = malloc(DummyClient.BufSize)) == NULL) {
6879                LOG_ERROR(("ConferenceCmd: Error: malloc failed\n"));
6880                break;
6881             }
6882             DummyClient.p = piLinkConf;
6883             p = &DummyClient;
6884          }
6885 
6886          if(CmdLine == NULL) {
6887             LOG_ERROR(("ConferenceCmd: Error: malloc failed\n"));
6888             break;
6889          }
6890          p->Count = 0;  // init for BufPrintf
6891          BufPrintf(p,"%c" NDATA,ILINK_DATA_PACKET);
6892          InitialCount = p->Count;
6893       }
6894 
6895       CmdsMatched = 0;
6896       if((cp = NextCommand) == NULL) {
6897          break;
6898       }
6899       NextCommand = NULL;
6900 
6901       while(*cp == ' ') {
6902          cp++;
6903       }
6904 
6905       if(*cp == '.') {
6906          cp++;
6907       }
6908 
6909       if(*cp == 0) {
6910          break;
6911       }
6912 
6913    // Find next command
6914 
6915       Command = cp;
6916       while(*cp) {
6917          if(*cp == ';') {
6918             if((QuoteCount & 1) == 0) {
6919                *cp++ = 0;
6920                NextCommand = cp;
6921                break;
6922             }
6923          }
6924          else if(*cp == '"') {
6925             QuoteCount++;
6926          }
6927          cp++;
6928       }
6929 
6930       pTbl = commandtable;
6931       for(i = 0; pTbl->CmdString != NULL; i++) {
6932          if((Result = MatchCmd(Command,pTbl->CmdString))) {
6933             CmdsMatched++;
6934             CurrentCmd = i;
6935             if(Result == 2) {
6936             // Exact match found
6937                CmdsMatched = 1;
6938                break;
6939             }
6940          }
6941          pTbl++;
6942       }
6943 
6944       if(CmdsMatched > 1) {
6945       // Ambiguous
6946          BufPrintf(p,"Error: Command is ambiguous.\r");
6947          Rcode = TBD_ERR_INVALID_CMD;
6948          break;
6949       }
6950 
6951       if(CmdsMatched == 0) {
6952       // not found
6953          EventHook("command %s %s",pCC->Callsign,CmdLine);
6954          Rcode = TBD_ERR_INVALID_CMD;
6955          break;
6956       }
6957 
6958       cp = Command;
6959       while(*cp && *cp != ' ') {
6960          cp++;
6961       }
6962       while(*cp && *cp == ' ') {
6963          cp++;
6964       }
6965 
6966       pTbl = &commandtable[CurrentCmd];
6967       if(pTbl->Flags & CMD_FLAG_NEED_DISK && !EnableDiskCommands) {
6968       // Command needs disk access and disk is disabled
6969          Rcode = TBD_ERR_NO_DISK;
6970          break;
6971       }
6972 
6973       if(pTbl->Flags & CMD_FLAG_NOSCRIPT && pCC->bCmdLine) {
6974          Rcode = TBD_ERR_INVALID_CMD;
6975          break;
6976       }
6977 
6978       if(pTbl->Flags & CMD_FLAG_SCRIPT && !pCC->bCmdLine) {
6979          Rcode = TBD_ERR_INVALID_CMD;
6980          break;
6981       }
6982 
6983       if((!(pTbl->Flags & CMD_FLAG_ADMIN) || pCC->bAdmin) &&
6984          (!(pTbl->Flags & CMD_FLAG_SYSOP) || pCC->bSysop))
6985       {
6986          Arg = strdup(cp);
6987          pTbl->CmdHandler(p,pCC,Arg);
6988          free(Arg);
6989       }
6990       if(bLogCmd) {
6991          LOG_NORM(("%s: \"%s\" (%d).\n",pCC->Callsign,Command,Rcode));
6992       }
6993       if(Rcode != TBD_OK) {
6994          break;
6995       }
6996    }
6997 
6998    if(p->Count > InitialCount && !pCC->bCmdLine) {
6999    // send response
7000       p->Count++; // include terminating null
7001       SendBuf2(p,pCC,FALSE);
7002    }
7003 
7004 // Save the result code and text for possible use by SayResult
7005    if(LastCmdText != NULL) {
7006       free(LastCmdText);
7007       LastCmdText = NULL;
7008    }
7009 
7010    LastRcode = Rcode;
7011    if(p->Count > InitialCount) {
7012       LastCmdText = strdup(&p->Buf[strlen(NDATA)+1]);
7013    }
7014    LastCmd = pTbl->CmdString;
7015 
7016    if(CmdLine != NULL) {
7017       free(CmdLine);
7018    }
7019 
7020    if(DummyClient.Buf != NULL) {
7021       free(DummyClient.Buf);
7022    }
7023 }
7024 
7025 
7026 // Return number of milliseconds of time since p
TimeLapse(struct timeval * p)7027 int TimeLapse(struct timeval *p)
7028 {
7029    int DeltaSeconds = TimeNow.tv_sec - p->tv_sec;
7030 
7031    if(DeltaSeconds > (0x7fffffff / 1000)) {
7032    // TimeLapse is too big to express in milliseconds, just return
7033    // the max we can.
7034       return 0x7fffffff;
7035    }
7036    return (DeltaSeconds * 1000) +
7037           ((TimeNow.tv_usec - p->tv_usec) / 1000);
7038 }
7039 
Send2(ConfClient * pCC,void * Data,int Count,int bControlPort)7040 void Send2(ConfClient *pCC,void *Data,int Count,int bControlPort)
7041 {
7042    ConfServer *pCS = pCC->pCS;
7043    int TxLen;
7044    IPAdrUnion HisAdr;
7045    int Socket;
7046 
7047    if(bEchoLinkEnabled || !pCS->biLinkConf) {
7048       HisAdr.ADDR = pCC->HisAdr.ADDR;
7049       HisAdr.i.sin_family = AF_INET;
7050       if(bControlPort) {
7051       // rtcp (control)
7052          Socket = pCS->pControl->Socket;
7053          HisAdr.PORT = htons((unsigned short) (pCS->AudioPort + 1));
7054       }
7055       else {
7056       // Even = audio port
7057          Socket = pCS->pAudio->Socket;
7058          HisAdr.PORT = htons((unsigned short) pCS->AudioPort);
7059       }
7060 
7061       TxLen = sendto(Socket,Data,Count,0,&HisAdr.s,sizeof(HisAdr));
7062 
7063       if(Count != TxLen) {
7064          pCS->TxErrs++;
7065          LOG_ERROR(("Send2(): sendto() failed, %s",Err2String(ERROR_CODE)));
7066       }
7067       else {
7068          pCS->TxCount++;
7069          pCC->TxBytes += Count + HEADER_OVERHEAD;
7070          pCS->TxBytes += Count + HEADER_OVERHEAD;
7071          TxBytesAllConf += Count + HEADER_OVERHEAD;
7072       }
7073    }
7074 }
7075 
SendBuf2(ClientInfo * p,ConfClient * pCC,int bControLPort)7076 void SendBuf2(ClientInfo *p,ConfClient *pCC,int bControLPort)
7077 {
7078    Send2(pCC,p->Buf,p->Count,bControLPort);
7079 }
7080 
CalcBW(ConfServer * pCS,int bClear)7081 void CalcBW(ConfServer *pCS,int bClear)
7082 {
7083    int DeltaT = TimeLapse(&pCS->StartTime);
7084 
7085    if(DeltaT > 5000) {
7086    // only calculate bandwidth based on samples >= 5 seconds
7087       if(pCS->StartTime.tv_sec != 0) {
7088          pCS->TxBandWidth = (pCS->TxBytes - pCS->TxBytesStart) * 8 / DeltaT;
7089          if(pCS->TxBandWidth > pCS->PeakTxBandWidth) {
7090             pCS->PeakTxBandWidth = pCS->TxBandWidth;
7091             pCS->PeakTxBandWidthTime = TimeNow.tv_sec;
7092          }
7093 
7094          pCS->RxBandWidth = (pCS->RxBytes - pCS->RxBytesStart) * 8 / DeltaT;
7095          if(pCS->RxBandWidth > pCS->PeakRxBandWidth) {
7096             pCS->PeakRxBandWidth = pCS->RxBandWidth;
7097             pCS->PeakRxBandWidthTime = TimeNow.tv_sec;
7098          }
7099       }
7100    }
7101 
7102    if(bClear) {
7103       if(pCS->TxBytes > 1000000) {
7104          pCS->TxMBytes += pCS->TxBytes / 1000000;
7105          pCS->TxBytes %= 1000000;
7106       }
7107 
7108       if(pCS->RxBytes > 1000000) {
7109          pCS->RxMBytes += pCS->RxBytes / 1000000;
7110          pCS->RxBytes %= 1000000;
7111       }
7112 
7113       if(TxBytesAllConf > 1000000) {
7114          TxMBytesAllConf += TxBytesAllConf / 1000000;
7115          TxBytesAllConf %= 1000000;
7116       }
7117 
7118       if(RxBytesAllConf > 1000000) {
7119          RxMBytesAllConf += RxBytesAllConf / 1000000;
7120          RxBytesAllConf %= 1000000;
7121       }
7122 
7123 
7124       pCS->StartTime = TimeNow;
7125       pCS->TxBytesStart = pCS->TxBytes;
7126       pCS->RxBytesStart = pCS->RxBytes;
7127    }
7128 }
7129 
SendToAllSysops(ClientInfo * p,ConfServer * pCS)7130 void SendToAllSysops(ClientInfo *p,ConfServer *pCS)
7131 {
7132    struct avl_traverser avl_trans;
7133    ConfClient *pCC = avl_t_first(&avl_trans,pCS->ConfTree);
7134 
7135    while(pCC != NULL) {
7136       if(pCC->bSysop) {
7137          SendBuf2(p,pCC,FALSE);
7138       }
7139       pCC = (ConfClient *) avl_t_next(&avl_trans);
7140    }
7141 }
7142 
SendSDES(ConfServer * pCS,ConfClient * pCC)7143 void SendSDES(ConfServer *pCS,ConfClient *pCC)
7144 {
7145    int i = pCC->Proto;
7146    if(gProtoData[i].SDESLen == 0 ||
7147       pCC->bTxtExtension != gProtoData[i].bTxtExtension)
7148    {
7149       GenSDES(i+1,pCC->bTxtExtension);
7150    }
7151 
7152    if(gProtoData[i].SDESLen > 0) {
7153       Send2(pCC,gProtoData[i].OurSDES,gProtoData[i].SDESLen,TRUE);
7154    }
7155 }
7156 
7157 /*
7158    For now we assume that RTP clients are running GSM so all we need to
7159    do is fudge the version numbers
7160 */
ConvertRTP2iLink(ProtoData * pPDat,char * inBuf,int inLen,int bNewStream)7161 void ConvertRTP2iLink(ProtoData *pPDat, char *inBuf,int inLen,int bNewStream)
7162 {
7163    ProtoData *pPD = &pPDat[ILINK_RTP_VERSION - 1];
7164    rtp_hdr_t *pRTP = (rtp_hdr_t *) (pPD->Temp);
7165 
7166    pPD->Data[0]= pPD->Temp;
7167    memcpy(pPD->Data[0],inBuf,inLen);
7168    pPD->DataLen[0] = inLen;
7169 
7170    pRTP->ssrc = 0;
7171    pRTP->version = ILINK_RTP_VERSION;
7172 }
7173 
ConvertiLink2RTP(ProtoData * pPDat,char * inBuf,int inLen,int bNewStream)7174 void ConvertiLink2RTP(ProtoData *pPDat,char *inBuf,int inLen,int bNewStream)
7175 {
7176    ProtoData *pPD = &pPDat[RTP_VERSION -1];
7177    rtp_hdr_t *pRTP = (rtp_hdr_t *) (pPD->Temp);
7178 
7179    if(pPDat[PROTO_ILINK].Type == PKT_TYPE_AUDIO) {
7180       pPD->Data[0]= pPD->Temp;
7181       memcpy(pPD->Data[0],inBuf,inLen);
7182       pPD->DataLen[0] = inLen;
7183 
7184       pRTP->ssrc = OurNodeID;
7185       pRTP->version = RTP_VERSION;
7186    }
7187 }
7188 
7189 // Speak Freely sends 10 GSM frames per UDP packet,  EchoLink
7190 // can't deal with that so we break the first Speak Freely packet into
7191 // 2 UDP packets of 4 GSM frames plus 2 left over.
7192 // The next time we'll use the 2 left over plus the 10 new frames to
7193 // create 3 EchoLink UDP packets.
7194 //
7195 // ProtoData.Temp after even Speak Freely Packet:
7196 // <iLink Frame n> <iLink Frame n+1> <iLink Frame n+2 (partial)>
7197 //
7198 // ProtoData.Temp after old Speak Freely Packet:
7199 // <iLink Frame n+3> <iLink Frame n+4> <iLink Frame n+2>
7200 //
ConvertSF2iLink(ProtoData * pPDat,char * inBuf,int inLen,int bNewStream)7201 void ConvertSF2iLink(ProtoData *pPDat,char *inBuf,int inLen,int bNewStream)
7202 {
7203    static u_int16 Sequence = 1;
7204    ProtoData *pPD = &pPDat[ILINK_RTP_VERSION-1];
7205    static int bEvenPacket = TRUE;
7206    soundbuf *pSFHdr = (soundbuf *) inBuf;
7207    int DataLen = ntohl(pSFHdr->buffer.buffer_len) - sizeof(u_int16);
7208    char *pGSM_In = pSFHdr->buffer.buffer_val + sizeof(u_int16);
7209    int Count = 0;
7210    union {
7211       char *cp;
7212       rtp_hdr_t *pRTP;
7213    } u;
7214 
7215    u.cp = pPD->Temp;
7216 
7217 
7218    if(bNewStream) {
7219    // Toss any left over data, this is a new talker
7220       bEvenPacket = TRUE;
7221    }
7222 
7223    if(bEvenPacket) {
7224       bEvenPacket = FALSE;
7225       pPD->Data[0] = pPD->Temp;
7226       pPD->DataLen[0] = ILINK_RTP_FRAME_LEN;
7227 
7228       pPD->Data[1] = &pPD->Temp[ILINK_RTP_FRAME_LEN];
7229       pPD->DataLen[1] = ILINK_RTP_FRAME_LEN;
7230 
7231       pPD->DataLen[2] = 0;
7232 
7233    }
7234    else {
7235    // Odd packet, complete last partial packet
7236       bEvenPacket = TRUE;
7237       Count = 2 * GSM_FRAME_LEN;
7238       pPD->Data[0] = &pPD->Temp[ILINK_RTP_FRAME_LEN*2];
7239       pPD->DataLen[0] = ILINK_RTP_FRAME_LEN;
7240       memcpy(&pPD->Data[0][RTP_HDR_SIZE+Count],pGSM_In,Count);
7241       pGSM_In += Count;
7242       DataLen -= Count;
7243 
7244       pPD->Data[1] = pPD->Temp;
7245       pPD->DataLen[1] = ILINK_RTP_FRAME_LEN;
7246 
7247       pPD->Data[2] = &pPD->Temp[ILINK_RTP_FRAME_LEN];
7248       pPD->DataLen[2] = ILINK_RTP_FRAME_LEN;
7249    }
7250 
7251    while(DataLen > 0) {
7252       u.pRTP->version = ILINK_RTP_VERSION;
7253       u.pRTP->p = 0;
7254       u.pRTP->x = 0;
7255       u.pRTP->cc = 0;
7256       u.pRTP->m = 0;
7257       u.pRTP->pt = RTP_PT_GSM;
7258       u.pRTP->seq = htons(Sequence++);
7259       u.pRTP->ts = 0;
7260       u.pRTP->ssrc = 0;
7261 
7262       Count = DataLen;
7263       if(Count >= GSM_FRAME_LEN * 4) {
7264          Count = GSM_FRAME_LEN * 4;
7265       }
7266       memcpy(&u.pRTP->csrc,pGSM_In,Count);
7267 
7268       pGSM_In += Count;
7269       DataLen -= Count;
7270       u.cp = u.cp + ILINK_RTP_FRAME_LEN;
7271    }
7272 }
7273 
7274 // Echolink sends 4 GSM frames per UDP packet, Speak Freely sends 10.
7275 // We repack the EchoLink frame so they contain the same number of GSM
7276 // frames as sent by a Speak Freely client.
7277 //
7278 // iLinkCount =-1: Initialize header
7279 // iLinkCount = 0: copy first 4 GSM frames in to buffer, return DataLen = 0
7280 // iLinkCount = 1: copy next 4 GSM frames in to buffer, return DataLen = 0
7281 // iLinkCount = 2: copy last 4 GSM frames in to buffer, return
7282 //                 DataLen = size of Speak Freely packet with 10 GSM frames
7283 // iLinkCount = 3: copy 2 left over GSM frames to beginning of buffer,
7284 //                 copy 4 new GSM frames into buffer, return DataLen = 0
7285 // iLinkCount = 4: copy last 4 GSM frames into buffer, clear iLinkCount, return
7286 //                 DataLen = size of Speak Freely packet with 10 GSM frames
7287 //
ConvertiLink2SF(ProtoData * pPDat,char * inBuf,int inLen,int bNewStream)7288 void ConvertiLink2SF(ProtoData *pPDat,char *inBuf,int inLen,int bNewStream)
7289 {
7290    int HdrSize = sizeof(rtp_hdr_t) - sizeof(u_int32);
7291    int DataLen = inLen - HdrSize;
7292    ProtoData *pPD = &pPDat[PROTO_SF];
7293    soundbuf *pSFHdr = (soundbuf *) pPD->Temp;
7294    char *pGSM_data = pSFHdr->buffer.buffer_val + sizeof(u_int16);
7295    u_int16 *pOrigDataLen = (u_int16 *) pSFHdr->buffer.buffer_val;
7296 
7297    if(pPDat[PROTO_ILINK].Type == PKT_TYPE_AUDIO) {
7298       if(bNewStream) {
7299       // Toss any left over data, this is a new talker
7300          pPDat->iLinkCount = 0;
7301       }
7302 
7303       switch(pPDat->iLinkCount++) {
7304          case 0:
7305          // Initialize Speak Freely Header
7306             pSFHdr->compression = htonl(fCompGSM | fProtocol);
7307             pSFHdr->buffer.buffer_len = htonl(GSM_FRAME_LEN*10+sizeof(u_int16));
7308             *pOrigDataLen = htons(SF_AUDIO_SAMPLES_PER_PACKET);
7309             memset(pSFHdr->sendinghost,0,sizeof(pSFHdr->sendinghost));
7310             if(pPDat == gProtoData) {
7311                strncpy(pSFHdr->sendinghost,ClientTalking->Callsign,
7312                        sizeof(pSFHdr->sendinghost));
7313             }
7314             else {
7315                strncpy(pSFHdr->sendinghost,pPDat->Callsign,
7316                        sizeof(pSFHdr->sendinghost));
7317             }
7318             break;
7319 
7320          case 1:
7321             pGSM_data += (4 * GSM_FRAME_LEN);
7322             break;
7323 
7324          case 2:
7325             pGSM_data += (8 * GSM_FRAME_LEN);
7326             pPD->Data[0] = pPD->Temp;
7327             pPD->DataLen[0] = SF_GSM_FRAME_LEN;
7328             break;
7329 
7330          case 3:
7331          // Move the 2 extra GSM frames down to the head of the buffer
7332             memcpy(pGSM_data,pGSM_data + (10 * GSM_FRAME_LEN),GSM_FRAME_LEN*2);
7333             pGSM_data += (2 * GSM_FRAME_LEN);
7334             break;
7335 
7336          case 4:
7337             pGSM_data += (6 * GSM_FRAME_LEN);
7338             pPDat->iLinkCount = 0;
7339             pPD->Data[0] = pPD->Temp;
7340             pPD->DataLen[0] = SF_GSM_FRAME_LEN;
7341             break;
7342       }
7343       memcpy(pGSM_data,&inBuf[HdrSize],DataLen);
7344    }
7345 }
7346 
7347 
7348 // The iLink protocol is used as a common basis, i.e. either the InputProto
7349 // or OutputProto must be PROTO_ILINK.
ConvertProtocol(ProtoData * pProtoDat,Protocol InputProto,Protocol OutputProto,int bNewStream)7350 void ConvertProtocol(
7351    ProtoData *pProtoDat,
7352    Protocol InputProto,
7353    Protocol OutputProto,
7354    int bNewStream)
7355 {
7356    char *Buf;
7357    int Len;
7358    int i;
7359 
7360    if(ProtoConvert[InputProto][OutputProto] != NULL) {
7361       for(i = 0; i < MAX_CONV_PACKETS; i++) {
7362          if((Len = pProtoDat[InputProto].DataLen[i]) > 0) {
7363             Buf = pProtoDat[InputProto].Data[i];
7364             if(i == 0 && bNewStream) {
7365                ProtoConvert[InputProto][OutputProto](pProtoDat,Buf,Len,TRUE);
7366             }
7367             else {
7368                ProtoConvert[InputProto][OutputProto](pProtoDat,Buf,Len,FALSE);
7369             }
7370          }
7371       }
7372    }
7373    pProtoDat[OutputProto].bDataValid = TRUE;
7374 }
7375 
OurUser(ConfServer * pCS,char * Callsign)7376 int OurUser(ConfServer *pCS,char *Callsign)
7377 {
7378    ConfClient *pCC;
7379    struct avl_traverser avl_trans;
7380    int Ret = FALSE;
7381 
7382    pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
7383    while(pCC != NULL) {
7384       if(strcmp(pCC->Callsign,Callsign) == 0) {
7385       // Yup he's one of ours
7386          Ret = TRUE;
7387          break;
7388       }
7389       pCC = (ConfClient *) avl_t_next(&avl_trans);
7390    }
7391 
7392    return Ret;
7393 }
7394 
ForwardCommandResponse(ClientInfo * p,ConfClient * pCC1)7395 void ForwardCommandResponse(ClientInfo *p,ConfClient *pCC1)
7396 {
7397    ConfClient *pCC;
7398    struct avl_traverser avl_trans;
7399    ConfServer *pCS = pCC1->pCS;
7400    char *cp = strdup(&p->Buf[sizeof(NDATA)]);
7401 
7402    if(cp != NULL) {
7403       p->Count = 0;  // init for BufPrintf
7404       BufPrintf(p,"%c" NDATA "%s>%s",ILINK_DATA_PACKET,pCC1->Callsign,cp);
7405       free(cp);
7406 
7407    // Forward all (remote) command responses to the command line client
7408    // if he's in chat mode since he didn't need to use the .quote command
7409    // to issue a command
7410 
7411       if(bCmdLineChatMode) {
7412          FwdTextCmdLine(p,p->Buf,&CmdLineCC);
7413       }
7414 
7415       if(ChatPort != 0) {
7416          FwdTextCmdLine(p,p->Buf,&ChatCC);
7417       }
7418 
7419       pCC = &CmdLineCC;
7420       while(pCC != NULL) {
7421          if(pCC->bSentQuotedCmd) {
7422             if(pCC == &CmdLineCC) {
7423                if(!bCmdLineChatMode) {
7424                   FwdTextCmdLine(p,p->Buf,&CmdLineCC);
7425                }
7426             }
7427             else if(TimeLapse(&pCC->FirstAudioIn) > 30000) {
7428             // Stop forwarding possible responses after 30 seconds
7429                pCC->bSentQuotedCmd = FALSE;
7430             }
7431             else {
7432                Send2(pCC,p->Buf,p->Count,FALSE);
7433                break;
7434             }
7435          }
7436 
7437          if(pCC == &CmdLineCC) {
7438             pCC = (ConfClient *) avl_t_first(&avl_trans,pCS->ConfTree);
7439          }
7440          else {
7441             pCC = (ConfClient *) avl_t_next(&avl_trans);
7442          }
7443       }
7444    }
7445 }
7446 
DuplicateTextMsg(ClientInfo * p,ConfClient * pCC)7447 int DuplicateTextMsg(ClientInfo *p,ConfClient *pCC)
7448 {
7449    int i;
7450    int j;
7451    unsigned short crc = 0;
7452    int Ret = FALSE;
7453    time_t Oldest = (time_t) -1;
7454    int OldestEntry = 0;
7455    DupTrackingEntry *pDup;
7456    char *DupMessage;
7457    char *cp;
7458    ConfServer *pCS = pCC->pCS;
7459    int TextLen = strlen(p->Buf);
7460 
7461 // Calculate a CRC of the text message
7462 // NB: exclude any SSRC appended to the message, there's a bug
7463 // in some versions of EchoLink that caused the SSRC to be corrupted
7464 // when text messages are forwarded.
7465 
7466    for(i = 0; i < TextLen; i++) {
7467       crc ^= p->Buf[i] << 8;
7468       for(j = 0; j < 8; j++) {
7469          if(crc & 0x8000) {
7470             crc = crc << 1 ^ 0x1021;
7471          }
7472          else {
7473             crc <<= 1;
7474          }
7475       }
7476    }
7477 
7478    pDup = DupTracking;
7479    for(i = 0; i < MAX_DUP_TRACKING; i++) {
7480       if(Oldest == (time_t) -1 || pDup->TimeStamp < Oldest) {
7481          Oldest = pDup->TimeStamp;
7482          OldestEntry = i;
7483       }
7484 
7485       if((TimeNow.tv_sec - pDup->TimeStamp) <= DUP_TRACKING_TIMEOUT &&
7486          crc == pDup->crc)
7487       {  // Duplicate!
7488          pDup->TimeStamp = TimeNow.tv_sec;
7489          if(!pDup->bLogged) {
7490             pDup->bLogged = TRUE;
7491             DupMessage = &p->Buf[sizeof(NDATA)];
7492             if((cp = strchr(DupMessage,'\r')) != NULL) {
7493             // remove CR
7494                *cp = 0;
7495             }
7496             LOG_WARN(("Dup from %s, %s dropped (0x%x):\n",pDup->FirstFrom,
7497                       pCC->Callsign,pDup->crc));
7498             LOG_WARN(("%s\n",DupMessage));
7499 
7500             p->Count = 0;  // init for BufPrintf
7501             BufPrintf(p,"%c" NDATA,ILINK_DATA_PACKET);
7502             BufPrintf(p,"%s>Dup from %s, %s dropped.\r",ConferenceCall,
7503                       pDup->FirstFrom,pCC->Callsign);
7504             SendToAllSysops(p,pCS);
7505          }
7506          Ret = TRUE;
7507          pCC->DupsReceived++;
7508          pCS->DupsReceived++;
7509          if(MaxDups != 0 && pCC->DupsReceived > MaxDups) {
7510             pCS->DupDisconnects++;
7511             LOG_ERROR(("Disconnecting %s too many dups.\n",pCC->Callsign));
7512             DisconnectCC(p,pCC,"Loop disconnect!");
7513 
7514          // We clobbered the buffer, reinitialize it for our response
7515             p->Count = 0;  // init for BufPrintf
7516             BufPrintf(p,"%c" NDATA,ILINK_DATA_PACKET);
7517             BufPrintf(p,"%s disconnected, too many dups.\r",pCC->Callsign);
7518             pCS->bSendStationList = TRUE;
7519             SendToAllSysops(p,pCS);
7520          }
7521          break;
7522       }
7523       pDup++;
7524    }
7525 
7526    if(!Ret) {
7527    // Add new text message to the cache
7528       pDup = &DupTracking[OldestEntry];
7529       if(pDup->TimeStamp != 0 &&
7530          (TimeNow.tv_sec - pDup->TimeStamp) <= DUP_TRACKING_TIMEOUT)
7531       {
7532       // Play it safe and assume this is a duplicate
7533          Ret = TRUE;
7534          LOG_WARN(("DupTracking cache full!\n"));
7535          LOG_WARN(("Oldest: %d\n",OldestEntry));
7536          for(i = 0; i < MAX_DUP_TRACKING; i++) {
7537             LOG_WARN(("%d: TimeStamp: %d, From:%s, CRC: %d\n",i,
7538                       DupTracking[i].TimeStamp,DupTracking[i].FirstFrom,
7539                       DupTracking[i].crc));
7540          }
7541       }
7542       pDup->crc = crc;
7543       pDup->TimeStamp = TimeNow.tv_sec;
7544       pDup->bLogged = FALSE;
7545       strncpy(pDup->FirstFrom,pCC->Callsign,MAX_CALL_LEN);
7546    }
7547 
7548    return Ret;
7549 }
7550 
GetPacketType(ClientInfo * p,ConfClient * pCC)7551 PacketType GetPacketType(ClientInfo *p,ConfClient *pCC)
7552 {
7553    char *cp;
7554    char *cp1;
7555    int Len;
7556    soundbuf *pSFHdr = (soundbuf *) p->Buf;
7557    rtp_hdr_t *pRTP = (rtp_hdr_t *) p->Buf;
7558    ConfServer *pCS = pCC->pCS;
7559    PacketType Ret = PKT_TYPE_UNKNOWN;
7560    FILE *fp;
7561    int CallLen;
7562    int Compression;
7563 
7564    switch(pCC->Proto) {
7565       case PROTO_SF:
7566       // Speak Freely sends text via the RTCP port, so if it's on the RTP
7567       // port it must be audio
7568          Compression = (int) ntohl(pSFHdr->compression);
7569          if(Compression == (fCompGSM | fProtocol)) {
7570             pCC->CompressionType = RTP_PT_GSM;
7571             Ret = PKT_TYPE_AUDIO;
7572          }
7573          else if(Compression == (fCompADPCM | fProtocol)) {
7574             pCC->CompressionType = RTP_PT_DVI4_8K;
7575             Ret = PKT_TYPE_AUDIO;
7576          }
7577          else if(Compression == fProtocol) {
7578          // No compression (uLaw)
7579             pCC->CompressionType = RTP_PT_PCMU;
7580             Ret = PKT_TYPE_AUDIO;
7581          }
7582          else {
7583             Ret = PKT_TYPE_IGNORE;
7584             DLOG(DLOG_CODEC_TYPE,
7585                  ("Ignoring SF CompressionType 0x%x from %s.\n",
7586                   ntohl(pSFHdr->compression),CallLog(pCC)));
7587          }
7588          break;
7589 
7590       case PROTO_RTP:
7591       // RTP doesn't send text so it must be audio
7592          pCC->bSendSSRC = TRUE;  // always send ssrc for RTP
7593          pCC->CompressionType = pRTP->pt;
7594          switch(pRTP->pt) {
7595             case RTP_PT_GSM:
7596             case RTP_PT_DVI4_8K: // aka adpcm
7597             case RTP_PT_PCMU:
7598             case RTP_PT_G726:
7599                Ret = PKT_TYPE_AUDIO;
7600                break;
7601 
7602             default:
7603                Ret = PKT_TYPE_IGNORE;
7604                DLOG(DLOG_CODEC_TYPE,
7605                     ("Ignoring RTP packet type 0x%x from %s.\n",pRTP->pt,
7606                      CallLog(pCC)));
7607                break;
7608          }
7609          break;
7610 
7611       case PROTO_ILINK:
7612          switch(p->Buf[0]) {
7613             case '/':
7614                Ret = PKT_TYPE_CMD;
7615                break;
7616 
7617             case ILINK_DATA_PACKET:
7618             // Data packet.  We only want to forward message line traffic,
7619             // not the general station info that we get when a new station
7620             // joins the conference.  The general form of message line traffic
7621             // is "callsign>data ..."  Check for "callsign>" at the begining of
7622             // the line.
7623 
7624             // Make sure this packet has an SSRC
7625                Ret = PKT_TYPE_IGNORE;
7626                cp = &p->Buf[sizeof(NDATA)];   // first character of data
7627                if(*cp != '\r' && (cp1 = strchr(cp,'>')) != NULL) {
7628                   *cp1 = 0;
7629                // Make sure there are no spaces from the beginning of the line
7630                // to the '>' and that the length of the callsign is reasonable
7631                   Len = strlen(cp);
7632                   if(Len <= MAX_CALL_LEN && strchr(cp,' ') == NULL) {
7633                   // Yup, it's a message line
7634                      Ret = PKT_TYPE_DATA;
7635                      if(pCC->bCmdLine || strcmp(cp,pCC->Callsign) == 0) {
7636                         if(!pCC->bCmdLine &&
7637                            (cp1[1] == '.' ||
7638                             strncmp(&cp1[1],"help\r",5) == 0 ||
7639                             strncmp(&cp1[1],"HELP\r",5) == 0))
7640                         {
7641                            Ret = PKT_TYPE_CMD;
7642                         }
7643                      }
7644                      else {
7645                      // Callsign doesn't match.  Assume the client is a
7646                      // conference bridge because he's sending us text
7647                      // messages from other stations.
7648                         pCC->bConf = TRUE;
7649 
7650                         if(*cp == '*' || OurUser(pCS,cp)) {
7651                         // Don't forward messages from our users or messages
7652                         // from conference bridges to break loops.
7653                         // Just ignore the packet
7654                            Ret = PKT_TYPE_IGNORE;
7655                         }
7656                      }
7657                   }
7658                   *cp1 = '>';
7659                   if(Ret != PKT_TYPE_CMD && !pCC->bCmdLine &&
7660                      DuplicateTextMsg(p,pCC))
7661                   {
7662                      Ret = PKT_TYPE_IGNORE;
7663                   }
7664                }
7665 
7666                if(Ret == PKT_TYPE_IGNORE && !pCC->bFilePlayer) {
7667                   if(pCC->bConf && *cp != '\r') {
7668                   // Forward possible command response anyone who has
7669                   // entered a quoted command
7670                      ForwardCommandResponse(p,pCC);
7671                   }
7672                   else if(*cp == '\r') {
7673                   // Must be the station's info text or station list
7674                      Len = p->Count - strlen(NDATA) - 1;
7675                      if(!pCC->bTBD) {
7676                      // Only save brag sheet info if it's not relayed from
7677                      // another other conference
7678                         if(pCC->Info != NULL) {
7679                            free(pCC->Info);
7680                         }
7681                         pCC->Info = malloc(Len);
7682                         if(pCC->Info != NULL) {
7683                            memcpy(pCC->Info,cp,Len);
7684                         }
7685                      }
7686 
7687                      if(SaveInfoFiles) {
7688                      // Convert \r to \n
7689                         cp1 = cp;
7690                         while((cp1 = strchr(cp1,'\r')) != NULL) {
7691                            *cp1++ = '\n';
7692                         }
7693 
7694                         CallLen = strlen(pCC->Callsign) + 1;   // include null
7695                         cp1 = malloc(CallLen+strlen(INFO_EXTENSION));
7696                         if(cp1 != NULL) {
7697                            strcpy(cp1,pCC->Callsign);
7698                            strcat(cp1,INFO_EXTENSION);
7699                            Len = strlen(&cp[1]);
7700                            if((fp = fopen(cp1,"w")) != NULL) {
7701                               if(fwrite(&cp[1],Len,1,fp) != 1) {
7702                               // Write error
7703                                  LOG_ERROR(("GetPacketType(): fwrite failed, %s"
7704                                             ,Err2String(errno)));
7705                               }
7706                               fclose(fp);
7707                            }
7708                            free(cp1);
7709                         }
7710                      }
7711                   }
7712                }
7713                break;
7714 
7715             case ILINK_UNKNOWN_PACKET:
7716                Ret = PKT_TYPE_IGNORE;
7717                break;
7718 
7719             default:
7720                if(pRTP->ssrc != 0) {
7721                // Assume that he can handle a nonzero ssrc if he sends one
7722                   pCC->bSendSSRC = TRUE;
7723                }
7724                Ret = PKT_TYPE_AUDIO;
7725                break;
7726          }
7727          break;
7728    }
7729 
7730    if(Ret == PKT_TYPE_AUDIO && pCC->CompressionType != pCS->CompressionType) {
7731    // It's audio, but not the compression type supported by this conference
7732       DLOG(DLOG_CODEC_TYPE,("Ignoring CompressionType %d from %s.\n",
7733            pCC->CompressionType,CallLog(pCC)));
7734       Ret = PKT_TYPE_OTHER_AUDIO;
7735    }
7736 
7737    return Ret;
7738 }
7739 
FromUs(ClientInfo * p,ConfClient * pCC)7740 int FromUs(ClientInfo *p,ConfClient *pCC)
7741 {
7742    rtp_hdr_t *pRTP = (rtp_hdr_t *) p->Buf;
7743    int Ret = FALSE;
7744 
7745    if(pCC->Proto == PROTO_ILINK || pCC->Proto == PROTO_RTP) {
7746      if(pRTP->ssrc == OurNodeID && OurNodeID != 0) {
7747         Ret = TRUE;
7748      }
7749    }
7750 
7751    return Ret;
7752 }
7753 
CheckPassword(ConfClient * pCC,ACL_User * pACL)7754 int CheckPassword(ConfClient *pCC,ACL_User *pACL)
7755 {
7756    int Ret = FALSE;
7757 
7758    if(pACL != NULL && *pACL->Password == '-') {
7759    // User doesn't require a password
7760       Ret = TRUE;
7761    }
7762    else if(pCC->Password != NULL && pACL != NULL) {
7763    // User password set in ACL
7764       if(strcmp(pACL->Password,pCC->Password) == 0) {
7765       // User's password matches
7766          Ret = TRUE;
7767       }
7768    }
7769    else if(RTP_Pass != NULL) {
7770    // Conference password set
7771       if(strcmp(RTP_Pass,"-") == 0) {
7772       // You asked for it, you got it ... no security for RTP / Speak Freely
7773          Ret = TRUE;
7774       }
7775       else if(pCC->Password != NULL && strcmp(pCC->Password,RTP_Pass) == 0) {
7776          Ret = TRUE;
7777       }
7778    }
7779 
7780    return Ret;
7781 }
7782 
7783 // return:
7784 //    0 - Client is not authorized
7785 //    1 - Client is authorized
7786 //    2 - Don't know yet. (Unknown EchoLink client, checking with
7787 //        directory server).
AuthorizedClient(ClientInfo * p,ConfClient * pCC)7788 int AuthorizedClient(ClientInfo *p,ConfClient *pCC)
7789 {
7790    UserInfo UserLookup;
7791    UserInfo *pUser;
7792    ACL_User *pACL;
7793    ACL_User *pACL1;
7794    ACL_User ACL_Lookup;
7795    char *Dash;
7796    int Authorized = 0;
7797    int bAllowUnknown = FALSE;
7798    char Temp[32];
7799    char *BaseCall = strdup(pCC->Callsign);
7800 
7801    UserLookup.Callsign = pCC->Callsign;
7802    ACL_Lookup.Callsign = BaseCall;
7803    ACL_Lookup.HisAdr.ADDR = pCC->HisAdr.ADDR;
7804 
7805    if((Dash = strchr(BaseCall,'-')) != NULL) {
7806       *Dash = 0;
7807    }
7808 
7809    if((pUser = avl_find(UserTree,&UserLookup)) != NULL) {
7810    // user is in directory, set his mute flag
7811       if(pUser->bMuted) {
7812          pCC->bMuted = TRUE;
7813       }
7814 
7815       if(pUser->bUnMuted) {
7816          pCC->bMuted = FALSE;
7817       }
7818 
7819       if(pUser->bChatMuted) {
7820          pCC->bMuteChat = TRUE;
7821       }
7822 
7823       if(pUser->bChatUnMuted) {
7824          pCC->bMuteChat = FALSE;
7825       }
7826    }
7827 
7828    if(RTP_Pass != NULL && strcmp(RTP_Pass,"!") == 0) {
7829    // You asked for it, you got it ... hardly any security at all !
7830       bAllowUnknown = TRUE;
7831    }
7832 
7833    if(pCC->Proto == PROTO_ILINK) {
7834    // Lookup EchoLink clients by callsign and check the IP address
7835       pACL = avl_find(ACL_Call_Tree,&ACL_Lookup);
7836 
7837       if(pACL != NULL && !pACL->bAuthorized) {
7838       // Banned client, ignore the turkey
7839       }
7840       else if((pACL1 = avl_find(ACL_IP_Tree,&ACL_Lookup)) != NULL) {
7841       // Client's IP address is in ACL
7842          if(pACL1->bAuthorized) {
7843          // Client Allowed by IP address
7844             Authorized = 1;
7845             LOG_NORM(("%s allowed by IP/hostname address %s/%s.\n",
7846                       pCC->Callsign,inet_ntoa(pACL1->HisAdr.i.sin_addr),
7847                       pACL1->HostName));
7848          }
7849          else {
7850          // Client Banned by IP address, ignore the turkey
7851             LOG_NORM(("%s banned IP/hostname address %s/%s.\n",
7852                       pCC->Callsign,inet_ntoa(pACL1->HisAdr.i.sin_addr),
7853                       pACL1->HostName));
7854          }
7855       }
7856       else if(bAllowUnknown || (bQuickStart && ActiveDirEntries == 0)) {
7857       // Allow anyone that's not specifically banned
7858          Authorized = 1;
7859       }
7860       else if(pUser != NULL) {
7861       // client found in directory
7862          if(pUser->HisAdr.ADDR == pCC->HisAdr.ADDR) {
7863          // IP address matches
7864             if(PrivateConference) {
7865                if(pACL != NULL) {
7866                   Authorized = pACL->bAuthorized ? 1 : 0;
7867                }
7868             }
7869             else {
7870                Authorized = pUser->bAuthorized ? 1 : 0;
7871             }
7872          }
7873          else {
7874          // IP address mismatch
7875             strcpy(Temp,inet_ntoa(pCC->HisAdr.i.sin_addr));
7876             LOG_NORM(("Rejecting %s, dir IP %s != IP %s.\n",
7877                       pCC->Callsign,inet_ntoa(pUser->HisAdr.i.sin_addr),Temp));
7878          // Perhaps he has a dynamic IP address and has a difference IP
7879          // address than he did when we last refreshed the list, check
7880          // with the directory server and see.
7881             if(LoginInterval > 0) {
7882                ValidateCallsign(pCC->Callsign,&pCC->HisAdr);
7883             }
7884             EchoLinkIPCompareFailures++;
7885          }
7886       }
7887       else if(pACL != NULL) {
7888       // client not in directory, but in ACL and not banned
7889          if(*pACL->HostName == '-' ) {
7890          // allowed client with any IP address
7891             Authorized = 1;
7892             LOG_WARN(("Accepted %s %s without IP address check.\n",
7893                       pCC->Callsign,inet_ntoa(pCC->HisAdr.i.sin_addr)));
7894          }
7895          else if(pCC->HisAdr.ADDR == pACL->HisAdr.ADDR) {
7896             Authorized = 1;
7897          }
7898       }
7899       else {
7900       // User is not in our ACL or in the directory server's list currently,
7901       // ask server about him specifically.
7902          LOG_NORM(("%s not found in directory.\n",pCC->Callsign));
7903          if(iLinkDirServer) {
7904          // The iLink server doesn't support individual callsign validation,
7905          // just refresh the entire directory list
7906             NextLoginTime = TimeNow.tv_sec + LoginInterval;
7907             NextStationListTime = TimeNow.tv_sec + StationListInterval;
7908             ServerRequest(SERV_REQ_LOGIN_AND_LIST,0,NULL);
7909          }
7910          else if(LoginInterval > 0) {
7911             Authorized = 2;      // Don't know yet
7912             ValidateCallsign(pCC->Callsign,&pCC->HisAdr);
7913          }
7914       }
7915 
7916       if(Authorized == 0) {
7917          EchoAuthenticationFailures++;
7918       }
7919    }
7920    else {
7921    // RTP / Speak Freely client
7922       if((pACL = avl_find(ACL_IP_Tree,&ACL_Lookup)) != NULL) {
7923       // Found IP address or hostname match
7924          if(pACL->bAuthorized && CheckPassword(pCC,pACL)) {
7925             Authorized = 1;
7926             if(avl_find(ACL_Call_Tree,&ACL_Lookup) == NULL) {
7927             // Didn't find a match on the callsign so RTCP didn't have
7928             // callsign info Get it from the ACL list
7929 
7930                if(pCC->Callsign != NULL) {
7931                   free(pCC->Callsign);
7932                }
7933                if(pCC->CallPlus != NULL) {
7934                   free(pCC->CallPlus);
7935                }
7936 
7937                pCC->CallPlus = strdup(pACL->CallPlus);
7938                pCC->Callsign = strdup(pACL->Callsign);
7939             }
7940          }
7941       }
7942       else if((pACL = avl_find(ACL_Call_Tree,&ACL_Lookup)) != NULL) {
7943       // Found callsign match
7944          if(pACL->bAuthorized && *pACL->HostName == '-' &&
7945             CheckPassword(pCC,pACL))
7946          {
7947             Authorized = 1;
7948          }
7949       }
7950       else if(CheckPassword(pCC,NULL)) {
7951       // Conference bridge password match
7952          Authorized = 1;
7953       }
7954       else if(bAllowUnknown) {
7955       // Allow anyone that's not specifically banned
7956          Authorized = 1;
7957       }
7958       else {
7959       // No thanks !
7960          p->Count = 0;  // init for BufPrintf
7961          BufPrintf(p,"%c" NDATA,ILINK_DATA_PACKET);
7962          BufPrintf(p,"Unauthorized connect request from %s.",
7963                   inet_ntoa(pCC->HisAdr.i.sin_addr));
7964          SendToAllSysops(p,pCC->pCS);
7965       }
7966 
7967       if(Authorized == 0) {
7968          AuthenticationFailures++;
7969       }
7970    }
7971 
7972    if(BaseCall != NULL) {
7973       free(BaseCall);
7974    }
7975 
7976    return Authorized;
7977 }
7978 
SaveBadPacket(ClientInfo * p,char * Filename)7979 void SaveBadPacket(ClientInfo *p,char *Filename)
7980 {
7981    FILE *fp = fopen(Filename,"w");
7982 
7983    if(fp != NULL) {
7984       if(fwrite(p->Buf,p->Count,1,fp) != 1) {
7985          LOG_ERROR(("SaveBadPacket(): fwrite failed, %s",Err2String(errno)));
7986       }
7987       fclose(fp);
7988    }
7989    else {
7990       LOG_ERROR(("SaveBadPacket(): fopen failed, %s",Err2String(errno)));
7991    }
7992 }
7993 
SendAvrsUpdate(AVRS_State State,char * Callsign)7994 void SendAvrsUpdate(AVRS_State State,char *Callsign)
7995 {
7996    char TempSDES[512];
7997    int   Len = 0;
7998    IPAdrUnion HisAdr;
7999    struct hostent *pHost;
8000    int Sent;
8001 
8002    if(AvrsEnable && LoginInterval > 0) {
8003       Len = CreateAvrsPacket(State,Callsign,TempSDES,sizeof(TempSDES));
8004    }
8005 
8006    if(Len > 0) {
8007       pHost = GetHostByName("aprs.echolink.org");
8008       if(pHost != NULL) {
8009          HisAdr.i.sin_addr.s_addr = IP_FROM_HOSTENT(pHost,0);
8010          HisAdr.i.sin_family = AF_INET;
8011          HisAdr.PORT = htons(ILINK_RTCP_PORT);
8012          Sent = sendto(piLinkConf->pControl->Socket,TempSDES,Len,0,&HisAdr.s,
8013                        sizeof(HisAdr));
8014          if(Sent != Len) {
8015             LOG_ERROR(("SendAvrsUpdate: sendto failed, %s",Err2String(errno)));
8016          }
8017       }
8018    }
8019 }
8020 
8021 // Begin K1RFD 1/30/08
SendFirewallOpenRequest(ConfServer * pCS,ConfClient * pCC)8022 void SendFirewallOpenRequest(ConfServer *pCS, ConfClient *pCC)
8023 {
8024     // Send an OPEN request to the specified peer via the addressing
8025     // server.  We do this each time we want to establish a connection,
8026     // in case the recipient of the connection request is behind an
8027     // unconfigured NAT firewall.  His software has already established
8028     // a "flow" with the addressing server, so the addressing server
8029     // should be able to relay this request to him successfully.  When
8030     // he receives it, he responds by sending us a set of packets through
8031     // his firewall, which establishes the necessary flow in his firewall
8032     // for a QSO between us to take place.
8033 
8034     // The OPEN request is in the SDES portion of an RTCP packet.
8035     // The fields are as follows:
8036     //     CNAME: callsign of the originating node (us)
8037     //     LOC:   "OPEN"
8038     //     EMAIL: dotted ip address of the destination node (him)
8039 
8040     int rtp_version = ILINK_RTP_VERSION;
8041     char *pFirstData;
8042     int PadCount;
8043     ConfClient cc;
8044     int Server = 0;  // for now
8045     char *DestNodeDottedIP;
8046     struct hostent *pTemp;
8047     char OurSDES[512];
8048     int SDESLen;
8049 
8050     union {
8051       char *cp;
8052       rtcp_t *p;
8053     } u;
8054 
8055     union {
8056       char *cp;
8057       rtcp_sdes_item_t *p;
8058     } Item;
8059 
8060 
8061     DestNodeDottedIP = inet_ntoa(pCC->HisAdr.i.sin_addr);
8062 
8063     u.cp = OurSDES;
8064 
8065     u.p->common.version = rtp_version;
8066     u.p->common.p = 0;
8067     u.p->common.count = 0;
8068     u.p->common.pt = RTCP_RR;
8069     u.p->common.length = htons(1);
8070     u.p->r.rr.ssrc = OurNodeID;
8071 
8072     u.cp += sizeof(u.p->common) + sizeof(u.p->r.rr.ssrc);
8073     u.p->common.version = rtp_version;
8074     u.p->common.p = 1;
8075     u.p->common.count = 1;
8076     u.p->common.pt = RTCP_SDES;
8077 
8078     u.p->r.sdes.src = OurNodeID;
8079     pFirstData = (char *) &u.p->r.sdes.src;
8080 
8081     Item.p = &u.p->r.sdes.item[0];
8082     Item.p->type = RTCP_SDES_CNAME;
8083     Item.p->length = (u_int8) strlen(ConferenceCall);
8084     memcpy(Item.p->data,ConferenceCall,Item.p->length);
8085 
8086     Item.cp = Item.cp + sizeof(rtcp_sdes_item_t) + Item.p->length - 1;
8087     Item.p->type = RTCP_SDES_LOC;
8088     Item.p->length = (u_int8) strlen("OPEN");
8089     memcpy(Item.p->data,"OPEN",Item.p->length);
8090 
8091     Item.cp = Item.cp + sizeof(rtcp_sdes_item_t) + Item.p->length - 1;
8092     Item.p->type = RTCP_SDES_EMAIL;
8093     Item.p->length = (u_int8) strlen(DestNodeDottedIP);
8094     memcpy(Item.p->data,DestNodeDottedIP,Item.p->length);
8095 
8096     Item.cp = Item.cp + sizeof(rtcp_sdes_item_t) + Item.p->length - 1;
8097     Item.p->type = RTCP_SDES_END;
8098     Item.p->length = 0;
8099     Item.cp = Item.cp + sizeof(rtcp_sdes_item_t) + Item.p->length - 1;
8100 
8101     PadCount = 4 - ((Item.cp - pFirstData) % 4);
8102 
8103     if(PadCount > 0 && PadCount < 4) {
8104         // Padding is needed
8105         u.p->common.p = 1;
8106         if(PadCount > 1) {
8107             memset(Item.cp,0,PadCount-1);
8108         }
8109         Item.cp[PadCount-1] = PadCount;
8110         Item.cp += PadCount;
8111     } else {
8112         // No padding needed
8113         u.p->common.p = 0;
8114     }
8115     u.p->common.length = htons((u_short) ((Item.cp - pFirstData)/4));
8116     SDESLen = Item.cp - OurSDES;
8117 
8118     // Construct a "client" structure for the addressing server.
8119     // Note that Send2() needs the pCS member.
8120     memset(&cc, 0, sizeof(cc));
8121     cc.pCS = pCS;
8122     cc.HisAdr.i.sin_family = AF_INET;
8123     cc.HisAdr.i.sin_port = ILINK_RTCP_PORT;
8124 
8125     // TODO: We really should send this packet to the same addressing
8126     // server we are currently using, rather than the first one in
8127     // the list, in case the first one in the list is offline.  However,
8128     // aside from that, it makes no difference which one we send it to
8129     // since they all relay these packets to one another.
8130     if (Server >= NUM_DIRECTORY_SERVERS || DirServerHost[Server] == NULL)
8131         Server = 0;
8132 
8133     // Apropos of the note above, if we keep track of the current addressing
8134     // server's Internet address, we can avoid this DNS lookup here.
8135     pTemp = GetHostByName(DirServerHost[Server]);
8136     if(pTemp != NULL) {
8137         cc.HisAdr.i.sin_addr.s_addr = IP_FROM_HOSTENT(pTemp,0);
8138     } else {
8139         cc.HisAdr.i.sin_addr.s_addr = inet_addr(DirServerHost[Server]);
8140     }
8141 
8142     Send2(&cc,OurSDES,SDESLen,TRUE);
8143     D2PRINTF(("Sent firewall OPEN request to %s via %s\n", DestNodeDottedIP,
8144               DirServerHost[Server]));
8145 }
8146 // End K1RFD 1/30/08
8147 
8148 
8149 // Update ConnectedStatus to reflect current connection status.
8150 // "real" conferences take precedence over "normal" nodes running as
8151 // conferences which take precedence over "normal" nodes.
UpdateConnectedStatus()8152 void UpdateConnectedStatus()
8153 {
8154    struct avl_traverser avl_trans;
8155    ConfClient *pCC = NULL;
8156    char *TempRealConf = NULL;
8157    char *TempConf = NULL;
8158    char *TempStation = NULL;
8159    int bCurrentTopStation;
8160 
8161    pCC = (ConfClient *) avl_t_first(&avl_trans,piLinkConf->ConfTree);
8162    while(pCC != NULL) {
8163       if(pCC->bInConf) {
8164       // This is an active connection
8165          bCurrentTopStation = strcmp(ConnectedStatus,pCC->Callsign) == 0;
8166          if(pCC->bConf) {
8167          // This client is a conference
8168             if(pCC->Callsign[0] == '*') {
8169             // And it's a "real" conference
8170                if(bCurrentTopStation || TempRealConf == NULL) {
8171                   TempRealConf = pCC->Callsign;
8172                }
8173             }
8174             else {
8175             // Not a "real" conference but better than nothing
8176                if(bCurrentTopStation || TempConf == NULL) {
8177                   TempConf = pCC->Callsign;
8178                }
8179             }
8180          }
8181          else if(ShowStatusInInfo == 2) {
8182             if(bCurrentTopStation || TempStation == NULL) {
8183                TempStation = pCC->Callsign;
8184             }
8185          }
8186       }
8187       pCC = (ConfClient *) avl_t_next(&avl_trans);
8188    }
8189 
8190    if(TempRealConf != NULL) {
8191       TempConf = TempRealConf;
8192    }
8193 
8194    if(TempConf != NULL) {
8195       snprintf(ConnectedStatus,sizeof(ConnectedStatus),"In Conference %s",
8196                TempConf);
8197    }
8198    else if(TempStation != NULL) {
8199       snprintf(ConnectedStatus,sizeof(ConnectedStatus),"Connected to %s",
8200                TempStation);
8201    }
8202    else {
8203       ConnectedStatus[0] = 0;
8204    }
8205 }
8206 
8207 // Return pointer to first character of the user's chat text
8208 // Follows CALLSIGN>
FirstChatChar(char * Buf)8209 char *FirstChatChar(char *Buf)
8210 {
8211    char *cp;
8212    char *cp1;
8213    char *Ret = NULL;
8214    int Len;
8215 
8216    cp = &Buf[sizeof(NDATA)];   // first character of data
8217    if(*cp != '\r' && (cp1 = strchr(cp,'>')) != NULL) {
8218       *cp1 = 0;
8219    // Make sure there are no spaces from the beginning of the line
8220    // to the '>' and that the length of the callsign is reasonable
8221       Len = strlen(cp);
8222       if(Len <= MAX_CALL_LEN && strchr(cp,' ') == NULL) {
8223       // Yup, looks like a chat line all right
8224          Ret = &cp1[1];
8225       }
8226       *cp1 = '>';
8227    }
8228    return Ret;
8229 }
8230 
ConferenceCleanup()8231 void ConferenceCleanup()
8232 {
8233    struct avl_traverser avl_trans;
8234    ConfServer *pCS;
8235    ConfServer *pNextCS;
8236 
8237    pNextCS = (ConfServer *) avl_t_first(&avl_trans,Conferences);
8238    while((pCS = pNextCS) != NULL) {
8239       pNextCS = (ConfServer *) avl_t_next(&avl_trans);
8240       DeleteConf(pCS);
8241    }
8242    avl_destroy(Conferences,NULL);
8243 
8244 #if 0 // !!! find out why this blows chow someday
8245    if(CmdClient != NULL) {
8246       DeleteClient(CmdClient);
8247    }
8248 
8249    if(ChatClient != NULL) {
8250       DeleteClient(ChatClient);
8251    }
8252 #endif
8253 }
8254 
SendChatEvent(char * Type,char * Buf)8255 void SendChatEvent(char *Type,char *Buf)
8256 {
8257    char *Text = &Buf[sizeof(NDATA)];
8258    char *cp;
8259 
8260 // Remove carrage return from text
8261    if((cp = strchr(Text,'\r')) != NULL) {
8262       *cp = 0;
8263    }
8264 
8265    if(strlen(Text) > 0) {
8266       EventHook("%s %s",Type,Text);
8267    }
8268 
8269    if(cp != NULL) {
8270       *cp = '\r';
8271    }
8272 }
8273 
GetCmdOptions(char ** args,const char * opts)8274 int GetCmdOptions(char **args,const char *opts)
8275 {
8276    int Ret = -1;
8277    char *cp = *args;
8278    char *cp1;
8279    int bQuoted = FALSE;
8280 
8281    CmdArg = NULL;
8282    do {
8283       while(isspace(*cp)) {
8284          cp++;
8285       }
8286       if(*cp != '-') {
8287       // not a switch we're done
8288          break;
8289       }
8290       cp++;
8291       if((cp1 = strchr(opts,*cp)) == NULL) {
8292       // undefined option
8293          Ret = '?';
8294          break;
8295       }
8296 
8297       Ret = *cp++;   // Valid option found
8298 
8299       if(cp1[1] == ':') {
8300       // option has an argument
8301          while(isspace(*cp)) {
8302             cp++;
8303          }
8304          if(!isprint(*cp) || *cp == '-') {
8305          // invalid or no argument specified
8306             break;
8307          }
8308 
8309          if(*cp == '"') {
8310             cp++;
8311             bQuoted = TRUE;
8312          }
8313       // Save pointer to argument
8314          CmdArg = cp;
8315       // Skip to the end of argument
8316 
8317          if(bQuoted) {
8318             while(isprint(*cp) && *cp != '"') {
8319                cp++;
8320             }
8321             if(*cp != '"') {
8322             // improper termination of quoted argument
8323                CmdArg = NULL;
8324             }
8325          }
8326          else {
8327             while(!isspace(*cp) && isprint(*cp) && *cp != '-') {
8328                cp++;
8329             }
8330          }
8331          *cp++ = 0;  // terminate it
8332       }
8333    } while(FALSE);
8334 
8335    *args = cp;
8336    return Ret;
8337 }
8338 
CallLog(ConfClient * pCC)8339 char *CallLog(ConfClient *pCC)
8340 {
8341    static char IPAddress[1024];
8342    char *Ret = pCC->Callsign;
8343 
8344    if(LogIPAddresses) {
8345       snprintf(IPAddress,sizeof(IPAddress),"%s (%s)",pCC->Callsign,
8346                inet_ntoa(pCC->HisAdr.i.sin_addr));
8347       Ret = IPAddress;
8348    }
8349 
8350    return Ret;
8351 }
8352