Пример #1
0
        /// <summary>
        /// Opens the specified file for appending. Please call GetReply() after you have successfully transfered the file to read the "OK" command sent by the server and prevent stale data on the socket.
        /// </summary>
        /// <param name="path">The full or relative path to the file to be opened</param>
        /// <param name="type">ASCII/Binary</param>
        /// <returns>A stream for writing to the file on the server</returns>
        /// <example><code source="..\Examples\OpenAppend.cs" lang="cs" /></example>
        public virtual Stream OpenAppend(string path, FtpDataType type)
        {
            FtpTrace.WriteFunc("OpenAppend", new object[] { path, type });

            FtpClient     client = null;
            FtpDataStream stream = null;
            long          length = 0;

            lock (m_lock) {
                if (m_threadSafeDataChannels)
                {
                    client = CloneConnection();
                    client.Connect();
                    client.SetWorkingDirectory(GetWorkingDirectory());
                }
                else
                {
                    client = this;
                }

                client.SetDataType(type);
                length = client.GetFileSize(path);
                stream = client.OpenDataStream(("APPE " + path.GetFtpPath()), 0);

                if (length > 0 && stream != null)
                {
                    stream.SetLength(length);
                    stream.SetPosition(length);
                }
            }

            return(stream);
        }
Пример #2
0
        /// <summary>
        /// Returns a file/directory listing using the NLST command.
        /// </summary>
        /// <param name="path">The path of the directory to list</param>
        /// <returns>A string array of file and directory names if any were returned.</returns>
        /// <example><code source="..\Examples\GetNameListing.cs" lang="cs" /></example>
        public string[] GetNameListing(string path)
        {
            FtpTrace.WriteFunc("GetNameListing", new object[] { path });

            List <string> listing = new List <string>();

            // calc path to request
            path = GetAbsolutePath(path);

#if !CORE14
            lock (m_lock) {
#endif
            // always get the file listing in binary
            // to avoid any potential character translation
            // problems that would happen if in ASCII.
            Execute("TYPE I");

            using (FtpDataStream stream = OpenDataStream(("NLST " + path.GetFtpPath()), 0)) {
                string buf;

                try {
                    while ((buf = stream.ReadLine(Encoding)) != null)
                    {
                        listing.Add(buf);
                    }
                } finally {
                    stream.Close();
                }
            }
#if !CORE14
        }
#endif

            return(listing.ToArray());
        }
Пример #3
0
        /// <summary>
        /// Returns a file/directory listing using the NLST command asynchronously
        /// </summary>
        /// <param name="path">The path of the directory to list</param>
        /// <returns>An array of file and directory names if any were returned.</returns>
        public async Task <string[]> GetNameListingAsync(string path)
        {
            //TODO:  Add cancellation support
            FtpTrace.WriteFunc(nameof(GetNameListingAsync), new object[] { path });

            List <string> listing = new List <string>();

            // calc path to request
            path = await GetAbsolutePathAsync(path);

            // always get the file listing in binary
            // to avoid any potential character translation
            // problems that would happen if in ASCII.
            await ExecuteAsync("TYPE I");

            using (FtpDataStream stream = await OpenDataStreamAsync(("NLST " + path.GetFtpPath()), 0))
            {
                string buf;

                try
                {
                    while ((buf = await stream.ReadLineAsync(Encoding)) != null)
                    {
                        listing.Add(buf);
                    }
                }
                finally
                {
                    stream.Close();
                }
            }

            return(listing.ToArray());
        }
Пример #4
0
		/// <summary>
		/// Log a function call with relevant arguments
		/// </summary>
		/// <param name="function">The name of the API function</param>
		/// <param name="args">The args passed to the function</param>
		public void LogFunc(string function, object[] args = null) {
			// log to attached logger if given
			if (OnLogEvent != null) {
				OnLogEvent(FtpTraceLevel.Verbose, ">         " + function + "(" + args.ItemsToString().Join(", ") + ")");
			}

			// log to system
			FtpTrace.WriteFunc(function, args);
		}
