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