36071edc0403574d51170357bab076e39d076826
galt
  Thu Mar 23 16:42:09 2017 -0700
Adds ftp proxy capability to system. Works with Squid 3.5 ftp_port at least. refs #19124

diff --git src/lib/net.c src/lib/net.c
index 0acfb05..a4492d0 100644
--- src/lib/net.c
+++ src/lib/net.c
@@ -944,70 +944,96 @@
 	}
     dataPos += rd;
     if (params->npu.byteRangeEnd != -1 && dataPos >= params->npu.byteRangeEnd)
 	break;	    
     }
 if (rd == -1)
     // Again, avoid abort in child process.
     errnoWarn("error reading ftp socket");
 close(params->pipefd[1]);  /* we are done with it */
 close(params->sd);
 close(params->sdata);
 return NULL;
 }
 
 static int netGetOpenFtpSockets(char *url, int *retCtrlSd)
-/* Return a socket descriptor for url data (url can end in ";byterange:start-end",
+/* Return a socket descriptor for url data (url can end in ";byterange:start-end)",
  * or -1 if error.
  * If retCtrlSd is non-null, keep the control socket alive and set *retCtrlSd.
  * Otherwise, create a pipe and fork to keep control socket alive in the child 
  * process until we are done fetching data. */
 {
 char cmd[256];
 
 /* Parse the URL and connect. */
 struct netParsedUrl npu;
+struct netParsedUrl pxy;
 netParseUrl(url, &npu);
 if (!sameString(npu.protocol, "ftp"))
     errAbort("netGetOpenFtpSockets: url (%s) is not for ftp.", url);
-int sd = openFtpControlSocket(npu.host, atoi(npu.port), npu.user, npu.password);
+
+boolean noProxy = checkNoProxy(npu.host);
+char *proxyUrl = getenv("ftp_proxy");
+if (noProxy)
+    proxyUrl = NULL;
+
+int sd = -1;
+if (proxyUrl)
+    {
+    netParseUrl(proxyUrl, &pxy);
+    if (!sameString(pxy.protocol, "ftp"))
+        errAbort("Unknown proxy protocol %s in %s.", pxy.protocol, proxyUrl);
+    char proxyUser[4096];
+    safef(proxyUser, sizeof proxyUser, "%s@%s:%s", npu.user, npu.host, npu.port);
+    sd = openFtpControlSocket(pxy.host, atoi(pxy.port), proxyUser, npu.password);
+    verbose(2, "%s as %s via proxy %s\n", url, proxyUser, proxyUrl);
+    }
+else
+    {
+    sd = openFtpControlSocket(npu.host, atoi(npu.port), npu.user, npu.password);
+    }
+
 if (sd == -1)
     return -1;
 
 struct dyString *rs = NULL;
 if (!sendFtpCommand(sd, "PASV\r\n", &rs, NULL))
     {
     close(sd);
     return -1;
     }
 /* 227 Entering Passive Mode (128,231,210,81,222,250) */
 
 if (npu.byteRangeStart != -1)
     {
     safef(cmd,sizeof(cmd),"REST %lld\r\n", (long long) npu.byteRangeStart);
     if (!sendFtpCommand(sd, cmd, NULL, NULL))
 	{
 	close(sd);
 	return -1;
 	}
     }
 
 /* RETR for files, LIST for directories ending in / */
 safef(cmd,sizeof(cmd),"%s %s\r\n",((npu.file[strlen(npu.file)-1]) == '/') ? "LIST" : "RETR", npu.file);
 sendFtpCommandOnly(sd, cmd);
 
-int sdata = netConnect(npu.host, parsePasvPort(rs->string));
+int sdata = -1;
+if (proxyUrl)
+    sdata = netConnect(pxy.host, parsePasvPort(rs->string));
+else
+    sdata = netConnect(npu.host, parsePasvPort(rs->string));
 dyStringFree(&rs);
 if (sdata < 0)
     {
     close(sd);
     return -1;
     }
 
 int secondsWaited = 0;
 while (TRUE)
     {
     if (secondsWaited >= 10)
 	{
 	warn("ftp server error on cmd=[%s] timed-out waiting for data or error", cmd);
 	close(sd);
 	close(sdata);
@@ -1025,31 +1051,31 @@
 	    return -1;
 	    }
 	}
     ++secondsWaited;
     }
 
 if (retCtrlSd != NULL)
     {
     *retCtrlSd = sd;
     return sdata;
     }
 else
     {
     /* Because some FTP servers will kill the data connection
      * as soon as the control connection closes,
-     * we have to develop a workaround using a partner process. */
+     * we have to develop a workaround using a partner thread. */
     fflush(stdin);
     fflush(stdout);
     fflush(stderr);
 
     struct netConnectFtpParams *params;
     AllocVar(params);
     params->sd = sd;
     params->sdata = sdata;
     params->npu = npu;
     /* make a pipe (fds go in pipefd[0] and pipefd[1])  */
     if (pipe(params->pipefd) != 0)
 	errAbort("netGetOpenFtpSockets: failed to create pipe: %s", strerror(errno));
     int rc;
     rc = pthread_create(&params->thread, NULL, sendFtpDataToPipeThread, (void *)params);
     if (rc)