static char rcsid[] = "urld_util.c,v 1.12 1996/01/04 04:14:46 duane Exp";
/*
 * urld_util.c - functions that urld uses to communicate over sockets
 *
 *      InitDSocket()
 *      WaitForRequest()
 *      DoURLOpen()
 *      DoURLRead()
 *      DoURLRetrieve()
 *      DoURLClose()
 *      ServiceClient() 
 *
 *  David Merkel, University of Colorado - Boulder, July 1994
 *
 *  ----------------------------------------------------------------------
 *  Copyright (c) 1994, 1995.  All rights reserved.
 *  
 *    The Harvest software was developed by the Internet Research Task
 *    Force Research Group on Resource Discovery (IRTF-RD):
 *  
 *          Mic Bowman of Transarc Corporation.
 *          Peter Danzig of the University of Southern California.
 *          Darren R. Hardy of the University of Colorado at Boulder.
 *          Udi Manber of the University of Arizona.
 *          Michael F. Schwartz of the University of Colorado at Boulder.
 *          Duane Wessels of the University of Colorado at Boulder.
 *  
 *    This copyright notice applies to software in the Harvest
 *    ``src/'' directory only.  Users should consult the individual
 *    copyright notices in the ``components/'' subdirectories for
 *    copyright information about other software bundled with the
 *    Harvest source code distribution.
 *  
 *  TERMS OF USE
 *    
 *    The Harvest software may be used and re-distributed without
 *    charge, provided that the software origin and research team are
 *    cited in any use of the system.  Most commonly this is
 *    accomplished by including a link to the Harvest Home Page
 *    (http://harvest.cs.colorado.edu/) from the query page of any
 *    Broker you deploy, as well as in the query result pages.  These
 *    links are generated automatically by the standard Broker
 *    software distribution.
 *    
 *    The Harvest software is provided ``as is'', without express or
 *    implied warranty, and with no support nor obligation to assist
 *    in its use, correction, modification or enhancement.  We assume
 *    no liability with respect to the infringement of copyrights,
 *    trade secrets, or any patents, and are not responsible for
 *    consequential damages.  Proper use of the Harvest software is
 *    entirely the responsibility of the user.
 *  
 *  DERIVATIVE WORKS
 *  
 *    Users may make derivative works from the Harvest software, subject 
 *    to the following constraints:
 *  
 *      - You must include the above copyright notice and these 
 *        accompanying paragraphs in all forms of derivative works, 
 *        and any documentation and other materials related to such 
 *        distribution and use acknowledge that the software was 
 *        developed at the above institutions.
 *  
 *      - You must notify IRTF-RD regarding your distribution of 
 *        the derivative work.
 *  
 *      - You must clearly notify users that your are distributing 
 *        a modified version and not the original Harvest software.
 *  
 *      - Any derivative product is also subject to these copyright 
 *        and use restrictions.
 *  
 *    Note that the Harvest software is NOT in the public domain.  We
 *    retain copyright, as specified above.
 *  
 *  HISTORY OF FREE SOFTWARE STATUS
 *  
 *    Originally we required sites to license the software in cases
 *    where they were going to build commercial products/services
 *    around Harvest.  In June 1995 we changed this policy.  We now
 *    allow people to use the core Harvest software (the code found in
 *    the Harvest ``src/'' directory) for free.  We made this change
 *    in the interest of encouraging the widest possible deployment of
 *    the technology.  The Harvest software is really a reference
 *    implementation of a set of protocols and formats, some of which
 *    we intend to standardize.  We encourage commercial
 *    re-implementations of code complying to this set of standards.  
 *  
 */
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/param.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include "url.h"
#include "ccache.h"
#include "urld.h"
#include "util.h"

/* Local functions */
int DoURLOpen();
int DoURLRead();
int DoURLRetrieve();
int DoURLClose();

/* Local variables */
static int urldError = NO_ERROR;


int GetURLDErr()
{
	return (urldError);
}

/*
 * InitDSocket() - initialize daemon socket for client requests
 */
