From: stolcke@ICSI.Berkeley.EDU (Andreas Stolcke)
Newsgroups: comp.infosystems.gopher
Subject: Re: FTP with BETA gopher 1.1b4
Date: 11 Dec 1992 22:23:07 GMT
Organization: International Computer Science Institute, Berkeley, CA, U.S.A.
Distribution: world
NNTP-Posting-Host: icsib30.icsi.berkeley.edu

In article <1992Dec11.193614.5528@msc.cornell.edu>, rick@jaguar.msc.cornell.edu (Rick Cochran) writes:
|> In article <1992Dec9.034852.15786@news2.cis.umn.edu>, lindner@mudhoney.micro.umn.edu (Paul Lindner) writes:
|> |> Second.  1.1 now speaks ftp protocol directly.  It uses the PASV
|> |> command to make it's life easier, A/UX however has a broken ftpd that
|> |> doesn't implement PASV correctly.  It returns negative values for the
|> |> port. 
|> 
|> FTP in 1.1b4 doesn't work for me either.  I'm afraid you are arguing
|> against a mounting body of evidence.

Since nobody replied to my query for a patch I did the next best thing:
dropping in the old ftp.c from gopher1.03.  It worked almost without
changes.  So now we have a working 1.1b4 gopherd using the old ftp
code.

I have included the complete gopherd/ftp.c below for those who care.
The path to the ftp binary at the top may need adjustment.

-- 
Andreas Stolcke					stolcke@icsi.berkeley.edu
International Computer Science Institute	stolcke@ucbicsi.bitnet
1947 Center St., Suite 600, Berkeley, CA 94704	(510) 642-4274 ext. 126

--cut here--
/* -------------------------------------------------
 *	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>

#ifndef FTP
#define FTP	"/usr/ucb/ftp"
#endif
#ifndef DATA
#define DATA	"/tmp/gftpD+"
#endif

#define GFILE	0		/* Gopher item types */
#define GDIR	1
#define GBINHEX	4
#define GDOSB	5
#define GUNIXB	9
#define GIMAGE  "I"
#define GSOUND  "s"

#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;


void
SendFtpQuery(sockfd, query)
  int sockfd;
  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.");
     
     GenerateUniqueFiles(tmpList, tmpData);
     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);
}

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

void
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");
	       fp = OpenOrDie(tmpData, "r");
	       fd = fileno(fp);
	       
	       if (DEBUG)
		    printf("fd %d\n",fd);
	       while ((nRead = read(fd, buf, sizeof buf)) > 0)
                    writen(sockfd, buf, nRead);
	       
	       writen(sockfd, buf, nRead);
	       fclose(fp);
	  } 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 *exts[] = {".au", ".snd"};
     static char *extI[] = {".gif", ".pict", ".tiff", ".tif", ".pcx"};
     static char *ext5[] = {".zip", ".zoo", ".arj", ".arc", ".lzh", ".hyp", 
				 ".pak", ".exe", ".com", ".ps", ".pct"};
     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 < 11; 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;
	  }
     }

     for (extType = 0; extType < 5; extType++) {  /** Image? **/
	  i = strcasecmp(buf + strlen(buf) - strlen(extI[extType]), 
			 extI[extType]);
	  if (i == 0) {
	       sprintf(theName, "%s%s", GIMAGE, tmpName);
	       return;
	  }
     }	  

     for (extType = 0; extType < 2; extType++) {  /** Image? **/
	  i = strcasecmp(buf + strlen(buf) - strlen(exts[extType]), 
			 exts[extType]);
	  if (i == 0) {
	       sprintf(theName, "%s%s", GSOUND, 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();
     }
}

/*--------------------------------*/
Newsgroups: comp.infosystems.gopher