1 /* Copyright (C) 2003 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: tbdcmd.c,v $
22    Revision 1.19  2009/09/13 23:14:36  wb6ymh
23    Added support for .rxlevel command.
24 
25    Revision 1.18  2009/04/04 16:29:21  wb6ymh
26    1. Added code to set port from the TLB_CMDPORT environment variable if present.
27    2. Added code to set the maximum wait time for play emumlation fomr the
28       TLB_MAX_PLAY environment variable if present.  The default timeout is
29       30 seconds.
30 
31    Revision 1.17  2009/02/08 16:43:46  wb6ymh
32    Added missing break in -x switch in EmulateImike.
33 
34    Revision 1.16  2008/08/15 17:30:27  wb6ymh
35    Added include for sys/types.h before sys/socket.h as needed by OSX.
36    Thanks to Wyatt KJ4CTD for the fix.
37 
38    Revision 1.15  2008/07/23 15:30:35  wb6ymh
39    1. Added full duplex (-x) and uLaw (-n) support to the iMike enulation.
40    2. Added dtmfregen script emulation profile.
41 
42    Revision 1.14  2008/05/28 18:04:50  wb6ymh
43    1. TLBPORT -> TLB_PORT.
44    2. Added code to Read/save history file to ~./.tlbcmd_history rather than
45       from the current directory.
46 
47    Revision 1.13  2008/04/12 14:27:06  wb6ymh
48    Moved TlbPort assignment past the last declaration at the top of main.
49    GCC 4.x allows declarations anywhere within a block in C mode, but most other
50    complilers (and earlier versions of GCC) do not.
51 
52    Revision 1.12  2008/03/08 18:10:51  wb6ymh
53    Added TLBPORT support for imike, ispeaker and play emulations.
54 
55    Revision 1.11  2008/03/08 06:40:18  wb6ymh
56    1. Modified EmulatePlay to add a -c (send completion status) optoin to the
57       say commadn and wait for an TBD_SAY_COMPLETE to be returned before exiting.
58    2. Added code to prefix commands with port <TLBPORT>; when the TLBPORT env
59       variable is set. This variable is set to the port that issued the
60       command that caused the script to be executed.  In many cases external
61       scripts can be blissfully ignorant of the port selection and just rely
62       on the man behind the curtain to do the right thing.
63    3. Added the -b (bare) command line switch to disable the automatic port
64       selection described above.
65    4. Removed the signal handler for SIGTERM and SIGINT for the play emulation
66       so play can be aborted by SIGTERM.
67 
68    Revision 1.10  2008/02/26 18:16:16  wb6ymh
69    1. Modified code to check if stdout is a tty and bypass readline if not.
70    2. Added check for NULL returned by readline() to avoid segfault when
71       user uses ^D to exit.
72 
73    Revision 1.9  2007/12/14 22:44:04  wb6ymh
74    1. Added GNU readline support.
75    2. Added support for the -p <port> iMike switch to EmulateImike.
76    3. Corrected log filename generation (thelinkbox builds).
77    4. Modified code to put logs into $TLB_HOME.
78 
79    Revision 1.8  2007/12/07 23:15:23  wb6ymh
80    Added emulation of play, imike, and ispeaker.
81 
82    Revision 1.7  2007/07/02 13:43:35  wb6ymh
83    Corrected C99 C declaration mixed with code in tbdcmd.c that's not accepted
84    by pre C89 compilers.  This is the first time I've run into code gcc 4.x
85    liked that other compilers don't, it's usually the other way around.
86 
87    Revision 1.6  2007/06/29 14:30:22  wb6ymh
88    1. Modified code to use 5199 by default instead of 5198 when involked as
89       tbdchat, tlbchat, or chat.
90    2. Modified all hard coded references to "tbdcmd" to use the current
91       program alias.
92    3. Added code to suppress stations lists received from conference bridges.
93    4. Added -S command line switch to disable station list suppression logic.
94 
95    Revision 1.5  2006/08/21 16:32:33  wb6ymh
96    Added code to set rlim_max to RLIM_INFINITY.  Fixes ages old "Unable to
97    enable core dumps" warning which occured on recent versions of the Linux
98    kernel.
99 
100    Revision 1.4  2005/01/09 00:00:30  wb6ymh
101    1. Added -t option to enable display of a timestamp before each line
102       of chat text.
103 
104    2. Fix typo and add "warning" to error message about failure to
105       enable core (not "code") dumps.  Apparently Fedora has changed
106       something as this always fails with "Invalid argument".
107 
108    3. Modified new tbdcmd misfeature that suppressed "error" codes of
109       zero to only suppress them when tbdcmd is run interactively.
110 
111    4. Corrected bug caused by uninitialized select timeout structure in
112       tbdcmd.  This bug only occurred on a few systems, thanks to ke4tte
113       for providing access to one where it did so I could debug it.
114 
115    Revision 1.3  2004/12/01 14:30:27  wb6ymh
116    1. Don't watch stdin unless it's a tty!
117    2. Don't suppress "error" codes of zero unless we're running interactively.
118 
119    Revision 1.2  2004/11/29 00:38:50  wb6ymh
120    1. Added timeout to avoid hanging if thebridge doesn't respond.
121    2. Added support for text chat.
122 
123    Revision 1.1  2003/08/16 14:27:27  wb6ymh
124    Initial import: command line utility to send commands to thebridge.
125 
126 */
127 
128 #include "common.h"
129 
130 #ifndef _WIN32
131    // FreeBSD, Linux, etc..
132    #include <stdio.h>
133 #ifdef HAVE_LIBREADLINE
134    #include <readline/readline.h>
135    #include <readline/history.h>
136 #endif
137    #include <stdarg.h>
138    #ifdef HAVE_UNISTD_H
139       #include <unistd.h>
140    #endif
141 
142    #ifdef STDC_HEADERS
143       #include <stdlib.h>
144    #endif
145 
146    #ifdef TIME_WITH_SYS_TIME
147       #include <sys/time.h>
148       #include <time.h>
149    #else
150       #ifdef HAVE_SYS_TIME_H
151          #include <sys/time.h>
152       #else
153          #include <time.h>
154       #endif
155    #endif
156    #include <errno.h>
157 
158    #ifdef HAVE_STRINGS_H
159       #include <string.h>
160    #endif
161    #include <assert.h>
162    #include <sys/types.h>
163    #include <sys/socket.h>
164    #include <netinet/in.h>
165    #include <sys/resource.h>
166    #include <arpa/inet.h>
167    #include <string.h>
168    #include <signal.h>
169    #include <termios.h>
170 #else
171    // Windoze
172    #include <stdio.h>
173    #include <winsock2.h>
174    #include <string.h>
175    #include <conio.h>
176    #include <time.h>
177 #endif
178 
179 #include "ilink.h"
180 #include "tbd.h"
181 
182 #ifndef  FALSE
183 #define  FALSE    0
184 #endif
185 
186 #ifndef  TRUE
187 #define  TRUE  !FALSE
188 #endif
189 
190 #undef  LOG_NORM
191 #define LOG_NORM(x)  Log x
192 
193 #undef  LOG_ERROR
194 #define LOG_ERROR(x) Log x
195 
196 #define RESP_TO      10    // wait for 10 seconds from responses from tbd
197 
198 typedef union {
199    struct sockaddr s;
200    struct sockaddr_in i;
201    #define ADDR   i.sin_addr.s_addr
202    #define PORT   i.sin_port
203 } IPAdrUnion;
204 
205 
206 #ifdef _WIN32
207 // Windoze
208    WSADATA wsad;
209 #endif
210 
211 int bQuiet = 0;
212 int bSlient = 0;
213 int bTimeStamp = 0;
214 int bAllowStationList = FALSE;
215 int Port = ILINK_RTP_PORT;
216 int bUseReadline = FALSE;
217 
218 #ifdef LINK_BOX
219 #define PROMPT "tlb> "
220 
221 int bShutdown = FALSE;
222 int LogFileRolloverType = -1;  // no rollover, single logfile
223 FILE *LogFp = NULL;
224 
225 static char *LogNames[] = {
226    "sun.log",
227    "mon.log",
228    "tue.log",
229    "wed.log",
230    "thr.log",
231    "fri.log",
232    "sat.log"
233 };
234 
235 static char *MonthNames[] = {
236    "Jan",
237    "Feb",
238    "Mar",
239    "Apr",
240    "May",
241    "Jun",
242    "Jul",
243    "Aug",
244    "Sep",
245    "Oct",
246    "Nov",
247    "Dec"
248 };
249 #else
250 #define PROMPT "tbd> "
251 #endif
252 
253 char *AppName = NULL;
254 char *TlbPort = NULL;
255 char *HistoryPath = NULL;
256 int MaxPlayWait = 30;
257 
258 #ifndef HAVE_GETOPT
259 // globals needed by getopt
260 int optind = 1;
261 int optopt;
262 char *optarg;
263 int getopt(int argc,char **argv,char *opts);
264 #endif
265 
266 char *TimeT2String(time_t time);
267 int IsStationList(char *Line);
268 
269 #ifdef LINK_BOX
270 void SigHandler(int Signal);
271 int EmulateImike(char **argv,int argc);
272 int EmulateIspeaker(char **argv,int argc);
273 int EmulatePlay(char **argv,int argc);
274 int EmulateDtmfRegen(char **argv,int argc);
275 
276 #endif
277 
278 void SetTerminalMode(int mode);
279 
Usage(char * Command)280 void Usage(char *Command)
281 {
282    printf("Usage: %s [-b] [-p<port>] [-q] [-s] [-S] [-t] [commands] \n",Command);
283    printf("\t-b - Bare, do not prepend port <TLBPORT>; to commands.\n");
284    printf("\t-p - Specify port (default %d).\n",Port);
285    printf("\t-q - Quiet, numeric result code only.\n");
286    printf("\t-s - Slient, suppress banner.\n");
287    printf("\t-S - Allow conference bridge's station list (default is suppress).\n");
288    printf("\t-t - Add timestamps to text chat messages.\n");
289 }
290 
main(int argc,char * argv[])291 int main(int argc, char* argv[])
292 {
293    int Ret = 0;
294    int Option;
295    SOCKET   MySocket;
296    IPAdrUnion HisAdr;
297    char  Line[1024];
298    char  *cp;
299    char  *cp1;
300    int   BytesRxed;
301    int   BytesSent;
302    int   FirstArg = 1;
303    int   ArgCount = argc;
304    int   WritePoint;
305    int   Wrote;
306    int   i;
307    fd_set ReadFDset;
308    struct timeval SelTO;
309    int   Selret;
310    int   bResponseWait = FALSE;
311    int   bDisplayPrompt = TRUE;
312    int   bNeedNewline = FALSE;
313    int   bInteractive = FALSE;
314    int   bCleanupCmd = FALSE;
315    time_t   TimeNow;
316    time_t   TimeCmdSent;
317    int      bChat = FALSE;
318    int bBare = FALSE;
319    int Response = 0;
320 
321 #ifndef _WIN32
322    struct rlimit rlim;
323 
324 // On some systems core dumps are disabled by default,
325 // enable them so we can debug this thing !
326 
327    rlim.rlim_cur = RLIM_INFINITY;
328    rlim.rlim_max = RLIM_INFINITY;
329    if(setrlimit(RLIMIT_CORE, &rlim)) {
330       perror("Warning - Unable to enable core dumps: ");
331    }
332 #else
333    /* Initialize Winsock*/
334    if(WSAStartup(0x0101, &wsad) != 0) {
335       printf("Fatal error: unable to initialize Winsock\n");
336       exit(3);
337    }
338 #endif
339 
340    TlbPort = getenv("TLB_PORT");
341    AppName = strrchr(argv[0],PATH_SEP);
342 
343    if(AppName != NULL) {
344       AppName++;
345    }
346    else {
347       AppName = argv[0];
348    }
349 
350    if(strcmp(AppName,"tbdchat") == 0 ||
351       strcmp(AppName,"tlbchat") == 0 ||
352       strcmp(AppName,"chat") == 0)
353    {
354       Port = ILINK_RTCP_PORT;
355       bChat = TRUE;
356    }
357 
358    if((cp = getenv("TLB_CMDPORT")) != NULL) {
359       if(sscanf(cp,"%d",&Port) != 1) {
360          printf("Warning: invalid TLB_CMDPORT \"%s\" ignored.\n",cp);
361       }
362    }
363 
364    if((cp = getenv("TLB_MAX_PLAY")) != NULL) {
365       if(sscanf(cp,"%d",&MaxPlayWait) != 1) {
366          printf("Warning: invalid TLB_MAX_PLAY \"%s\" ignored.\n",cp);
367       }
368    }
369 
370    if(isatty(fileno(stdin))) {
371       bUseReadline = TRUE;
372    }
373 
374 #ifdef LINK_BOX
375    if(strcmp(AppName,"imike") == 0) {
376       return EmulateImike(argv,argc);
377    }
378    else if(strcmp(AppName,"ispeaker") == 0) {
379       return EmulateIspeaker(argv,argc);
380    }
381    else if(strcmp(AppName,"play") == 0) {
382       return EmulatePlay(argv,argc);
383    }
384    else if(strcmp(AppName,"dtmfregen") == 0) {
385       return EmulateDtmfRegen(argv,argc);
386    }
387 #endif
388 
389    while((Option = getopt(argc, argv, "bp:qtsS")) != -1 && !Ret) {
390       FirstArg++;
391       ArgCount--;
392       switch(Option) {
393          case 'b':   // bare
394             bBare = TRUE;
395             break;
396 
397          case 'p':   // port
398             if(sscanf(optarg,"%d",&Port) != 1 || Port <= 0 || Port > 0xffff) {
399                printf("%s: Error invalid port number \"%s\"\n",AppName,optarg);
400                exit(3);
401             }
402             break;
403 
404          case 'q':   // quiet
405             bQuiet = TRUE;
406             break;
407 
408          case 's':   // slient
409             bSlient = TRUE;
410             break;
411 
412          case 'S':   // Allow station list
413             bAllowStationList = TRUE;
414             break;
415 
416          case 't':   // Timestamps
417             bTimeStamp = TRUE;
418             break;
419 
420          case '?':
421             Usage(AppName);
422             exit(3);
423       }
424    }
425 
426    if(ArgCount == 1) {
427       bInteractive = TRUE;
428 #ifdef HAVE_LIBREADLINE
429       if(bUseReadline) {
430          char *HistoryFilename = ".tlbcmd_history";
431          char *HomeDir = getenv("HOME");
432          int PathLen = strlen(HistoryFilename) + 1;
433 
434          if(HomeDir != NULL) {
435             PathLen += strlen(HomeDir) + 1;
436          }
437 
438          HistoryPath = malloc(PathLen);
439 
440          if(HistoryPath != NULL) {
441             if(HomeDir != NULL) {
442                strcpy(HistoryPath,HomeDir);
443                strcat(HistoryPath,"/");
444                strcat(HistoryPath,HistoryFilename);
445             }
446             else {
447                strcpy(HistoryPath,HistoryFilename);
448             }
449             read_history(HistoryPath);
450          }
451       }
452 #endif
453    }
454 
455    if(!bQuiet && !bSlient && !Ret) {
456       printf("%s Version " VERSION " compiled "__DATE__  " " __TIME__"\n",
457              AppName);
458    }
459 
460    if(ArgCount > 1) {
461       WritePoint = 0;
462       for(i = 0; i < ArgCount -1 ; i++) {
463          Wrote = snprintf(&Line[WritePoint],sizeof(Line)-WritePoint-1,"%s%s",
464                           WritePoint == 0 ? "" : " ",argv[FirstArg+i]);
465          if(Wrote > 0 && WritePoint + Wrote < sizeof(Line)) {
466             WritePoint += Wrote;
467          }
468          else {
469          // Buffer overflow
470             break;
471          }
472       }
473       Line[WritePoint] = 0;
474    }
475    else {
476       Line[0] = 0;
477    }
478 
479    if((MySocket = socket(AF_INET,SOCK_DGRAM,0)) == SOCKET_ERROR) {
480       printf("Socket() failed.\n");
481    }
482    else {
483       HisAdr.i.sin_family = AF_INET;
484       HisAdr.PORT = htons((u_short) Port);
485       HisAdr.ADDR = inet_addr("127.0.0.1");
486 
487       SetTerminalMode(0);
488 
489       do {
490          if(bInteractive && bDisplayPrompt && Line[0] == 0) {
491             bDisplayPrompt = FALSE;
492             printf(bChat ? "chat> " : PROMPT);
493             fflush(stdout);
494          }
495 
496          if(strlen(Line) > 0) {
497             if(!bBare && !bInteractive && TlbPort != NULL) {
498             // prefix commands with tlb port selection from the environment
499                char *Temp = strdup(Line);
500                snprintf(Line,sizeof(Line),"port %s;%s",TlbPort,Temp);
501                free(Temp);
502             }
503             BytesSent = sendto(MySocket,Line,strlen(Line)+1,0,
504                                &HisAdr.s,sizeof(HisAdr));
505 
506             if(BytesSent == SOCKET_ERROR) {
507                Ret = ERROR_CODE;
508                printf("Error: sendto() failed (%d)\n",Ret);
509                break;
510             }
511             Line[0] = 0;
512             if(bCleanupCmd) {
513                break;
514             }
515             else {
516                bResponseWait = TRUE;
517                time(&TimeCmdSent);
518             }
519          }
520 
521          FD_ZERO(&ReadFDset);
522          FD_SET(MySocket,&ReadFDset);
523 
524 #ifdef _WIN32
525       // Windoze can't select on standard in 'cuz it ain't so standard
526       // so wake up every 100 milliseconds to poll the keyboard
527 
528          SelTO.tv_sec = 0;
529          SelTO.tv_usec = 100000;
530          Selret = select(FD_SETSIZE,&ReadFDset,NULL,NULL,&SelTO);
531 #else
532          if(bInteractive) {
533             FD_SET((SOCKET) fileno(stdin),&ReadFDset);
534          }
535          if(bResponseWait) {
536          // Wait a second for the response
537             SelTO.tv_sec = 1;
538             SelTO.tv_usec = 0;
539             Selret = select(FD_SETSIZE,&ReadFDset,NULL,NULL,&SelTO);
540          }
541          else {
542             Selret = select(FD_SETSIZE,&ReadFDset,NULL,NULL,NULL);
543          }
544 #endif
545          time(&TimeNow);
546 
547 
548 #ifndef _WIN32
549          if(FD_ISSET(fileno(stdin),&ReadFDset))
550 #else
551          if(kbhit())
552 #endif
553          {
554 #ifdef HAVE_LIBREADLINE
555             if(bUseReadline) {
556                if(bNeedNewline) {
557                   bNeedNewline = FALSE;
558                   printf("\n%s",bChat ? "chat> " : PROMPT);
559                }
560                SetTerminalMode(2);
561                rl_already_prompted = 1;
562                cp = readline(bChat ? "chat> " : PROMPT);
563                if(cp != NULL) {
564                   add_history(cp);
565                   memcpy(Line,cp,sizeof(Line));
566                   free(cp);
567                   Line[sizeof(Line)-1] = 0;
568                }
569                else {
570                // EOF read by readline
571                   printf("\n");
572                   Line[0] = 0;
573                }
574                SetTerminalMode(1);
575             }
576             else {
577 #endif
578             if(bNeedNewline) {
579                bNeedNewline = FALSE;
580                printf("\n%s",bChat ? "chat> " : PROMPT);
581             }
582             if(fgets(Line,sizeof(Line),stdin) == NULL) {
583                break;
584             }
585 #ifdef HAVE_LIBREADLINE
586             }
587 #endif
588 
589             if((cp = strchr(Line,'\n')) != NULL) {
590                *cp = 0;
591             }
592 
593             bDisplayPrompt = TRUE;
594             bResponseWait = TRUE;
595             time(&TimeCmdSent);
596             if(strlen(Line) == 0) {
597             // exit on empty line
598                if(Response != TBD_RX_LEVEL_RPT) {
599                   break;
600                }
601                else {
602                // We're receiving continous level reports, send a final
603                // .rxlevel command to silence them! (You think this is a
604                // kludge?  You betcha !)
605                   bCleanupCmd = TRUE;
606                   strcpy(Line,".rxlevel");
607                   continue;
608                }
609             }
610          }
611 
612          if(FD_ISSET(MySocket,&ReadFDset)) {
613             BytesRxed = recv(MySocket,Line,sizeof(Line)-1,0);
614             if(BytesRxed == SOCKET_ERROR) {
615                Ret = ERROR_CODE;
616                printf("Error: recv() failed (%d)\n",Ret);
617                break;
618             }
619 
620             Line[BytesRxed] = 0;
621             bDisplayPrompt = TRUE;
622 
623             Ret = atoi(Line);
624             Response = Ret;
625             if(Ret != TBD_CHAT_TEXT && Ret != TBD_RX_LEVEL_RPT) {
626                bResponseWait = FALSE;
627             }
628 
629             if(bQuiet) {
630                if((cp = strchr(Line,'\n')) != NULL) {
631                // Only want a single line, truncate the output
632                   cp[1] = 0;
633                }
634             }
635             if(bInteractive) {
636             // Interactive mode, overwrite the prompt
637                printf("\r      \r");
638             }
639 
640          // Remove any extra line feeds at the end of the line.  Thebridge's
641          // output contains a single <lf>, but text chat messages have two.
642 
643             if((cp = strrchr(Line,'\n')) != NULL && cp[-1] == '\n') {
644             // Only want a single linefeed, remove the second one
645                *cp = 0;
646             }
647 
648             time(&TimeNow);
649 
650             if(Ret == TBD_CHAT_TEXT && bTimeStamp) {
651                printf("%s: ",TimeT2String(TimeNow));
652             }
653 
654             if(Ret == TBD_CHAT_TEXT_SENT && bInteractive) {
655             // Interactive mode and this is just an ack for a message we
656             // sent, don't display it
657             }
658             else if(!bAllowStationList && bInteractive && IsStationList(Line)) {
659             // Suppress station list.
660             }
661             else if(bInteractive && Ret == TBD_RX_LEVEL_RPT &&
662                     (cp = strchr(Line,'\n')) != NULL)
663             {
664             // Rx levels, supress return code and remove newline
665                cp++;
666                if((cp1 = strchr(cp,'\n')) != NULL) {
667                   *cp1 = 0;
668                }
669                if(!bResponseWait) {
670                   printf("\r%s",cp);
671                   fflush(stdout);
672                   bDisplayPrompt = FALSE;
673                   bNeedNewline = TRUE;
674                }
675             }
676             else if(bInteractive &&
677                     (Ret == 0 || Ret == TBD_CHAT_TEXT) &&
678                     (cp = strchr(Line,'\n')) != NULL)
679             {  // return code is not interesting, don't display it
680                printf("%s",&cp[1]);
681             }
682             else {
683                printf("%s",Line);
684             }
685             Line[0] = 0;
686          }
687 
688          if(bResponseWait && (TimeNow - TimeCmdSent) >= RESP_TO) {
689          // Timeout waiting for a response from tbd
690 
691             Ret = TBD_ERR_TIMEOUT;
692             printf("%d\n",Ret);
693             break;
694          }
695       } while(bInteractive);
696    }
697 
698    SetTerminalMode(3);
699 
700    if(HistoryPath != NULL) {
701       free(HistoryPath);
702    }
703    return Ret;
704 }
705 
TimeT2String(time_t time)706 char *TimeT2String(time_t time)
707 {
708    struct tm *pTm;
709    static char TimeString[] = "mm/dd hh:mm";
710 
711    if(time != 0) {
712       pTm = localtime(&time);
713       if(pTm != NULL) {
714          snprintf(TimeString,sizeof(TimeString),"%2d/%02d %2d:%02d",
715                  pTm->tm_mon + 1,pTm->tm_mday,pTm->tm_hour,pTm->tm_min);
716       }
717       else {
718          return "";
719       }
720    }
721    else {
722       return "";
723    }
724    return TimeString;
725 }
726 
727 
728 // If we have ">CONF " on the first line then assume this is a station list
IsStationList(char * Line)729 int IsStationList(char *Line)
730 {
731    char *cp;
732    int Ret = FALSE;
733 
734    if((cp = strchr(Line,'\n')) != NULL && (cp = strchr(cp+1,'\n')) != NULL) {
735       *cp = 0;
736       if(strstr(Line,">CONF ") != NULL) {
737          Ret = TRUE;
738       }
739       *cp = '\n';
740    }
741    return Ret;
742 }
743 
744 #ifdef LINK_BOX
SendCommand(char * Cmd)745 int SendCommand(char *Cmd)
746 {
747    static int MySocket = -1;
748    int BytesSent;
749    int BytesRxed;
750    int Ret = -1;
751    int Selret;
752    IPAdrUnion HisAdr;
753    fd_set ReadFDset;
754    struct timeval SelTO;
755    char  Line[1024];
756 
757    do {
758       if(MySocket == -1) {
759          if((MySocket = socket(AF_INET,SOCK_DGRAM,0)) == SOCKET_ERROR) {
760             Ret = errno;
761             LOG_ERROR(("%s: socket() failed - %s\n",__FUNCTION__,
762                        strerror(errno)));
763             break;
764          }
765       }
766 
767       HisAdr.i.sin_family = AF_INET;
768       HisAdr.PORT = htons((u_short) Port);
769       HisAdr.ADDR = inet_addr("127.0.0.1");
770 
771       if(Cmd != NULL) {
772          LOG_ERROR(("%s: sending command \"%s\"\n",__FUNCTION__,Cmd));
773          if(TlbPort != NULL) {
774          // prefix commands with tlb port selection from the environment
775             char *Temp = strdup(Cmd);
776             snprintf(Line,sizeof(Line),"port %s;%s",TlbPort,Temp);
777             free(Temp);
778             Cmd = Line;
779          }
780          BytesSent = sendto(MySocket,Cmd,strlen(Cmd)+1,0,&HisAdr.s,
781                             sizeof(HisAdr));
782 
783          if(BytesSent == SOCKET_ERROR) {
784             Ret = errno;
785             LOG_ERROR(("%s: sendto() failed - %s\n",__FUNCTION__,
786                        strerror(errno)));
787             break;
788          }
789       }
790       Line[0] = 0;
791 
792       FD_ZERO(&ReadFDset);
793       FD_SET(MySocket,&ReadFDset);
794 
795    // Wait a second for the response
796       SelTO.tv_sec = 1;
797       SelTO.tv_usec = 0;
798       Selret = select(FD_SETSIZE,&ReadFDset,NULL,NULL,&SelTO);
799 
800       if(FD_ISSET(MySocket,&ReadFDset)) {
801          BytesRxed = recv(MySocket,Line,sizeof(Line)-1,0);
802          if(BytesRxed == SOCKET_ERROR) {
803             Ret = errno;
804             LOG_ERROR(("%s: recv() failed - %s\n",__FUNCTION__,
805                        strerror(errno)));
806          }
807          else {
808             int LineLen;
809 
810             Line[BytesRxed] = 0;
811             LineLen = strlen(Line);
812             if(LineLen > 0) {
813                if(Line[LineLen-1] == '\n') {
814                // remove terminating linefeed if present
815                   LineLen--;
816                }
817 
818                if(Line[LineLen-1] == '\n') {
819                // remove second line if it is blank
820                   LineLen--;
821                }
822                Line[LineLen] = 0;
823             }
824             LOG_ERROR(("%s: tlb returned %s\n",__FUNCTION__,Line));
825             Ret = atoi(Line);
826          }
827       }
828    } while(FALSE);
829    return Ret;
830 }
831 
InstallSigHandler()832 void InstallSigHandler()
833 {
834    struct sigaction SigAction;
835 
836 // Setup signal handlers using sigaction() rather than the signal() quagmire!
837 
838    SigAction.sa_handler = SigHandler;
839    SigAction.sa_flags = 0;
840    sigemptyset(&SigAction.sa_mask);
841 
842    sigaction(SIGTERM,&SigAction,NULL);
843    sigaction(SIGINT,&SigAction,NULL);
844 }
845 
LogCmdLine(char ** argv,int argc)846 void LogCmdLine(char **argv,int argc)
847 {
848    char Line[120];
849    int BytesLeft = sizeof(Line) - 1;
850    char *cp = Line;
851    int i;
852    int Count;
853 
854    for(i = 0; i < argc; i++) {
855       Count = snprintf(cp,BytesLeft,"%s ",argv[i]);
856 
857       if(Count == -1 || Count >= BytesLeft) {
858       // Pre-C99 style snprintf returns -1 on buffer overflow.
859       // C99 style snprintf returns the number of characters that would
860       // have been written on buffer overflow.
861       // In either case the output was truncated and p->Count is bogus.
862 
863          Count = BytesLeft;
864       }
865       cp += Count;
866       if((BytesLeft -= Count) == 0) {
867          break;
868       }
869    }
870    LOG_NORM(("CmdLine: %s\n",Line));
871 }
872 
GetIRLPCall()873 char *GetIRLPCall()
874 {
875    char *Ret = NULL;
876    char *Local = getenv("LOCAL");
877    char  Temp[1024];
878    FILE *fp;
879    char *cp;
880 
881    if(Local != NULL) {
882       snprintf(Temp,sizeof(Temp),"%s/active",Local);
883       if((fp = fopen(Temp,"r")) != NULL) {
884          fgets(Temp,sizeof(Temp),fp);
885          if((cp = strchr(Temp,'\n')) != NULL) {
886             *cp = 0;
887          }
888 
889          Ret = strdup(Temp);
890          fclose(fp);
891       }
892       else {
893          LOG_ERROR(("%s: Couldn't open \"%s\" - %s\n",__FUNCTION__,Temp,
894                     strerror(errno)));
895       }
896    }
897    else {
898       LOG_ERROR(("%s: Error environment variable \"LOCAL\" not set.\n",
899                  __FUNCTION__));
900    }
901    return Ret;
902 }
903 
904 /* Usage: imike hostname[:port] [options] [ file1 / . ]...
905    -A         Always transmit
906    -B         Push to talk using keyboard
907    -CELP      CELP compression
908    -D         Enable debug output
909    -F         ADPCM compression
910    -H         Disables RADIO COS mode (monitor only)
911    -L         Remote loopback
912    -N         No compression
913    -Phostname[:port] Party line, add host to list
914 *  -Q         Disable debug output
915 *  -T         Telephone (GSM) compression
916    -TD        Debug: send GSM with swapped byte order
917    -U         Print this message
918    -x         Full duplex audio
919    -Yindev[:ctldev] Override default audio device file name or specify open #fd
920    -8         Set audio device to 8-bit mu-law
921 */
EmulateImike(char ** argv,int argc)922 int EmulateImike(char **argv,int argc)
923 {
924    int i;
925    char *Hostname = NULL;
926    char *cp;
927    int bFullDuplex = FALSE;
928    int Port = 2074;  // default audio port
929    char CmdLine[120];
930    char *Station = NULL;
931    char *CodecOption = "";
932 
933    InstallSigHandler();
934    LogCmdLine(argv,argc);
935 
936    for(i = 1; i < argc; i++) {
937       if(argv[i][0] == '-') {
938          switch(argv[i][1]) {
939             case 't':
940             case 'T':
941                LOG_ERROR(("%s: set GSM mode\n",__FUNCTION__));
942                CodecOption = "";
943                break;
944 
945             case 'x':
946             case 'X':
947                bFullDuplex = TRUE;
948                break;
949 
950             case 'n':
951             case 'N':
952                CodecOption = "-u ";
953                break;
954 
955             case 'f':
956             case 'F':
957                CodecOption = "-a ";
958                break;
959 
960             default:
961                LOG_ERROR(("%s: ignoring switch \"%s\"\n",__FUNCTION__,argv[i]));
962                break;
963          }
964       }
965       else {
966       // Not a switch, hostname hopefully
967          Hostname = argv[i];
968          LOG_ERROR(("%s: hostname \"%s\"\n",__FUNCTION__,Hostname));
969       }
970    }
971 
972    do {
973       if(Hostname == NULL) {
974          break;
975       }
976 
977       if((cp = strchr(Hostname,':')) != NULL) {
978       // Port number specified
979          *cp++ = 0;
980          if(sscanf(cp,"%d",&Port) != 1) {
981             LOG_ERROR(("%s: unable to parse port \"%s\"\n",__FUNCTION__,cp));
982          }
983       }
984 
985       if(strcmp(Hostname,"127.0.0.1") != 0) {
986          if((Station = GetIRLPCall()) == NULL) {
987             Station = "IRLP";
988          }
989 
990          snprintf(CmdLine,sizeof(CmdLine),"connect -p %d -s %s%s%s %s",
991                   Port,CodecOption,bFullDuplex ? "-f " : "",
992                   Hostname,Station);
993 
994          SendCommand(CmdLine);
995       }
996 
997       while(!bShutdown) {
998          sleep(1000);
999       }
1000 
1001       if(strcmp(Hostname,"127.0.0.1") != 0) {
1002       // EchoIRLP connects to localhost to talk to tbd, ignore it
1003          snprintf(CmdLine,sizeof(CmdLine),".disconnect %s",Station);
1004          SendCommand(CmdLine);
1005       }
1006    } while(FALSE);
1007 
1008    return 0;
1009 }
1010 
1011 /*
1012 Options:
1013            -D               Force debug output
1014            -fip_address     Sets firewall to only allow from ip_address
1015            -Jwait,idle      Jitter delay wait and idle in milliseconds
1016            -Mhost/ip        Join multicast to given name or IP address
1017            -pport           Listen on given port
1018            -Q               Prevent debug output
1019            -U               Print this message
1020            -x               Full duplex audio
1021            -Youtdev[:ctldev] Audio device names
1022            -8               Set audio device to 8-bit mu-law
1023 */
EmulateIspeaker(char ** argv,int argc)1024 int EmulateIspeaker(char **argv,int argc)
1025 {
1026    InstallSigHandler();
1027    LogCmdLine(argv,argc);
1028 
1029    while(!bShutdown) {
1030       sleep(1000);
1031    }
1032    printf("Exiting\n");
1033 
1034     return 0;
1035 }
1036 
1037 #define MAX_PLAY_WAIT      30
1038 
EmulatePlay(char ** argv,int argc)1039 int EmulatePlay(char **argv,int argc)
1040 {
1041    int i;
1042    int j;
1043    char CmdLine[120];
1044    int Response;
1045 
1046    LogCmdLine(argv,argc);
1047 
1048    for(i = 1; i < argc; i++) {
1049       snprintf(CmdLine,sizeof(CmdLine),"say -c %s",argv[i]);
1050       if((Response = SendCommand(CmdLine)) != TBD_OK) {
1051          LOG_ERROR(("%s: tlb returned %d for \"%s\"\n",__FUNCTION__,
1052                     Response,CmdLine));
1053          break;
1054       }
1055 
1056       for(j = 0; j < MaxPlayWait; j++) {
1057          Response = SendCommand(NULL);
1058          if(Response == TBD_SAY_COMPLETE) {
1059             break;
1060          }
1061          else if(Response != -1) {
1062             LOG_ERROR(("%s: tlb returned %d for \"%s\"\n",__FUNCTION__,
1063                        Response,CmdLine));
1064          }
1065       }
1066 
1067       if(j == MaxPlayWait) {
1068          LOG_ERROR(("%s: timed out waiting for TBD_SAY_COMPLETE\n",__FUNCTION__));
1069       }
1070    }
1071 
1072    return 0;
1073 }
1074 
Substitute(char * String,char From,char To)1075 void Substitute(char *String,char From,char To)
1076 {
1077    char *cp = String;
1078 
1079    while((cp = strchr(cp,From)) != NULL) {
1080       *cp++ = To;
1081    }
1082 }
1083 
1084 //
EmulateDtmfRegen(char ** argv,int argc)1085 int EmulateDtmfRegen(char **argv,int argc)
1086 {
1087    char CmdLine[120];
1088    char *Station = NULL;
1089    char *Dtmf;
1090    int i;
1091 
1092    LogCmdLine(argv,argc);
1093 
1094    if((Station = GetIRLPCall()) == NULL) {
1095       Station = "IRLP";
1096    }
1097 
1098    if(argc > 1) {
1099       Dtmf = strdup(argv[1]);
1100 
1101       Substitute(Dtmf,'P','#');
1102       Substitute(Dtmf,'p','#');
1103       Substitute(Dtmf,'S','*');
1104       Substitute(Dtmf,'s','*');
1105 
1106       snprintf(CmdLine,sizeof(CmdLine),"port %s; dtmfdecode %s",Station,Dtmf);
1107 
1108    // EchoIRLP connects to localhost to talk to tbd, ignore it
1109       SendCommand(CmdLine);
1110    }
1111 
1112    if(argc != 2) {
1113       LOG_ERROR(("Unexpected argument count (%d):\n",argc));
1114       for(i = 1; i < argc; i++) {
1115          LOG_ERROR(("   %d: %s\n",i,argv[i]));
1116       }
1117    }
1118 
1119    return 0;
1120 }
1121 
Log(char * fmt,...)1122 void Log(char *fmt, ...)
1123 {
1124    int WriteLen;
1125    char  Temp[1024];
1126    char  Temp1[1024];
1127    time_t ltime;
1128    struct tm *tm;
1129    static int LastLogDay = -1;
1130    static int LastLogType;
1131    char *LogDir;
1132    va_list args;
1133    va_start(args,fmt);
1134 
1135    time(&ltime);
1136    tm = localtime(&ltime);
1137 
1138    if(LogFileRolloverType != 0 &&
1139          (LogFp == NULL ||
1140           tm->tm_mday != LastLogDay ||
1141           LastLogType != LogFileRolloverType))
1142    {
1143       LastLogType = LogFileRolloverType;
1144 
1145       if(LogFp != NULL) {
1146          fclose(LogFp);
1147          LogFp = NULL;
1148       }
1149 
1150       do {
1151          if((LogDir = getenv("TLB_HOME")) != NULL) {
1152             break;
1153          }
1154 
1155          if((LogDir = getenv("LOG")) != NULL) {
1156             break;
1157          }
1158          LogDir = ".";
1159       } while(FALSE);
1160 
1161    // Assume the name will be tbd.log
1162       snprintf(Temp,sizeof(Temp),"%s/%s.log",LogDir,AppName);
1163 
1164       switch(LogFileRolloverType) {
1165          case 2:  // daily log
1166             snprintf(Temp,sizeof(Temp),"%s/%s%02d%02d%02d.log",LogDir,AppName,
1167                      tm->tm_mon+1,tm->tm_mday,tm->tm_year % 100);
1168             break;
1169 
1170          case 3:  // daily log
1171             if(LastLogDay != -1 && tm->tm_mday != LastLogDay) {
1172             // A new day is born, rename the old log to .bak and open a new log
1173                snprintf(Temp1,sizeof(Temp1),"%s/%s.bak",LogDir,AppName);
1174                unlink(Temp1);
1175                rename(Temp,Temp1);
1176             }
1177             break;
1178 
1179          case 4:  // weekly
1180             snprintf(Temp,sizeof(Temp1),"%s/%s",LogDir,
1181                      LogNames[tm->tm_wday]);
1182             if(LastLogDay != -1 && tm->tm_mday != LastLogDay) {
1183             // delete last weeks log
1184                unlink(Temp);
1185             }
1186             break;
1187       }
1188 
1189       LastLogDay = tm->tm_mday;
1190       LogFp = fopen(Temp,"a");
1191    }
1192 
1193    WriteLen = vsnprintf(Temp,sizeof(Temp),fmt,args);
1194    va_end(args);
1195 
1196    if(LogFp != NULL) {
1197       if(LogFileRolloverType == 1 || LogFileRolloverType == -1) {
1198       // one big file log date & time
1199          fprintf(LogFp,"%s %d %d:%02d:%02d %s",MonthNames[tm->tm_mon],
1200                  tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec,Temp);
1201       }
1202       else {
1203       // Not one big file, just log time
1204          fprintf(LogFp,"%d:%02d:%02d %s",tm->tm_hour,tm->tm_min,
1205                  tm->tm_sec,Temp);
1206       }
1207       fflush(LogFp);
1208 
1209       if(ftell(LogFp) > 10000000) {
1210       // shit, get out of dodge
1211          fprintf(LogFp,"LOG FILE OVERFLOW, shutting down !\n");
1212          fclose(LogFp);
1213          LogFp = NULL;
1214          if(isatty(fileno(stdin))) {
1215             printf("LOG FILE OVERFLOW, shutting down !\n");
1216          }
1217       }
1218    }
1219 }
1220 
SigHandler(int Signal)1221 void SigHandler(int Signal)
1222 {
1223    switch(Signal) {
1224       case SIGINT:
1225          LOG_NORM(("Received SIGINT, shutting down\n"));
1226          break;
1227 
1228       case SIGTERM:
1229          LOG_NORM(("Received SIGTERM, shutting down\n"));
1230          break;
1231    }
1232 
1233    if(Signal == SIGINT || Signal == SIGTERM) {
1234    // Try an orderly shutdown
1235       bShutdown = TRUE;
1236    }
1237 }
1238 #endif
1239 
1240 // Mode:
1241 // 0 - startup, save initial settings, turn off ICANON and ECHO
1242 // 1 - disable ECHO, ICANON
1243 // 2 - enable ECHO
1244 // 3 - restore original terminal settings for exit
SetTerminalMode(int mode)1245 void SetTerminalMode(int mode)
1246 {
1247 #ifdef HAVE_LIBREADLINE
1248    struct termios options;
1249    static struct termios InitialMode;
1250 
1251    if(bUseReadline) do {
1252       if(tcgetattr(fileno(stdin),&options) < 0) {
1253          printf("%s: Unable to read port configuration: %s",__FUNCTION__,
1254                 strerror(errno));
1255          break;
1256       }
1257 
1258       switch(mode) {
1259          case 0:
1260             InitialMode = options;
1261          // intentional fall through
1262 
1263          case 1:
1264             options.c_lflag &= ~(ICANON | ECHO);
1265             break;
1266 
1267          case 2:
1268             options.c_lflag |= ECHO;
1269             break;
1270 
1271          case 3:
1272             if(HistoryPath != NULL) {
1273                write_history(HistoryPath);
1274             }
1275             options = InitialMode;
1276             break;
1277 
1278       }
1279 
1280       if(tcsetattr(fileno(stdin),TCSANOW,&options) < 0) {
1281          printf("%s: tcsetattr() failed: %s",__FUNCTION__,strerror(errno));
1282       }
1283    } while(FALSE);
1284 #endif
1285 }
1286 
1287 #ifndef HAVE_GETOPT
1288 
1289 /*
1290  * getopt a wonderful little function that handles the command line.
1291  * available courtesy of AT&T.
1292  */
getopt(int argc,char ** argv,char * opts)1293 int getopt(int argc,char **argv,char *opts)
1294 {
1295    static int sp = 1;
1296    register int c;
1297    register char *cp;
1298 
1299    if(sp == 1)
1300       if(optind >= argc ||
1301          argv[optind][0] != '-' || argv[optind][1] == '\0')
1302          return(EOF);
1303       else if(strcmp(argv[optind], "--") == 0) {
1304          optind++;
1305          return(EOF);
1306       }
1307    optopt = c = argv[optind][sp];
1308    if(c == ':' || (cp=strchr(opts, c)) == NULL) {
1309       printf("%s: illegal option -- ", argv[0],c);
1310       if(argv[optind][++sp] == '\0') {
1311          optind++;
1312          sp = 1;
1313       }
1314       return('?');
1315    }
1316    if(*++cp == ':') {
1317       if(argv[optind][sp+1] != '\0')
1318          optarg = &argv[optind++][sp+1];
1319       else if(++optind >= argc) {
1320          printf("%s: option requires an argument -- ", argv[0],c);
1321          sp = 1;
1322          return('?');
1323       }
1324       else
1325          optarg = argv[optind++];
1326       sp = 1;
1327    }
1328    else {
1329       if(argv[optind][++sp] == '\0') {
1330          sp = 1;
1331          optind++;
1332       }
1333       optarg = NULL;
1334    }
1335    return(c);
1336 }
1337 #endif
1338