/********************************************************************
 * $Author: lindner $
 * $Revision: 1.1.1.3 $
 * $Date: 1993/01/11 19:54:06 $
 * $Source: /home/mudhoney/GopherSrc/release1.11/object/RCS/GSgopherobj.c,v $
 * $Status: $
 *
 * Paul Lindner, University of Minnesota CIS.
 *
 * Copyright 1991, 1992 by the Regents of the University of Minnesota
 * see the file "Copyright" in the distribution for conditions of use.
 *********************************************************************
 * MODULE: GSgopherobj.c
 * Implement gopher directory functions.
 *********************************************************************
 * Revision History:
 * $Log: GSgopherobj.c,v $
 * Revision 1.1.1.3  1993/01/11  19:54:06  lindner
 * Fixed syntax in UCX #defines. (DuH!)
 *
 * Revision 1.1.1.2  1993/01/08  19:00:00  lindner
 * Mods for UCX
 *
 * Revision 1.1.1.1  1992/12/31  05:01:31  lindner
 * Chnages for VMS.
 *
 * Revision 1.1  1992/12/17  21:03:54  jqj
 * Initial revision
 *
 * Revision 1.1  1992/12/10  23:27:52  lindner
 * gopher 1.1 release
 *
 *
 *********************************************************************/

#include "GSgopherobj.h"

#if defined(mips) && defined(ultrix)   /*** Gross hack, yuck! ***/
#define _SIZE_T
#endif

#include "String.h"
#include <stdio.h>
#include "compatible.h"
extern int DEBUG;

/*
 * These are needed for GSconnect().  Ick.
 */
#ifdef VMS
#include <socket.h>
#include <in.h>
#include <file.h>
#include <inet.h>
#include <netdb.h>

#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/file.h>
#ifndef hpux
#include <arpa/inet.h>
#endif
#include <netdb.h>
#endif  /* not VMS */

#include "Malloc.h"

/*
 * Make a new gopherobj, set the fields accordingly...
 */

GopherObj *
GSnewSet(objtype, Title, Selstr, Host, Port)
  char objtype;
  char *Title;
  char *Selstr;
  char *Host;
  int Port;
{
     GopherObj *temp;

     temp = (GopherObj *) malloc(sizeof(GopherObj));
     temp->Selstr   = STRnewSet(Selstr);
     temp->Title    = STRnewSet(Title);
     temp->Host     = STRnewSet(Host);
     temp->iPort    = Port;
     GSsetNum(temp, -1);
     GSsetWeight(temp, 0);

     return(temp);
}


/*
 * Make a new gopherobj...  Should reuse destroyed GopherObjs...
 */

GopherObj *
GSnew()
{
     GopherObj *temp;

     temp = (GopherObj *) malloc(sizeof(GopherObj));
     temp->Selstr = STRnew();
     temp->Title  = STRnew();
     temp->Host   = STRnew();

     GSinit(temp);
     
     return(temp);
}

void
GSdestroy(gs)
  GopherObj *gs;
{
     STRdestroy(gs->Selstr);
     STRdestroy(gs->Title);
     STRdestroy(gs->Host);

     free(gs);
}
     


/*
 * Clear out all the crud 
 */

void
GSinit(gs)
  GopherObj *gs;
{
     GSsetType(gs, '\0');
     
     STRinit(gs->Title);
     STRinit(gs->Selstr);
     STRinit(gs->Host);

     gs->iPort = 0;
     GSsetNum(gs, -1);
     GSsetWeight(gs, 0);
}


void
GStoNet(gs, sockfd)
  GopherObj *gs;
  int sockfd;
{
     static char buf[1024];

     buf[0] = GSgetType(gs);

     sprintf(buf + 1, "%s\t%s\t%s\t%d\r\n",
	     GSgetTitle(gs),
	     GSgetPath(gs),
	     GSgetHost(gs),
	     GSgetPort(gs));
     
     writestring(sockfd, buf);
     
     if (DEBUG)
	  fprintf(stderr, buf);

}


