static char rcsid[] = "urld_client.c,v 1.12 1996/01/04 04:14:44 duane Exp";
/*
 * urld_stub.c - replaces liburl
 *
 * url_open()
 * url_read()
 * url_retrieve()
 * url_close()
 * ConnectToServer()
 * CheckServMsg()
 * SockClose()
 * CheckForDisconnect()
 *
 *  David Merkel & Mark Peterson, 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 <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/param.h>
#include <netdb.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/time.h>
#include "url.h"
#include "ccache.h"
#include "util.h"
#include "urld.h"

extern int select(), bcopy(), gethostname();
extern void bzero();

int theSocket = -1;

#define DISCONNECTED_CODE	-1
#define OP_ERROR_CODE		-2

/* Local functions */
int CheckForDisconnect();
int ConnectToServer();
int CheckServMsg();
void SockClose();


/* 
 * url_open() - parse url into URL struct
 */
URL *url_open(url)
     char *url;
{
	URL *theURL;
	char *message;
	int index, msgLen = INIT_PARAM_LEN;


	if (theSocket < 0) {
		theSocket = ConnectToServer();
		if (theSocket < 0) {
#ifdef DEBUG
			printf("no server connection\n");
#endif
			return (NULL);
		}
	}
	/* see if server has forced disconnect */
	if (CheckForDisconnect() < 0) {
		SockClose();
#ifdef DEBUG
		printf("disconnect issued by server\n");
#endif
		return (NULL);
	}
	message = (char *) malloc(msgLen);
	if (!message) {
		SockClose();
		return (NULL);
	}
	/* prepare message with operation code and url */
	bcopy(URL_OPEN_STR, message, MSG_LEN);
	if ((index = AddParam(url, STR, &message, &msgLen, MSG_LEN, TRUE)) < 0) {
		SockClose();
#ifdef DEBUG
		printf("AddParam error\n");
#endif
		free(message);
		return (NULL);
	}
	/* send message */
	if (SocketWrite(theSocket, message, index) < index) {
		SockClose();
		free(message);
		return (NULL);
	}
	free(message);
	if (CheckServMsg() != NO_ERROR) {
#ifdef NO_SERV_Q
		SockClose();
#endif
		return (NULL);
	}
	/* get return data */
	theURL = (URL *) malloc(sizeof(URL));
	if (!theURL) {
		SockClose();
		return (NULL);
	}
	if (GetURL(theURL, theSocket) < 0) {
#ifdef DEBUG
		printf("GetURL error\n");
#endif
		SockClose();
		free(theURL);
		return (NULL);
	}
#ifdef NO_SERV_Q
	SockClose();
#endif
	return (theURL);
}


/*
 * url_read() - perform liburl call, read bytes from url into buf
 */
int url_read(buf, bufsiz, offset, up)
     char *buf;
     int bufsiz;
     int offset;
     URL *up;
{
	char *message, *tempbuf;
	int index, *returnLen, msgLen = INIT_URL_LEN;

	if (theSocket < 0) {
		theSocket = ConnectToServer();
		if (theSocket < 0)
			return (-1);
	}
	/* check for forced disconnect from server */
	if (CheckForDisconnect() < 0) {
		SockClose();
		return (-1);
	}
	message = (char *) malloc(msgLen);
	if (!message) {
		SockClose();
		return (-1);
	}
	/* construct message */
	bcopy(URL_READ_STR, message, MSG_LEN);
	if (((index = AddParam((char *) &bufsiz, INT, &message, &msgLen, MSG_LEN, FALSE)) < 0) ||
	    ((index = AddParam((char *) &offset, INT, &message, &msgLen, index, FALSE)) < 0) ||
	    ((index = AddURL(up, &message, msgLen, index, TRUE)) < 0)) {
		SockClose();
		free(message);
		return (-1);
	}
	/* send message */
	if (SocketWrite(theSocket, message, index) < index) {
		SockClose();
		free(message);
		return (-1);
	}
	free(message);

	/* check for operation success */
	if (CheckServMsg() != NO_ERROR) {
#ifdef NO_SERV_Q
		SockClose();
#endif
		return (-1);
	}
	returnLen = (int *) malloc(sizeof(int));
	if (!returnLen) {
		SockClose();
		return (-1);
	}
	tempbuf = (char *) malloc(bufsiz);
	if (!tempbuf) {
		free(returnLen);
		SockClose();
		return (-1);
	}
	/* get size (in bytes) of data returned, and the data itself */
	if ((GetParam((char **) returnLen, INT, theSocket) < 0) ||
	    (GetParam((char **) &tempbuf, STR, theSocket) < 0)) {
		SockClose();
		free(returnLen);
		return (-1);
	}
	index = *returnLen;
	free(returnLen);
	bcopy(tempbuf, buf, index);
	free(tempbuf);
#ifdef NO_SERV_Q
	SockClose();
#endif
	return (index);
}


/*
 * url_retrieve() - retrieve object referred to by a url
 */
