/*
** 	Cute li'l baby client for debugging the HASS-D server, informally
**	called the "type-to" client.  Not used in production use.
**
**	Read from stdin, write each line to the
**	stream socket (aka the server process) then read a line back
**	from the socket and write it to the standard output.
**
**	Return to caller when EOF comes back.
*/

#include	"inet.h"
#include	<errno.h>
#include	<string.h>

#include <krb.h>
#include <netdb.h>
#include <sys/time.h>
#include <hesiod.h>

extern int errno;
extern char *sys_errlist[];

#define	safestrlen(foo)	((foo) ? strlen(foo) : 0)

static struct timeval timeout = { CLIENT_KRB_TIMEOUT, 0 };

int		sockfd;

main (argc, argv)
int	argc;
char	*argv[];
{

	MyPhOpenServer();
	PhSendAuthenticator();

/*
	SendAuthenticator(stdin, sockfd);
*/
	str_cli (stdin, sockfd);
	exit(0);
}
	


str_cli(fp, sockfd)
FILE	*fp;
int	sockfd;
{
	int	n, counter;
	char	sendline[MAXLINE], recvline[MAXLINE+1];

	while (fgets (sendline, MAXLINE, fp) != NULL) {
		n = strlen (sendline);
		if (writen (sockfd, sendline, n) != n)
			err_dump("str_cli:  writen error on socket");
/*
** Now read from the socket and write it to our standard out.
*/
		n = readline (sockfd, recvline, MAXLINE);

		if (n < 0)
			err_dump("str_cli: readline error");
		if (n == 0)
			err_dump("str_cli: server has gone down");

/*
** The code "(number)" indicates "number" more incoming lines.  If "number"
** >= 1, don't print the count code.
*/
		if (recvline[0] == '(') {
			counter = atoi (&(recvline[1]));
			if (counter > 0 )
				n = readline (sockfd, recvline, MAXLINE);
		}
		else
			counter = 1;

		while (--counter) {
			recvline[n] = 0;
			fputs(recvline, stdout);
			n = readline (sockfd, recvline, MAXLINE);
		}

		recvline[n] = 0;
		fputs(recvline, stdout);
	}

	if (ferror (fp))
		err_dump("str_cli: error reading input file");
}

#ifdef	OBSOLETE
/*
** Get Kerberos ticket; binhex it and send it over the wire.
*/

SendAuthenticator(fp, sockfd)
FILE	*fp;
int	sockfd;
{
	char	sendline[MAXLINE], recvline[MAXLINE+1];
	int	bytecount = 26;
	int	n = 0;
	unsigned char	*auth;

	bytecount = BuildAuthenticator(&auth);

	sprintf (sendline, "Authenticate (%d)\n", bytecount);
	if (writen (sockfd, sendline, strlen(sendline)) != strlen(sendline))
		err_dump("str_cli:  writen error on socket");

	n = SendBinaryData(auth, bytecount, sockfd);

	if (n) err_dump("Error sending authenticator\n");

	n = readline (sockfd, recvline, MAXLINE);
	if (n < 0)
		err_dump("str_cli: readline error");
	if (n == 0)
		err_dump("str_cli: server has gone down");
	fputs(recvline, stdout);
}


/*
** convert a stream of "bytecount" arbitrary ints into a stream of
** "bytecount" * 2 + 1 (binhexed chars plus newline) and send them
** out the socket.
*/

SendBinaryData(data, bytecount, sockfd)
unsigned char	*data;
int		bytecount;
int		sockfd;
{
	int	n, charstosend;
	char	*sendline;

	sendline = (char *) calloc((bytecount * 2) + 2, sizeof(char));

	for (n = 0; n < bytecount; n++) {
		sprintf ((char *) (sendline + (2 * n)), "%02x", *(data + n));
	}
	sprintf ((char *) (sendline + 2 * n), "\n");

	charstosend = bytecount * 2 + 1;

	if (writen (sockfd, sendline, charstosend) != charstosend) {
		free (sendline);
		return(1);
	}

	free (sendline);
	return(0);
}

/*
** Get a Kerberos ticket, fill in a pointer to it, and return its length.
*/