void
GStoNetHTML(gs, sockfd)
  GopherObj *gs;
  int sockfd;
{
     static char buf[1024];
     static char pathbuf[1024];

     buf[0] = '\0';
     pathbuf[0] = '\0';

     /** Convert Path so that spaces are %20 **/
     Tohexstr(GSgetPath(gs), pathbuf);

     sprintf(buf, "<A HREF=http://%s:%d/%s>%s</A>",
	     GSgetHost(gs),
	     GSgetPort(gs),
	     pathbuf,
	     GSgetTitle(gs));

     writestring(sockfd, buf);

     if (DEBUG)
	  fprintf(stderr, "HTML: %s", buf);
     
     if (GSgetWeight(gs) != 0) {
	  sprintf(buf, "Score: %d\r\n", GSgetWeight(gs));
	  writestring(sockfd, buf);
     }
     else
	  writestring(sockfd, "\r\n");

}


/*
 * Fill in a GopherObj, given an HREF= link from a WWW anchor..
 * So far only works with http
 */

void
GSfromHREF(gs, href)
  GopherObj *gs;
  char *href;
{
     char *cp;
     char *host;
     char path[1024];
     
     if (strncasecmp(href, "http://", 7)==0) {
	  host = href +7;
	  cp  = strchr(href+7, '/');
	  if (cp == NULL)
	       return;
	  
	  *cp = '\0';
	  strcpy(path, "GET ");
	  strcat(path, cp+1);
	  GSsetPath(gs, path);

	  GSsetType(gs, 'h');

	  GSsetPath(gs, path);
	  
	  cp = strchr(host, ':');
	  if (cp==NULL) 
	       GSsetPort(gs, 80);  /** default WWW port **/
	  else {
	       GSsetPort(gs, atoi(cp+1));
	       *cp = '\0';
	  }

	  GSsetHost(gs, host);
     }
}
	  



extern int readfield();
extern int readline();

int
GSfromNet(gs, sockfd)
  GopherObj *gs;
  int sockfd;
{
     char foo[1024];
     
     if (readfield(sockfd, foo, 1024)<= 0) {
	  /* EOF or error */
	  return(-1);
     }

     GSsetType(gs, foo[0]);

     /** Get the kind of file from the first character **/
     /** Filter out files that we can't deal with **/

     switch (GSgetType(gs)) {
       case A_FILE:
       case A_DIRECTORY:
       case A_MACHEX:
       case A_PCBIN:
       case A_CSO:
       case A_INDEX:
       case A_TELNET:
       case A_SOUND:
       case A_UNIXBIN:
       case A_GIF:
       case A_HTML:
       case A_TN3270:
       case A_MIME:
       case A_IMAGE:
	  break;
       case A_EOI:
	  if (foo[1] == '\r' && foo[2] == '\n')
	       return(1);
     default:
	return(-2);  
     }

     /** Suck off the User Displayable name **/
     GSsetTitle(gs, foo+1);
     
     /** Suck off the Pathname **/
     if (readfield(sockfd, foo, 1024) == 0)
	  return(-1);
     GSsetPath(gs, foo);

     /** Suck off the hostname **/
     if (readfield(sockfd, foo, 1024) == 0)
	  return(-1);
     GSsetHost(gs, foo);

     if (readline(sockfd, foo, 1024)==0)
	  return(-1); 

     GSsetPort(gs, 0);

     /** Get the port number **/
     GSsetPort(gs, atoi(foo));

     return(0);
}

/** Copy a GopherObj ***/

void
GScpy(dest, orig)
  GopherObj *dest, *orig;
{
     dest->sFileType = orig->sFileType;
     dest->iPort     = orig->iPort;
     dest->Itemnum   = orig->Itemnum;

     GSsetTitle(dest, GSgetTitle(orig));
     GSsetPath(dest, GSgetPath(orig));
     GSsetHost(dest, GSgetHost(orig));
}

/** Compare two GopherObjs ***/

int
GScmp(gs1, gs2)
  GopherObj *gs1, *gs2;
{
     if (GSgetTitle(gs1) == NULL)
	  return(1);
     if (GSgetTitle(gs2) == NULL)
	  return(-1);

     return(strcmp(GSgetTitle(gs1), GSgetTitle(gs2)));
}




/* GSconnect performs a connection to socket 'service' on host
 * 'host'.  Host can be a hostname or ip-address.  If 'host' is null, the
 * local host is assumed.   The parameter full_hostname will, on return,
 * contain the expanded hostname (if possible).  Note that full_hostname is a
 * pointer to a char *, and is allocated by connect_to_gopher()
 *
 * Errors:
 *
 * -1 get service failed
 *
 * -2 get host failed
 *
 * -3 socket call failed
 *
 * -4 connect call failed
 */

