/* 
 * vanswer.c
 *
 * Handle a voice call: Play answering message, beep, record
 * and switch back to data mode if appropiate. 
 *
 */

char vanswer_c[] = "$Id: vanswer.c,v 1.48 1995/04/11 19:18:33 marc Exp $";

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/times.h>

#include "voice.h"

#include "../fax_lib.h"

#ifdef NO_STRSTR
char *
strstr _P2((haystack, needle), const char* haystack, const char* needle)
{
    /* This isn't terribly efficient, but it'll do for now. */
    int l, n, i;
    l=strlen(haystack);
    n=strlen(needle);

    for (i = 0; i <= l-n; i++) {
        if (strncmp(&haystack[i], needle, n) == 0) return haystack + i;
    }
    return 0;
}
#endif

void
enter_data_mode _P1((answer_mode), int answer_mode)
{
    answer_mode &= (ANSWER_FAX | ANSWER_DATA);
    if (answer_mode == 0) {
	lprintf(L_AUDIT, "fax and data both disabled, hanging up");
	exit(1);
    }
    
    switch (voice_modem) {
      case VM_ZYXEL:
      case VM_DOLPHIN:
	switch (answer_mode) {
	  case ANSWER_FAX | ANSWER_DATA:
	    /* let the modem decide if it's fax or data */
	    lprintf(L_MESG, "Trying fax/data connection...");
	    if ( modem_type == Mt_class2_0 )
		voice_command("AT+FCLASS=2.0;+FAA=1", "OK", STDIN);
	    else
		voice_command("AT+FCLASS=2;+FAA=1", "OK", STDIN);
	    break;
	  case ANSWER_FAX:
	    /* answer in FAX mode only */
	    if ( modem_type == Mt_class2_0 )
		voice_command("AT+FCLASS=2.0;+FAA=0", "OK", STDIN);
	    else
		voice_command("AT+FCLASS=2;+FAA=0", "OK", STDIN);
	    break;
	  case ANSWER_DATA:
	    /* answer in DATA mode only */
	    voice_command("AT+FCLASS=0 +FAA=0", "OK", STDIN);
	    break;
	}
	/* set bit order properly, is reset by +FCLASS=2[.0] */
	if ( answer_mode != ANSWER_DATA ) fax_set_bor( STDIN, 1 );
	break;
      case VM_ROCKWELL:
	switch (answer_mode) {
	  case ANSWER_FAX | ANSWER_DATA:
	    /* let the modem decide if it's fax or data */
	    lprintf(L_NOISE, "Trying fax/data connection...");
	    voice_command("AT#CLS=2 +FAA=1", "OK", STDIN);
	    break;
	  case ANSWER_FAX:
	    /* answer in FAX mode only */
	    /* FIXME: class 2.0 ? (has no class 2.0 mode...) */
	    voice_command("AT#CLS=2 +FAA=0", "OK", STDIN);
	    break;
	  case ANSWER_DATA:
	    /* answer in DATA mode only */
	    voice_command("AT#CLS=0 +FAA=0", "OK", STDIN);
	    break;
	}
	break;
    }
}

void
voice_rings _P1((rings_wanted), int *rings_wanted)
{
    extern char *DevID;
    FILE *fin, *flag;
    int i,l;
    char buf[MAXLINE];

    sprintf(buf, vgetty_answer_file, DevID);
    fin=fopen(buf, "r");
    if (fin) {
	while (fgets(buf, MAXLINE, fin)) {
	    l=strlen(buf);
	    for(i=0; i<l; i++) buf[i]=tolower(buf[i]);

	    if (strstr(buf, "rings")) {
		sscanf(buf, "rings %d", rings_wanted);
		lprintf(L_NOISE, "rings_wanted = %d", *rings_wanted);
		if (*rings_wanted <=0) *rings_wanted=1;
	    }
	}
	fclose(fin);
    }

    if (strcmp(message_flag_file, ""))
    {
    /* Are there messages waiting ? */
    flag=fopen(make_path(voice_dir, message_flag_file), "r");

    if ((flag) && !messages_waiting_ack) 
    {
     messages_waiting_ack = 1;
	*rings_wanted -= toll_saver_rings;
	if (*rings_wanted < 1) *rings_wanted=1;
	fclose(flag);
    }
    }
}

