/*
 * $Source: $
 * $Revision: $
 * $Date: $
 * $State: $
 * $Author: $
 *
 *
 * $Log: $
 *
 */

/*
 *	Copyright (c) 1991 by the Massachusetts Institute of Technology,
 *	For copying and distribution information, see the file
 *	"mit-copyright.h".
 *
 * kphone.c
 * 
 * client program for the MITDIR.  This program allows for interactive
 * queries to the nameserver until the user issues a quit or the 
 * connection is broken.
 *
 * this program is like phone.c, except it does kerberos authentication
 * to compile:  gcc -o kphone kphone.c -lkrb -ldes
 */

#include <stdio.h>
#include "replies.h"
#include "mit-copyright.h"

/* network includes */
#ifdef MACINTOSH
#include <bsd-mac-compat.h>
#include <errno.h>
#include <strings.h>
#include <Windows.h>
#include "pips.h"

#else
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <strings.h>

/* networking stuff */
#include <sys/param.h>  /* used for kerberos */
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#endif

#ifdef MACINTOSH
/* redefine the fprintf */
#define fprintf(output,format,message) {\
	OpenAlertDialog(WRITE_ERROR,message); \
}
#endif

int	inet_establish_connection(char *hostname, char *portname);

#define SOCKBUF		    1024
#define MAXLINESIZE       80

char	outbuf[SOCKBUF];
char	inbuf[SOCKBUF];

int     debug = 0;
short   MacOSErr = 0;
char    MacOSErrStr[132];
int		listCtr;

FILE    *fopen();
static FILE *result = NULL;

#pragma segment phone 
do_phone(char  *query)
{
	extern int		errno;

	int	     sock;
	int	     red, inlen;
	int      more;
	extern   int optind;
	Str255	 host;
	Str255	 port;
	char	 new_query[256]; /* we should check that string does not exceed size */
	fd_set   readfds;
	struct   timeval timeout;
	short	 watch_cursor_index = -1;
    short	 watch_cursor_offset = 5;

	pref_get_string(NAMESERVER_HOST, host);
	pref_get_string(NAMESERVER_PORT, port);		
	listCtr = 0;
	
#ifndef MACINTOSH
	if (!result)
		result = fopen("result","a");
	if (!result) {
		fprintf(stdout,"could not open audit file"); 
		return -1;
	}
	
#endif

	/* set cursor to rotating watch */
	wind_set_cursor((watch_cursor_index++ % 8) + watch_cursor_offset);
	
	sock = inet_establish_connection(host, port);
	if (sock < 0) {
		MacTCPErr(MacOSErr, MacOSErrStr);
		net_error(MacOSErr,MacOSErrStr);
		wind_set_cursor(0); 			/* turn cursor into arrow */
		return -1;
	}
	
	/* write the user input to the server */
	sprintf(new_query,"query %s\r",query);  /* should check that query !> 250 */
	inlen = strlen(new_query);
	more = writeline(sock, new_query, inlen);	
	
	FD_ZERO(&readfds);
	FD_SET(sock,&readfds);
	timeout.tv_sec = 60;   /* seconds */
	timeout.tv_usec = 0;
	
	while(more) {
		/* move rotating watch */
		wind_set_cursor((watch_cursor_index++ % 8) + watch_cursor_offset);

		red = select(FD_SETSIZE, &readfds, 0, 0, &timeout);
		if (red > 0)
			red = read(sock, inbuf, sizeof(inbuf));
		else
			more = 0;
		
		if (red < 0) {
			MacTCPErr(MacOSErr, MacOSErrStr);
			net_error(MacOSErr,MacOSErrStr);
			more = 0;
			continue;
		}
		
		more = EvaluateResponse(inbuf,red);
	}

#ifndef MACINTOSH
	close(result);
#endif

	close(sock);
	
	wind_set_cursor(0); 			/* turn cursor into arrow */
	
	return listCtr;
}

/***********************************************************************
 * Process users input, ie. Write the line to the server
 ***********************************************************************/
#pragma segment phone 
int
writeline(int sock, char *outbuf, int inlen)
{
  int  status;

	if (status = write(sock, outbuf, inlen) < 0) {
		MacTCPErr(MacOSErr, MacOSErrStr);
		net_error(MacOSErr,MacOSErrStr);
		return 0;
    }
	
  /* are we waiting for a response from the server */
  return 1;
}


/***********************************************************************
 * Create a socket and establish a connection over it to the given host and
 * port.  Returns the (connected) socket created, or -1 in case of error.
 ***********************************************************************/
