/***************************************************************************/
/*  Module:      $Id: io.c,v 1.6 1995/08/28 03:40:43 maf Exp $
/*  Description: Lower level io for PET protocol and modem
/*  Author:      maf
/*
/* Copyright (c) 1995 Mark Fullmer and The Ohio State University
/***************************************************************************/

/*
$Log: io.c,v $
 * Revision 1.6  1995/08/28  03:40:43  maf
 * tabs, Linux patch for read() and EAGAIN
 *
 * Revision 1.5  1995/08/28  02:59:32  maf
 * ioctl.h
 *
 * Revision 1.4  1995/08/28  02:55:06  maf
 * osf/1 patches
 *
 * Revision 1.3  1995/05/24  00:56:04  maf
 * all unknown string sizes use %.512s for report()
 * merged in AIX contributed port daryl@tcomeng.com & mark@tcomeng.com
 * merged in HP/UX contributed port Mike Lutz & Heath Kehoe @ norand.com
 * I haven't tested the contributed ports...
 *
 * Revision 1.2  1995/03/15  04:40:53  maf
 * *** empty log message ***
 *
 * Revision 1.1  1995/01/10  01:43:05  maf
 * Initial revision
 *
*/


#include <sys/types.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <limits.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <syslog.h>
#include <stdlib.h>
#include "report.h"
#include "sendpage.h"

#ifdef NEED_MALLOC_H
#include <malloc.h>
#endif /* NEED_MALLOC_H */

#ifdef NEED_IOCTL_H
#include <sys/ioctl.h> /* DOES AIX really want this is <ioctl.h>??? TODO */
#endif /* NEED_IOCTL_H */

#ifdef hpux
#include <sys/modem.h>
#endif /* hpux */


