/*
 * Copyright 1993 by the Massachusetts Institute of Technology.
 * For copying and distribution information, see the file
 * "mit-copyright.h".
 *
 * $Source: /afs/athena.mit.edu/project/net_dev/service/RCS/mdst.c,v $
 * $Author: jweiss $
 * $Header: /afs/athena.mit.edu/project/net_dev/service/RCS/mdst.c,v 1.18 94/03/24 15:57:08 jweiss Exp Locker: jweiss $
 *
 * A simple curses-based menu to provide access to various services
 * via telnet/dialup.
 */

#define MAXCMDLEN 256
#define MAXLINELEN 256

#ifndef INSTALLDIR
#define INSTALLDIR "/var/mdst"
#endif 

#include <mit-copyright.h>
#include <curses.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <syslog.h>
#include <stdio.h>
#include <sgtty.h>
#include <sys/file.h>

#include <signal.h>
int endit();
int pid;
char chr;
void 
main()
{
    char ch, cmd[MAXCMDLEN];
    WINDOW *wmenu, *wapp;
    int x, y, yi;
    int done, dirty;
    int homex, homey;
    int error;

    /* Check for a motd file.  This means somebody wants to
       shut us down for some reason, so exit after printing it.  */
    sprintf(cmd,"%s/motd",INSTALLDIR);
    if(NonemptyFile(cmd)) {
	PrintFile(NULL, cmd);
	exit(0);
    }
signal(SIGINT,endit);
signal(SIGHUP,endit);
signal(SIGTERM,endit);
signal(SIGPIPE,endit); /* added by ST 2/2/94 */

    /* Set up syslog */
    openlog("mdst", LOG_PID, LOG_INFO);

    /* Set up for Kerberos ("Nice doggy...") */
    {
	char tkfname[32];
	sprintf(tkfname,"/tmp/tkt_moira_%d",getpid());
	setenv("KRBTKFILE",tkfname,1);
    }

    /* Set up curses */
    initscr();
    raw();
    noraw();

    wmenu=newwin(0,0,0,0);    /* Window for menu */
    wapp=newwin(0,0,0,0);     /* Window for applications called from menu */
   
    /* Create the menu */
    x=(COLS-27)/2;            /* Center it */
    PrintTitle(wmenu," Mini-dialup Services Tool ");
    y=1; yi=2;

    if(LINES < (7+2*5)) yi=1;
    PrintAt(wmenu,x,y+=yi,"1. Mailing lists");
    PrintAt(wmenu,x,y+=yi,"2. Mail forwarding");
    PrintAt(wmenu,x,y+=yi,"3. Change Kerberos password");
    PrintAt(wmenu,x,y+=yi,"4. Register for a username");
    PrintAt(wmenu,x,y+=yi,"5. MIT directory");
    PrintAt(wmenu,x,y+=yi,"q. QUIT");
    PrintCentered(wmenu,y+=yi,"Please enter a number from 1-5, or q to Exit: ");

    getyx(wmenu,homey,homex); /* Notes location of end of prompt */

    dirty=TRUE;               /* Forces menu to be drawn in main loop */

    done=FALSE;
    while(!done) {
	/* Make menu visible */
	if(dirty) overwrite(wmenu,wapp);
	wmove(wmenu,homey,homex);
	if(!dirty) {
	    /* Write over bad key (Is this desirable?  I like it.) */
	    wclrtoeol(wmenu);
	}
	touchwin(wmenu);
	wrefresh(wmenu);
	ch=GetOneChar(wmenu);

	/* Clear application window */
#define CLEAR_APP_WINDOW { overwrite(wapp,wmenu); wclear(wapp); wrefresh(wapp); dirty=TRUE; }
	
	switch(ch) {
	  case '1': /* Mailing lists */
	    CLEAR_APP_WINDOW;
	    if(HasTickets(wapp) || Authenticate(wapp)) {
		sprintf(cmd,"%s/maillists",INSTALLDIR);
		if(error=ExecuteApp(wapp,"maillists",cmd))
		    WaitForKey(wapp);
	    }
	    break;
	  case '2': /* Mail forwarding */
	    CLEAR_APP_WINDOW;
	    if(HasTickets(wapp) || Authenticate(wapp)) {
		sprintf(cmd,"%s/mailfwd",INSTALLDIR);
		if(error=ExecuteApp(wapp,"mailfwd",cmd))
		    WaitForKey(wapp);
	    }
	    break;
	  case '3': /* Change kerberos password */
	    CLEAR_APP_WINDOW;
	    {
		char username[80];
		sprintf(cmd,"%s/kpasswd.text",INSTALLDIR);
		PrintFile(wapp, cmd, COLS);
		Input(wapp,"\nEnter your username: ",username,8);
		if(*username) {
		    sprintf(cmd,"/usr/athena/kpasswd -n %s",username);
		    if(error=ExecuteApp(wapp,"kpasswd",cmd)) 
			WaitForKey(wapp);
		}
	    }
	    break;
	  case '4': /* Register */
	    CLEAR_APP_WINDOW;
/*	wanted to display different test than normal ST 
    if(error=ExecuteApp(wapp,"register","/afs/athena/system/register/@sys/userreg"))*/
	    if(error=ExecuteApp(wapp,"register","/afs/athena.mit.edu/project/net_dev/service/userreg/userreg"))
		WaitForKey(wapp);
	    break;
	  case '5': /* MIT Directory */
	    CLEAR_APP_WINDOW;
	    error=ExecuteApp(wapp,"kphone","/afs/net.mit.edu/project/directory/cso/decmipsbin/kphone mitdir 105");
	    break;
	  case CTRL('C'): /* Exit */
	  case 'q':
	  case 'Q':
	    done=TRUE;
	    break;
	  case CTRL('L'): /* Refresh */
	    overwrite(wapp,wmenu);  /* Force menu to be redrawn  */
	    dirty=TRUE;             /* Force app window to clear */
	    break;
	  default:
	    dirty=FALSE;
	    break;
	}
	if(dirty) { wclear(wapp); wrefresh(wapp); } 
    }

    /* Clean up tickets */
    Execute(wapp,"/usr/athena/bin/kdestroy");
   
    /* Clean up curses */
    delwin(wapp);
    delwin(wmenu);
    endwin();
    { int arg = FWRITE; ioctl(1, TIOCFLUSH, &arg); } /* Flush the tty */

    closelog();        /* Close the syslog */
}

