#ifndef lint
static char *sccsid = "@(#)page.c	1.4	MS/ACF	89/05/08";
#endif

/*
 *	tip/page.c	(Douglas Kingston, Morgan Stanley & Co.)
 *
 *	This file contains code to send alphanumeric pages via
 *	dialup paging services that support "Remote Entry Device
 *	Protocol" (aka "IXO").  These services typically send to
 *	devices like the Motorola PMR-2000 pager.
 *
 *	This module is designed to use the ACK/NAK packet protocol
 *	to ensure the highest likelyhood of correct delivery of the
 *	message.  The packets are checksummed and all messages are
 *	acknowledged by the receiver.
 *
 *	To use this facility, tip must be called with the -p option,
 *	a system name (what system to dial up) and finally a data
 *	string which contains 3 items:  A login code/password, a list
 *	pager id's to receive the message, and the message text.
 *	The three fields are separated by tabs.  The pager id field
 *	is further subdivided into a list of pager ids by commas.
 *
 *	For example:
 *	tip -p metro "12345678\t20001,20002\tPlease call the office."
 *
 *	Would call the system called "metro" as listed in /etc/remote
 *	and login with the ID 12345678.  The message "Please call
 *	the office." would be sent to pagers 20001 and 20002.
 *
 *	This code has been tested only on the Metromedia Paging Services
 *	system around New York City, though it should work on other
 *	similiar systems.
 *
 *	Douglas Kingston
 *	Morgan Stanley & Co. (19th Floor)
 *	1251 Avenue of the Americas
 *	NY, NY, 10020
 */

#include <stdio.h>
#include <ctype.h>
#include <signal.h>
#include <sys/time.h>
#include "tip.h"

#define STX	002
#define	ETX	003
#define	EOT	004
#define	ACK	006
#define	NAK	025
#define	ETB	027
#define	ESC	033
#define	RS	036

extern int vflag, FD;
extern jmp_buf	jmpbuf;

extern char *strchr();
extern int recvtimeout();

char	inbuf[270];
char	mbuf[32];
char	packet[270];

static char *addstr();

sendpage(data)
char *data;
{
	int ret = 1;
	int trys, i;
	char *cp, *pager, *message;

	if (boolean(value(VERBOSE)))
		vflag++;
	if (vflag)
		fputs("[Connected]\n", stderr);

	cp = data;
	if (pager = strchr(data, '\t')) {
		*pager = 0;
		pager++;
	} else {
		fprintf(stderr, "Bad paging data: '%s'\n", data);
		return(1);
	}

	trys = 0;
	do {
		if (trys++ > 3) {
			fprintf(stderr, "ID= retries failed\n");
			goto done;
		}
		if (pgsend("\r"))
			return(1);
	} while (pgrecv("ID=", 5) < 3);

	trys = 0;
login_again:

	if (pgsend("\033") || pgsend(cp) || pgsend("\r"))
		return(1);

	/* Wait for ACK, NAK, EOT or Ready(?) */
	if (pgrecv("\r", 10) <= 0) {
		fprintf(stderr, "login timeout\n");
		goto done;
	}
	switch(inbuf[0]) {
	default:	if (trys++ < 4) goto login_again; else goto done;
	case ACK:	break;
	case NAK:	if (trys++ < 4) goto login_again; else goto done;
	case RS:	fprintf(stderr, "login got RS\n"); goto done;
	case ESC:
		switch(inbuf[1]) {
		case EOT:	goto done;
		case '[':	if(inbuf[2] == 'p') break;
		default:	fprintf(stderr, "got unknown ESC code\n");
				goto login_again;
		}
	}
	pgrecv("\r", 10);	/* Swallow Go Ahead <ESC>[p */

	if (message = strchr(pager, '\t')) {
		*message = 0;
		message++;
	} else
		message = "[Message not specified]";
next_pager:
	if (cp = strchr(pager, ','))
		*cp++ = 0;
	buildpkt(pager, message);

	trys = 0;
resend:
	if (pgsend(packet))
		return(1);
	pgrecv("^C\r\n", 10);	/* ^C\r\n */

	/* Wait for ACK, NAK, EOT or Ready(?) */
	if ((i = pgrecv("\r", 10)) <= 0) {
		fprintf(stderr, "page send no reply\n");
		goto done;
	}
	switch(inbuf[i-2]) {
	default:	fprintf(stderr, "page send reply error\n");
			goto done;
	case ACK:	ret = 0; goto sent;
	case NAK:	if (trys++ < 4) goto resend; else goto done;
	case RS:	fprintf(stderr, "page send got RS\n"); goto done;
	case ESC:
		switch(inbuf[1]) {
		case EOT:	goto done;
		case '[':	if(inbuf[2] == 'p') { ret = 1; goto sent; }
		default:	fprintf(stderr, "got unknown ESC code\n");
				goto done;
		}
	}
sent:
	if(cp) {
		pager = cp;
		goto next_pager;
	}

done:
	/* Logout: EOT CR */
	if (pgsend("\004\r"))
		return(ret);
	pgrecv("\r", 3);
	fputs(ret ? "[Error in delivery]\n" : inbuf, stderr);
	pgrecv("\r", 3);
	return(ret);
}

