/* -------------------------------------------------
*	g2fd.c          Gopher to FTP gateway daemon.
*	Version 0.3 Hacked up: April 1992.  Farhad Anklesaria.
*	Based on a Perl story by John Ladwig.
*	Based on a Perl story by Farhad Anklesaria.
*
*	To be run by inetd.
*	For installation, read the companion README.... or
*	if you don't feel like doing that, the brief instructions
*	are below:
*	
*	First edit the local parameters in defines below.
*	Then do a "make g2fd"
*	Then place the binary (g2fd) in some reasonable place,
*		eg in /usr/local/bin
*	Finally, edit /etc/services and /etc/servers or
*	 	/etc/inetd.conf as the case may be.  Kill
*		and restart inetd.
*
---------------------------------------------------- */

#include "gopherd.h"
#include <signal.h>

#include <stdio.h>


#define GFILE	0		/* Gopher item types */
#define GDIR	1
#define GBINHEX	4
#define GDOSB	5
#define GUNIXB	9

#define SLEN	255	/* Generic small buffer length */
#define	TMOUT	1200	/* 20 minutes is plenty long enough */

char	*appname;
char	ftp[SLEN] = FTP;
char	tmpList[SLEN] = LIST;
char	tmpData[SLEN] = DATA;
char	query[BUFSIZ];				/* input redirected by inetd */
char 	*host, *thing;				/* pointers into query */
int		gettingFile = 1;
int		gettingBinary = 0;
int		childpid;


SendFtpQuery(query)
  char *query;
{
     int   sLen, termCh;
     char  *at;
     FILE  *fd, *popen();
     
     if (DEBUG)
	  printf("The full query was %s\n", query);
     
     if ((sLen = strlen(query)) <= 2) Abort("No host name specified.");
     host = query;
     at = strchr(query, '@');

     if (at == NULL) 
	  Abort("Not a valid ftp query.");

     thing = at + 1;
     *at = '\0'; 			/*Sneakily chop it into two strings*/
     sprintf(ftp + strlen(ftp), " -n %s > %s", host, tmpList);

     if ((fd = popen(ftp, "w")) == NULL) 
	  Abort("Can't run ftp.");

     FailErr(fprintf(fd, "user anonymous gopher@%s\n", Zehostname));
	
     sLen = strlen(thing);
     termCh = thing[sLen - 1];		    /* Grab possible end char: / etc */

     if ((termCh == '*') || (termCh == '@'))  /*  || (termCh == '/')  */
	  thing[sLen - 1] = '\0';

     if (DEBUG)
	  printf("At this point host: %s   thing: %s\n", host, thing);

     if (termCh == '/') {	/* We have a directory */
	  gettingFile = 0;
	  if (strlen(thing) > 0) 
	       FailErr(fprintf(fd, "cd \"%s\"\n", thing));
	  FailErr(fprintf(fd, "ls -F\n"));
     } 
     else {	                /* We have a file */
	  gettingFile = 1;
	  if (gettingBinary = IsBinaryType(thing))  
	       FailErr(fprintf(fd, "binary\n"));
	  FailErr(fprintf(fd, "get \"%s\" \"%s\"\n", thing, tmpData));
     }
     FailErr(fprintf(fd, "quit\n"));
     pclose(fd);
}

/*--------------------------------*/

TranslateResults(sockfd)
{
     FILE  *fp, *OpenOrDie();
     char  buf[BUFSIZ], theName[SLEN];
     int   fd, nRead, checkIt;
     char  outputline[512];
     
     checkIt = 1;

     if (gettingFile) {
	  if (gettingBinary) {				/* icky binary file */
	       if (DEBUG)
		    printf("Whoa!  That's a binary file\n");
	       fd = uopen(tmpData, "r");
	       while ((nRead = read(fd, buf, sizeof buf)) > 0) {
		    writen(sockfd, buf, nRead);
	       }
	       close(fd);
	  } else {		    /* must be a nice texty file */
	       fp = OpenOrDie(tmpData, "r");
	       while (fgets(buf, sizeof buf, fp) != NULL) {
		    if (checkIt) { /* Just peek at it once */
			 checkIt = 0;
			 if (NotText(buf)) {
			      fclose(fp);
			      Abort("Sorry.  File does not appear to contain text.");
			 }
		    }
		    ZapCRLF(buf);
		    writestring(sockfd, buf);
		    writestring(sockfd, "\r\n");
		    /*FailErr(printf("%s\r\n", buf));*/
	       }
	       fclose(fp);
	       FailErr(writestring(sockfd,".\r\n"));
	  }
     } else {					   /* Must be a directory */
	  fp = OpenOrDie(tmpList, "r");
	  while (fgets(buf, sizeof buf, fp) != NULL) {
	       GopherType(buf, theName);
	       sprintf(outputline, "%s\tftp:%s@%s%s\t%s\t%d\r\n", theName, 
		       host, thing, buf, Zehostname, GopherPort);
	       writestring(sockfd, outputline);
	  }
	  fclose(fp);
	  FailErr(writestring(sockfd, ".\r\n"));
     }
}

