/* dialog.c

   Modem dialog code... */

/*
 * Copyright (c) 1995 RadioMail Corporation.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of RadioMail Corporation nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY RADIOMAIL CORPORATION AND CONTRIBUTORS
 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
 * RADIOMAIL CORPORATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
 * OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software was written for RadioMail Corporation by Ted Lemon
 * under a contract with Vixie Enterprises, and is based on an earlier
 * design by Paul Vixie.
 */

#ifndef lint
static char copyright[] =
"@(#) Copyright (c) 1995 RadioMail Corporation.  All rights reserved.\n";
#endif /* not lint */

#include "osdep.h"
#include "cdefs.h"
#include "global.h"
#include <stdio.h>
#include <fcntl.h>
#include <syslog.h>
#include "mcap.h"
#include "ttio.h"

char *atterr = "Can't get modem's attention on %s.";

/* Get the modem to say \r\nOK\r\n... */

int modemAttn (tty)
     int tty;
{
  ttdrain (tty);

  /* If requested, we'll try to reset the modem by dropping DTR... */
  if (modemcap.reset_dtr)
    {
      RESET_DTR (tty);
      /* Give the modem time to be sure we mean it... */
      DELAY_ONE_SECOND();
      SET_DTR (tty);
      /* Wait for the modem to reboot... */
      DELAY_ONE_SECOND();
    }
  ttdrain (tty);

  /* Try to get the modem's attention... */
  DELAY_ONE_SECOND();
  ttwriteslow (tty, "+++");
  DELAY_ONE_SECOND();
  ttdrain (tty);

  /* Hang it up, tell it to echo, not to be quiet, and to be verbose. */
  ttwriteslow (tty, "\rATE1Q0V1H0\r");
  DELAY_ONE_SECOND();
  /* Discard whatever it may have said... */
  ttdrain (tty);

  /* Tell the modem to say OK... */
  ttwriteslow (tty, "\rAT\r");
  /* See if it does... */
  if ((ttmatch (tty, 1000000, "AT", (char *)0) <= 0)
      || (ttmatch (tty, 1000000, "OK", (char *)0) <= 0))
    {
      ttdrain (tty);
      return 0;
    }
  /* Discard trailing newline... */
  ttdrain (tty);
  return 1;
}

/* Send the modem its init string, and make sure that it got it. */
void modemInit (tty)
     int tty;
{
  char **pp;
  int count = 1;
  int done;
  int sendat;
  int modemError = 0;
  int matchID;

  /* If specified, reset the modem's configuration to the factory default. */
  if (modemcap.factory_defaults)
    {
      ttdrain (tty);
      ttwriteslow (tty, "AT");
      if (ttmatch (tty, 1000000, "AT", (char *)0) <= 0)
	error (atterr, "AT");
      ttwriteslow (tty, modemcap.factory_defaults);
      if (ttmatch (tty, 1000000, modemcap.factory_defaults, (char *)0) <= 0)
	error (atterr, modemcap.factory_defaults);
      ttwrite (tty, "\r");
      if (ttmatch (tty, 1000000, "OK", (char *)0) <= 0)
	error (atterr, modemcap.factory_defaults);
    }
      
  /* If there's no initialization string, don't do anything. */
  if (!modemcap.init_vector)
    return;

  /* Busy out the modem if specified. */
  if (modemcap.busy_out)
    {
      ttdrain (tty);
      ttwriteslow (tty, "ATH1\r");
      if (ttmatch (tty, 1000000, "ATH1", (char *)0) <= 0)
	error (atterr, "AT");
      if (ttmatch (tty, 1000000, "OK", (char *)0) <= 0)
	error ("Can't get modem's attention to busy it out.");
    }

  do {
    /* Send each command... */
    done = 1;
    sendat = 1;

    for (pp = modemcap.init_vector; pp && *pp; pp++)
      {
	if (sendat)
	  {
	    ttdrain (tty);
	    ttwriteslow (tty, "AT");
	    if (ttmatch (tty, 1000000, "AT", (char *)0) <= 0)
	      error (atterr, "AT");
	    count += 2;
	    sendat = 0;
	  }

	ttwrite (tty, *pp);
	matchID = ttmatch (tty, 1000000, *pp, "ERROR", (char *)0);
	if (matchID <= 0)
	  error (atterr, *pp);
	/* Some modems will send ERROR in the middle of a too-long
	   command string, so be ready to deal with it. */
	if (matchID == 2)
	  {
	    syslog (LOG_DEBUG, "got ERROR from modem...");
	    done = 0;
	    modemError = 1;
	    /* We may need to get modem's attention after an ERROR - some
	       modem's parsers aren't terribly robust. */
	    if (!modemAttn (tty) ||
		!modemAttn (tty))
	      error ("Can't get modem's attention after ERROR.");
	    break;
	  }
	count += strlen (*pp);

	if (modemError
	    || !pp [1]
	    || count + strlen (pp [1]) >= modemcap.ibuf_size)
	  {
	    ttwrite (tty, "\r");
	    matchID = ttmatch (tty, 1000000, "OK", "ERROR", (char *)0);
	    if (matchID == 2)
	      {
		/* If the modem said ERROR, one of the init strings
		   was bogus.  Try again to narrow it down, or if
		   that's already been done, complain. */
		if (!modemError)
		  {
		    syslog (LOG_DEBUG, "got ERROR from modem... (1)");
		    done = 0;
		    modemError = 1;
		    if (!modemAttn (tty) ||
			!modemAttn (tty))
		      error ("Can't get modem's attention after ERROR.");
		    break;
		  }
		error ("Modem rejects AT%s", *pp);
	      }
	    else if (matchID <= 0)
	      {
		/* If it timed out, they probably sent something stupid.
		   Reset the modem and try again to narrow it down, or
		   if that's already been done, flame. */
		if (!modemError)
		  {
		    done = 0;
		    modemError = 1;
		    if (!modemAttn (tty)
			&& !modemAttn (tty))
		      error ("Can't get modem's attention after timeout");
		    break;
		  }
		warn ("Timeout waiting for OK after AT%s", *pp);
		error ("Try taking %s out of init string.", *pp);
	      }
	    count = 1;
	    sendat = 1;
	  }
      }
  } while (!done);

  if (modemError)
    warn ("Modem rejected ganged commands; try reducing bs#");

  /* Make sure we can still talk to the modem... */
  ttdrain (tty);
  ttwriteslow (tty, "AT\r");

  if (ttmatch (tty, 1000000, "AT", (char *)0) <= 0)
    error (atterr, *pp);

  matchID = ttmatch (tty, 1000000, "OK", (char *)0);
  if (matchID <= 0)
    error ("Modem not responding after init string.");

  /* If we busied out the modem, hang it up. */
  if (modemcap.busy_out)
    {
      ttdrain (tty);
      ttwriteslow (tty, "ATH\r");
      if (ttmatch (tty, 1000000, "ATH", (char *)0) <= 0)
	error (atterr, "AT");
      if (ttmatch (tty, 1000000, "OK", (char *)0) <= 0)
	error ("Can't get modem's attention to hang it up.");
    }

  ttdrain (tty);
}
