/// <summary> /// Opens a FXP PASV connection between the source FTP Server and the destination FTP Server /// </summary> /// <param name="remoteClient">Valid FTP connection to the destination FTP Server</param> /// <returns>A data stream ready to be used</returns> private async Task <FtpFxpSession> OpenPassiveFXPConnectionAsync(FtpClient remoteClient, bool trackProgress, CancellationToken token) { FtpReply reply; Match m; FtpClient sourceClient = null; FtpClient destinationClient = null; FtpClient progressClient = null; // create a new connection to the source FTP server if EnableThreadSafeDataConnections is set if (m_threadSafeDataChannels) { sourceClient = CloneConnection(); sourceClient._AutoDispose = true; sourceClient.CopyStateFlags(this); await sourceClient.ConnectAsync(token); await sourceClient.SetWorkingDirectoryAsync(await GetWorkingDirectoryAsync(token), token); } else { sourceClient = this; } // create a new connection to the target FTP server if EnableThreadSafeDataConnections is set if (remoteClient.EnableThreadSafeDataConnections) { destinationClient = remoteClient.CloneConnection(); destinationClient._AutoDispose = true; destinationClient.CopyStateFlags(remoteClient); await destinationClient.ConnectAsync(token); await destinationClient.SetWorkingDirectoryAsync(await remoteClient.GetWorkingDirectoryAsync(token), token); } else { destinationClient = remoteClient; } // create a new connection to the target FTP server to track progress // if progress tracking is enabled during this FXP transfer if (trackProgress) { progressClient = remoteClient.CloneConnection(); progressClient._AutoDispose = true; progressClient.CopyStateFlags(remoteClient); await progressClient.ConnectAsync(token); await progressClient.SetWorkingDirectoryAsync(await remoteClient.GetWorkingDirectoryAsync(token), token); } await sourceClient.SetDataTypeAsync(sourceClient.FXPDataType, token); await destinationClient.SetDataTypeAsync(destinationClient.FXPDataType, token); // send PASV command to destination FTP server to get passive port to be used from source FTP server if (!(reply = await destinationClient.ExecuteAsync("PASV", token)).Success) { throw new FtpCommandException(reply); } m = Regex.Match(reply.Message, @"(?<quad1>\d+)," + @"(?<quad2>\d+)," + @"(?<quad3>\d+)," + @"(?<quad4>\d+)," + @"(?<port1>\d+)," + @"(?<port2>\d+)"); if (!m.Success || m.Groups.Count != 7) { throw new FtpException("Malformed PASV response: " + reply.Message); } // Instruct source server to open a connection to the destination Server if (!(reply = await sourceClient.ExecuteAsync($"PORT {m.Value}", token)).Success) { throw new FtpCommandException(reply); } // the FXP session stores the active connections used for this FXP transfer return(new FtpFxpSession { SourceServer = sourceClient, TargetServer = destinationClient, ProgressServer = progressClient, }); }