Пример #5
0
        /// <summary>
        /// Opens the specified file for reading
        /// </summary>
        /// <param name="path">The full or relative path of the file</param>
        /// <param name="type">ASCII/Binary</param>
        /// <param name="restart">Resume location</param>
        /// <returns>A stream for reading the file on the server</returns>
        /// <example><code source="..\Examples\OpenRead.cs" lang="cs" /></example>
        public virtual Stream OpenRead(string path, FtpDataType type, long restart)
        {
            // verify args
            if (path.IsBlank())
            {
                throw new ArgumentException("Required parameter is null or blank.", "path");
            }

            FtpTrace.WriteFunc("OpenRead", new object[] { path, type, restart });

            FtpClient     client = null;
            FtpDataStream stream = null;
            long          length = 0;

#if !CORE14
            lock (m_lock) {
#endif
            if (m_threadSafeDataChannels)
            {
                client = CloneConnection();
                client.Connect();
                client.SetWorkingDirectory(GetWorkingDirectory());
            }
            else
            {
                client = this;
            }

            client.SetDataType(type);
            length = client.GetFileSize(path);
            stream = client.OpenDataStream(("RETR " + path.GetFtpPath()), restart);
#if !CORE14
        }
#endif

            if (stream != null)
            {
                if (length > 0)
                {
                    stream.SetLength(length);
                }

                if (restart > 0)
                {
                    stream.SetPosition(restart);
                }
            }

            return(stream);
        }
Пример #6
0
        /// <summary>
        /// Disconnects a data stream
        /// </summary>
        /// <param name="stream">The data stream to close</param>
        internal FtpReply CloseDataStream(FtpDataStream stream)
        {
            FtpTrace.WriteFunc("CloseDataStream");

            FtpReply reply = new FtpReply();

            if (stream == null)
            {
                throw new ArgumentException("The data stream parameter was null");
            }

#if !CORE14
            lock (m_lock) {
#endif
            try {
                if (IsConnected)
                {
                    // if the command that required the data connection was
                    // not successful then there will be no reply from
                    // the server, however if the command was successful
                    // the server will send a reply when the data connection
                    // is closed.
                    if (stream.CommandStatus.Type == FtpResponseType.PositivePreliminary)
                    {
                        if (!(reply = GetReply()).Success)
                        {
                            throw new FtpCommandException(reply);
                        }
                    }
                }
            } finally {
                // if this is a clone of the original control
                // connection we should Dispose()
                if (IsClone)
                {
                    Disconnect();
                    Dispose();
                }
            }
#if !CORE14
        }
#endif

            return(reply);
        }
Пример #7
0
        /// <summary>
        /// Opens the specified file for writing. Please call GetReply() after you have successfully transfered the file to read the "OK" command sent by the server and prevent stale data on the socket.
        /// </summary>
        /// <param name="path">Full or relative path of the file</param>
        /// <param name="type">ASCII/Binary</param>
        /// <param name="checkIfFileExists">Only set this to false if you are SURE that the file does not exist. If true, it reads the file size and saves it into the stream length.</param>
        /// <returns>A stream for writing to the file on the server</returns>
        /// <example><code source="..\Examples\OpenWrite.cs" lang="cs" /></example>
        public virtual Stream OpenWrite(string path, FtpDataType type, bool checkIfFileExists)
        {
            // verify args
            if (path.IsBlank())
            {
                throw new ArgumentException("Required parameter is null or blank.", "path");
            }

            FtpTrace.WriteFunc("OpenWrite", new object[] { path, type });

            FtpClient client     = null;
            FtpDataStream stream = null;
            long length          = 0;

#if !CORE14
            lock (m_lock) {
#endif
            if (m_threadSafeDataChannels)
            {
                client = CloneConnection();
                client.Connect();
                client.SetWorkingDirectory(GetWorkingDirectory());
            }
            else
            {
                client = this;
            }

            client.SetDataType(type);
            length = checkIfFileExists ? client.GetFileSize(path) : 0;
            stream = client.OpenDataStream(("STOR " + path.GetFtpPath()), 0);

            if (length > 0 && stream != null)
            {
                stream.SetLength(length);
            }
#if !CORE14
        }
#endif

            return(stream);
        }