WaitForKey(w)
    WINDOW *w;
{
    wstandout(w);
    wprintw(w,"Press any key to continue: ");
    wstandend(w);
    wrefresh(w);
   /*  wgetch(w); */
getchr(chr);
addch(chr);
}


/**************** Execute(w,"kinit") wrapper ****************/

Authenticate(w)
    WINDOW *w;
{
    char ch, fn[256];
    int punt, y, x;
    
    punt=0; 
    while(1) {
	sprintf(fn,"%s/kinit.text",INSTALLDIR);
	PrintFile(w, fn, COLS);

	strcpy(fn,"/usr/athena/bin/kinit");
	Execute(w,fn);

	if(HasTickets(w)) break;
	
	getyx(w,y,x);

	wmove(w,y,0);  
	wprintw(w,"\nAuthentication failed.  Try again? (y/N) ");
	wrefresh(w);
	ch=GetOneChar(w);
	if(ch!='y' && ch!='Y') { punt=1; break; }
	wclear(w);
	wrefresh(w);
    }
    return(!punt);
}

HasTickets(w)
    WINDOW *w;
{
    return(!Execute(w,"/usr/athena/bin/klist -t"));
}

/**************** stat(2) wrapper ****************/

extern int errno;

NonemptyFile(path)
    char *path;
{
    struct stat status;
    int retval;
    if((retval=stat(path,&status))==0) {
	return(status.st_size>0);
    } else {
#ifdef DEBUG
	fprintf(stderr,"Woops! errno=%d\n",errno);
#endif
	return(0);
    }
}

/**************** execve(2) wrappers ****************/

int ExecuteApp(w,name,cmd)
    WINDOW *w;
    char *name;
    char *cmd;
{
    syslog(LOG_INFO,"Starting \"%s\"",name);
    return(Execute(w,cmd));
}

int Execute(w,scmd)
    WINDOW *w;
    char *scmd;
{
    char *cmd, acmd[MAXCMDLEN];
    int result=0;

    strncpy(acmd,scmd,MAXCMDLEN); /* Don't alter original string; use copy */
    acmd[MAXCMDLEN-1]='\0';
    cmd=acmd; /* crock-o-stimpy */

/*    savetty(); echo(); noraw(); */
    pid=vfork();
    switch(pid) {
      case -1: /* Fail */
	/** Use com_err() here instead **/
	waddstr(w,"Couldn't fork()!\n"); 
	wrefresh(w);
	result = -1;
	break;

      case 0:  /* Child */
	{
	    char *name, *argv[128];
	    int i;

	    /* Set name to full pathname and argv[0] to basename */
	    name=cmd;
	    argv[0]=cmd;
	    while(*cmd && *cmd!=' ') {
		if(*cmd=='/') argv[0]=cmd+1;
		cmd++;
	    }

	    /* Set up argv[] and NUL-terminate arguments */
	    i=0;
	    while(*cmd) {
		*cmd++='\0';
		while(*cmd && *cmd==' ') cmd++;
		if(!*cmd) break;
		argv[++i]=cmd;
		while(*cmd && *cmd!=' ') cmd++;
	    }
	    argv[++i]=NULL;
#ifdef DEBUG	    
	    wprintw(w,"\ncmd = \"%s\\0\"\n",name);
	    DumpCList(w,"argv",argv);
#endif
	    /* Start the new process */
	    execv(name,argv);
            waddstr(w,"\nexecve() failed.\n");
	    wrefresh(w);
	    _exit(1); 
 	}
	break;

      default: /* Parent */
	{
	    union wait status;
	    (void) wait(&status);
	    result = status.w_T.w_Retcode;
        }
     	break;
    }
/*    resetty(); */
    return(result);
}
endit() {
kill(pid,SIGKILL);
exit();
}
#ifdef DEBUG
DumpCList(w, name, argv) 
    WINDOW *w;
    char *name;
    char **argv;
{
    int i;
    for(i=0; argv[i]; i++) {
	wprintw(w,"%s[%d] = \"%s\\0\"\n",name,i,argv[i]);
    }
    wrefresh(w);
}
#endif