int
GSconnect(gs)
  GopherObj *gs;
{
     struct sockaddr_in Server;
     struct hostent *HostPtr;
     int iSock = 0;
     int ERRinet = -1;

#ifdef _CRAY
     ERRinet = 0xFFFFFFFF;  /* -1 doesn't sign extend on 64 bit machines */
#endif

     /*** Find the hostname address ***/
     
     if ((Server.sin_addr.s_addr = inet_addr(GSgetHost(gs))) == ERRinet) {
	  if (HostPtr = gethostbyname(GSgetHost(gs))) {
	       bzero((char *) &Server, sizeof(Server));
	       bcopy(HostPtr->h_addr, (char *) &Server.sin_addr, HostPtr->h_length);
	       Server.sin_family = HostPtr->h_addrtype;
	  } else
	       return (-2);
     } else
	  Server.sin_family = AF_INET;

     Server.sin_port = (unsigned short) htons(GSgetPort(gs));

     /*** Open the socket ***/

     if ((iSock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	  return (-3);

#ifndef UCX
     setsockopt(iSock, SOL_SOCKET, ~SO_LINGER, 0, 0);
#endif

     setsockopt(iSock, SOL_SOCKET, SO_REUSEADDR, 0, 0);
     setsockopt(iSock, SOL_SOCKET, SO_KEEPALIVE, 0, 0);

     /*** Connect ***/

     if (connect(iSock, (struct sockaddr *) &Server, sizeof(Server)) < 0) {
	  closenet(iSock);
	  return (-4);
     }
     return(iSock);
}

/*
 * GSfromLink takes an open file descriptor and starts reading from it.
 * It keeps going until it findsL
 *    enough fields for a gopherobj
 *    no data left
 * 
 * returns 0 with success, -1 on an error.
 */

#define	G_PATH	(1<<0)
#define	G_TYPE  (1<<1)
#define	G_NAME	(1<<2)
#define	G_PORT	(1<<3)
#define	G_HOST	(1<<4)
#define	G_ALL (G_PATH | G_TYPE | G_NAME | G_PORT | G_HOST)

int
GSfromLink(gs, fd, host, port)
  GopherObj *gs;
  int       fd;
  char      *host;
  int       port;
{
     int doneflags = 0;
     char buf[1024];

     while ((doneflags != G_ALL) && readline(fd, buf, 1024)) {
	  if (buf[0] == '#')
	       continue;   /* comment */

	  ZapCRLF(buf);

	  if (strncmp(buf, "Type=", 5)==0) {
	       GSsetType(gs, buf[5]);
	       doneflags |= G_TYPE;
	  }
	  
	  if (strncmp(buf, "Name=", 5)==0) {
	       GSsetTitle(gs, buf+5);
	       doneflags |= G_NAME;
	  }
 	  
	  if (strncmp(buf, "Path=", 5)==0) {
	       GSsetPath(gs, buf+5);
	       doneflags |= G_PATH;
	  }
 	  
	  if (strncmp(buf, "Host=", 5)==0) {
	       if (buf[5] == '+' && buf[6] == '\0')
		    GSsetHost(gs, host);
	       else
		    GSsetHost(gs, buf+5);

	       doneflags |= G_HOST;
	  }
	  if (strncmp(buf, "Port=", 5)==0) {
	       if (buf[5] == '+' && buf[6] == '\0')
		    GSsetPort(gs, port);
	       else
		    GSsetPort(gs, atoi(buf+5));

	       doneflags |= G_PORT;
	  }
	  if (strncmp(buf, "Numb=", 5)==0)
	       GSsetNum(gs, atoi(buf+5));
	  
     }
     return ((doneflags == G_ALL) ? 0 : -1); /* 0 == success */
}


void
GStoLink(gs, fd)
  GopherObj *gs;
  int fd;
{
     char gtype[2];
     char portnum[16];
     
     gtype[0] = GSgetType(gs);
     gtype[1] = '\0';

     writestring(fd, "#");
     writestring(fd, "\nType=");
     writestring(fd, gtype);
     writestring(fd, "\nName=");
     writestring(fd, GSgetTitle(gs));
     writestring(fd, "\nPath=");
     writestring(fd, GSgetPath(gs));
     writestring(fd, "\nHost=");
     writestring(fd, GSgetHost(gs));
     writestring(fd, "\nPort=");
     sprintf(portnum, "%d", GSgetPort(gs));
     writestring(fd, portnum);
     writestring(fd, "\n");
}

