/* zplay.c
 *
 * (mostly stolen from Gert Doering's fax routines)
 *
 * Play or record a voice file.
 *
 */

char zplay_c[] = "$Id: zplay.c,v 1.38 1995/04/11 19:18:35 marc Exp $";

#include <sys/types.h>
#include <sys/wait.h>

#include "voice.h"

boolean verbose = FALSE;

void exit_usage _P1((program), char * program )
{
    fprintf( stderr,
	    "usage: %s [options] [<files to play >] [-b beep] [-d dialout] "
	    "[-r <output>]\n", program);
    fprintf( stderr,
	    "\t-h this help message\n");
    fprintf( stderr,
	    "\t-r s record voice data in <s>\n");
    fprintf( stderr,
	    "\t-c n use compression type <n> (default is %d)\n",
	    REC_COMPRESSION);
    fprintf( stderr,
	    "\t-t   use telco line for i/o (default)\n");
    fprintf( stderr,
	    "\t-s   output to internal speaker\n");
    fprintf( stderr,
	    "\t-m   input from external microphone\n");
    fprintf( stderr,
	    "\t-T n set silence time to n/10 seconds\n");
    fprintf( stderr,
	    "\t-V n set silence volume to n, n=0..1\n");
    fprintf( stderr,
	    "\t-L n set maximum recording length to n seconds\n");
    fprintf( stderr,
	    "\t-b s beep (AT+VTS=<s>), i.e. -b '[933,0,12]'\n");
    fprintf( stderr,
	    "\t-d s dialout (ATD<s>), i.e. -d '3261816'\n");
    fprintf( stderr,
	    "\t-v   verbose output\n");
    fprintf( stderr,
	    "\t-x n set debugging level to <n>\n");
    fprintf( stderr,
	    "\t-l s set device string to <s>, i.e. -l ttyS1:ttyS2\n");
    fprintf( stderr,
	    "\t-S   modem is on stdin (for use in shell scripts)\n");
    fprintf( stderr,
	    "\t-C   enable DTMF commands (for shell scripts)\n");
    fprintf( stderr,
	    "\t-D   print DTMF return code on stdout (for shell scripts)\n");
    fprintf( stderr,
	    "\t-I s ignore DTMF codes specified in <s> (for shell scripts)\n");
    fprintf( stderr,
	    "\t-K   flush DTMF codes from modem buffer (for shell scripts)\n");
    fprintf( stderr,
	    "\t-X s connect the modem to stdin, call the shell <s>\n");
    exit(1);
}