/**************** curses wrappers ****************/

PrintTitle(w,title)
    WINDOW *w;
    char *title;
{
    wstandout(w);
    PrintCentered(w,0,title);
    wstandend(w);
}

PrintCentered(w,line,text)
    WINDOW *w;
    int line;
    char *text;
{
    int len;
    
    if((len=strlen(text))<COLS) 
	wmove(w,line,(COLS-len)/2);
    else
	wmove(w,line,0);
    waddstr(w,text);
}

PrintAt(w,x,y,text)
    WINDOW *w;
    int x;
    int y;
    char *text;
{
    wmove(w,y,x);
    waddstr(w,text);
}

Input(w,prompt,buffer,length)
    WINDOW *w;
    char *prompt;
    char *buffer;
    int length;
{
    if(prompt && *prompt) {
	waddstr(w,prompt);   wrefresh(w);
    }
    myWGetStr(w,buffer,length);
}

/* Prompt the user for input in the input window of cur_ms */
myWGetStr(w,buf,buflen) 
    WINDOW *w;
    char *buf;
    int buflen;
{
    int c;
    char *p;
    int y, x, oldx, oldy;
    int done;

    getyx(w, oldy, oldx);
    x = oldx; y = oldy;
    raw(); noecho();
    p = buf;
    
    done=0;
    while(!done) {
	c = wgetch(w) & 0x7f;

	switch (c) {
	  case CTRL('C'):
	    *p = '\0';
	    return 0;

	  case CTRL('L'):
	    touchwin(w);
	    wrefresh(w);
	    break;
	    
	  case '\n':
	  case '\r':
	    done=1;
	    break;

	  /* these should be obtained by doing ioctl() on tty */
	  case '\b':
	  case '\177':
	    if (p > buf) {
		p--; x--;
		wmove(w,y,x);
		wclrtoeol(w);
	    }
	    break;

	  case CTRL('U'):
	  case CTRL('G'):
	  case CTRL('['):
	    x = oldx;  y = oldy;
	    p = buf;
	    wmove(w,y,x);
	    wclrtoeol(w);
	    break;

	  default:
	    /* (buflen - 1) leaves room for the \0 */
	    if (isprint(c) && (p - buf < buflen - 1) && (x < COLS)) {
		waddch(w, c);
		*p++ = c;
		x++;
	    } else
		putchar(CTRL('G'));
	    break;
	}
	wrefresh(w);
    }

    wclrtoeol(w);
    waddch(w, '\n');
    wrefresh(w);
    noraw(); echo();
    *p = '\0';
    return 1;
}

GetOneChar(w) 
    WINDOW *w;
{
    char c;
    int y,x,rc;
    int key = -1;
    int done=0;

    getyx(w,y,x);  /* Remember where we started */
    raw(); noecho();

    while(!done) {
	c=wgetch(w);
	switch(c) {
	  case '\n':
	  case '\r':           /* Try to accept the input */
	    done=1;
	    break;

	  case '\b':
	  case '\177':         /* Back up the cursor & erase */
	    if(key = -1) {
		key = -1;
		wmove(w,y,x);
		wclrtoeol(w);
		wrefresh(w);
	    }
	    key = -1;
	    break;

	  case CTRL('L'):        /* Refresh the screen */
	    touchwin(w);
	    wrefresh(w);
	    break;

	  case CTRL('C'):
	    key=c; done=1;
	    break;

	  default:
	    if(key==-1) {
		key=c;
		waddch(w,c);
		wrefresh(w);
	    } else {
		rc = putchar(CTRL('G'));
		if (rc == EOF) /* added ST */
		  exit();  
	    }
	    break;
	}
    }

    noraw(); echo();

    return(key);
}


PrintFile(w,fn)
    WINDOW *w;
    char *fn;
{
    char buf[MAXLINELEN];
    FILE *fp;

    if(fp=fopen(fn,"r")) {
	while(fgets(buf,MAXLINELEN,fp)) {
	    if(w) 
		waddstr(w,buf);
 	     else
		printf("%s",buf);
	}
	if(w) wrefresh(w); else fflush(stdout);

	fclose(fp);
    } else 
	return(-1);
    return(0);
}

/* eof:foo.c */
static int
getchr()
{
        char    c = -1;
        int     rc;

        rc = read(0, &c, 1);         /* Cant use getchar cause it buffers things
 */
        if (rc <= 0)
          exit();
        return c;
}






