/*
 * xfree - program to free up a workstation under the control of
 * xscreensaver.  The user can run this on a remote workstation
 * such as the next one, and kill the xscreensaver without having
 * to remember the telnet port to access.  Xfree will also make sure
 * that the password is not shown on the screen.
 * 
 * Written by:          Derek Atkins <warlord@athena.mit.edu>
 *
 */

#include <errno.h>       /* obligatory includes */
#include <signal.h>
#include <stdio.h>
#include <strings.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netdb.h>
#include <sgtty.h>
#include <pwd.h>
#include <ttyent.h>

#define MAXPWSIZE 128		/* Biggest key getlongpass will return */

char *whoami;
int PORTNUM = 32253;

main(argc, argv)
int argc;
char *argv[];
{
    int s, arg;
    char host[80];
    char *pp;
    char *getlongpass();
    char *buf;
    char *read_data();
    
    whoami = argv[0];

    if (argc < 2) 
	usage(whoami);

    arg = 1;
    for (; arg<argc; arg++) {
	if (*argv[arg] != '-') {
	    strcpy(host,argv[arg]);
	    continue;
	}
	if (strlen(argv[arg]) > 2)
	    usage(whoami);

	switch (argv[arg][1]) {

	case 'p':
	    if (arg == argc-1)
		usage(whoami);
	    arg++;
	    PORTNUM = atoi(argv[arg]);
	    break;
	default:
	    usage(whoami);
	}
    }

    if ((s=call_socket(host, PORTNUM))==-1) {
	printf("%s: unknown host\n",host);
	exit(-1);
    } else if (s == -2) {
	printf("%s: Error getting socket\n",host);
	exit(-1);
    } else if (s == -3) {
	printf("%s: Cannot connect\n",host);
	exit(-1);
    }
    printf("Looking for xscreensaver on %s. . .\n", host);
    buf = read_data(s);
    if (!strcasecmp(buf, "Type password to unlock screen:\n")) {
	pp = getlongpass("Password: ");
	write(s,"\n",1);
	buf = read_data(s);
	if (write(s, pp, strlen(pp)) != strlen(pp)) {
	    printf("Error while sending Password\n");
	    close(s);
	    exit(-1);
	}
	buf = read_data(s);
	if (!strcasecmp(buf, "Password incorrect.\n")) {
	    printf("Password incorrect. . .\n");
	    printf("Sorry.\n");
	}
	else if (!strcasecmp(buf, "Password correct, unlocking screen.\n")) {
	    printf("Password correct. . .\n");
	    printf("Xscreensaver should be cleared from %s\n",host);
	}
	close(s);
    } else
	printf("Doesn't seem to be an xscreensaver listening on %s\n",host);
    close(s);
}

char *read_data(s)
int s;			/* connected socket */
{
    int bcount;		/* Buffer count */
    char c, buf[BUFSIZ];

    bcount = 0;

    while ((read(s, &c, 1)) == 1) {
	if ((buf[bcount++] = c) == '\n') {
	    buf[bcount] = '\0';
	    return(buf);
	} 
    }	
    return(buf);
}

int call_socket(hostname, portnum)
char *hostname;
{ struct sockaddr_in sa;
  struct hostent     *hp;
  int s;
  
  if ((hp= gethostbyname(hostname)) == NULL) { /* do we know the host's */
      errno= ECONNREFUSED;                       /* address? */
      return(-1);                                /* no */
  }
  
  bzero(&sa,sizeof(sa));
  bcopy(hp->h_addr,(char *)&sa.sin_addr,hp->h_length); /* set address */
  sa.sin_family= hp->h_addrtype;
  sa.sin_port= htons((u_short)portnum);
  
  if ((s= socket(hp->h_addrtype,SOCK_STREAM,0)) < 0)   /* get socket */
      return(-2);
  if (connect(s,&sa,sizeof sa) < 0) {                  /* connect */
      close(s);
      return(-3);
  }
  return(s);
}
char *getlongpass(prompt)
char *prompt;
{
    struct sgttyb ttyb;
    int flags;
    register char *p;
    register c;
    FILE *fi;
    static char pbuf[MAXPWSIZE+2];
    int (*sig)();
    
    if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
	fi = stdin;
    else
	setbuf(fi, (char *)NULL);
    ioctl(fileno(fi), TIOCGETP, &ttyb);
    flags = ttyb.sg_flags;
    ttyb.sg_flags &= ~ECHO;
    ioctl(fileno(fi), TIOCSETP, &ttyb);
    fprintf(stderr, "%s", prompt); fflush(stderr);
    for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) {
	if (p < &pbuf[MAXPWSIZE])
	    *p++ = c;
    }
    *p++ = '\n';
    *p++ = '\0';
    fprintf(stderr, "\n"); fflush(stderr);
    ttyb.sg_flags = flags;
    ioctl(fileno(fi), TIOCSETP, &ttyb);
    if (fi != stdin)
	fclose(fi);

    pbuf[MAXPWSIZE]='\0';
    return(pbuf);
}

usage(whoami)
char *whoami;
{
    printf("Usage:  %s [-p port] machine\n", whoami);
    exit(1);
}