int url_retrieve(up)
     URL *up;
{
	char *message;
	URL *tempURL;
	int index, msgLen = INIT_URL_LEN;

	if (theSocket < 0) {
		theSocket = ConnectToServer();
		if (theSocket < 0)
			return (-1);
	}
	/* check for forced disconnect */
	if (CheckForDisconnect() < 0) {
		SockClose();
		return (-1);
	}
	message = (char *) malloc(msgLen);
	if (!message) {
		SockClose();
		return (-1);
	}
	/* construct message */
	bcopy(URL_RETR_STR, message, MSG_LEN);
	if ((index = AddURL(up, &message, msgLen, MSG_LEN, TRUE)) < 0) {
		SockClose();
		free(message);
		return (-1);
	}
	if (SocketWrite(theSocket, message, index) < index) {
		SockClose();
		free(message);
		return (-1);
	}
	free(message);

	/* check server reply */
	if (CheckServMsg() != NO_ERROR) {
#ifdef NO_SERV_Q
		SockClose();
#endif
		return (-1);
	}
	tempURL = (URL *) malloc(sizeof(URL));
	if (!tempURL) {
		SockClose();
		return (-1);
	}
	/* get returned URL */
	if (GetURL(tempURL, theSocket) < 0) {
		SockClose();
		free(tempURL);
		return (-1);
	}
	if (up->filename)
		free(up->filename);
	up->filename = (char *) malloc(strlen(tempURL->filename));
	if (!up->filename) {
		SockClose();
		free(tempURL);
		return (-1);
	}
	/* copy filename of retrieved url to caller's buffer */
	bcopy(tempURL->filename, up->filename, strlen(tempURL->filename));
	free(tempURL);
	return (0);
}


/*
 * url_close() - dispose of space and file referred to by a URL struct
 */
void url_close(up)
     URL *up;
{
	char *message;
	int index, msgLen = INIT_URL_LEN;

	if (theSocket < 0) {
		theSocket = ConnectToServer();
		if (theSocket < 0)
			return;
	}
	/* check for forced disconnect */
	if (CheckForDisconnect() < 0) {
		SockClose();
		return;
	}
	message = (char *) malloc(msgLen);
	if (!message) {
		SockClose();
		return;
	}
	/* construct message */
	bcopy(URL_CLOSE_STR, message, MSG_LEN);
	if ((index = AddURL(up, &message, msgLen, MSG_LEN, TRUE)) < 0) {
		SockClose();
		free(message);
		return;
	}
	if (SocketWrite(theSocket, message, index) < index) {
		SockClose();
		free(message);
		return;
	}
	free(message);
	(void) CheckServMsg();
	return;
}


/* 
 * ConnectToServer() - connects client to urld on local machine
 */
int ConnectToServer(void)
{
	int theSocket;
	char hostName[MAXHOSTNAMELEN];
	struct sockaddr_in theAddr;
	struct hostent *theEntry;

	if (gethostname(hostName, MAXHOSTNAMELEN) < 0)
		return (-1);

	/* get a socket */
	theSocket = socket(AF_INET, SOCK_STREAM, 0);
	if (theSocket < 0)
		return (-1);

	/* zero out theAddr structure */
	(void) bzero(&theAddr, sizeof(theAddr));

	theAddr.sin_family = AF_INET;
	theAddr.sin_port = htons(SERVER_PORT);

	/* get hostent for hostAddr */
	theEntry = gethostbyname(hostName);
	if (!theEntry) {
#ifdef DEBUG
		printf("can't gethostbyname\n");
#endif
		return (-1);
	}
	/* copy host information into theAddr */
	(void) bcopy(theEntry->h_addr, &theAddr.sin_addr.s_addr,
	    theEntry->h_length);

	/* establish connection */
	if (connect(theSocket, (struct sockaddr *) &theAddr, sizeof(theAddr)) < 0) {
#ifdef DEBUG
		perror("can't connect\n");
#endif
		return (-1);
	}
	return (theSocket);
}


/*
 * CheckServMsg() - check urld replies for errors
 */
int CheckServMsg(void)
{
	char reply[MSG_LEN + 1];
	u_long replyChk;
	int readBytes;

	/* check for server response...if we timeout then assume
	 * server error and interpret as disconnect message
	 */
	readBytes = MyRead(theSocket, reply, MSG_LEN, CLIENT_TIMEOUT);
	if (readBytes < MSG_LEN)
		return (DISCONNECT);

	reply[MSG_LEN] = '\0';

	replyChk = htonl(*(u_long *) reply);
	switch (replyChk) {
	case OPEN_OK:
	case READ_OK:
	case RETR_OK:
	case CLOSE_OK:
		return (NO_ERROR);

	case OPEN_ERR:
	case READ_ERR:
	case RETR_ERR:
	case CLOSE_ERR:
		return (OP_ERROR_CODE);

	case DISCNCT:
		return (DISCONNECTED_CODE);

	default:
		return (NO_ERROR);
	}
}


/* 
 * SockClose() - close connection to urld
 */
void SockClose()
{
	close(theSocket);
	theSocket = -1;
}


/*
 * CheckForDisconnect() - check to see if server has forced disconnect
 */
int CheckForDisconnect(void)
{
	fd_set readDetect;
	struct timeval timeout;

	timeout.tv_sec = 0;
	timeout.tv_usec = 0;
	FD_ZERO(&readDetect);
	FD_SET(theSocket, &readDetect);

	if (select(theSocket + 1, &readDetect, NULL, NULL, &timeout) < 0)
		return (-1);

	if (FD_ISSET(theSocket, &readDetect)) {
		if (CheckServMsg() == DISCONNECTED_CODE)
			return (-1);
	}
	return (1);
}

void init_url()
{
}
void finish_url()
{
}
