////////////////////////////////////////////////////////////////////////////////
//  yserver.C                                                                 //
//  This file contains a server that provides YART/IOM functionality to multi-//
//  ple clients (e.g. via network) and a demo client.                         //
//                                                                            //
//  * currently no support for return value of evaluation                     //
//  * currently no soft logout of the clients (the first client that sends an //
//    exit will terminate the server, too.)                                   //
//                                                                            //
//  start the YART server with: yserver | rtsh > /dev/null &                  //
//  start the demo client with: yclient <hostname>                            //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
// Makefile:                                                                  //
// CC = CC								      //
// 									      //
// all: yserver.C							      //
// 	rm -f yserver yclient						      //
// 	$(CC) -o yserver -DYSERVER yserver.C -lsun			      //
// 	$(CC) -o yclient yserver.C -lsun				      //
//                                                                            //
//  LAST EDIT: Thu Mar  9 17:57:24 1995 by ekki(@prakinf.tu-ilmenau.de)
////////////////////////////////////////////////////////////////////////////////
//  This file belongs to the YART implementation. Copying, distribution and   //
//  legal info is in the file COPYRGHT which should be distributed with this  //
//  file. If COPYRGHT is not available or for more info please contact:       //
//                                                                            //
//		yart@prakinf.tu-ilmenau.de                                    //
//                                                                            //
// (C) Copyright 1993 - 1995 YART team                                        //
////////////////////////////////////////////////////////////////////////////////

#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <rpc/types.h>
#include <rpc/xdr.h>
#include <sys/types.h>

extern "C" int select(int, fd_set*, fd_set*, fd_set*, struct timeval*);

#ifdef LINUX
#include <linux/in.h>
#endif

#define bzero(a,b) memset(a,0,b)

// socket adress:
const int PortAdr = 4711;

// number of allowed clients:
const int PortNum = 10;

void createXDR(XDR *, int, enum xdr_op);

fd_set tfds;

class XDRData {
    XDR rxdr, wxdr;
    int no;

    XDRData(XDR _rxdr, XDR _wxdr): rxdr(_rxdr), wxdr(_wxdr) {}
    ~XDRData() {
	int fd = rxdr.x_handy;
	xdr_destroy( &rxdr );
	xdr_destroy( &wxdr );
	close( fd );
    }
    static XDRData *data[PortNum];
  public:
    static void init() {
	for (int i = 0; i < PortNum; i++) data[i] = 0;
    }
    static void create(XDR, XDR);
    static void check();
};

void XDRData::create(XDR _rxdr, XDR _wxdr) {
    for (int i = 0; i < PortNum; i++) {
	if (!data[i]) {
	    data[i] = new XDRData( _rxdr, _wxdr );
	    return;
	}
    }
    fputs( "YSVR: To many connections. Increase PortNum and recompile, please.\n", stderr );
    exit( 1 );
}

void XDRData::check() {
    for (int i = 0; i < PortNum; i++) {
	if (data[i]) {
	    if ( FD_ISSET( data[i]->rxdr.x_handy, &tfds ) ) {
		char *cmd = 0;
		if (!xdr_wrapstring( &data[i]->rxdr, &cmd )) {
		    fputs( "YSVR: Received bad command.\n", stderr );
		    exit( 1 );
		}
		else {
		    // put it to stdout:
		    fprintf( stdout, "%s\n", cmd );
		    fflush(stdout);
		    free( cmd );
		}
	    }
	}
    }
}

XDRData *XDRData::data[PortNum];

#ifdef YSERVER

// the YART server:

int main() {
    XDRData::init();

    sockaddr_in saddr;
    memset( &saddr, 0, sizeof( sockaddr_in ) );
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons( PortAdr );

    int sofd;
    if ((sofd = socket( AF_INET, SOCK_STREAM, 0 )) == -1
	|| bind( sofd, (sockaddr*)&saddr, sizeof( sockaddr_in )) == -1
	|| listen( sofd, 5 ) == -1 ) {
	fputs( "YSVR: Couldn't create the socket. May be the program is already running on this host? Or try it some minutes later again.\n", stderr );
	exit( 1 );
    }

    fd_set rfds;
    FD_ZERO( &rfds );
    FD_SET( sofd, &rfds );
    int fdMax = sofd;

    while (1) {
	tfds = rfds;
	if ( select( fdMax + 1, &tfds, 0, 0, 0 ) > 0 ) {
	    if ( FD_ISSET( sofd, &tfds ) ) {
		// create connection:
		int co = accept( sofd, 0, 0 );
		if (co == -1) fputs( "YSVR: Couldn't accept connection.\n", stderr );
		else {
		    XDR rxdr, wxdr;
		    createXDR( &rxdr, co, XDR_DECODE );
		    createXDR( &wxdr, co, XDR_ENCODE );

		    char *cmd = 0;
		    if (!xdr_wrapstring( &rxdr, &cmd ))
			fputs( "YSVR: Couldn't read initial command.\n", stderr );
		    else {
			XDRData::create( rxdr, wxdr );
			// put it to stdout:
			fprintf( stdout, "%s\n", cmd );
			fflush(stdout);

			FD_SET( co, &rfds );
			if (co > fdMax ) fdMax = co;
		    }
		}
	    }
	    XDRData::check();
	}
    }
}
#else