Пример #8
0
        /// <summary>
        /// Gets a file listing from the server. Each <see cref="FtpListItem"/> object returned
        /// contains information about the file that was able to be retrieved.
        /// </summary>
        /// <remarks>
        /// If a <see cref="DateTime"/> property is equal to <see cref="DateTime.MinValue"/> then it means the
        /// date in question was not able to be retrieved. If the <see cref="FtpListItem.Size"/> property
        /// is equal to 0, then it means the size of the object could also not
        /// be retrieved.
        /// </remarks>
        /// <param name="path">The path of the directory to list</param>
        /// <param name="options">Options that dictacte how a list is performed and what information is gathered.</param>
        /// <returns>An array of FtpListItem objects</returns>
        /// <example><code source="..\Examples\GetListing.cs" lang="cs" /></example>
        public FtpListItem[] GetListing(string path, FtpListOption options)
        {
            FtpTrace.WriteFunc("GetListing", new object[] { path, options });

            FtpListItem        item       = null;
            List <FtpListItem> lst        = new List <FtpListItem>();
            List <string>      rawlisting = new List <string>();
            string             listcmd    = null;
            string             buf        = null;

            // read flags
            bool isIncludeSelf = (options & FtpListOption.IncludeSelfAndParent) == FtpListOption.IncludeSelfAndParent;
            bool isForceList   = (options & FtpListOption.ForceList) == FtpListOption.ForceList;
            bool isNoPath      = (options & FtpListOption.NoPath) == FtpListOption.NoPath;
            bool isNameList    = (options & FtpListOption.NameList) == FtpListOption.NameList;
            bool isUseLS       = (options & FtpListOption.UseLS) == FtpListOption.UseLS;
            bool isAllFiles    = (options & FtpListOption.AllFiles) == FtpListOption.AllFiles;
            bool isRecursive   = (options & FtpListOption.Recursive) == FtpListOption.Recursive && RecursiveList;
            bool isDerefLinks  = (options & FtpListOption.DerefLinks) == FtpListOption.DerefLinks;
            bool isGetModified = (options & FtpListOption.Modify) == FtpListOption.Modify;
            bool isGetSize     = (options & FtpListOption.Size) == FtpListOption.Size;

            // calc path to request
            path = GetAbsolutePath(path);

            // MLSD provides a machine readable format with 100% accurate information
            // so always prefer MLSD over LIST unless the caller of this method overrides it with the ForceList option
            bool machineList = false;

            if ((!isForceList || m_parser == FtpParser.Machine) && HasFeature(FtpCapability.MLSD))
            {
                listcmd     = "MLSD";
                machineList = true;
            }
            else
            {
                if (isUseLS)
                {
                    listcmd = "LS";
                }
                else if (isNameList)
                {
                    listcmd = "NLST";
                }
                else
                {
                    string listopts = "";

                    listcmd = "LIST";

                    if (isAllFiles)
                    {
                        listopts += "a";
                    }

                    if (isRecursive)
                    {
                        listopts += "R";
                    }

                    if (listopts.Length > 0)
                    {
                        listcmd += " -" + listopts;
                    }
                }
            }

            if (!isNoPath)
            {
                listcmd = (listcmd + " " + path.GetFtpPath());
            }

#if !CORE14
            lock (m_lock) {
#endif
            Execute("TYPE I");

            // read in raw file listing
            using (FtpDataStream stream = OpenDataStream(listcmd, 0)) {
                try {
                    FtpTrace.WriteLine(FtpTraceLevel.Verbose, "+---------------------------------------+");
                    while ((buf = stream.ReadLine(Encoding)) != null)
                    {
                        if (buf.Length > 0)
                        {
                            rawlisting.Add(buf);
                            FtpTrace.WriteLine(FtpTraceLevel.Verbose, "Listing:  " + buf);
                        }
                    }
                    FtpTrace.WriteLine(FtpTraceLevel.Verbose, "-----------------------------------------");
                } finally {
                    stream.Close();
                }
            }
#if !CORE14
        }
#endif

            for (int i = 0; i < rawlisting.Count; i++)
            {
                buf = rawlisting[i];

                if (isNameList)
                {
                    // if NLST was used we only have a file name so
                    // there is nothing to parse.
                    item = new FtpListItem()
                    {
                        FullName = buf
                    };

                    if (DirectoryExists(item.FullName))
                    {
                        item.Type = FtpFileSystemObjectType.Directory;
                    }
                    else
                    {
                        item.Type = FtpFileSystemObjectType.File;
                    }

                    lst.Add(item);
                }
                else
                {
                    // if this is a result of LIST -R then the path will be spit out
                    // before each block of objects
                    if (listcmd.StartsWith("LIST") && isRecursive)
                    {
                        if (buf.StartsWith("/") && buf.EndsWith(":"))
                        {
                            path = buf.TrimEnd(':');
                            continue;
                        }
                    }

                    // if the next line in the listing starts with spaces
                    // it is assumed to be a continuation of the current line
                    if (i + 1 < rawlisting.Count && (rawlisting[i + 1].StartsWith("\t") || rawlisting[i + 1].StartsWith(" ")))
                    {
                        buf += rawlisting[++i];
                    }

                    item = m_listParser.ParseSingleLine(path, buf, m_caps, machineList);

                    // FtpListItem.Parse() returns null if the line
                    // could not be parsed
                    if (item != null)
                    {
                        if (isIncludeSelf || !(item.Name == "." || item.Name == ".."))
                        {
                            lst.Add(item);
                        }
                        else
                        {
                            //FtpTrace.WriteStatus(FtpTraceLevel.Verbose, "Skipped self or parent item: " + item.Name);
                        }
                    }
                    else
                    {
                        FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Failed to parse file listing: " + buf);
                    }
                }

                // load extended information that wasn't available if the list options flags say to do so.
                if (item != null)
                {
                    // try to dereference symbolic links if the appropriate list
                    // option was passed
                    if (item.Type == FtpFileSystemObjectType.Link && isDerefLinks)
                    {
                        item.LinkObject = DereferenceLink(item);
                    }

                    // if need to get file modified date
                    if (isGetModified && HasFeature(FtpCapability.MDTM))
                    {
                        // if the modified date was not loaded or the modified date is more than a day in the future
                        // and the server supports the MDTM command, load the modified date.
                        // most servers do not support retrieving the modified date
                        // of a directory but we try any way.
                        if (item.Modified == DateTime.MinValue || listcmd.StartsWith("LIST"))
                        {
                            DateTime modify;

                            if (item.Type == FtpFileSystemObjectType.Directory)
                            {
                                FtpTrace.WriteStatus(FtpTraceLevel.Verbose, "Trying to retrieve modification time of a directory, some servers don't like this...");
                            }

                            if ((modify = GetModifiedTime(item.FullName)) != DateTime.MinValue)
                            {
                                item.Modified = modify;
                            }
                        }
                    }

                    // if need to get file size
                    if (isGetSize && HasFeature(FtpCapability.SIZE))
                    {
                        // if no size was parsed, the object is a file and the server
                        // supports the SIZE command, then load the file size
                        if (item.Size == -1)
                        {
                            if (item.Type != FtpFileSystemObjectType.Directory)
                            {
                                item.Size = GetFileSize(item.FullName);
                            }
                            else
                            {
                                item.Size = 0;
                            }
                        }
                    }
                }
            }

            return(lst.ToArray());
        }