void
voice_message_light _P0(void)
{
    /* With external modems, the auto-answer LED can be used
       to show a status flag. vgetty uses this to indicate
       that new messages have arrived.
       
       Since vgetty doesn't remember what happened during previous
       calls, a flag file is used to mark the status. */

    if (strcmp(message_flag_file, ""))
    {
    /* Are there messages waiting ? */
    FILE *flag=fopen(make_path(voice_dir, message_flag_file), "r");

    if (flag) {
	/* turn on the AA lamp */
	voice_command("ATS0=255", "OK", STDIN);
	fclose(flag);
    } else {
	/* turn it off */
	voice_command("ATS0=0", "OK", STDIN);
    }
    }
}

static int /* answer_mode */
get_answer_mode _P0(void)
{
    /* check for a "answer mode" file (/etc/answer.<DevID>) */

    extern char *DevID;
    FILE *fin;
    int i,l;
    char buf[MAXLINE];
    int answer_mode = 0;

    sprintf(buf, vgetty_answer_file, DevID);
    fin=fopen(buf, "r");
    if (!fin) return answer_mode = ANSWER_FAX | ANSWER_VOICE | ANSWER_DATA;

    while (fgets(buf, MAXLINE, fin)) {
	l=strlen(buf);
	for(i=0; i<l; i++) buf[i]=tolower(buf[i]);

	if (strstr(buf, "voice")) answer_mode |= ANSWER_VOICE;
	if (strstr(buf, "fax" )) answer_mode |= ANSWER_FAX;
	if (strstr(buf, "data")) answer_mode |= ANSWER_DATA;
    }
    fclose(fin);
    lprintf(L_NOISE, "answer_mode is %d", answer_mode);
    if (answer_mode==0) {
	lprintf(L_MESG, "don't accept any kind of call!");
	clean_line(STDIN, 80);	/* wait for ringing to stop */
	exit(1);
    }
    return answer_mode;
}

static char *
greeting_message_file _P0(void)
{
    static char path[MAXPATH];
    
    FILE *list;
    char msg[MAXPATH];
    
    list=fopen(make_path(vgetty_message_dir, vgetty_message_list), "r");
    if (!list) {
	strcpy(msg, vgetty_backup_message);
    } else {
	int i, m, n=0;
	/* count the messages */
	while (fgets(msg, MAXPATH, list)) {
	    if (strlen(msg)>1) n++;
	}
	lprintf(L_MESG, "found %d messages", n);
	/* pick a random one (could use rand() here) */
	m=getpid() % n;
	rewind(list);
	for(i=0; i<=m; i++) fgets(msg, MAXPATH, list);
	i=strlen(msg);
	if (i && msg[i-1]=='\n') msg[i-1]=0;
	fclose(list);
    };
    
    strcpy(path, make_path(vgetty_message_dir, msg));
    lprintf(L_NOISE, "picked file %s", path);
    return path;
}

static void
remove_message _P1((message), char *message)
{
    /* throw away the recording */
    lprintf(L_MESG, "removing recording.");
    unlink(message);
}

static void
button_program_and_exit _P0(void)
{
    /* exec button program */
    lprintf(L_NOISE, "button pressed");

    if (strcmp(button_program, ""))
    {
    lprintf(L_NOISE, "executing %s", button_program);
    (void) execl(make_path(voice_dir, button_program), make_path(voice_dir,
      button_program), "button", (char *) NULL);
    (void) execl("/bin/sh", "sh", make_path(voice_dir, button_program),
		 "button", (char *) NULL);
    lprintf(L_ERROR, "cannot execute %s", button_program);
    }
    exit(1);
}

static void
fork_message_program _P1((message), char *message)
{
    /* don't exit afterward, hence the fork. */
    if (strcmp(message_program, ""))
    {
    int status, pid;
    extern char *CallerId;
    
    switch(pid=fork()) {
      case -1:
	lprintf(L_WARN, "can't fork message program");
	break;
      case 0:
	/* child */
	lprintf(L_NOISE, "executing %s %s %s",
		message_program, message, CallerId);
	(void) execl(make_path(voice_dir, message_program), make_path(voice_dir,
	  message_program), message, CallerId, (char *) NULL);
	(void) execl("/bin/sh", "sh", make_path(voice_dir, message_program),
	  message, CallerId, (char *) NULL);
	lprintf(L_ERROR, "cannot execute %s", message_program);
	exit(1);
    }
    /* parent */
    lprintf(L_NOISE, "waiting for message program to exit...");
    while (wait(&status) != pid)
	;
    lprintf(L_NOISE, "... done");
    }
}