#pragma segment phone 
int
inet_establish_connection(char *hostname, char *portname)
{
	struct  sockaddr_in	sname;
	struct  hostent		*hp;
	struct  servent		*sp;
	struct  in_addr		sa;

	int	sock, portnum;

	/*
	 * resolve hostname
	 */

	if (isdigit(*hostname)) {
		if ((sa.s_addr = inet_addr(hostname)) == -1)
			hp = gethostbyname(hostname);
		else
			hp = gethostbyaddr(&sa, sizeof(sa), AF_INET);
	} else
		hp = gethostbyname(hostname);	

	if (hp == NULL) {
		fprintf(result,"Locally unknown host: [%s]\n", hostname);
		return -1;
	}

	/*
	 * resolve portname
	 */

	if (isdigit(*portname))
		portnum = htons(atoi(portname));
	else {
		sp = getservbyname(portname, "tcp");
		if (sp == NULL) {
			fprintf(result, "unknown port %s\n", portname);
			return -1;
		}
		portnum = sp->s_port;
		portname = sp->s_name;
	}


	/*
	 * build server socket name
	 */

	bzero(&sname, sizeof(sname));
	sname.sin_family = AF_INET;

	if (hp)
		bcopy(hp->h_addr, &sname.sin_addr, hp->h_length);
	else
		bcopy(&sa.s_addr, &sname.sin_addr, sizeof(sa.s_addr));
	sname.sin_port = portnum;

	/*
	 * create a network socket
	 */

	sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock < 0) {
		fprintf(result, "%s\n","error: socket");
		return -1;
	}

	/*
	 * make a connection to the specified server on the socket
	 */

	if (connect(sock, (struct sockaddr*)&sname, sizeof(sname)) < 0) {
	  fprintf(result,"%s\n","error: connect");
	  return -1;
	}

	/*
	if (debug) {
	  fprintf(result,"debug> Connected to %s, port %s (%d).\n",
	       hp ? hp->h_name : hostname, portname, ntohs(portnum));
	  fflush(result);
	}
	*/

	return sock;
}

/***********************************************************************
 * print what the QI (Query Interpreter; nameserver) says.
 * read replies from nameserver until code indicates a completed
 * command.  
 ***********************************************************************/
#pragma segment phone 
int EvaluateResponse(buffer,size)
char *buffer;
int   size;
{
  char   scratch[MAXLINESIZE+1];  /* some space     */
  int    code;                    /* the reply code */
  short  ctr;
  char  *entities;
  char  *findStart;
  int	 lost;
  short  text_done = 0;
  extern WindowPtr	gTextWindow;
  
  static short found = 0;

  /* ParseBuffer returns only complete lines (scratch) for processing */
  while (ParseBuffer(buffer, scratch, size)) {

    /* null out buffer as the original is a static in ParseBuffer */
    *buffer = NULL;

    code = atoi(scratch);

#ifdef MACINTOSH
	/* replace \r with \n */
	scratch[strlen(scratch)-1] = '\n';
#endif
	
    switch(code) {
    case LR_NUMRET:
      /* print number of entities found sans the return code 
      findStart = index(scratch,':');
	  if (!text_done) {
	  	text_show_new_text(gTextWindow);
		text_done++;
	  }
	  lost = text_bind( findStart+1 );
	  */
	  
      /* get the number of entities found */
      for(strtok(scratch," "), ctr=1; ctr < 3; ctr++, entities = (char*)strtok(0," "));
      found = atoi(entities);
      break;

    case LR_OK:
      /* if we were processing a multiple person record, print last record */
      if ( found > 1 )
		ParsePerson(scratch);

      /* now we are done */
      found = 0;
      break;

    default:
      if (found > 1 )
		/* print multiple entity person record */
		ParsePerson(scratch);
      else {
		/* print the line sans the return code and index if present*/
		findStart = index(scratch,':');
		if (code == -LR_OK && findStart)
	  		findStart = index(findStart+1,':');
		if (!text_done) {
	  		text_show_new_text(gTextWindow); /* erase window and force to front */
			text_done++;
		}

		lost = text_bind( findStart ? findStart+1 : scratch);
      }
      break;
    }
  }

  return (code >= LR_OK ? 0 : 1);   /* all done */
}

  struct data {
    char name[31];
    char department[21];
    char title[51];
    char alias[31]; /* don't know actual length, but it should not exceed name */
    char phone[15];
    char phone2[15];
  };