Пример #9
0
        /// <summary>
        /// Returns information about a file system object. Returns null if the server response can't
        /// be parsed or the server returns a failure completion code. The error for a failure
        /// is logged with FtpTrace. No exception is thrown on error because that would negate
        /// the usefulness of this method for checking for the existence of an object.
        /// </summary>
        /// <param name="path">The path of the file or folder</param>
        /// <param name="dateModified">Get the accurate modified date using another MDTM command</param>
        /// <returns>A FtpListItem object</returns>
        public FtpListItem GetObjectInfo(string path, bool dateModified = false)
        {
            FtpTrace.WriteFunc("GetObjectInfo", new object[] { path, dateModified });

            FtpReply reply;

            string[] res;

            bool supportsMachineList = (Capabilities & FtpCapability.MLSD) == FtpCapability.MLSD;

            FtpListItem result = null;

            if (supportsMachineList)
            {
                // USE MACHINE LISTING TO GET INFO FOR A SINGLE FILE

                if ((reply = Execute("MLST " + path)).Success)
                {
                    res = reply.InfoMessages.Split('\n');
                    if (res.Length > 1)
                    {
                        string info = "";

                        for (int i = 1; i < res.Length; i++)
                        {
                            info += res[i];
                        }

                        result = m_listParser.ParseSingleLine(null, info, m_caps, true);
                    }
                }
                else
                {
                    FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Failed to get object info for path " + path + " with error " + reply.ErrorMessage);
                }
            }
            else
            {
                // USE GETLISTING TO GET ALL FILES IN DIR .. SLOWER BUT AT LEAST IT WORKS

                string        dirPath  = path.GetFtpDirectoryName();
                FtpListItem[] dirItems = GetListing(dirPath);

                foreach (var dirItem in dirItems)
                {
                    if (dirItem.FullName == path)
                    {
                        result = dirItem;
                        break;
                    }
                }

                FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Failed to get object info for path " + path + " since MLST not supported and GetListing() fails to list file/folder.");
            }

            // Get the accurate date modified using another MDTM command
            if (result != null && dateModified && HasFeature(FtpCapability.MDTM))
            {
                result.Modified = GetModifiedTime(path);
            }

            return(result);
        }