static void
keep_message_and_exit _P1((message), char *message)
{
    lprintf(L_MESG, "Voice message received, stored in %s.",
	    message);

    /* beep */
    voice_beep(STDIN, VIO_TELCO, "[933,0,12]");
    fork_message_program(message);
    exit(0);
}

static int
fork_call_program _P1((DevID), char *DevID)
{
    /* hand over control to a shell script - note that this
     * isn't as well tested and stable as the C code. You also
     * won't get proper logging or random greeting message selection.
     * Don't enable this unless you know what you are doing.
     */
    int status, pid;
    
    switch(pid=fork()) {
      case -1:
	lprintf(L_WARN, "can't call message program");
	return ERROR;
	break;
      case 0:
	/* child */
	lprintf(L_NOISE, "executing %s", call_program);
	(void) execl(make_path(voice_dir, call_program), make_path(voice_dir,
	  call_program), DevID, (char *) NULL);
	(void) execl("/bin/sh", "sh", make_path(voice_dir, call_program),
	  DevID, (char *) NULL);
	lprintf(L_ERROR, "cannot execute %s %s", call_program, DevID);
	exit(1);
    }
    /* parent */
    lprintf(L_NOISE, "waiting for call script to exit...");
    while (wait(&status) != pid)
	;
    lprintf(L_NOISE, "...done, call script status is %d", status);
    return status;
}

static void dtmf_program_and_exit _P2((dtmf, message), char *dtmf,
  char *message)
	{

	if (message) 
		{
		/* Remove the recording? */
          if (rec_always_keep_message)
			fork_message_program(message);
          else
			remove_message(message);
		};
    
	lprintf(L_MESG, "got DTMF command.");

	/* call the external program to handle the DTMF command. */

     if (strcmp(dtmf_program, ""))
     	{
		lprintf(L_NOISE, "executing %s <digits>", dtmf_program);
		(void) execl(make_path(voice_dir, dtmf_program), make_path(
		  voice_dir, dtmf_program), dtmf, (char *) NULL);
		(void) execl("/bin/sh", "sh", make_path(voice_dir, dtmf_program),
		  dtmf, (char *) NULL);
		lprintf(L_ERROR, "cannot execute %s", dtmf_program);
		}
		
	exit(1);
	};

void
voice_button _P1((rings), int rings)
{
    if ((strcmp(button_program, "")) && (rings==0)) {
	/* The data/voice button was pushed, but the phone didn't ring. */
	/* Call the external program. */
	button_program_and_exit();
    }
    
    /* answer in fax/data mode */
    enter_data_mode(ANSWER_FAX | ANSWER_DATA);
    return;
}

static void
log_call_length _P1((logmsg), char *logmsg) {
    /* add the call length data to the log message */   
    time_t call_end;
    extern time_t call_start;
    
    time(&call_end);
    call_end -= call_start;
    
    sprintf(logmsg+strlen(logmsg), ", time=%02d:%02d:%02d",
	    (int)(call_end / 3600), (int)((call_end / 60) % 60),
	    (int)(call_end % 60));
}