int InitDSocket(portNumber)
     u_short portNumber;
{
	struct sockaddr_in theAddr;
	struct hostent *theEntry;
	char hostName[MAXHOSTNAMELEN];
	int theSocket;

	if (gethostname(hostName, MAXHOSTNAMELEN) < 0) {
		urldError = URLD_GET_HOSTNAME_ERR;
		return (-1);
	}
	theEntry = gethostbyname(hostName);
	if (!theEntry) {
		urldError = URLD_GET_HOSTBYNAME_ERR;
		return (-1);
	}
#ifdef HAVE_BZERO
	bzero(&theAddr, sizeof(theAddr));
#else
	memset(&theAddr, '\0', sizeof(theAddr));
#endif
	theAddr.sin_family = theEntry->h_addrtype;
	theAddr.sin_port = htons(portNumber);

	if ((theSocket = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		urldError = URLD_GET_SOCK_ERR;
		return (-1);
	}
	if (bind(theSocket, (struct sockaddr *) &theAddr, sizeof(theAddr)) < 0) {
		urldError = BIND_SOCK_ERR;
		return (-1);
	}
	if (listen(theSocket, BACK_LOG) < 0) {
		urldError = URLD_LISTEN_ERR;
		return (-1);
	}
	urldError = NO_ERROR;
	return (theSocket);
}


/* 
 * WaitForRequest() - sits and listens to all open connections to clients,
 * then accepts new connections or services client requests
 */
int WaitForRequest(theSocket, sockDetect, lastSocket)
     int theSocket;
     fd_set *sockDetect;
     int lastSocket;
{
	struct sockaddr_in theAddr;
	int newSocket, addrLen, err;

	addrLen = sizeof(theAddr);
	bzero(&theAddr, addrLen);

	if (getsockname(theSocket, (struct sockaddr *) &theAddr, &addrLen) < 0) {
		urldError = URLD_GET_SOCKNAME_ERR;
		return (-1);
	}
	/* need this because addrLen is used later and may have been
	 * changed in previous call
	 */
	addrLen = sizeof(theAddr);

	/* if we want STDIN recognized we need to add it here so
	 * the loop breaks when user input occurs
	 */

	while (1) {
		if ((err = select(lastSocket + 1, sockDetect, NULL, NULL, NULL)) <= 0) {
			/* handled interrupted system call case */
			if (err < 0) {
				if (errno != EINTR) {
					urldError = SELECT_ERR;
					return (-1);
				}
			} else {
				urldError = SELECT_ERR;
				return (-1);
			}
		}
		if (FD_ISSET(theSocket, &(*sockDetect))) {
			newSocket = accept(theSocket, (struct sockaddr *) &theAddr, &addrLen);
			if (newSocket < 0) {
				urldError = ACCEPT_ERR;
				return (-1);
			}
			urldError = NO_ERROR;
			return (newSocket);
		} else {
			urldError = SERVICE_REQUEST;
			return (-1);
		}
	}
}


/* 
 * DoURLOpen() - handles client request for url_open() call
 */
int DoURLOpen(theSocket)
     int theSocket;
{
	char *buffer, reply[MSG_LEN];
	URL *openURL;
	int index;

	/* get the url string */
	if (GetParam(&buffer, STR, theSocket) < 0) {
		free(buffer);
		urldError = GET_PARAM_ERR;
		return (-1);
	}
	/* perform liburl call */
	openURL = url_open(buffer);
	free(buffer);
	if (!openURL) {
		urldError = URL_OPEN_ERR;
		return (-1);
	}
	/* now we send URL by having something create a ParamBlock for
	 * us, parsed correctly by urld protocol
	 */

	buffer = (char *) malloc(INIT_PARAM_LEN);

	if ((index = AddURL(openURL, &buffer, INIT_PARAM_LEN, 0, TRUE)) < 0) {
		urldError = ADD_URL_ERR;
		free(buffer);
		return (-1);
	}
	/* tell client operation successful */
	bcopy(OPEN_OK_STR, reply, MSG_LEN);
	if (SocketWrite(theSocket, reply, MSG_LEN) < MSG_LEN) {
		urldError = SOCK_WRITE_ERR;
		return (-1);
	}
	/* return URL to client */
	if (SocketWrite(theSocket, buffer, index) < index) {
		urldError = SOCK_WRITE_ERR;
		free(buffer);
		return (-1);
	}
	free(buffer);
	return (1);
}


/*
 * DoURLRead() - performs url_read call
 */
int DoURLRead(theSocket)
     int theSocket;
{
	int *bufsiz, *offset, readSize, index, resultLen;
	URL *theURL;
	char *readBuf, reply[MSG_LEN], *resultMsg;

	bufsiz = (int *) malloc(sizeof(int));
	offset = (int *) malloc(sizeof(int));

	if ((!bufsiz) || (!offset)) {
		urldError = URLD_MEMORY_ERROR;
		return (-1);
	}
	/* get bufsiz and offset params for call */
	if (GetParam((char **) bufsiz, INT, theSocket) < 0) {
		free(bufsiz);
		free(offset);
		urldError = GET_PARAM_ERR;
		return (-1);
	}
	if (GetParam((char **) offset, INT, theSocket) < 0) {
		free(bufsiz);
		free(offset);
		urldError = GET_PARAM_ERR;
		return (-1);
	}
	theURL = (URL *) malloc(sizeof(URL));
	if (!theURL) {
		free(bufsiz);
		free(offset);
		urldError = URLD_MEMORY_ERROR;
		return (-1);
	}
	/* get URL to read */
	if (GetURL(theURL, theSocket) < 0) {
		free(bufsiz);
		free(offset);
		free(theURL);
		urldError = GET_URL_ERR;
		return (-1);
	}
#if DEBUG > 5
	printf("bufsiz: %d, offset: %d\n", *bufsiz, *offset);
	PrintURL(theURL);
#endif
	readBuf = (char *) malloc(*bufsiz + 1);
	if (!readBuf) {
		free(bufsiz);
		free(offset);
		free(theURL);
		urldError = URLD_MEMORY_ERROR;
		return (-1);
	}
	/* perform liburl call */
	readSize = url_read(readBuf, *bufsiz, *offset, theURL);
	readBuf[*bufsiz] = '\0';

#if DEBUG > 5
	printf("readBuf: %s, readSize: %d\n", readBuf, readSize);
#endif
	free(bufsiz);
	free(offset);
	free(theURL);
	if (readSize < 0) {
		free(readBuf);
		urldError = URL_READ_ERR;
		return (-1);
	}
	/* last step, pass to client number of bytes read and
	 * data
	 */

	resultLen = readSize + sizeof(int) + (2 * TERM_LEN) + 1;
	resultMsg = (char *) malloc(resultLen);
	if (!resultMsg) {
		urldError = URLD_MEMORY_ERROR;
		free(readBuf);
		return (-1);
	}
	if ((index = AddParam((char *) &readSize, INT, &resultMsg,
		    &resultLen, 0, FALSE)) < 0) {
		urldError = ADD_PARAM_ERR;
		free(readBuf);
		free(resultMsg);
		return (-1);
	}
	if ((index = AddParam(readBuf, STR, &resultMsg,
		    &resultLen, index, TRUE)) < 0) {
		urldError = ADD_PARAM_ERR;
		free(readBuf);
		free(resultMsg);
		return (-1);
	}
	/* tell client operation successful */
	bcopy(READ_OK_STR, reply, MSG_LEN);
	if (SocketWrite(theSocket, reply, MSG_LEN) < MSG_LEN) {
		urldError = SOCK_WRITE_ERR;
		free(readBuf);
		free(resultMsg);
		return (-1);
	}
	/* return data */
	if (SocketWrite(theSocket, resultMsg, resultLen) < resultLen) {
		urldError = SOCK_WRITE_ERR;
		free(readBuf);
		free(resultMsg);
		return (-1);
	}
	free(readBuf);
	free(resultMsg);
	return (1);
}


/* 
 * DoURLRetrieve() - performs url_retrieve all
 */
int DoURLRetrieve(theSocket)
     int theSocket;
{
	URL *theURL;
	char *message, reply[MSG_LEN];
	int length;

	theURL = (URL *) malloc(sizeof(URL));
	if (!theURL) {
		urldError = URLD_MEMORY_ERROR;
		return (-1);
	}
	/* get URL to retrieve from client */
	if (GetURL(theURL, theSocket) < 0) {
		free(theURL);
		urldError = GET_URL_ERR;
		return (-1);
	}
	/* perform liburl call */
	if (url_retrieve(theURL)) {
		free(theURL);
		urldError = URL_RETR_ERR;
		return (-1);
	}
	message = (char *) malloc(INIT_URL_LEN);
	if (!message) {
		free(theURL);
		urldError = URLD_MEMORY_ERROR;
		return (-1);
	}
	if ((length = AddURL(theURL, &message, INIT_URL_LEN, 0, TRUE)) < 0) {
		urldError = ADD_URL_ERR;
		free(theURL);
		free(message);
		return (-1);
	}
	/* notify client of op success */
	bcopy(RETR_OK_STR, reply, MSG_LEN);
	if (SocketWrite(theSocket, reply, MSG_LEN) < MSG_LEN) {
		urldError = SOCK_WRITE_ERR;
		free(theURL);
		free(message);
		return (-1);
	}
	/* return data */
	if (SocketWrite(theSocket, message, length) < length) {
		urldError = SOCK_WRITE_ERR;
		free(theURL);
		free(message);
		return (-1);
	}
	free(theURL);
	free(message);
	return (1);
}


/* 
 * DoURLClose() - performs url_close call
 */
int DoURLClose(theSocket)
     int theSocket;
{
	URL *theURL;
	char reply[MSG_LEN];

	theURL = (URL *) malloc(sizeof(URL));
	if (!theURL) {
		urldError = URLD_MEMORY_ERROR;
		return (-1);
	}
	/* get URL to close */
	if (GetURL(theURL, theSocket) < 0) {
		urldError = GET_URL_ERR;
		free(theURL);
		return (-1);
	}
	/* close URL */
	url_close(theURL);

	/* notify client of op success */
	bcopy(CLOSE_OK_STR, reply, MSG_LEN);
	if (SocketWrite(theSocket, reply, MSG_LEN) < MSG_LEN) {
		urldError = SOCK_WRITE_ERR;
		free(theURL);
		return (-1);
	}
	free(theURL);
	return (1);
}


/* 
 * ServiceClient() - service client requests
 */
int ServiceClient(theSocket)
     int theSocket;
{
	char message[MSG_LEN + 1];
	char reply[MSG_LEN];
	u_long msgType;
	int readLength;

	readLength = MyRead(theSocket, message, MSG_LEN, SERVER_TIMEOUT);
	if (readLength <= 0) {
		urldError = NO_REQ_PRESENT;
		return (-1);
	}
	message[MSG_LEN] = '\0';
	msgType = htonl(*(u_long *) message);

	switch (msgType) {
	case URL_OPEN:
#if DEBUG > 3
		printf("performing DoURLOpen\n");
#endif
		if (DoURLOpen(theSocket) < 0) {
			bcopy(OPEN_ERR_STR, reply, MSG_LEN);
			(void) SocketWrite(theSocket, reply, MSG_LEN);
			return (-1);
		}
		break;

	case URL_READ:
#if DEBUG > 3
		printf("performing DoURLRead\n");
#endif
		if (DoURLRead(theSocket) < 0) {
			bcopy(READ_ERR_STR, reply, MSG_LEN);
			(void) SocketWrite(theSocket, reply, MSG_LEN);
			return (-1);
		}
		break;

	case URL_RETR:
#if DEBUG > 3
		printf("performing DoURLRetrieve\n");
#endif
		if (DoURLRetrieve(theSocket) < 0) {
			bcopy(RETR_ERR_STR, reply, MSG_LEN);
			(void) SocketWrite(theSocket, reply, MSG_LEN);
			return (-1);
		}
		break;

	case URL_CLOSE:
#if DEBUG > 3
		printf("performing DoURLClose\n");
#endif
		if (DoURLClose(theSocket) < 0) {
			bcopy(CLOSE_ERR_STR, reply, MSG_LEN);
			(void) SocketWrite(theSocket, reply, MSG_LEN);
			return (-1);
		}
		break;

	case DISCNCT:
		urldError = CLIENT_DISCONNECT;
		return (-1);

	default:
		break;
	}

	return (1);
}
