/* remote command server */

#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "netuser.h"
#include "timer.h"
#include "tcp.h"
#include "password.h"
#include "rcmd.h"
#include "cmdparse.h"

static struct tcb *rcmd_tcb;		/* SERVER prototype tcb */
static char *rcmd_paswrd = NULLCHAR;	/* password for rcmd */

static struct mbuf *rcmdbuf = NULLBUF;	/* output buffer for rcmdputc */
static struct mbuf *rcmdout = NULLBUF;	/* chain of rcmd output bufs */

static void rcmd_recv(),rcmd_state();
static int rcmdputc();

extern char version[];			/* program version */
extern char hostname[];			/* hostname to use in prompt */
extern char prompt[];			/* the net> prompt */
extern char nospace[];			/* "no space" message */
extern struct cmds cmds[];		/* commands */

/* Start up rcmd server */
rcmd1(argc,argv)
int argc;
char *argv[];
{
	struct socket lsocket;
	char tos = 0;

	if (rcmd_paswrd != NULLCHAR){
		free(rcmd_paswrd);
		rcmd_paswrd = NULLCHAR;
	}

	if (argc > 2){
		if ((rcmd_paswrd = malloc(strlen(argv[2]) + 1)) == NULLCHAR) {
			printf(nospace);
			return 1;
		}
		strcpy(rcmd_paswrd,argv[2]);
	}

	lsocket.address = ip_addr;
	if(argc < 2)
		lsocket.port = RCMD_PORT;
	else {
		if ((lsocket.port = atoi(argv[1])) == 0)
			lsocket.port = RCMD_PORT;
	}
	if(argc > 3)
		tos = get_tos(argv[3]);
	rcmd_tcb = open_tcp(&lsocket,NULLSOCK,TCP_SERVER,0,rcmd_recv,NULLVFP,rcmd_state,tos,(char *)NULL);
	return 0;
}

/* Shut down rcmd server */
rcmd0()
{
	if(rcmd_tcb != NULLTCB)
		close_tcp(rcmd_tcb);

	if (rcmd_paswrd != NULLCHAR){
		free(rcmd_paswrd);
		rcmd_paswrd = NULLCHAR;
	}
	return 0;
}

/* rcmd server receiver upcall */
static
void
rcmd_recv(tcb,cnt)
struct tcb *tcb;
int16 cnt;
{
	struct rcmd *rcmd;
	struct mbuf *bp;
#ifdef MWC
	int (*save_pt)();
#else
# if (defined(MSDOS) && !defined(MSC) && !defined(__TURBOC__))
	extern int (*put_stdout)();
# else
	int c, save_handle;
#  ifdef MSC
	char *tmpfilename;
#  else
	char tmpfilename[L_tmpnam];
#  endif
# endif
#endif
	char cmdline[RCMD_LINE];

	rcmd = (struct rcmd *) tcb->user;	/* get control block */

	if(recv_tcp(tcb,&bp,cnt) > 0){
	    append(&rcmd->input,bp);		/* append received bytes */
	    tcp_output(tcb);			/* ack the received data */

	    fflush(stdout);			/* make sure stdout is flushed */

#ifdef MWC
	    save_pt = stdout->_pt;		/* save current stdout putc */
	    stdout->_pt = rcmdputc;		/* set stdout putc routine */
#else
# if (defined(MSDOS) && !defined(MSC) && !defined(__TURBOC__))
	    put_stdout = rcmdputc;		/* set stdout putc routine */
# else
	    /* a portable way is to open a file on (ram) disk */

	    save_handle = dup(fileno(stdout));	/* save stdout */
#  ifdef MSC
	    tmpfilename = tempnam(NULL,"rcmd");
#  else
	    tmpnam(tmpfilename);
#  endif
	    freopen(tmpfilename,"w+b",stdout);
# endif
#endif

	    while((bp = pullline(&rcmd->input,0)) != NULLBUF){
		cmdline[pullup(&bp,cmdline,sizeof(cmdline) - 1)] = '\0';
		free_p(bp);
		if (rcmd->pass.okay) {
		    cmdparse(cmds,cmdline);
		    printf("%s %s",hostname,prompt);
		} else {
		    if (ok_password(&rcmd->pass,cmdline)) {
			log_tcp(tcb,"open rcmd %d",tcb->conn.local.port);
			printf("%s %s",hostname,prompt);
		    } else {
			log_tcp(tcb,"fail rcmd %d",tcb->conn.local.port);
			printf("sorry\n");
		    }
		}
	    }

#ifdef MWC
	    stdout->_pt = save_pt;
#else
# if (defined(MSDOS) && !defined(MSC) && !defined(__TURBOC__))
	    put_stdout = (int (*)()) 0; /* go back to default stdout */
# else
	    fseek(stdout,0L,0);
	    while((c = getc(stdout)) != EOF)
		rcmdputc(c);		/* copy tmpfile in rcmdbuffer */

	    fseek(stdout,0L,0);		/* don't write to disk */
	    fflush(stdout);
	    dup2(save_handle,fileno(stdout));
	    unlink(tmpfilename);	/* remove file */
	    close(save_handle);
# endif
#endif

	    append(&rcmdout,rcmdbuf);
	    send_tcp(tcb,rcmdout);
	    rcmdout = rcmdbuf = NULLBUF;
	}

	if (!rcmd->pass.okay)
	    close_tcp(tcb);
}

/* character output routine for use in rcmd server */
static
int
rcmdputc (c,outfile)
int c;
FILE *outfile;
{
    if (c == '\n')
	rcmdputc('\r',outfile);

    if (rcmdbuf == NULLBUF &&
	(rcmdbuf = alloc_mbuf(RCMD_OBUF)) == NULLBUF)
	return EOF;

    rcmdbuf->data[rcmdbuf->cnt++] = c;

    if (rcmdbuf->cnt >= rcmdbuf->size){
	append(&rcmdout,rcmdbuf);
	rcmdbuf = NULLBUF;
    }

    return (c);
}

/* Log connection state changes; also respond to remote closes */
static
void
rcmd_state(tcb,old,new)
register struct tcb *tcb;
char old,new;
{
	char buf[256];
	struct rcmd *rcmd;

	switch(new){
	case ESTABLISHED:
		if((rcmd = (struct rcmd *) calloc(1,sizeof(struct rcmd))) == NULLRCMD){
			/* No space, kill connection */
			close_tcp(tcb);
			return;
		}
		rcmd->conn = tcb;		/* Downward link */
		tcb->user = (char *)rcmd;	/* Upward link */

		if ((rcmd->pass.paswrd = rcmd_paswrd) == NULLCHAR){
			log_tcp(tcb,"open rcmd %d (no pw)",tcb->conn.local.port);
			rcmd->pass.okay = 1;
			sprintf(buf,"rcmd %s\r\n%s %s",version,hostname,prompt);
		} else {
			sprintf(buf,"rcmd %s %s\r\n%s\r\n",version,hostname,
					gen_challenge(&rcmd->pass));
		}
		send_tcp(tcb,qstring(buf));
		break;
	case CLOSE_WAIT:
		close_tcp(tcb);
		break;
	case CLOSED:
		if(tcb != rcmd_tcb)
			log_tcp(tcb,"close rcmd %d",tcb->conn.local.port);
		if (tcb->user != NULLCHAR)
			free(tcb->user);
		del_tcp(tcb);
		/* Clean up if server is being shut down */
		if(tcb == rcmd_tcb)
			rcmd_tcb = NULLTCB;
		break;
	}
}