int
BuildAuthenticator(retval)
unsigned char **retval;
{
  KTEXT_ST authent;
  static unsigned char packet[2048];
  int gotit;
  struct hostent *hp;
  fd_set readfds;
  static char hostname[] = "cascade.mit.edu";
  char *krb_get_phost();
  char lrealm[REALM_SZ];
  char linst[INST_SZ];
  char sinst[INST_SZ];
  char *cp;
  int status;
  void bombout();

  bzero(packet, sizeof(packet));

  krb_get_lrealm(lrealm, 1);	/* Get our Kerberos realm */

  cp = krb_get_phost(hostname);
  if (cp == NULL) {
    bombout(1);
  }

  strcpy(sinst, cp);

  hp = gethostbyname(hostname);

  if (hp == NULL) {
    bombout(2);
  }

  strcpy(linst, "rcmd");
  status = krb_mk_req(&authent, linst, sinst, lrealm, 111);
  if (status != KSUCCESS) {
    fprintf(stderr, "Error getting kerberos authentication.\n");
    fprintf(stderr, "Are your tickets valid?\n");
    exit (1);
  }
  (void) bcopy((char *)&authent, (char *) packet, sizeof(authent));
  *retval = packet;
  return (sizeof(authent));
}
  

void bombout(mess)
int mess;
{

  fprintf(stderr, "A problem occurred while attempting authentication. (%d)\n",
	  mess);
  fprintf(stderr, "Please try again later. If this problem persists,\n");
  fprintf(stderr, "please report it to a consultant.\n");
  exit (1);
}
#endif