Пример #10
0
        /// <summary>
        /// Opens the specified type of passive data stream
        /// </summary>
        /// <param name="type">Type of passive data stream to open</param>
        /// <param name="command">The command to execute that requires a data stream</param>
        /// <param name="restart">Restart location in bytes for file transfer</param>
        /// <returns>A data stream ready to be used</returns>
        FtpDataStream OpenPassiveDataStream(FtpDataConnectionType type, string command, long restart)
        {
            FtpTrace.WriteFunc("OpenPassiveDataStream", new object[] { type, command, restart });

            FtpDataStream stream = null;
            FtpReply      reply;
            Match         m;
            string        host = null;
            int           port = 0;

            if (m_stream == null)
            {
                throw new InvalidOperationException("The control connection stream is null! Generally this means there is no connection to the server. Cannot open a passive data stream.");
            }

            if (type == FtpDataConnectionType.EPSV || type == FtpDataConnectionType.AutoPassive)
            {
                if (!(reply = Execute("EPSV")).Success)
                {
                    // if we're connected with IPv4 and data channel type is AutoPassive then fallback to IPv4
                    if (reply.Type == FtpResponseType.PermanentNegativeCompletion && type == FtpDataConnectionType.AutoPassive && m_stream != null && m_stream.LocalEndPoint.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    {
                        return(OpenPassiveDataStream(FtpDataConnectionType.PASV, command, restart));
                    }
                    throw new FtpCommandException(reply);
                }

                m = Regex.Match(reply.Message, @"\(\|\|\|(?<port>\d+)\|\)");
                if (!m.Success)
                {
                    throw new FtpException("Failed to get the EPSV port from: " + reply.Message);
                }

                host = m_host;
                port = int.Parse(m.Groups["port"].Value);
            }
            else
            {
                if (m_stream.LocalEndPoint.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork)
                {
                    throw new FtpException("Only IPv4 is supported by the PASV command. Use EPSV instead.");
                }

                if (!(reply = Execute("PASV")).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));
                }

                // PASVEX mode ignores the host supplied in the PASV response
                if (type == FtpDataConnectionType.PASVEX)
                {
                    host = m_host;
                }
                else
                {
                    host = (m.Groups["quad1"].Value + "." + m.Groups["quad2"].Value + "." + m.Groups["quad3"].Value + "." + m.Groups["quad4"].Value);
                }

                port = (int.Parse(m.Groups["port1"].Value) << 8) + int.Parse(m.Groups["port2"].Value);
            }

            stream = new FtpDataStream(this);
            stream.ConnectTimeout = DataConnectionConnectTimeout;
            stream.ReadTimeout    = DataConnectionReadTimeout;
            Connect(stream, host, port, InternetProtocolVersions);
            stream.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, System.Net.Sockets.SocketOptionName.KeepAlive, m_keepAlive);

            if (restart > 0)
            {
                if (!(reply = Execute("REST " + restart)).Success)
                {
                    throw new FtpCommandException(reply);
                }
            }

            if (!(reply = Execute(command)).Success)
            {
                stream.Close();
                throw new FtpCommandException(reply);
            }

            // the command status is used to determine
            // if a reply needs to be read from the server
            // when the stream is closed so always set it
            // otherwise things can get out of sync.
            stream.CommandStatus = reply;

#if !NO_SSL
            // this needs to take place after the command is executed
            if (m_dataConnectionEncryption && m_encryptionmode != FtpEncryptionMode.None)
            {
                stream.ActivateEncryption(m_host,
                                          this.ClientCertificates.Count > 0 ? this.ClientCertificates : null,
                                          m_SslProtocols);
            }
#endif

            return(stream);
        }