int main _P2((argc, argv), int argc, char ** argv )
{
    int argidx;
    int fd;
    int ch;
    char *dtmf_chars;

    static char	voice_device_string[] = FAX_MODEM_TTYS;	/* writable! */
    char *voice_devices = voice_device_string; /* override with "-l" */

    int	voc_io = VIO_TELCO;
    int compr = REC_COMPRESSION;
    int voice_max_len = REC_MAX_LEN;
    int voice_silence_len = REC_SILENCE_LEN;
    double voice_silence_threshold = REC_SILENCE_THRESHOLD;
    boolean use_stdin = FALSE;
    boolean print_ret = FALSE;
    boolean use_commands = FALSE;
    char *beep = 0;
    char *dialout = 0;
    char *record = 0;
    int zreturn = 0;
    char *shell = 0;
    boolean do_fft = FALSE;
    int do_flush = FALSE;
    
    voice_config(argc, argv);

    /* initialize logging */
    log_init_paths( argv[0], zplay_log_path, NULL );
    log_set_llevel( L_NOISE );

    while ((ch = getopt(argc, argv,
			"b:c:d:l:mr:stvx:CDFI:KL:ST:V:X:h"))
	   != EOF) {
	switch (ch) {
	  case 'T':
	    voice_silence_len=atoi(optarg); /* time in n/10 seconds */
	    break;
	  case 'V':
	    voice_silence_threshold=atof(optarg); /* 0..1 */
	    break;
	  case 'L':
	    voice_max_len = atoi(optarg);   /* length in seconds */
	    break;
	  case 'S':
	    use_stdin = TRUE;	/* modem connected to stdin */
	    break;
	  case 'r':		/* record voice file */
	    record = optarg;
	    break;
	  case 'b':		/* beep */
	    beep = optarg;
	    break;
	  case 'd':		/* dialout */
	    dialout = optarg;
	    break;
	  case 'c':		/* compression mode */
	    compr = atoi( optarg );
	    break;
	  case 'v':		/* switch on verbose mode */
	    verbose = TRUE;
	    break;
	  case 'x':		/* set debug level */
	    log_set_llevel( atoi(optarg) );
	    break;
	  case 'l':		/* set device(s) to use */
	    if ( geteuid() != getuid() || getegid() != getgid() ) {
		lprintf( L_ERROR, "don't use -l when set?id\n" );
		fprintf( stderr, "don't use -l when set?id\n" );
		exit(1);
	    }
	    voice_devices = optarg;
            if ( optarg[0] == '/' &&
                 strncmp( optarg, "/dev/", 5 ) != 0 )
            {
                fprintf( stderr, "%s: -l: device must be located in /dev!\n",
                                 argv[0]);
                exit(1);
            }
            break;
	  case 't':		/* i/o: telco line */
	    voc_io=VIO_TELCO;
	    break;
	  case 's':		/* output: speaker */
	    voc_io=VIO_SPEAKER;
	    break;
	  case 'm':		/* input: microphone */
	    voc_io=VIO_MIC;
	    break;
	  case 'D':		/* print return code */
	    print_ret = TRUE;
	    break;
	  case 'C':		/* enable DTMF commands */
	    use_commands = TRUE;
	    voice_activate_digits();
	    break;
	  case 'I':		/* ignored DTMF codes */
	    voice_set_ignore(optarg);
	    break;
	  case 'K':
	    do_flush = TRUE;
	    break;
	  case 'X':
	    shell = optarg;
	    break;
	  case 'F':
	    do_fft = TRUE;
	    break;
	  case 'h':
	  case '?':		/* unrecognized parameter */
	    exit_usage(argv[0]);
	    break;
	}
    }

    argidx = optind;

    if (argidx >= argc && !beep && !dialout && !record && !do_flush) 
	exit_usage(argv[0]);

    if ( ! use_stdin ) {
	fd = voice_open( voice_devices );

	if ( fd == -1 ) {
	    lprintf( L_WARN, "cannot open voice device(s)" );
	    fprintf( stderr, "%s: cannot access voice device(s) (locked?)\n", argv[0] );
	    exit(1);
	}

	if ( voice_command( "ATQ0V1", "OK", fd ) == ERROR ||
	    mg_init_voice( fd ) == FAIL ) {
	    lprintf( L_ERROR, "cannot initialize faxmodem" );
	    fprintf( stderr, "%s: cannot initialize faxmodem\n", argv[0] );
	    voice_close( fd );
	    exit(1);
	}
	
    } else {
	fd = STDIN;
    }

    if (shell) {
	int status;
	
	if (fd > 0) {
	    (void) close(0);
	    if (dup(fd) != 0) {
		lprintf(L_ERROR,"cannot open stdin");
		exit(FAIL);
	    }
	}

	switch(fork()) {
	    char **nargv;
	    int i;
	  case -1:
	    lprintf(L_ERROR, "cannot fork");
	    exit(1);
	    break;
	  case 0:
	    /* child */
	    /* be a bit paranoid here, zplay shouldn't be running
	     * set?id anyway, but if you can't avoid it, this should
	     * reduce the risk.
	     */
	    setuid(getuid());
	    setgid(getgid());

	    /* export a variable to inform shell scripts */
	    putenv("ZPLAY_X=true");
	    nargv = (char**)malloc((argc-argidx+2)*sizeof(char**));
	    nargv[0]="sh";
	    for (i=0; i<argc-argidx; i++) {
		nargv[i+1]=argv[argidx+i];
	    }
	    nargv[argc-argidx+1]=(char*)0;
	    (void) execv(shell, nargv);
	    lprintf(L_WARN, "cannot execute '%s %s'", shell, argv[argidx]);
	    fprintf(stderr, "cannot execute '%s %s'\n", shell, argv[argidx]);
	    exit(1);
	}
	/* parent */
	wait(&status);
	voice_mode_off( fd );
	voice_close( fd );
	exit(0);
    }

    dtmf_chars = voice_flush(fd);

   if (print_ret) {
	char *p;
	for (p=dtmf_chars; *p; p++) {
	    printf("%c%s", *p, use_commands ? " " : "\n");
	}
    }

    if (dtmf_chars[0] && !do_flush) {
	return zreturn;
    }

    while ( argidx < argc ) {
	/* play voice file */
	int ret;

	if ( verbose ) printf("sending '%s'...\n", argv[ argidx ]);
	ret = voice_send_file(argv[ argidx ], fd , voc_io,
			      ((print_ret || use_commands)
			       ? voice_silence_len : 0));
	if ( ret=='E' )
	    fprintf(stderr, "Error sending %s%s\n",
		    argv[argidx],
		    argidx+1<argc ? ", trying to continue" : "");
	if (ret=='b' || ret=='d') zreturn = 2;
	    
	argidx++;
    }

    if ( beep ) {
	voice_beep( fd, voc_io, beep );
    }

    if ( dialout ) {
	printf("%d\n", voice_dialout(fd, voc_io, dialout));
    }

    if ( record ) {
	int ret;

	if ( verbose ) printf( "recording '%s'...\n", record );
	ret = voice_record_file(record, fd , voc_io, compr,
				voice_silence_len,
				voice_silence_threshold,
				voice_max_len,
				do_fft);
	if (ret=='b' || ret=='d' || ret=='s') zreturn = 2;
    }

    if ( ! use_stdin ) voice_mode_off( fd );

    dtmf_chars = voice_flush(fd);

    if ( ! use_stdin ) voice_close( fd );

   if (print_ret) {
	char *p;
	for (p=dtmf_chars; *p; p++) {
	    printf("%c%s", *p, use_commands ? " " : "\n");
	}
    }

    if ( use_commands ) printf("%s\n", voice_get_digits());
    return zreturn;
}