int
MyPhOpenServer()
{
	struct sockaddr_in	serv_addr;
	struct hostent		*host;

	bzero ( (char *) &serv_addr, sizeof (serv_addr));
	host = gethostbyname(SERV_HOST_NAME);

	serv_addr.sin_family = AF_INET;

	/*
	serv_addr.sin_addr.s_addr = inet_addr (SERV_HOST_ADDR);
	*/
	if (host) {
		 bcopy(host->h_addr, &serv_addr.sin_addr, host->h_length);
	}
	else {
		err_dump ("Can't locate server");
		return (0);
	}

	serv_addr.sin_port = htons (SERV_TCP_PORT);

	if ( (sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0) {
		err_dump ("can't open stream socket");
		return (0);
	}

	if (connect (	sockfd, 
			(struct sockaddr *) &serv_addr,
			sizeof(serv_addr)) < 0) {
		err_dump ("can't connect to server");
		return (0);
	}

	return(1);
}

/*
** Get Kerberos ticket; binhex it and send it over the wire.
** return 1 if everything's okay, some other value otherwise.
*/

int
PhSendAuthenticator()
{
	char	sendline[MAXLINE], recvline[MAXLINE+1];
	int	bytecount = 26;
	int	n = 0;
	unsigned char	*auth;

	bytecount = BuildAuthenticator(&auth);
	if (bytecount == 0) {
		err_dump("PhSendAuthenticator: Cannot build authenticator");
		return (0);
	}

	sprintf (sendline, "Authenticate (%d)\n", bytecount);
	if (writen (sockfd, sendline, safestrlen(sendline)) != safestrlen(sendline)) {
		err_dump("PhSendAuthenticator:  writen error on socket");
		return (0);
	}

	n = SendBinaryData(auth, bytecount, sockfd);

	if (n) {
		err_dump("Error sending authenticator\n");
		return (0);
	}

	n = readline (sockfd, recvline, MAXLINE);
	if (n < 0) {
		err_dump("PhSendAuthenticator: readline error");
		return (0);
	}
	if (n == 0) {
		err_dump("str_cli: server has gone down");
		return (0);
	}
/*
	fputs(recvline, stdout);
*/
	return (1);
}


/*
** convert a stream of "bytecount" arbitrary ints into a stream of
** "bytecount" * 2 + 1 (binhexed chars plus newline) and send them
** out the socket.
*/

static int
SendBinaryData(data, bytecount, sockfd)
unsigned char	*data;
int		bytecount;
int		sockfd;
{
	int	n, charstosend;
	char	*sendline;

	sendline = (char *) calloc((bytecount * 2) + 2, sizeof(char));

	for (n = 0; n < bytecount; n++) {
		sprintf ((char *) (sendline + (2 * n)), "%02x", *(data + n));
	}
	sprintf ((char *) (sendline + 2 * n), "\n");

	charstosend = bytecount * 2 + 1;

	if (writen (sockfd, sendline, charstosend) != charstosend) {
		free(sendline);
		return(1);
	}

	free(sendline);
	return(0);
}

/*
** Get a Kerberos ticket, fill in a pointer to it, and return its length.
*/

static int
BuildAuthenticator(retval)
unsigned char **retval;
{
  KTEXT_ST authent;
  static unsigned char packet[2048];
  struct hostent *hp;
  static char hostname[] = SERV_HOST_NAME;
  char *krb_get_phost();
  char lrealm[REALM_SZ];
  char linst[INST_SZ];
  char sinst[INST_SZ];
  char *cp;
  int status;

  bzero(packet, sizeof(packet));

  krb_get_lrealm(lrealm, 1);	/* Get our Kerberos realm */

  cp = krb_get_phost(hostname);
  if (cp == NULL) {
    err_dump("Null return from krb_get_phost");
    return (0);
  }

  strcpy(sinst, cp);

  hp = gethostbyname(hostname);

  if (hp == NULL) {
    err_dump("Null return from gethostbyname");
    return (0);
  }

  strcpy(linst, "rcmd");
  status = krb_mk_req(&authent, linst, sinst, lrealm, 111);
  if (status != KSUCCESS) {
    ErrorMessage("Error getting kerberos authentication.");
    return (0);
  }
  (void) bcopy((char *)&authent, (char *) packet, sizeof(authent));
  *retval = packet;
  return (sizeof(authent));
}

/*
static void
bombout(mess)
int mess;
{

  fprintf(stderr, "A problem occurred while attempting authentication. (%d)\n",
	  mess);
  fprintf(stderr, "Please try again later. If this problem persists,\n");
  fprintf(stderr, "please report it to a consultant.\n");
  exit (1);
}
*/

int
readn (fd, ptr, nbytes)
int	fd;
char	*ptr;
int	nbytes;
{
	int nleft, nread;

	nleft = nbytes;
	while (nleft > 0) {
		nread = read (fd, ptr, nleft);
		if (nread < 0)
			return (nread);		/* error, return < 0 */

		else if (nread == 0)
			break;			/* EOF */

		nleft -= nread;
		ptr += nread;
	}
	return (nbytes - nleft);
}

int
writen(fd, ptr, nbytes)
int	fd;
char	*ptr;
int	nbytes;
{
	int nleft, nwritten;

	nleft = nbytes;
	while (nleft > 0) {
		nwritten = write (fd, ptr, nleft);
		if (nwritten <= 0)
			return (nwritten);		/* error */

		nleft -= nwritten;
		ptr += nwritten;
	}
	return (nbytes - nleft);
}

/*
**	Read a line from a descriptor.  Read the line one byte at a time,
**	looking for the newline.  We store the newline in the buffer,
**	then follow it with a null (the same as fgets(3)).
**	We return the number of characters up to, but not including
**	the null (the same as strlen(3)).
*/

int
readline(fd, ptr, maxlen)
int	fd;
char	*ptr;
int	maxlen;
{
	int	n, rc;
	char	c;

	for (n = 1; n < maxlen; n++) {
		if ( (rc = read (fd, &c, 1)) == 1) {
			*ptr++ = c;
			if (c == '\n')
				break;
		} else if (rc == 0) {
			if (n == 1)
				return(0);	/* EOF, no data read */
			else
				break;		/* EOF, some data read */
		} else
			return (-1);		/* error */
	}

	*ptr = 0;
	return (n);
}

err_dump(string)
char	*string;
{
	ErrorMessage (string);
	ErrorMessage (strerror(errno));
/*
	fprintf (	stderr, 
			"%s, error '%s'\n", 
			 string, strerror(errno));
 */
}


ErrorMessage(foo)
char	*foo;
{
	err_dump(foo);
}