Пример #11
0
        /// <summary>
        /// Opens the specified type of active data stream
        /// </summary>
        /// <param name="type">Type of passive data stream to open</param>
        /// <param name="command">The command to execute that requires a data stream</param>
        /// <param name="restart">Restart location in bytes for file transfer</param>
        /// <returns>A data stream ready to be used</returns>
        FtpDataStream OpenActiveDataStream(FtpDataConnectionType type, string command, long restart)
        {
            FtpTrace.WriteFunc("OpenActiveDataStream", new object[] { type, command, restart });

            FtpDataStream stream = new FtpDataStream(this);
            FtpReply      reply;

#if !CORE
            IAsyncResult ar;
#endif

            if (m_stream == null)
            {
                throw new InvalidOperationException("The control connection stream is null! Generally this means there is no connection to the server. Cannot open an active data stream.");
            }

            if (m_ActivePorts == null || !m_ActivePorts.Any())
            {
                // Use random port
                stream.Listen(m_stream.LocalEndPoint.Address, 0);
            }
            else
            {
                var success = false;
                // Use one of the specified ports
                foreach (var port in m_ActivePorts)
                {
                    try {
                        stream.Listen(m_stream.LocalEndPoint.Address, port);
                        success = true;
                    } catch (SocketException se) {
#if NETFX
                        // Already in use
                        if (se.ErrorCode != 10048)
                        {
                            throw;
                        }
#else
                        throw;
#endif
                    }
                }

                // No usable port found
                if (!success)
                {
                    throw new Exception("No valid active data port available!");
                }
            }
#if !CORE
            ar = stream.BeginAccept(null, null);
#endif

            if (type == FtpDataConnectionType.EPRT || type == FtpDataConnectionType.AutoActive)
            {
                int ipver = 0;

                switch (stream.LocalEndPoint.AddressFamily)
                {
                case System.Net.Sockets.AddressFamily.InterNetwork:
                    ipver = 1;                             // IPv4
                    break;

                case System.Net.Sockets.AddressFamily.InterNetworkV6:
                    ipver = 2;                             // IPv6
                    break;

                default:
                    throw new InvalidOperationException("The IP protocol being used is not supported.");
                }

                if (!(reply = Execute("EPRT |" + ipver + "|" + GetLocalAddress(stream.LocalEndPoint.Address) + "|" + stream.LocalEndPoint.Port + "|")).Success)
                {
                    // if we're connected with IPv4 and the data channel type is AutoActive then try to fall back to the PORT command
                    if (reply.Type == FtpResponseType.PermanentNegativeCompletion && type == FtpDataConnectionType.AutoActive && m_stream != null && m_stream.LocalEndPoint.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
                    {
                        stream.ControlConnection = null;                         // we don't want this failed EPRT attempt to close our control connection when the stream is closed so clear out the reference.
                        stream.Close();
                        return(OpenActiveDataStream(FtpDataConnectionType.PORT, command, restart));
                    }
                    else
                    {
                        stream.Close();
                        throw new FtpCommandException(reply);
                    }
                }
            }
            else
            {
                if (m_stream.LocalEndPoint.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork)
                {
                    throw new FtpException("Only IPv4 is supported by the PORT command. Use EPRT instead.");
                }

                if (!(reply = Execute("PORT " +
                                      GetLocalAddress(stream.LocalEndPoint.Address).Replace('.', ',') + "," +
                                      stream.LocalEndPoint.Port / 256 + "," +
                                      stream.LocalEndPoint.Port % 256)).Success)
                {
                    stream.Close();
                    throw new FtpCommandException(reply);
                }
            }

            if (restart > 0)
            {
                if (!(reply = Execute("REST " + restart)).Success)
                {
                    throw new FtpCommandException(reply);
                }
            }

            if (!(reply = Execute(command)).Success)
            {
                stream.Close();
                throw new FtpCommandException(reply);
            }

            // the command status is used to determine
            // if a reply needs to be read from the server
            // when the stream is closed so always set it
            // otherwise things can get out of sync.
            stream.CommandStatus = reply;

#if CORE
            stream.AcceptAsync().Wait();
#else
            ar.AsyncWaitHandle.WaitOne(m_dataConnectionConnectTimeout);
            if (!ar.IsCompleted)
            {
                stream.Close();
                throw new TimeoutException("Timed out waiting for the server to connect to the active data socket.");
            }

            stream.EndAccept(ar);
#endif

#if !NO_SSL
            if (m_dataConnectionEncryption && m_encryptionmode != FtpEncryptionMode.None)
            {
                stream.ActivateEncryption(m_host,
                                          this.ClientCertificates.Count > 0 ? this.ClientCertificates : null,
                                          m_SslProtocols);
            }
#endif

            stream.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, System.Net.Sockets.SocketOptionName.KeepAlive, m_keepAlive);
            stream.ReadTimeout = m_dataConnectionReadTimeout;

            return(stream);
        }