b3433bd3690adf7488695c3d43c7d1d9b553c859
galt
  Tue Jul 20 02:24:50 2021 -0700
Adding needed EPSV extended passive command for IPv6 FTP. fixes #27860

diff --git src/lib/net.c src/lib/net.c
index f57e054..f44a066 100644
--- src/lib/net.c
+++ src/lib/net.c
@@ -848,70 +848,88 @@
 	
     if (strlen(startLastLine)>4)
       if (
 	isdigit(startLastLine[0]) &&
 	isdigit(startLastLine[1]) &&
 	isdigit(startLastLine[2]) &&
 	startLastLine[3]==' ')
 	break;
 	
     if (readSize == 0)
 	break;  // EOF
     /* must be some text info we can't use, ignore it till we get status code */
     }
 
 int reply = atoi(startLastLine);
+if (retCode)
+    *retCode = reply;
 if ((reply < 200) || (reply > 399))
     {
+    if (!(sameString(cmd,"PASV\r\n") && reply==501))
 	warn("ftp server error on cmd=[%s] response=[%s]",cmd,rs->string);
     return FALSE;
     }
     
 if (retReply)
     *retReply = rs;
 else
     dyStringFree(&rs);
-if (retCode)
-    *retCode = reply;
 return TRUE;
 }
 
 static boolean sendFtpCommand(int sd, char *cmd, struct dyString **retReply, int *retCode)
 /* send command to ftp server and check resulting reply code, 
  * warn and return FALSE if not desired reply.  If retReply is non-NULL, store reply text there. */
 {   
 sendFtpCommandOnly(sd, cmd);
 return receiveFtpReply(sd, cmd, retReply, retCode);
 }
 
 static int parsePasvPort(char *rs)
 /* parse PASV reply to get the port and return it */
 {
 char *words[7];
 int wordCount;
 char *rsStart = strchr(rs,'(');
 char *rsEnd = strchr(rs,')');
 int result = 0;
 rsStart++;
 *rsEnd=0;
 wordCount = chopString(rsStart, ",", words, ArraySize(words));
 if (wordCount != 6)
     errAbort("PASV reply does not parse correctly");
 result = atoi(words[4])*256+atoi(words[5]);    
 return result;
 }    
 
+static int parseEpsvPort(char *rs)
+/* parse EPSV reply to get the port and return it */
+{
+char *words[6];
+int wordCount;
+char *rsStart = strchr(rs,'(');
+char *rsEnd = strchr(rs,')');
+int result = 0;
+rsStart++;
+*rsEnd=0;
+wordCount = chopString(rsStart, "|", words, ArraySize(words));
+if (wordCount != 1)
+    errAbort("EPSV reply does not parse correctly");
+result = atoi(words[0]); // multiple separators treated as one.   
+return result;
+}    
+
 
 static long long parseFtpSIZE(char *rs)
 /* parse reply to SIZE and return it */
 {
 char *words[3];
 int wordCount;
 char *rsStart = rs;
 long long result = 0;
 wordCount = chopString(rsStart, " ", words, ArraySize(words));
 if (wordCount != 2)
     errAbort("SIZE reply does not parse correctly");
 result = atoll(words[1]);    
 return result;
 }    
 
@@ -1132,57 +1150,70 @@
     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);
     char *logProxy = getenv("log_proxy");
     if (sameOk(logProxy,"on"))
 	verbose(1, "%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;
 
+int retCode = 0;
 struct dyString *rs = NULL;
-if (!sendFtpCommand(sd, "PASV\r\n", &rs, NULL))
+sendFtpCommand(sd, "PASV\r\n", &rs, &retCode);
+/* 227 Entering Passive Mode (128,231,210,81,222,250) */  
+boolean isIpv6 = FALSE;
+if (retCode == 501)
+    {
+
+    if (!sendFtpCommand(sd, "EPSV\r\n", &rs, NULL))
+    /* 229 Entering Extended Passive Mode (|||44022|) */
+	{
+	close(sd);
+	return -1;
+	}
+    isIpv6 = TRUE;
+
+    }
+else if (retCode != 227)
     {
     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))
 	{
+	dyStringFree(&rs);
 	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 = -1;
-if (proxyUrl)
-    sdata = netConnect(pxy.host, parsePasvPort(rs->string));
-else
-    sdata = netConnect(npu.host, parsePasvPort(rs->string));
+sdata = netConnect(proxyUrl ? pxy.host : npu.host, isIpv6 ? parseEpsvPort(rs->string) : 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);