Exemple #1
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)
        {
            this.LogFunc("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());
        }
Exemple #2
0
        /// <summary>
        /// Returns a file/directory listing using the NLST command asynchronously
        /// </summary>
        /// <param name="path">The path of the directory to list</param>
        /// <param name="token">Cancellation Token</param>
        /// <returns>An array of file and directory names if any were returned.</returns>
        public async Task <string[]> GetNameListingAsync(string path, CancellationToken token = default(CancellationToken))
        {
            this.LogFunc(nameof(GetNameListingAsync), new object[] { path });

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

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

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

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

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

            return(listing.ToArray());
        }
        /// <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);
        }
        /// <summary>
        /// Opens a data stream.
        /// </summary>
        /// <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>The data stream.</returns>
        FtpDataStream OpenDataStream(string command, long restart)
        {
            FtpDataConnectionType type   = m_dataConnectionType;
            FtpDataStream         stream = null;

#if !CORE14
            lock (m_lock) {
#endif
            if (!IsConnected)
            {
                Connect();
            }

            // The PORT and PASV commands do not work with IPv6 so
            // if either one of those types are set change them
            // to EPSV or EPRT appropriately.
            if (m_stream.LocalEndPoint.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
            {
                switch (type)
                {
                case FtpDataConnectionType.PORT:
                    type = FtpDataConnectionType.EPRT;
                    FtpTrace.WriteLine(FtpTraceLevel.Info, "Changed data connection type to EPRT because we are connected with IPv6.");
                    break;

                case FtpDataConnectionType.PASV:
                case FtpDataConnectionType.PASVEX:
                    type = FtpDataConnectionType.EPSV;
                    FtpTrace.WriteLine(FtpTraceLevel.Info, "Changed data connection type to EPSV because we are connected with IPv6.");
                    break;
                }
            }

            switch (type)
            {
            case FtpDataConnectionType.AutoPassive:
            case FtpDataConnectionType.EPSV:
            case FtpDataConnectionType.PASV:
            case FtpDataConnectionType.PASVEX:
                stream = OpenPassiveDataStream(type, command, restart);
                break;

            case FtpDataConnectionType.AutoActive:
            case FtpDataConnectionType.EPRT:
            case FtpDataConnectionType.PORT:
                stream = OpenActiveDataStream(type, command, restart);
                break;
            }

            if (stream == null)
            {
                throw new InvalidOperationException("The specified data channel type is not implemented.");
            }
#if !CORE14
        }
#endif

            return(stream);
        }
        /// <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);
        }
        /// <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);
        }
        /// <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)
        {
            this.LogFunc("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");

            // read in raw listing
            try {
                using (FtpDataStream stream = OpenDataStream(("NLST " + path.GetFtpPath()), 0)) {
                    string buf;

                    try {
                        while ((buf = stream.ReadLine(Encoding)) != null)
                        {
                            listing.Add(buf);
                        }
                    } finally {
                        stream.Close();
                    }
                }
            } catch (FtpMissingSocketException) {
                // Some FTP server does not send any response when listing an empty directory
                // and the connection fails because no communication socket is provided by the server
            } catch (IOException) {
                // Some FTP servers forcibly close the connection, we absorb these errors
            }
#if !CORE14
        }
#endif

            return(listing.ToArray());
        }
Exemple #8
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);
        }
        /// <summary>
        /// Returns a file/directory listing using the NLST command asynchronously
        /// </summary>
        /// <param name="path">The path of the directory to list</param>
        /// <param name="token">Cancellation Token</param>
        /// <returns>An array of file and directory names if any were returned.</returns>
        public async Task <string[]> GetNameListingAsync(string path, CancellationToken token = default(CancellationToken))
        {
            this.LogFunc(nameof(GetNameListingAsync), new object[] { path });

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

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

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

            // read in raw listing
            try {
                using (FtpDataStream stream = await OpenDataStreamAsync(("NLST " + path.GetFtpPath()), 0, token))
                {
                    string buf;

                    try
                    {
                        while ((buf = await stream.ReadLineAsync(Encoding, token)) != null)
                        {
                            listing.Add(buf);
                        }
                    }
                    finally
                    {
                        stream.Close();
                    }
                }
            } catch (FtpMissingSocketException) {
                // Some FTP server does not send any response when listing an empty directory
                // and the connection fails because no communication socket is provided by the server
            } catch (IOException) {
                // Some FTP servers forcibly close the connection, we absorb these errors
            }

            return(listing.ToArray());
        }