pgsend(cp)
char *cp;
{
	int	i, len = strlen(cp);

	nap(40);
	if(vflag)
		fprintf(stderr, "sending: ");
	for (i = 0; i < len; i++) {
		nap(75);
		if(vflag)
			show(cp, 1, stderr);
		if (write(FD, cp, 1) != 1) {
			fprintf(stderr, "pgsend error\n");
			return(-1);
		}
		cp++;
	}
	if(vflag)
		fputc('\n', stderr);
	return(0);
}

pgrecv(want, secs)
char *want;
int secs;
{
	jmp_buf savebuf;
	int i, size;
	char *cp, *endcp;
	
	bcopy(jmpbuf, savebuf, sizeof(jmpbuf));
	if (setjmp(jmpbuf)) {
		if(vflag)
			fprintf(stderr, "\nrecv: timeout\n");
		bcopy(savebuf, jmpbuf, sizeof(jmpbuf));
		return(0);
	}
	signal(SIGALRM, recvtimeout);
	alarm(secs);

	size = strlen(want);
	if(vflag) {
		fprintf(stderr, "recv (want ");
		show(want, size, stderr);
		fprintf(stderr, "/%d): ", size);
	}
	i = size-1;

	cp = inbuf; endcp = inbuf+sizeof(inbuf)-1;
	*cp = 0;
	mbuf[0] = 0;
	while (strncmp(mbuf, want, size) != 0 && cp < endcp) {
		bcopy(mbuf+1, mbuf, size);
		if (read(FD, &mbuf[i], 1) != 1)
			goto readerror;
		mbuf[i] &= 0177;
		*cp++ = mbuf[i];
		*cp = 0;
		if(vflag)
			show(&mbuf[i], 1, stderr);
	}
	alarm(0);
	bcopy(savebuf, jmpbuf, sizeof(jmpbuf));
	if (vflag)
		fputs(" -- got it\n", stderr);
	return(cp - inbuf);
readerror:
	alarm(0);
	bcopy(savebuf, jmpbuf, sizeof(jmpbuf));
	return(-1);
}

buildpkt(pager, message)
char *pager, *message;
{
	char	*cp = &packet[0];
	int	sum;

	*cp++ = STX;
	sum = STX;

	cp = addstr(pager, &sum, cp);
	cp = addstr(message, &sum, cp);

	*cp++ = ETX;
	sum += ETX;
	*cp++ = '0' + ((sum>>8)&0xf);
	*cp++ = '0' + ((sum>>4)&0xf);
	*cp++ = '0' + (sum&0xf);
	*cp++ = '\r';
	*cp = 0;
}

static char *
addstr(s, sump, cp)
char *s, *cp;
int *sump;
{
	char c;
	int sum = *sump;

	while(c = *s++) {
		*cp = (isprint(c) ? c : '?');
		sum += *cp;
		cp++;
	}
	*cp++ = '\r';
	sum += '\r';

	*sump = sum;
	return(cp);
}
