#ifndef lint
static char *RCSid = "$Header: /source/users/msg/msg/RCS/remote.c,v 1.5 91/08/13 23:59:09 jmsellens Exp $";
#endif

/* remote.c
   Deliver a message to a user on a remote machine using TCP/IP.
*/

#include "msg.h"
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#if vms
#include "twg$tcp:[netdist.include.arpa]inet.h"
#else
#include <arpa/inet.h>
#endif
char *syserr();

private	int			sock = -1;	/* the socket */
private	struct sockaddr_in	sin;


private struct hostent *
find_host( desthost )
char *desthost;
{
    /* stolen from finger */
    static struct hostent def;
    static struct in_addr defaddr;
    static char *alist[1];
    static char namebuf[128];
    struct hostent *hp;

    hp = gethostbyname(desthost);
    if (hp == NULL) {
	defaddr.s_addr = inet_addr(desthost);
	if (defaddr.s_addr == -1)
	    return( (struct hostent *)NULL );
	strcpy(namebuf, desthost);
	def.h_name = namebuf;
#ifdef h_addr
	/* older things don't have this field.  Newer ones #define h_addr */
	def.h_addr_list = alist;
#endif
	def.h_addr = (char *)&defaddr;
	def.h_length = sizeof (struct in_addr);
	def.h_addrtype = AF_INET;
	def.h_aliases = 0;
	hp = &def;
    }
    return( hp );
}


private int
start_remote( desthost )
char *desthost;
{
    static struct servent *sp = NULL;
    struct hostent *hp;
    char buf[BUFSIZ];
    version ver;
    int errcode, i;

    if (sp == NULL)
	    sp = getservbyname( "msg", "tcp" );
    if (sp == NULL)
	fatal( "msg/tcp not defined in services file" );
    hp = find_host( desthost );
    if (hp == NULL) {
	errmsg( BAD_HOST, desthost );
	return( BAD_HOST );
    }
    bzero( (char *)&sin, sizeof(sin) );
    bcopy( hp->h_addr, (char *)&sin.sin_addr, hp->h_length );
    sin.sin_family	= hp->h_addrtype;
    sin.sin_port	= sp->s_port;
    sock = socket( hp->h_addrtype, SOCK_STREAM, 0 );
    if (sock < 0)
	fatal( "couldn't create socket: %s", syserr() );
    if (connect( sock, (char *)&sin, sizeof(sin) ) < 0) {
	errmsg( NO_CONNECT, desthost, syserr() );
	(void) close(sock);
	sock = -1;
	return( NO_CONNECT );
    }
    /* Now make sure we have a compatible version */
    ver.v.zero = htonl(0);
    ver.v.vnum = htonl(VERSION);
#if vms
    if ( netwrite( sock, &ver, sizeof(ver) ) < 0 )
#else
    if ( write( sock, &ver, sizeof(ver) ) < 0 )
#endif
	return( REMOTE_WRITE );
    errcode = readint( &i );
    if ( errcode == OK )
	errcode = i;
    if ( errcode == VERSION_OK ) {
	errcode = OK;
    } else if ( errcode == ERROR_MESSAGE ) {
	int code = readint( &i );
	if ( code == OK ) {
#if vms
	    code = netread( sock, buf, i );
#else
	    code = read( sock, buf, i );
#endif
	    if ( code == -1 ) {
		errcode = REMOTE_READ;
	    } else {
		buf[i] = '\0';
		errprintf( buf );
	    }
	} else
	    errcode = code;
    }
    return( errcode );
}


private
stop_remote()
{
    if ( sock >= 0 ) {
	(void) close( sock );
	sock = -1;
    }
}


int
remote( sender, user, desthost, message, silent )
char *sender, *user, *desthost, *message;
int silent;
{
    char buf[BUFSIZ];
    int errcode;
    if ( (errcode = start_remote( desthost )) == OK ) {
	int writeerr = 0;
	header h;
	h.slen = htonl(strlen(sender));
	h.ulen = htonl(strlen(user));
	h.mlen = htonl(strlen(message));
#if vms
	if ( netwrite( sock, (char *)&h, sizeof(h) ) < 0 ) writeerr++;
	if ( netwrite( sock, sender, strlen(sender) ) < 0 ) writeerr++;
	if ( netwrite( sock, user, strlen(user) ) < 0 ) writeerr++;
	if ( netwrite( sock, message, strlen(message) ) < 0 ) writeerr++;
#else
	if ( write( sock, (char *)&h, sizeof(h) ) < 0 ) writeerr++;
	if ( write( sock, sender, strlen(sender) ) < 0 ) writeerr++;
	if ( write( sock, user, strlen(user) ) < 0 ) writeerr++;
	if ( write( sock, message, strlen(message) ) < 0 ) writeerr++;
#endif
	if ( writeerr ) {
	    errcode = REMOTE_WRITE;
	} else {
	    do {
		int i;
		errcode = readint( &i );
		if ( errcode == OK ) errcode = i;
		if ( errcode == ERROR_MESSAGE ) {
		    int code = readint( &i );
		    if ( code == OK ) {
#if vms
			code = read( sock, buf, i );
#else
			code = read( sock, buf, i );
#endif
			if ( code == -1 ) {
			    errcode = REMOTE_READ;
			} else {
			    buf[i] = '\0';
			    if (!silent) errprintf( buf );
			}
		    } else
			errcode = code;
		}
	    } while ( errcode == ERROR_MESSAGE );
	}
    }
    switch ( errcode ) {
	case REMOTE_READ:
	case REMOTE_WRITE:
	    errmsg( errcode, desthost, syserr() );
	    break;
	case NO_ACK:
	    errmsg( errcode, desthost );
	    break;
	default:	/* do nothing - got message back from remote */
	    break;
    }
    stop_remote(); /* hope close doesn't change errno */
    return( errcode );
}


int
readint(np)
int *np;
{
    int n;

#if vms
    switch (netread(sock, &n, sizeof n)) {
#else
    switch (read(sock, &n, sizeof n)) {
#endif
	case 0:
	    return( NO_ACK );
	case sizeof(n):
	    *np = ntohl(n);
	    return( OK );
	case -1:
	default:	/* for incomplete data */
	    return( REMOTE_READ );
    }
    /* NOTREACHED */
}