Exemple #10
0
        /// <summary>
        /// Gets a file listing from the server asynchronously. 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 to list</param>
        /// <param name="options">Options that dictate how the list operation is performed</param>
        /// <param name="token">Cancellation Token</param>
        /// <returns>An array of items retrieved in the listing</returns>
        public async Task <FtpListItem[]> GetListingAsync(string path, FtpListOption options, CancellationToken token = default(CancellationToken))
        {
            // start recursive process if needed and unsupported by the server
            if ((options & FtpListOption.Recursive) == FtpListOption.Recursive && !RecursiveList)
            {
                return(await GetListingRecursiveAsync(GetAbsolutePath(path), options));
            }

            this.LogFunc(nameof(GetListingAsync), 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 = await GetAbsolutePathAsync(path, token);

            // 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());
            }

            await ExecuteAsync("TYPE I", token);

            // read in raw file listing
            try
            {
                using (FtpDataStream stream = await OpenDataStreamAsync(listcmd, 0, token))
                {
                    try
                    {
                        this.LogLine(FtpTraceLevel.Verbose, "+---------------------------------------+");

                        if (this.BulkListing)
                        {
                            // increases performance of GetListing by reading multiple lines of the file listing at once
                            foreach (var line in await stream.ReadAllLinesAsync(Encoding, this.BulkListingLength, token))
                            {
                                if (!FtpExtensions.IsNullOrWhiteSpace(line))
                                {
                                    rawlisting.Add(line);
                                    this.LogLine(FtpTraceLevel.Verbose, "Listing:  " + line);
                                }
                            }
                        }
                        else
                        {
                            // GetListing will read file listings line-by-line (actually byte-by-byte)
                            while ((buf = await stream.ReadLineAsync(Encoding, token)) != null)
                            {
                                if (buf.Length > 0)
                                {
                                    rawlisting.Add(buf);
                                    this.LogLine(FtpTraceLevel.Verbose, "Listing:  " + buf);
                                }
                            }
                        }

                        this.LogLine(FtpTraceLevel.Verbose, "-----------------------------------------");
                    }
                    finally
                    {
                        stream.Close();
                    }
                }
            }
            catch (FtpMissingSocketException)
            {
                // Some FTP server does not send any response when listing an empty directory
                // and the authentication fails, if no communication socket is provided by the server
            }

            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 (await DirectoryExistsAsync(item.FullName, token))
                    {
                        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];
                    }

                    try
                    {
                        item = m_listParser.ParseSingleLine(path, buf, m_caps, machineList);
                    }
                    catch (FtpListParser.CriticalListParseException)
                    {
                        this.LogStatus(FtpTraceLevel.Verbose, "Restarting parsing from first entry in list");
                        i = -1;
                        lst.Clear();
                        continue;
                    }

                    // FtpListItem.Parse() returns null if the line
                    // could not be parsed
                    if (item != null)
                    {
                        if (isIncludeSelf || !(item.Name == "." || item.Name == ".."))
                        {
                            lst.Add(item);
                        }
                        else
                        {
                            //this.LogStatus(FtpTraceLevel.Verbose, "Skipped self or parent item: " + item.Name);
                        }
                    }
                    else
                    {
                        this.LogStatus(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 = await DereferenceLinkAsync(item, token);
                    }

                    // 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)
                            {
                                this.LogStatus(FtpTraceLevel.Verbose, "Trying to retrieve modification time of a directory, some servers don't like this...");
                            }

                            if ((modify = await GetModifiedTimeAsync(item.FullName, token: token)) != 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 = await GetFileSizeAsync(item.FullName, token);
                            }
                            else
                            {
                                item.Size = 0;
                            }
                        }
                    }
                }
            }

            return(lst.ToArray());
        }
        /// <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());
        }
        /// <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);
        }
        /// <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);
        }
        /// <summary>
        /// Get the records of a file listing and retry if temporary failure.
        /// </summary>
        private async Task <List <string> > GetListingInternalAsync(string listcmd, bool retry, CancellationToken token)
        {
            List <string> rawlisting = new List <string>();

            // always get the file listing in binary to avoid character translation issues with ASCII.
            await SetDataTypeNoLockAsync(FtpDataType.Binary, token);

            try {
                // read in raw file listing
                using (FtpDataStream stream = await OpenDataStreamAsync(listcmd, 0, token)) {
                    try {
                        this.LogLine(FtpTraceLevel.Verbose, "+---------------------------------------+");

                        if (this.BulkListing)
                        {
                            // increases performance of GetListing by reading multiple lines of the file listing at once
                            foreach (var line in await stream.ReadAllLinesAsync(Encoding, this.BulkListingLength, token))
                            {
                                if (!FtpExtensions.IsNullOrWhiteSpace(line))
                                {
                                    rawlisting.Add(line);
                                    this.LogLine(FtpTraceLevel.Verbose, "Listing:  " + line);
                                }
                            }
                        }
                        else
                        {
                            // GetListing will read file listings line-by-line (actually byte-by-byte)
                            string buf;
                            while ((buf = await stream.ReadLineAsync(Encoding, token)) != null)
                            {
                                if (buf.Length > 0)
                                {
                                    rawlisting.Add(buf);
                                    this.LogLine(FtpTraceLevel.Verbose, "Listing:  " + buf);
                                }
                            }
                        }

                        this.LogLine(FtpTraceLevel.Verbose, "-----------------------------------------");
                    } finally {
                        stream.Close();
                    }
                }
            } catch (FtpMissingSocketException) {
                // Some FTP server does not send any response when listing an empty directory
                // and the connection fails because no communication socket is provided by the server
            } catch (IOException ioEx) {
                // Some FTP servers forcibly close the connection, we absorb these errors

                // Fix #410: Retry if its a temporary failure ("Received an unexpected EOF or 0 bytes from the transport stream")
                if (retry && ioEx.Message.IsKnownError(unexpectedEOFStrings))
                {
                    // retry once more, but do not go into a infinite recursion loop here
                    return(await GetListingInternalAsync(listcmd, false, token));
                }
                else
                {
                    // suppress all other types of exceptions
                }
            }

            return(rawlisting);
        }