void
voice_answer _P3((rings, rings_wanted, ring_action), 
		 int rings, int rings_wanted,
		 action_t ring_action)
{
    char *voice_answer_chat_seq[] = { "", vgetty_ata, "VCON",
						 NULL };
    extern char *DevID;
    int answer_mode;
    char *path;
    int ret;
    char message[MAXPATH];
    char logmsg[1024];

    /* check for a "answer mode" file (/etc/answer.<DevID>) */
    answer_mode = get_answer_mode();
    
    if (ring_action >= A_RING1 && ring_action <= A_RING5) {
	if (ring_action != dist_ring_fax &&
	    ring_action != dist_ring_data &&
	    ring_action != dist_ring_voice)
	{
	    lprintf(L_MESG, "ignoring distinctive RING %d",
		    ring_action - A_RING1 + 1);
	    clean_line(STDIN, 80);
	    lprintf(L_AUDIT, "call ignored");
	    exit(1);
	}
    }
    
    if (ring_action == dist_ring_fax) {
	/* caller is known to be a fax */
	enter_data_mode(answer_mode & ANSWER_FAX);
	return;
    }

    if (rings < rings_wanted || !(answer_mode & ANSWER_VOICE)) {
	/* enter fax/data mode */
	enter_data_mode(answer_mode);
	return;
    }
    
    /*** answer the phone normally ***/
    
    /* answer the phone in voice mode */
    if (do_chat(STDIN, voice_answer_chat_seq, NULL,
		 NULL, 10, TRUE) == FAIL) {
	lprintf(L_FATAL, "can't answer the phone in voice mode");
	exit(1);
    }
    
    if (strcmp(call_program, ""))
    {
    /* let the shell script handle the call */
    ret = fork_call_program(DevID);
    if (ret == 0) {
	exit(0); /* everything taken care of */
    } else if (ret != ERROR) {
	enter_data_mode(answer_mode);
	return;
    }
    /* something went wrong, try again using the C code */
    }
    
    sprintf(logmsg, "dev=%s, pid=%d, caller=%s, name='%s'",
	    DevID, getpid(), CallerId, CallName);

    /*** play a greeting message ***/
    
    path=greeting_message_file();
    ret=voice_send_file(path, STDIN, VIO_TELCO, 0) ;
    lprintf(L_NOISE, "voice_send_file returned '%c'", ret);
    
    if (ret=='E') {
	lprintf(L_AUDIT, "error during playback, %s", logmsg);
	exit(1);
    } else if (ret=='#' || ret=='V') {
	lprintf(L_AUDIT, "hangup request, %s", logmsg);
	exit(1);
    } else if (ret=='d' || ret=='b') {
	lprintf(L_AUDIT, "nobody there, %s", logmsg);
	exit(1);
    } else if (ret=='c') {
	enter_data_mode(answer_mode & ANSWER_FAX); /* it's a fax */
	return;
    } else if (ret=='e') {
	enter_data_mode(answer_mode & ANSWER_DATA); /* it's data */
	return;
    } else if (ret>='0' && ret<='9') {
	char dtmf[2];
	lprintf(L_AUDIT, "voice dtmf <digit>, %s\n", logmsg);
	dtmf[0]=ret;
	dtmf[1]=0;
	dtmf_program_and_exit(dtmf, 0);
    }

    /*** beep ***/

    voice_beep(STDIN, VIO_TELCO, "[933,0,12]");
	    
    /*** record a message and/or DTMF tones ***/

    message[0] = '\0';
    
    sprintf(message, "%s/voc-XXXXXX", vgetty_receive_dir);
    mktemp(message);

    voice_activate_digits();

    ret=voice_record_file(message, STDIN, VIO_TELCO, rec_compression,
			  rec_silence_len, rec_silence_threshold,
			  rec_max_len, TRUE);
    lprintf(L_NOISE, "voice_record_file returned '%c'", ret);

    if (ret == 's') {
	/* assume it's a fax/data call */
	remove_message(message);
	enter_data_mode(answer_mode);
	return;
    } else if (ret == 'c') {
	/* it's a fax */
	remove_message(message);
	enter_data_mode(answer_mode & ANSWER_FAX);
	return;
    } else if (ret == 'e') {
	/* it's data */
	remove_message(message);
	enter_data_mode(answer_mode & ANSWER_DATA);
	return;
    }
    
    log_call_length(logmsg);

    if (ret=='#') {
	char *dtmf;
	dtmf=voice_get_digits();
	if (dtmf && dtmf[0]) {
	    /* Caller typed a DTMF command */
	    lprintf(L_AUDIT, "voice dtmf <digits>, %s\n", logmsg);
	    dtmf_program_and_exit(dtmf, message);
	}
    }
    
    /* seems to have been a normal voice message. */
    /* res should be one of [qbd#ET] */
    lprintf(L_AUDIT, "voice keep %s\n", logmsg);
    keep_message_and_exit(message);

    /* call finished, exit */
    exit(0); 
}
