Skip to content

Commit

Permalink
robinrodricks#739 and robinrodricks#745 - improve machine listing han…
Browse files Browse the repository at this point in the history
…dling and name list detection
  • Loading branch information
robinrodricks committed Aug 27, 2021
1 parent b06f497 commit 74043a9
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 22 deletions.
13 changes: 11 additions & 2 deletions FluentFTP/Client/FtpClient_Connection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -492,9 +492,18 @@ public virtual void Connect() {
}
#endif

// Unless a custom list parser has been set,
// Detect the listing parser and prefer machine listings over any other type
// FIX : #739 prefer using machine listings to fix issues with GetListing and DeleteDirectory
if (ListingParser != FtpParser.Custom) {
ListingParser = ServerHandler != null ? ServerHandler.GetParser() : FtpParser.Auto;
if (HasFeature(FtpCapability.MLSD)) {
ListingParser = FtpParser.Machine;
}
}

// Create the parser even if the auto-OS detection failed
var forcedParser = ServerHandler != null ? ServerHandler.GetParser() : FtpParser.Auto;
m_listParser.Init(m_serverOS, forcedParser);
m_listParser.Init(m_serverOS, ListingParser);

// FIX : #318 always set the type when we create a new connection
ForceSetDataType = true;
Expand Down
4 changes: 2 additions & 2 deletions FluentFTP/Client/FtpClient_FileProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ private FtpListItem DereferenceLink(FtpListItem item, int recMax, ref int count)
throw new FtpException("The link target was null. Please check this before trying to dereference the link.");
}