/*--------------------------------*/

FILE *OpenOrDie(file, mode)
  char *file, *mode;
{
     FILE *fp, *fopen();
     if ((fp = ufopen(file, mode)) != NULL) {
	  return(fp);
     } else {
	  Abort("Could not complete the transfer.");
     }
}


/*--------------------------------*/
NotText(buf)
  char * buf;
{
     int max;   char *c;
     
     if ((max = strlen(buf)) >= (BUFSIZ - 50)) max = BUFSIZ - 50;
     for (c = buf; c < (buf + max); c++) {
	  if (*c > '~') return(1);
     }
     return(0);
}

/*--------------------------------*/

Abort(complaint)
  char *complaint;
  
{
     printf("3 Error: %s\r\n.\r\n", complaint); 
     Cleanup();
     exit(1);
}

/*--------------------------------*/

IsBinaryType(thing)
  char *thing;
{
     static char *binExt[] = {
	  ".zip", ".zoo", ".arj", ".arc", ".lzh", ".hyp", ".pak", ".exe", ".com",
	  ".ps", ".gif", ".pict", ".pct", ".tiff", ".tif", ".tar", ".Z"
	  };
     
     int extType, i;
     
     for (extType = 0; extType < 17; extType++) { 
	  i = strcasecmp(thing + strlen(thing) - strlen(binExt[extType]),
			 binExt[extType]);
	  if (i == 0) return(1);
     }
     return(0);
     
}

/*--------------------------------*/

GenerateUniqueFiles(tmpList, tmpData)
  char *tmpList, *tmpData;
{
     char *s;
     int pid;
     
     pid = getpid();
     s = strchr(tmpList, '+');
     sprintf(s, "%d", pid);
     s = strchr(tmpData, '+');
     sprintf(s, "%d", pid);
}

/*--------------------------------*/

GopherType(buf, theName)
  char *buf, *theName;
  
{
     static char ext4[] = ".hqx";
     static char *ext5[] = {".zip", ".zoo", ".arj", ".arc", ".lzh", ".hyp", 
				 ".pak", ".exe", ".com", ".ps", ".gif", ".pict", 
				 ".pct", ".tiff", ".tif"};
     static char *ext9[] = {".tar", ".Z"};
     int extType, i, last;
     char	tmpName[SLEN];	
     
     last = strlen(buf) - 1;     buf[last--] = '\0';  /* Munge the LF */
     strcpy(tmpName, buf);
     if (buf[last] == '/') {
	  tmpName[last] = '\0';
	  sprintf(theName, "%d%s", GDIR, tmpName);
	  return;
     }
     if ((buf[last] == '*') || (buf[last] == '@')) {		/* Hack out * and @ */
	  buf[last] = '\0';
	  tmpName[last] = '\0';
     }
     
     /* At this point we're looking at a file */
     if (strcasecmp(buf + strlen(buf) - strlen(ext4), ext4) == 0) { /* BinHex? */
	  sprintf(theName, "%d%s", GBINHEX, tmpName);
	  return;
     }
     
     for (extType = 0; extType < 15; extType++) {			/* PC garbage? */ 
	  i = strcasecmp(buf + strlen(buf) - strlen(ext5[extType]), 
			 ext5[extType]);
	  if (i == 0) {
	       sprintf(theName, "%d%s", GDOSB, tmpName);
	       return;
	  }
     }
     
     for (extType = 0; extType < 2; extType++) {				/* unix binary? */ 
	  i = strcasecmp(buf + strlen(buf) - strlen(ext9[extType]), 
			 ext9[extType]);
	  if (i == 0) {
	       sprintf(theName, "%d%s", GUNIXB, tmpName);
	       return;
	  }
     }
     
     sprintf(theName, "%d%s", GFILE, tmpName);
     return;		/* Some other and hopefully text file */
}

/*--------------------------------*/

Cleanup()
{
     unlink(tmpList);
     unlink(tmpData);
     exit(1);
}

/*--------------------------------*/

RoundEmUp()
{
     
     kill(childpid, SIGKILL);
     Cleanup();
}

/*--------------------------------*/

FailErr(result)
  int result;
{
     if (result < 0) {
	  Cleanup();
     }
}

/*--------------------------------*/