/*********************************************************************/
/* Function: OpenModem
/*
/* Returns: 0 for good (the device was opened and initialized)
/*          !0  for bad 
/*
/*********************************************************************/
OpenModem(pcinfo, fd)
struct pcinfo *pcinfo;
int *fd;
{

	extern int errno, debug;
	struct termios t;

#ifdef DEBUG
	if (debug > 2)
		report (LOG_INFO, "opening modem");
#endif /* DEBUG */

	/* open the modem device */
#if defined(hpux) || defined(__osf__)
	if ((*fd = open (pcinfo->modemdev, O_RDWR | O_NOCTTY, 0)) == -1) {
#else
	if ((*fd = open (pcinfo->modemdev, O_RDWR | O_NDELAY, 0)) == -1) {
#endif /* hpux || osf */
		report(LOG_ERR, "open (%.512s): %s", pcinfo->modemdev, strerror(errno));
		return 1; /* bad */
	}

	/* setup the device (baud rate, etc) */
	
	/* get the current device params */
	if (tcgetattr(*fd, &t)) {
		report (LOG_ERR, "tcgetattr (modem): %s", strerror(errno));
		close (*fd);
		return 1; /* bad */
	}

	/* input flags :
	/* ignore breaks, use XON/XOFF flow control
	/* ignore parity errors */
	t.c_iflag = IGNBRK|IXON|IXOFF|IGNPAR;

	/* strip the 8th bit if not on 8 bit mode */
	if (pcinfo->databits != CS8)
		t.c_iflag |= ISTRIP;

	/* output flags:
	/* (none) */
	t.c_oflag = 0;

	/* hardware control flags:
	/* enable receiver, modem control lines go low on close(), use RTS/CTS
		(hardwire) flow control to modem */
	t.c_cflag = pcinfo->speed|pcinfo->databits|pcinfo->parity|pcinfo->stopbits|
		CREAD|C_FLAG;

	/* local flags:
	/* (none) */
	t.c_lflag = 0;

	/* allow read() to return 0 bytes (ie don't block), accept wait atleast */
	/* one second before returning 0 bytes */
	t.c_cc[VMIN] = 0;
	t.c_cc[VTIME] = 10; /* 1/10ths of seconds */

	/* set device params */
	if (tcsetattr(*fd, TCSANOW, &t)) {
		report (LOG_ERR, "tcsetattr (modem): %s", strerror(errno));
		close (*fd);
		return 1; /* bad */
	}

	return 0; /* good */

} /* OpenModem */

/*********************************************************************/
/* Function: ResetModem
/*
/* Returns: 0 for good (the device was reset and is responding)
/*          !0  for bad 
/*
/*********************************************************************/
ResetModem(fd, cbuf, pcinfo)
int fd;
struct cbuf *cbuf;
struct pcinfo *pcinfo;
{

	extern int errno, debug;
#ifdef hpux
	unsigned long modem;
#else
	int modem;
#endif /* hpux */
	

#ifdef TOGGLE_DTR
#ifdef DEBUG
	if (debug >90)
		report (LOG_INFO, "Toggle DTR enter");
#endif /* DEBUG */

	/* first toggle DTR - some modems will go to command state when
		doing this */

	/* get the current modem control lines */
	if (debug >90)
        	report (LOG_INFO, "Get current Modem Control lines");

#ifdef hpux
	if (ioctl(fd, MCGETA, &modem) == -1) {
		report (LOG_ERR, "ioctl (modem, MCGETA): %s", strerror(errno));
#else 
	if (ioctl(fd, TIOCMGET, &modem) == -1) {
		report (LOG_ERR, "ioctl (modem, TIOMGET): %s", strerror(errno));
#endif /* hpux */
		return 1; /* bad */
	}
	
	/* turn off DTR */
#ifdef hpux
	modem &= ~MDTR;
#else
	modem &= ~TIOCM_DTR;
#endif /* hpux */

	sleep (1);

	/* tell the modem */
#ifdef DEBUG
	if (debug >90)
        	report (LOG_INFO, "Tell Modem to Turn Off DTR");
#endif /* DEBUG */

#ifdef hpux
	if (ioctl(fd, MCSETA, &modem) == -1) {
		report (LOG_ERR, "ioctl (modem, MCSETA): %s", strerror(errno));
#else
#ifdef DEBUG
	if (debug >90)
		report (LOG_INFO, "Setting current Modem Control lines");
#endif /* DEBUG */
	if (ioctl(fd, TIOCMSET, &modem) == -1) {
		report (LOG_ERR, "ioctl (modem, TIOCMSET): %s", strerror(errno));
#endif /* hpux */
		return 1; /* bad */
	}

	sleep (1);

#ifdef hpux
	modem |= MDTR;
#else
	modem |= TIOCM_DTR;
#endif /* hpux */

	/* tell the modem */
#ifdef DEBUG
	if (debug >90)
		report (LOG_INFO, "Tell Modem to Turn On DTR");
#endif /* DEBUG */

#ifdef hpux
	if (ioctl(fd, MCSETA, &modem) == -1) {
		report (LOG_ERR, "ioctl (modem, MCSETA): %s", strerror(errno));
#else
	if (ioctl(fd, TIOCMSET, &modem) == -1) {
		report (LOG_ERR, "ioctl (modem, TIOCMSET): %s", strerror(errno));
#endif /* hpux */
		return 1; /* bad */
	}

#ifdef DEBUG
	if (debug >90)
		report (LOG_INFO, "Toggle DTR exit");
#endif /* DEBUG */

#endif /* TOGGLE_DTR */
	
	/* give the modem a chance to recover */
	sleep (1);

	/* now send the +++ wait ath */

	if (SendString (fd, " "))
		return 1;

	sleep (2);

	if (SendString(fd, MODEM_PLUS))
		return 1;

	sleep (3);

	if (SendString (fd, "\r"))
		return 1;

	/* depending on the previous state of the modem, different things
	could show up here - OK\r or the short number command for OK or
	whatever else some goofy clone decided to do.  Forget the return
	code here, instead just go ahead and send the init command */

	sleep (1);

#ifdef DEBUG
	if (debug >90)
		report (LOG_INFO, "Send Modem init sequence");
#endif /* DEBUG */

	if (SendString(fd, pcinfo->modeminit))
		return 1;

	if (SendString(fd, "\r"))
		return 1;

	/* Now wait for the OK short result code */

	if (WaitString(fd, "0\r", cbuf)) 
		return 1;

	/* the previous result code could of been from a few sources */
	/* sync up us to the modem by setting the return code to the word
	/* form, then the number form */

#ifdef DEBUG
	if (debug >90)
		report (LOG_INFO, "Send Modem Word Form ret code init");
#endif /* DEBUG */

	if (SendString (fd, MODEM_WORDS))
		return 1;

	/* should get an OK\r */
	if (WaitString (fd, MODEM_WORD_OK, cbuf))
		return 1;

#ifdef DEBUG
	if (debug >90)
		report (LOG_INFO, "Send Modem Number form ret code init");
#endif /* DEBUG */

	if (SendString (fd, MODEM_NUMS))
		return 1;

	/* should get an 0\r */
	if (WaitString (fd, MODEM_NUM_OK, cbuf))
		return 1;

	/* that's it */

	return 0; /* good */

} /* Reset Modem */

/*********************************************************************/
/* Function: SendString
/*
/* Returns: 0 for good (string was sent)
/*          !0  for bad 
/*
/*********************************************************************/
SendString (fd, string)
int fd;
char *string;
{
	return SendBytes (fd, string, strlen(string));

} /* SendString */


/*********************************************************************/
/* Function: WaitString
/*
/* Returns: 0 for good (string was received)
/*          !0  for bad 
/*
/*	It's assumed the fd is setup so that a read() will block for atleast
/*	1 second before returning 0 bytes.
/*
/*********************************************************************/
WaitString (fd, msg, cbuf)
int fd;
char *msg;
struct cbuf *cbuf;
{

	int msglen, goodbytes, x, n, retries, retries2, matchflag;
	extern int errno, debug;

	msglen = strlen(msg);
	retries = 5;		/* try the loop 5 times */
	retries2 = 50;		/* inner loop 50 times (just a safety) */
	goodbytes = 0;		/* no bytes matched yet */
	matchflag = 0;		/* no full match of msg */

#ifdef DEBUG
	if (debug > 2) 
		report (LOG_INFO, "WaitString (%d bytes) - %.512s",
			msglen, PrintBytes (msg, msglen));
#endif /* DEBUG */

	/* try retries times to read when there is nothing to read */
	/* if there was something to read, then retries2 also gets counted */

	for (; (retries > 0) && (!matchflag); --retries) {

#ifdef DEBUG
	if (debug > 2)
		report (LOG_INFO, "retry = %d", retries);
#endif /* DEBUG */


		if ((n = SafeRead(fd, cbuf->buf+cbuf->bufused,
			CBUF_BYTES - cbuf->bufused)) == -1) 
			return 1;

		cbuf->bufused += n;

#ifdef DEBUG
	if (debug > 2)
		report (LOG_INFO, "cbuf (%d bytes) - %.512s", cbuf->bufused,
			PrintBytes (cbuf->buf, cbuf->bufused));
#endif

		/* try to match */
		for (x = goodbytes; x < cbuf->bufused; ++x) {

			/* if the byte doesn't match, discard the previous bytes
				that matched, else setup to try next */
			if (msg[goodbytes] != cbuf->buf[x]) 
				goodbytes = 0;
			else
				++goodbytes;

			if (goodbytes == msglen) { /* full match */
				matchflag = 1;
				++x;
				break;
			}
		} /* for x */

#ifdef DEBUG
	if (debug > 2)
		report (LOG_INFO,
			"matchflag=%d, x=%d, bufused=%d, goodbytes=%d msglen=%d", 
			matchflag, x, cbuf->bufused, goodbytes, msglen);
#endif /* DEBUG */

		/* save the end of the buffer that didn't match for next time */
		if (matchflag) {
			bcopy(cbuf->buf+x, cbuf->buf, cbuf->bufused-x);
			cbuf->bufused -= x;
		} else {
			bcopy(cbuf->buf+(x-goodbytes), cbuf->buf, goodbytes);
			cbuf->bufused = goodbytes;
		}

		/* give the device some time to talk */
		/* if (!matchflag && n) /* 'n' could be 0 -ByB */
		if (!matchflag)
			sleep (1);

		/* if bytes were actually read, keep trying a little longer */
		if (n && retries2) 
			--retries2, ++retries;
		
	} /* for retries */
			
#ifdef DEBUG
	if (debug > 2)
		report (LOG_INFO, "%smatch", matchflag ? "" : "no" );
#endif /* DEBUG */

	return (matchflag) ? 0 : 1;

} /* WaitString */
	
/*********************************************************************/
/* Function: SafeRead
/*
/* Execute read with check and log
/*	Returns -1 for error.
/*          
/*
/*********************************************************************/
SafeRead (fd, buf, len)
int fd;
char *buf;
int len;
{
	int n;

	if (((n = read(fd, buf, len)) == -1) && (errno != EAGAIN)) {
		report (LOG_ERR, "read(): %s", strerror(errno));
		return -1;
	}

	/* getting an EAGAIN is the same as returning 0 bytes */
	if ((n == -1) && (errno == EAGAIN))
		return 0;

	return n;
	
}

/*********************************************************************/
/* Function: PrintBytes
/*
/* Returns: pointer to upto 132 chars of string
/*          
/*
/*********************************************************************/

#ifdef DEBUG
char *PrintBytes (buf, len) 
char *buf;
int len;

{

	static char tmpBuf[132];
	char s[10], *c;
	int x;

	for (c = buf, x = 0; c != (buf + len); ++c) {

		if (isalnum((int)*c)) {
			if (x <= 129) {
				tmpBuf[x++] = *c; 
				tmpBuf[x++] = ' '; 
			} /* if */
				else break;
		} /* if */
		else {
			if (x <= 128) {
				sprintf(s, "%2.2x ", (int)*c);
				bcopy(s, &tmpBuf[x], 3);
				x += 3;
			} /* if */
				else break;
		} /* else */
			
	} /* for */

	tmpBuf[x] = 0;

	return tmpBuf;

} /* PrintBytes */

#endif /* DEBUG */

/*********************************************************************/
/* Function: DialModem
/*
/* Returns: MODEM_RES_?? constants
/*
/*********************************************************************/

DialModem(fd, cbuf, pcinfo)
int fd;
struct cbuf *cbuf;
struct pcinfo *pcinfo;
{

	extern int errno, debug;
	int x, res;
	char dialstring[PCINFO_STR_LEN*2+1];

	strcpy(dialstring, pcinfo->modemdial);
	strcat(dialstring, pcinfo->phone);
	strcat(dialstring, "\r");
	
	if (SendString (fd, dialstring))
		return MODEM_RES_INTER;

	/* GetResult in cbuf */
	if (WaitLine (fd, cbuf, pcinfo->answertime))
		return MODEM_RES_INTER;

	/* parse and return the result */

	/* convert to int, then discard */
	for (x = 0; x < cbuf->bufused; ++x) {

		if (cbuf->buf[x] == 0x0d) {
			cbuf->buf[x] = 0;
			break;
		}
	} /* for */

	res = atoi((char*)cbuf->buf);

	/* discard what just got picked off */
	bcopy(cbuf->buf+x, cbuf->buf, cbuf->bufused-x);
	cbuf->bufused -= x;

	return res;

} /* DialModem */

/*********************************************************************/
/* Function: WaitLine
/*
/* Returns: 0 for good (a line (string ending in 0x0d) was received)
/*          !0  for bad 
/*
/*********************************************************************/
WaitLine (fd, cbuf, retries)
int fd;
struct cbuf *cbuf;
{

	int x, n, retries2, matchflag;
	extern int errno, debug;

	retries2 = 50;		/* inner loop 50 times (just a safety) */
	matchflag = 0;		/* got a line */

	/* try retries times to read when there is nothing to read */
	/* if there was something to read, then retries2 also gets counted */

	for (; (retries > 0) && (!matchflag); --retries) {

#ifdef DEBUG
	if (debug > 2)
		report (LOG_INFO, "retry = %d", retries);
#endif /* DEBUG */


		if ((n = SafeRead(fd, cbuf->buf+cbuf->bufused,
			CBUF_BYTES - cbuf->bufused)) == -1) 
			return 1;

		cbuf->bufused += n;

#ifdef DEBUG
	if (debug > 2)
		report (LOG_INFO, "cbuf (%d bytes) - %.512s", cbuf->bufused,
			PrintBytes (cbuf->buf, cbuf->bufused));
#endif

		/* try to match */
		for (x = 0; x < cbuf->bufused; ++x) {

			if (cbuf->buf[x] == 0x0d) {
				matchflag = 1;
				break;
			}

		} /* for x */

		/* give the device some time to talk */
		/* if (!matchflag && n) /* 'n' could be 0 -ByB */
		if (!matchflag)
			sleep (1);

		/* if bytes were actually read, keep trying a little longer */
		if (n && retries2) 
			-- retries2, ++retries;
		
	} /* for retries */
			
	return (matchflag) ? 0 : 1;

} /* WaitLine */

	
/*********************************************************************/
/* Function: StartPET
/*
/* Returns: 0 for good (The logon sequence succeeded) 
/*			1 for bad 
/*
/*********************************************************************/

StartPET(fd, cbuf, pcinfo)
int fd;
struct cbuf *cbuf;
struct pcinfo *pcinfo;
{

	extern int errno, debug;
	int retrystart, retrylogin, gotid, gotlogin, res, earlygoahead;
	char passwd[8];
	char *prot;

	gotid = 0;		/* got the PET_ID */
	gotlogin = 0;	/* got through the login sequence */
	retrystart = 3;	/* number of times to try for PET_ID */
	retrylogin = 3;	/* number of times to try for */
	earlygoahead = 0;	/* got an early go ahead message */

	/* construct password line */
	strncpy(passwd, pcinfo->password, 6);
	passwd[6] = 0x0d;
	passwd[7] = 0;

	/* Protocol requires 3 retries of sending CR, waiting for ID= */
	while (--retrystart >= 0) {

		if (SendString (fd, "\r"))
			return 1; /* bad */

		if (!WaitString (fd, PET_ID, cbuf)) {
			gotid = 1;
			break;
		}
	}

	if (!gotid) {
		report (LOG_ERR, "Never got %s (initial logon sequence)", PET_ID);
		return 1; /* bad */
	}

	while (--retrylogin >= 0) {

		/* put remote side into automatic remote entry mode */

		if (pcinfo->protocol == PC_PET1)
			prot = PET_START_SEQ1;
		else if (pcinfo->protocol == PC_PET3)
			prot = PET_START_SEQ3;
		else
			prot = "";

		if (SendString (fd, prot))
			return 1; /* bad */

		/* send the password followrd by 0x0d */
		if (SendString (fd, passwd))
			return 1; /* bad */

		/* at this point, paging central can do a few things */
		/* 1) reply with ID= meaning send the PET_START_SEQ/passwd again 
		/* 2) reply with <CR> <ACK> <CR> meaning ok, go ahead
		/* 3) reply with <CR> <NAK> <CR> meaning try again
		/* 4) reply with <CR> <ESC> <EOT> <CR> meaning get lost
		/* 5) reply with something else, meaning who knows what */

		/* The ID= response is problematic, since it could of been an ID=
		/* that previously wasn't read.  For this reason, the ID= response
		/* is handled as case 5 (ie 2,3,4 weren't matched), and things
		/* start over with the PET_START_SEQ upto retrylogin times */

		/* Wait for a PET reply */
		res = WaitPETReply (fd, cbuf);

		/* accept PET_REPLY_ACCEPT */
		if (res == PET_REPLY_ACCEPT) {
			gotlogin = 1;
			break;
		}
		/* my spec says this is wrong, ofcourse that means nothing when
		/* paging central goes ahead and does it anyways */
		if (res == PET_REPLY_GOAHEAD) {
			gotlogin = 1;
			earlygoahead = 1;
			break;
		}

		report (LOG_INFO, "Unexpected reply during login, retrying - %d", res);

	} /* while retrylogin */

	if (!gotlogin) {
		report (LOG_ERR, "Never got through login sequence - reason %d", res);
		return 1; /* bad */
	}

	/* wait for PET_GOAHEAD message */
	if (!earlygoahead) {

		res = WaitPETReply (fd, cbuf);

		if (res != PET_REPLY_GOAHEAD) {
			report (LOG_ERR, "Never got go ahead message - reason %d", res);
			return 1; /* bad */
		}
	}

	return 0; /* good */

} /* StartPET */

/*********************************************************************/
/* Function: WaitPETReply
/*
/* Returns: PET_REPLY_??
/*
/*********************************************************************/
WaitPETReply (fd, cbuf)
int fd;
struct cbuf *cbuf;
{

	int x, n, retries, retries2, pet, matchflag, goodbytes, j;
	extern int errno, debug;
	int pet_goodbytes[PET_REPLIES];
	extern struct pet_lookup petlookup[];

	retries = 10;		/* upto 10 seconds of idle time per protocol spec */
	retries2 = 50;		/* 50 times (just a safety) */
	matchflag = 0;
	goodbytes = 0;

	for (pet = 0; pet < PET_REPLIES; ++pet)
		pet_goodbytes[pet] = 0;

	/* try retries times to read when there is nothing to read */
	/* if there was something to read, then retries2 also gets counted */

	for (; (retries > 0) && (!matchflag); --retries) {

#ifdef DEBUG
	if (debug > 2)
		report (LOG_INFO, "retry = %d", retries);
#endif /* DEBUG */


		if ((n = SafeRead(fd, cbuf->buf+cbuf->bufused,
			CBUF_BYTES - cbuf->bufused)) == -1) 
			return 1;

		cbuf->bufused += n;

#ifdef DEBUG
	if (debug > 2)
		report (LOG_INFO, "cbuf (%d bytes) - %.512s", cbuf->bufused,
			PrintBytes (cbuf->buf, cbuf->bufused));
#endif


		/* try to match PET_REPLY_?? */
		for (x = goodbytes; x < cbuf->bufused; ++x) {

			/* protocol allows optional linefeeds after carriage returns -
				just ignore them all. */
			if (cbuf->buf[x] == ASCII_LF) 
				continue;

			for (pet = 0; pet < PET_REPLIES; ++pet) {

				if (cbuf->buf[x] != pet_lookup[pet].msg[pet_goodbytes[pet]])
					pet_goodbytes[pet] = 0;
				else 
					++pet_goodbytes[pet];

				if (pet_goodbytes[pet] == pet_lookup[pet].msglen) {
					matchflag = 1;
					++x;
					break;
				} /* if */
#ifdef DEBUG
	if (debug > 2)
	report (LOG_INFO,
		"pet=%d, matchflag=%d, pet_lookup[].msglen=%d, pet_goodbytes[]=%d",
		pet, matchflag, pet_lookup[pet].msglen, pet_goodbytes[pet]);
#endif /* DEBUG */

			} /* for pet */

			if (matchflag)
				break;

		} /* for x */

	/* calculate the max # of bytes in this buf to save */
	for (j = 0, goodbytes = 0; j < PET_REPLIES; ++j) 
		if (pet_goodbytes[j] > goodbytes)
			goodbytes = pet_goodbytes[j];

#ifdef DEBUG
	if (debug > 2)
		report (LOG_INFO, "matchflag=%d, x=%d, bufused=%d, goodbytes=%d pet=%d",
			matchflag, x, cbuf->bufused, goodbytes, pet);
#endif /* DEBUG */

		/* save the end of the buffer that didn't match for next time */
		if (matchflag) {
			bcopy(cbuf->buf+x, cbuf->buf, cbuf->bufused-x);
			cbuf->bufused -= x;
		} else {
			bcopy(cbuf->buf+(x-goodbytes), cbuf->buf,
				goodbytes);
			cbuf->bufused = goodbytes;
		}

#ifdef DEBUG
	if (debug > 2)
		report (LOG_INFO, "after bcopy buf (%d bytes) - %.512s", cbuf->bufused,
			PrintBytes (cbuf->buf, cbuf->bufused));
#endif /* DEBUG */


		/* give the device some time to talk */
		/* if (!matchflag && n) /* 'n' could be 0 -ByB */
		if (!matchflag)
			sleep (1);

		/* if bytes were actually read, keep trying a little longer */
		if (n && retries2)
			-- retries2, ++retries;
		
	} /* for retries */

	return (matchflag) ? pet : PET_ERROR;

} /* WaitPETReply */

	
/*********************************************************************/
/* Function: SendPETTransaction
/*
/* Returns: 0 for good (the transaction was accepted)
/*			1 for bad (some kind of error that should be retried)
/*			2 for bad (page rejected)
/*			3 for bad (all pages rejected)
/*
/*********************************************************************/

SendPETTransaction(fd, cbuf, field1, field2, protocol)
int fd;
struct cbuf *cbuf;
char *field1, *field2;
{

	extern int errno, debug;
	int retries, goodsend;
	char pet_packet [256];
	int pet_len, res, pagereject, abandon;

	pet_len = 256;
	retries = 3;
	goodsend = 0;
	pagereject = 0;
	abandon = 0;

	/* create the pet_packet */
	if (MakePETPacket(pet_packet, &pet_len, field1, field2, protocol))
		return 1;

	while ((--retries >= 0) && (!goodsend)) {

		if (SendBytes (fd, pet_packet, pet_len))
			return 1;

		res = WaitPETReply (fd, cbuf);


		switch (res) {

			case PET_REPLY_ACCEPT:
				report (LOG_INFO, "transaction accepted");
				goodsend = 1;
				break;

			case PET_REPLY_REJECT:
				report (LOG_WARNING, "transaction rejected, retrying");
				pagereject = 1;
				break;

			case PET_REPLY_ABANDON:
				report (LOG_ERR, "abandon reply, giving up");
				abandon = 1;
				retries = 0;
				break;

			case PET_REPLY_GOODBYE:
				report (LOG_ERR, "goodby after transaction -- did it send?");
				retries = 0;
				break;

			default:
				report (LOG_ERR, "unexpected response from WaitPETReply - %d",
					res);
				break;
		} /* switch */

	} /* while --retries */

#ifdef DEBUG
	if (debug > 2)
		report (LOG_INFO, "SendPETTransaction exiting with goodsend=%d", 
			goodsend);
#endif /* DEBUG */

	if (goodsend)
		return 0;
	else if (abandon)
		return 3;
	else if (pagereject)
		return 2;
	else
		return 1;

} /* SendPETTransaction */

/*********************************************************************/
/* Function: MakePETPacket
/*
/* Returns: 0 for good (the packet was successfully constructed)
/*			1 for bad 
/*
/*********************************************************************/

MakePETPacket(buf, buflen, field1, field2, protocol) 
char *buf, *field1, *field2;
int *buflen;
int protocol;
{

	int len1, len2, x, i;
	int csum;
	char *p;

	len1 = strlen(field1);
	len2 = strlen(field2);
	csum = 0;

	if ((len1 + len2 + 8) > *buflen)
		return 1;

	/* start with a STX */
	buf[0] = ASCII_STX;
	i = 1;

	/* add field 1 */
	bcopy(field1, &buf[i], len1);
	i += len1;

	/* field 1 ends with CR */
	buf[i] = ASCII_CR;
	++i;

	/* add field 2 */
	bcopy(field2, &buf[i], len2);
	i += len2;

	/* field 2 ends with CR */
	buf[i] = ASCII_CR;
	++i;

	/* if the protocol is PC_PET3 need to add CR again for destination */
	if (protocol == PC_PET3) 
		buf[i++] = ASCII_CR;

	/* end fields with ETX */
	buf[i] = ASCII_ETX;
	++i;

	/* 7 bit checksum follows */
	for (x = 0; x < i; ++x)
		csum += (buf[x] & 0x7f);

	/* the checksum is represented as 3 ascii characters having the values
		between 0x30 and 0x3f */
	/* tack it on backwards */
	buf[i+2] = 0x30 + (csum & 0x0f);
	csum >>= 4;

	buf[i+1] = 0x30 + (csum & 0x0f);
	csum >>= 4;

	buf[i] = 0x30 + (csum & 0x0f);

	/* end it all with a CR */
	buf[i+3] = ASCII_CR;

	*buflen = i+4;

	return 0;

} /* MakePETPacket 

/*********************************************************************/
/* Function: SendBytes
/*
/* Returns: 0 for good (bytes were sent)
/*          !0  for bad 
/*
/*********************************************************************/
SendBytes (fd, bytes, len)
int fd;
char *bytes;
int len;
{

	extern int errno, debug;

#ifdef DEBUG
	if (debug > 2) 
		report (LOG_INFO, "SendBytes (%d bytes) -  %.512s", len,
			PrintBytes (bytes, len));
#endif /* DEBUG */

	if (write (fd, bytes, len) == -1) {
		report(LOG_ERR, "write(): %s", strerror(errno));
		return 1; /* bad */
	}

	return 0; /* good */
} /* SendBytes */

/*********************************************************************/
/* Function: EndPETTransaction
/*
/* Returns: 0 for good the (transaction was ended)
/*			1 for bad (unknown state)
/*			2 got ABANDON packet (this invalidates all previous transactions)
/*
/*********************************************************************/
EndPETTransaction(fd, cbuf)
int fd;
struct cbuf *cbuf;
{

	extern int errno, debug;
	int res;

	if (SendString (fd, PET_END_SEQ))
		return 1;

	res = WaitPETReply(fd, cbuf);

	if (res == PET_REPLY_ABANDON)
		return 2;

	if (res == PET_REPLY_GOODBYE)
		return 0;

	/* be liberal on what we accept...*/
	report (LOG_WARNING, "unexpected termination reply %d", res);

	return 0;

} /* EndPETTransaction */


#ifdef UUCP_LOCKING

/*********************************************************************/
/* Function: LockTTY
/*
/* Returns: 1 don't have lock
/*			0 got lock
/*
/* error reporting done via report()
/*
/*********************************************************************/

LockTTY(ttyname)
char *ttyname;
{

	char lockfile[_POSIX_PATH_MAX], tmpfile[_POSIX_PATH_MAX];
	char *tty;
	int fd, unlocked, isrunning;
	pid_t pid, pid2;
	char cpid[12]; /* 32 bits is 10 chars max + \n + terminator */
	FILE *FP;

	unlocked = 1;	/* don't have lock */
	fd = -1;

	/* point to the last component of the pathname for the tty */
	if ((tty = strrchr(ttyname, '/')))
		++tty;
	else
		tty = ttyname;

	pid = getpid();

	/* create a temp file and lock file pathnames */
	sprintf(tmpfile, "%sLTMP.%u", TTY_LOCKDIR, (u_int)pid);
	sprintf(lockfile, "%sLCK..%s", TTY_LOCKDIR, tty);

	/* make sure there's not an extra one laying around */
	unlink(tmpfile);

	/* create the temp file */
	if ((fd = open(tmpfile, O_WRONLY|O_CREAT|O_EXCL, 0444)) == -1) {
		report(LOG_ERR, "open(%.512s): %s", tmpfile, strerror(errno));
		goto LockTTYout;
	}

	sprintf(cpid, "%u\n", (u_int)pid);

	/* stick our pid in the file */
	if (write(fd, cpid, strlen(cpid)) == -1) {
		report(LOG_ERR, "write(%.512s): %s", tmpfile, strerror(errno));
		goto LockTTYout;
	}

	if (close(fd) == -1) {
		fd = -1;
		goto LockTTYout;
	}

	fd = -1;


	/* try to link our temp file to the real one */

	if (!link(tmpfile, lockfile)) {
		unlocked = 0;
		goto LockTTYout1; /* good */
	}

	/* couldn't get the lock, but all is not lost.  The lockfile has
	a process id in it, Maybe that process died an untimely death and
	couldn't cleanup after itself */

	isrunning = 1;

	if ((FP = fopen(lockfile, "r"))) {

		fscanf(FP, "%lu", &pid2);
		fclose(FP);

		if ((pid2) && (kill((int)pid2, 0))) 
			isrunning = 0;
	}

	if (!isrunning) {
		report(LOG_INFO, "ignoring stale lock file: %.512s", lockfile);

		unlink(lockfile);

		if (!link(tmpfile, lockfile)) {
			unlocked = 0;
			goto LockTTYout1; /* good */
		}

		report(LOG_ERR, "ignore lock file failed");
	}


LockTTYout1:

	/* remove the temp file */
	if (unlink(tmpfile) == -1) 
		report(LOG_WARNING, "unlink(%.512s): %s", tmpfile, strerror(errno));


LockTTYout:

	if (fd != -1)
		close (fd);

	return unlocked;

} /* LockTTY */

/*********************************************************************/
/* Function: UnLockTTY
/*
/* Returns: 0 removed lock
/*			1 couldn't remove lock
/*
/* error reporting done via report()
/*
/*********************************************************************/

UnLockTTY(ttyname)
char *ttyname;
{
	char lockfile[_POSIX_PATH_MAX];
	char *tty;

	/* point to the last component of the pathname for the tty */
	if ((tty = strrchr(ttyname, '/')))
		++tty;
	else
		tty = ttyname;

	sprintf(lockfile, "%sLCK..%s", TTY_LOCKDIR, tty);

	if (unlink(lockfile) == -1) {
		report (LOG_ERR, "can't remove lock for %.512s", tty);
		return 1; /* bad */
	}

	return 0; /* good */

} /* UnLockTTY */

#endif /* UUCP_LOCKING

/*********************************************************************/
/* Function: HangUpModem
/*
/* Returns: 0 for good - it was able to send the modem hangup comand
/*			and get a response.
/*          !0  for bad 
/*
/*********************************************************************/
HangUpModem(fd, cbuf)
int fd;
struct cbuf *cbuf;
{

	/* give the modem a chance to recover */
	sleep (1);

	/* now send the +++ wait ath */
	if (SendString (fd, " "))
		return 1;

	sleep (2);

	if (SendString(fd, MODEM_PLUS))
		return 1;

	sleep (3);

	if (SendString (fd, "\r"))
		return 1;

	/* Now wait for the OK short result code */
	if (WaitString(fd, "0\r", cbuf)) 
		return 1;

	return 0;
}