foreach (var obj in GetListing(item.LinkTarget.GetFtpDirectoryName(), FtpListOption.ForceList)) {
foreach (var obj in GetListing(item.LinkTarget.GetFtpDirectoryName())) {
if (item.LinkTarget == obj.FullName) {
if (obj.Type == FtpFileSystemObjectType.Link) {
if (++count == recMax) {
Expand Down Expand Up @@ -156,7 +156,7 @@ public FtpListItem EndDereferenceLink(IAsyncResult ar) {
if (item.LinkTarget == null) {
throw new FtpException("The link target was null. Please check this before trying to dereference the link.");
}
var listing = await GetListingAsync(item.LinkTarget.GetFtpDirectoryName(), FtpListOption.ForceList, token);
var listing = await GetListingAsync(item.LinkTarget.GetFtpDirectoryName(), token);
foreach (FtpListItem obj in listing) {
if (item.LinkTarget == obj.FullName) {
if (obj.Type == FtpFileSystemObjectType.Link) {
Expand Down
6 changes: 3 additions & 3 deletions FluentFTP/Client/FtpClient_FolderManagement.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void DeleteDirectory(string path) {
}

LogFunc(nameof(DeleteDirectory), new object[] { path });
DeleteDirInternal(path, true, FtpListOption.ForceList | FtpListOption.Recursive);
DeleteDirInternal(path, true, FtpListOption.Recursive);
}

/// <summary>
Expand Down Expand Up @@ -159,7 +159,7 @@ private bool WasGetListingRecursive(FtpListOption options) {
/// <returns>IAsyncResult</returns>
/// <example><code source="..\Examples\BeginDeleteDirectory.cs" lang="cs" /></example>
public IAsyncResult BeginDeleteDirectory(string path, AsyncCallback callback, object state) {
return BeginDeleteDirectory(path, FtpListOption.ForceList | FtpListOption.Recursive, callback, state);
return BeginDeleteDirectory(path, FtpListOption.Recursive, callback, state);
}

/// <summary>
Expand Down Expand Up @@ -206,7 +206,7 @@ public void EndDeleteDirectory(IAsyncResult ar) {
}

LogFunc(nameof(DeleteDirectoryAsync), new object[] { path });
return DeleteDirInternalAsync(path, true, FtpListOption.ForceList | FtpListOption.Recursive, token);
return DeleteDirInternalAsync(path, true, FtpListOption.Recursive, token);
}

/// <summary>
Expand Down
22 changes: 14 additions & 8 deletions FluentFTP/Client/FtpClient_Listing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ private void CalculateGetListingCommand(string path, FtpListOption options, out
}
else {
// use machine listing if supported by the server
if ((!isForceList || m_parser == FtpParser.Machine) && HasFeature(FtpCapability.MLSD)) {
if ((!isForceList || ListingParser == FtpParser.Machine) && HasFeature(FtpCapability.MLSD)) {
listcmd = "MLSD";
machineList = true;
}
Expand Down Expand Up @@ -504,7 +504,7 @@ private bool IsServerSideRecursionSupported(FtpListOption options) {
if (!isUseStat) {

// if not using machine listing (MSLD)
if ((!isForceList || m_parser == FtpParser.Machine) && HasFeature(FtpCapability.MLSD)) {
if ((!isForceList || ListingParser == FtpParser.Machine) && HasFeature(FtpCapability.MLSD)) {
}
else {

Expand Down Expand Up @@ -1259,16 +1259,19 @@ public string[] GetNameListing(string path) {
// read in raw listing
try {
using (var stream = OpenDataStream("NLST " + path.GetFtpPath(), 0)) {
string buf;
LogLine(FtpTraceLevel.Verbose, "+---------------------------------------+");
string line;

try {
while ((buf = stream.ReadLine(Encoding)) != null) {
listing.Add(buf);
while ((line = stream.ReadLine(Encoding)) != null) {
listing.Add(line);
LogLine(FtpTraceLevel.Verbose, "Listing: " + line);
}
}
finally {
stream.Close();
}
LogLine(FtpTraceLevel.Verbose, "+---------------------------------------+");
}
}
catch (FtpMissingSocketException) {
Expand Down Expand Up @@ -1358,16 +1361,19 @@ public string[] EndGetNameListing(IAsyncResult ar) {
// read in raw listing
try {
using (FtpDataStream stream = await OpenDataStreamAsync("NLST " + path.GetFtpPath(), 0, token)) {
string buf;
LogLine(FtpTraceLevel.Verbose, "+---------------------------------------+");
string line;

try {
while ((buf = await stream.ReadLineAsync(Encoding, token)) != null) {
listing.Add(buf);
while ((line = await stream.ReadLineAsync(Encoding, token)) != null) {
listing.Add(line);
LogLine(FtpTraceLevel.Verbose, "Listing: " + line);
}
}
finally {
stream.Close();
}
LogLine(FtpTraceLevel.Verbose, "+---------------------------------------+");
}
}
catch (FtpMissingSocketException) {
Expand Down
9 changes: 6 additions & 3 deletions FluentFTP/Client/FtpClient_Properties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,8 @@ public FtpDataType ListingDataType {

/// <summary>
/// File listing parser to be used.
/// Automatically calculated based on the type of the server, unless changed.
/// Automatically calculated based on the type of the server at the time of connection.
/// If you want to override this property, make sure to do it after calling Connect.
/// </summary>
public FtpParser ListingParser {
get => m_parser;
Expand Down Expand Up @@ -748,11 +749,13 @@ public CustomParser ListingCustomParser {
get => m_customParser;
set {
m_customParser = value;

// modify the ListingParser to note that a custom parser is set
if (value != null) {
m_parser = FtpParser.Custom;
ListingParser = FtpParser.Custom;
}
else {
m_parser = FtpParser.Auto;
ListingParser = FtpParser.Auto;
}
}
}
Expand Down
22 changes: 19 additions & 3 deletions FluentFTP/Helpers/FileListings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,41 @@ public static bool FileExistsInNameListing(string[] fileList, string path) {
var pathName = path.GetFtpFileName();
var pathPrefixed = path.EnsurePrefix("/");


// FAST MODE

// per entry in the name list
foreach (var fileListEntry in fileList) {
// FIX: support servers that return: 1) full paths, 2) only filenames, 3) full paths without slash prefixed
// support servers that return: 1) full paths, 2) only filenames, 3) full paths without slash prefixed
if (fileListEntry == pathName || fileListEntry == path || fileListEntry.EnsurePrefix("/") == pathPrefixed) {
return true;
}
}

// SLOW MODE

// SLOW MODE 1

// per entry in the name list
foreach (var fileListEntry in fileList) {
// support servers that return: 2) only filenames
if (fileListEntry.GetFtpFileName() == pathName) {
return true;
}
}


// SLOW MODE 2
// Fix #745: FileExists returns false when file exists [Windows NT Server]

// per entry in the name list
foreach (var fileListEntry in fileList) {
// support servers that return: 4) full paths with invalid slashes
if (fileListEntry.GetFtpFileName() == pathName) {
if (fileListEntry.GetFtpPath() == path) {
return true;
}
}


return false;
}

Expand Down
9 changes: 8 additions & 1 deletion FluentFTP/Helpers/RemotePaths.cs
Original file line number Diff line number Diff line change
Expand Up @@ -101,23 +101,30 @@ public static string GetFtpFileName(this string path) {
var tpath = path == null ? null : path;
var lastslash = -1;

// no change in path
if (tpath == null) {
return null;
}

// find the index of the right-most slash character
lastslash = tpath.LastIndexOf('/');
if (lastslash < 0) {
lastslash = tpath.LastIndexOf('\\');
if (lastslash < 0) {

// no change in path
return tpath;
}
}

lastslash += 1;
if (lastslash >= tpath.Length) {

// no change in path
return tpath;
}

// only return the filename and extension portion
// skipping all the path folders
return tpath.Substring(lastslash, tpath.Length - lastslash);
}

Expand Down

0 comments on commit 74043a9

Please sign in to comment.