/***********************************************************************
* Determine what to do with the line.
***********************************************************************/
#pragma segment phone 
ParsePerson(transaction)
char *transaction;
{
  static int indexno = 1;
  static struct data Person;

  int    type;
  int    person;
  char  *indexPtr;
  char  *labelStart;
  char  *labelEnd;
  char   listString[255];
  int	 rc;

  type = atoi(transaction);

  /* are we processing a person record? */
  if ( type == -LR_OK ) {
    /* find index number */
    indexPtr = index(transaction,':');
    person = atoi(indexPtr + 1);

    /* there is no reason this should be zero, but ... */
    if ( person > 0 ) {

      /* do we have a new person? */
      if ( person != indexno ) {
		sprintf(listString,"1:0:0:0:key:%-30.30s %-20.20s:%s::phone::", 
			Person.name, Person.department, Person.alias);
		bzero(&Person,sizeof(Person));
		
		rc = list_Bind(listString,strlen(listString),listCtr);
		
		listCtr++;	 /* number of records in list */
		indexno = person;
      }

      labelStart = index(indexPtr+1, ':');
      labelEnd   = index(labelStart+1, ':');
      if (labelStart != NULL && labelEnd != NULL) {

		/* remove leading spaces */
		for(labelStart = labelStart+1; *labelStart == ' '; labelStart++);
	
		/* remove trailing \n */
		*(transaction + strlen(transaction) - 1) = '\0';
	
		if (!strncmp(labelStart,"name",4)) strcpy(Person.name,labelEnd+2);
		if (!strncmp(labelStart,"alias",5)) strcpy(Person.alias,labelEnd+2);
		if (!strncmp(labelStart,"department",10)) strcpy(Person.department,labelEnd+2);
		if (!strncmp(labelStart,"title",5)) strcpy(Person.title,labelEnd+2);
		if (!strncmp(labelStart,"phone",5)) strcpy(Person.phone,labelEnd+2);
		if (!strncmp(labelStart,"phone2",6)) strcpy(Person.phone2,labelEnd+2);
      }
    }
  }

  else {
    /* all done with printing the result of the query - print final record */
    if ( type == LR_OK ) {
      indexno = 1;
	  sprintf(listString,"1:0:0:0:key:%-30.30s %-20.20s:%s::phone::", 
			Person.name, Person.department, Person.alias);
		
	  rc = list_Bind(listString,strlen(listString),listCtr);
		
	  listCtr++;	 /* number of records in list */
      bzero(&Person,sizeof(Person));
    }
  }
    
}

/***********************************************************************
* Get a line from the buffer.
***********************************************************************/
#pragma segment phone 
int ParseBuffer(buffer, theString, size)
char *buffer;           /* data read from socket  */
char *theString;        /* space to put the chars */
int   size;             /* data read from socket  */

{
  static char Qbuf [SOCKBUF+1] = { '\0' } ;
  static char partial[MAXLINESIZE+1] = {'\0'};
  static int  pos = {0}, end = {0}, len = {0} ;
  char  *linp;
  int    count = 0;
  short  real;
	
  /* new buffer, initialzie static var */
  if (*buffer) {
    strncpy(Qbuf,buffer,size);
    Qbuf[size] = '\0';
    pos = end = 0;
    len = size - 1;
  }

  linp = (char*)index(Qbuf+pos, '\r');  /* find next newline char */
  if (linp == NULL) {
    end = len;                   /* no newline chars left */

    /* make sure we don't exceed the bounds of s1 */
    /* this should not happen, but if it did, we would lose information */
    for(real=end-pos+1; real > MAXLINESIZE; end--, real--);

    strncpy(partial, Qbuf+pos, end-pos+1);
    *(partial+end-pos+1) = '\0';
    *theString = NULL;
  }
  else {
    end = linp - Qbuf;           /* convert pointer to index */
    if (*partial) {
      strcpy(theString,partial);

      /* make sure we don't exceed the bounds of s1 */
      for(real=end-pos+1; real + strlen(partial) > MAXLINESIZE; end--, real--);
      strncat(theString, Qbuf+pos, end-pos+1);

      *partial = NULL;

	  /*
      if (debug)
		fprintf(result,"debug> following line is a join: %d bytes\n",strlen(theString));
	  */
    }
    else {
      /* make sure we don't exceed the bounds of s1 */
      for(real=end-pos+1; real > MAXLINESIZE; end--, real--);

      strncpy(theString, Qbuf+pos, end-pos+1);
      *(theString+end-pos+1) = '\0';
    }
  }

  pos = end + 1;                 /* save new position for next time */

  if (!*theString)
    return (0);                  /* empty string==end of stream */
  else
    return (1);
  
}