// the demo client reading from console:

int main(int argc, char *argv[]) {
    if (argc != 2) {
	fputs( "YCLN: Need one argument: the hostname to connect!\n", stderr );
	exit( 1 );
    }

    hostent *he = gethostbyname(argv[1]);

    if (!he) {
	extern int h_errno;
	switch( h_errno ) {
	  case HOST_NOT_FOUND: fprintf( stderr, "YCLN: Host %s not found.\n", argv[1] ); exit( 1 );
	  case TRY_AGAIN: fputs( "YCLN: NIS error, try again.\n", stderr ); exit( 1 );
	  case NO_RECOVERY: fputs( "YCLN: Non-recoverable server error.\n", stderr ); exit( 1 );
	  case NO_DATA: fputs( "YCLN: No address associated with this name.\n", stderr ); exit( 1 );
	}
    }
    sockaddr_in saddr;

    memset( &saddr, 0, sizeof( sockaddr_in ) );
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons( PortAdr );
    saddr.sin_addr = *(struct in_addr*)he->h_addr_list[0];

    // initialize the scene socket:
    int sofd;
    if (( sofd = socket( AF_INET, SOCK_STREAM, 0 )) == -1
	|| connect( sofd, (sockaddr*)&saddr, sizeof( saddr )) == -1 ) {
	fputs( "YCLN: Couldn't connect to the server.\n", stderr );
	exit( 1 );
    }

    XDR rxdr, wxdr;
    createXDR( &rxdr, sofd, XDR_DECODE );
    createXDR( &wxdr, sofd, XDR_ENCODE );

    char *cmd = new char[5000];
    fputs( "YCLN: ready.\n", stdout );
    while (1) {
	gets( cmd );
	xdr_wrapstring( &wxdr, &cmd );
	fputs( "YCLN: text sent.\n", stdout );
    }
}

#endif

static bool_t xdrfd_getlong(XDR *, long *);
static bool_t xdrfd_putlong(XDR *, long *);
static bool_t xdrfd_getbytes(XDR *, void *, u_int);
static bool_t xdrfd_putbytes(XDR *, void *, u_int);
static u_int xdrfd_getpostn(XDR *);
static bool_t xdrfd_setpostn(XDR *, u_int);
static long *xdrfd_inline(XDR *, int);
static void xdrfd_destroy(XDR *);

static struct __xdr_s::xdr_ops default_xdrfd_ops = {
    xdrfd_getlong,
    xdrfd_putlong,
    xdrfd_getbytes,
    xdrfd_putbytes,
    xdrfd_getpostn,
    xdrfd_setpostn,
    xdrfd_inline,
    xdrfd_destroy
};

void createXDR(XDR *xdrs, int fd, enum xdr_op op) {
    xdrs->x_op = op;
    xdrs->x_ops = &default_xdrfd_ops;
    xdrs->x_public = xdrs->x_private = xdrs->x_base = NULL;
    xdrs->x_handy = fd;
}

static bool_t xdrfd_getlong(XDR *xdrs, long *lp) {
    u_char buf[4];

    if (!xdrfd_getbytes(xdrs, buf, 4)) return FALSE;
    *lp = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
    return TRUE;
}

static bool_t xdrfd_putlong(XDR *xdrs, long *lp) {
    u_long ul;
    u_char buf[4];

    ul = (u_long)*lp;
    buf[3] = (u_char)ul;
    buf[2] = (u_char)(ul >>= 8);
    buf[1] = (u_char)(ul >>= 8);
    buf[0] = (u_char)(ul >> 8);
    if (write(xdrs->x_handy, buf, 4) != 4) return FALSE;
    return TRUE;
}

static bool_t xdrfd_getbytes(XDR *xdrs, void *buf, u_int n) {
    int r;
    while (n != 0) {
	if ((r = read(xdrs->x_handy, buf, n)) == -1 || r == 0)
	    return FALSE;
	n -= r;
	buf = (char *)buf + r;
    }
    return TRUE;
}

static bool_t xdrfd_putbytes(XDR *xdrs, void *buf, u_int n) {
    if (write(xdrs->x_handy, buf, n) != n) return FALSE;
    return TRUE;
}

static u_int xdrfd_getpostn(XDR *) {
    fprintf(stderr, "YSVR: xdrfd_getpostn() called\n");
    assert( 0 );
    return 0;
}

static bool_t xdrfd_setpostn(XDR *, u_int) {
    fprintf(stderr, "YSVR: xdrfd_setposn() called\n");
    assert( 0 );
    return 0;
}

static long *xdrfd_inline(XDR *, int) {
    fprintf(stderr, "YSVR: xdrfd_inline() called\n");
    assert( 0 );
    return 0;
}

static void xdrfd_destroy(XDR *) {}
