1
0
Эх сурвалжийг харах

Bug 1933: Allow using SFTP servers that have problems with SSH_FXP_REALPATH requests

https://winscp.net/tracker/1933

Source commit: a682443b544e9e21a13736d2294118a693fa9054
Martin Prikryl 4 жил өмнө
parent
commit
af3adfbcab

+ 9 - 0
source/core/SessionData.cpp

@@ -244,6 +244,7 @@ void __fastcall TSessionData::DefaultSettings()
   SFTPListingQueue = 2;
   SFTPMaxVersion = ::SFTPMaxVersion;
   SFTPMaxPacketSize = 0;
+  SFTPRealPath = asAuto;
 
   for (unsigned int Index = 0; Index < LENOF(FSFTPBugs); Index++)
   {
@@ -417,6 +418,7 @@ void __fastcall TSessionData::NonPersistant()
   PROPERTY(SFTPListingQueue); \
   PROPERTY(SFTPMaxVersion); \
   PROPERTY(SFTPMaxPacketSize); \
+  PROPERTY(SFTPRealPath); \
   \
   for (unsigned int Index = 0; Index < LENOF(FSFTPBugs); Index++) \
   { \
@@ -800,6 +802,7 @@ void __fastcall TSessionData::DoLoad(THierarchicalStorage * Storage, bool PuttyI
   SFTPDownloadQueue = Storage->ReadInteger(L"SFTPDownloadQueue", SFTPDownloadQueue);
   SFTPUploadQueue = Storage->ReadInteger(L"SFTPUploadQueue", SFTPUploadQueue);
   SFTPListingQueue = Storage->ReadInteger(L"SFTPListingQueue", SFTPListingQueue);
+  SFTPRealPath = TAutoSwitch(Storage->ReadInteger(L"SFTPRealPath", SFTPRealPath));
 
   Color = Storage->ReadInteger(L"Color", Color);
 
@@ -1172,6 +1175,7 @@ void __fastcall TSessionData::DoSave(THierarchicalStorage * Storage,
     WRITE_DATA(Integer, SFTPDownloadQueue);
     WRITE_DATA(Integer, SFTPUploadQueue);
     WRITE_DATA(Integer, SFTPListingQueue);
+    WRITE_DATA(Integer, SFTPRealPath);
 
     WRITE_DATA(Integer, Color);
 
@@ -3875,6 +3879,11 @@ void __fastcall TSessionData::SetSFTPMaxPacketSize(unsigned long value)
   SET_SESSION_PROPERTY(SFTPMaxPacketSize);
 }
 //---------------------------------------------------------------------
+void __fastcall TSessionData::SetSFTPRealPath(TAutoSwitch value)
+{
+  SET_SESSION_PROPERTY(SFTPRealPath);
+}
+//---------------------------------------------------------------------
 void __fastcall TSessionData::SetSFTPBug(TSftpBug Bug, TAutoSwitch value)
 {
   DebugAssert(Bug >= 0 && static_cast<unsigned int>(Bug) < LENOF(FSFTPBugs));

+ 3 - 0
source/core/SessionData.h

@@ -184,6 +184,7 @@ private:
   int FSFTPListingQueue;
   int FSFTPMaxVersion;
   unsigned long FSFTPMaxPacketSize;
+  TAutoSwitch FSFTPRealPath;
   TDSTMode FDSTMode;
   TAutoSwitch FSFTPBugs[SFTP_BUG_COUNT];
   bool FDeleteToRecycleBin;
@@ -359,6 +360,7 @@ private:
   void __fastcall SetSFTPListingQueue(int value);
   void __fastcall SetSFTPMaxVersion(int value);
   void __fastcall SetSFTPMaxPacketSize(unsigned long value);
+  void __fastcall SetSFTPRealPath(TAutoSwitch value);
   void __fastcall SetSFTPBug(TSftpBug Bug, TAutoSwitch value);
   TAutoSwitch __fastcall GetSFTPBug(TSftpBug Bug) const;
   void __fastcall SetSCPLsFullTime(TAutoSwitch value);
@@ -627,6 +629,7 @@ public:
   __property int SFTPListingQueue = { read = FSFTPListingQueue, write = SetSFTPListingQueue };
   __property int SFTPMaxVersion = { read = FSFTPMaxVersion, write = SetSFTPMaxVersion };
   __property unsigned long SFTPMaxPacketSize = { read = FSFTPMaxPacketSize, write = SetSFTPMaxPacketSize };
+  __property TAutoSwitch SFTPRealPath = { read = FSFTPRealPath, write = SetSFTPRealPath };
   __property TAutoSwitch SFTPBug[TSftpBug Bug]  = { read=GetSFTPBug, write=SetSFTPBug };
   __property TAutoSwitch SCPLsFullTime = { read = FSCPLsFullTime, write = SetSCPLsFullTime };
   __property TAutoSwitch FtpListAll = { read = FFtpListAll, write = SetFtpListAll };

+ 4 - 0
source/core/SessionInfo.cpp

@@ -1307,6 +1307,10 @@ void __fastcall TSessionLog::DoAddStartupInfo(TSessionData * Data)
       }
       ADF(L"SFTP Bugs: %s", (Bugs));
       ADF(L"SFTP Server: %s", ((Data->SftpServer.IsEmpty()? UnicodeString(L"default") : Data->SftpServer)));
+      if (Data->SFTPRealPath != asAuto)
+      {
+        ADF(L"SFTP Real Path: %s", (EnumName(Data->SFTPRealPath, AutoSwitchNames)));
+      }
     }
     bool FtpsOn = false;
     if (Data->FSProtocol == fsFTP)

+ 58 - 51
source/core/SftpFileSystem.cpp

@@ -2733,68 +2733,75 @@ int __fastcall TSFTPFileSystem::SendPacketAndReceiveResponse(
 //---------------------------------------------------------------------------
 UnicodeString __fastcall TSFTPFileSystem::RealPath(const UnicodeString Path)
 {
-  try
+  if (FTerminal->SessionData->SFTPRealPath == asOff)
   {
-    FTerminal->LogEvent(0, FORMAT(L"Getting real path for '%s'", (Path)));
-
-    TSFTPPacket Packet(SSH_FXP_REALPATH);
-    AddPathString(Packet, Path);
-
-    // In SFTP-6 new optional field control-byte is added that defaults to
-    // SSH_FXP_REALPATH_NO_CHECK=0x01, meaning it won't fail, if the path does not exist.
-    // That differs from SFTP-5 recommendation that
-    // "The server SHOULD fail the request if the path is not present on the server."
-    // Earlier versions had no recommendation, though canonical SFTP-3 implementation
-    // in OpenSSH fails.
-
-    // While we really do not care much, we anyway set the flag to ~ & 0x01 to make the request fail.
-    // First for consistency.
-    // Second to workaround a bug in ProFTPD/mod_sftp version 1.3.5rc1 through 1.3.5-stable
-    // that sends a completelly malformed response for non-existing paths,
-    // when SSH_FXP_REALPATH_NO_CHECK (even implicitly) is used.
-    // See http://bugs.proftpd.org/show_bug.cgi?id=4160
-
-    // Note that earlier drafts of SFTP-6 (filexfer-07 and -08) had optional compose-path field
-    // before control-byte field. If we ever use this against a server conforming to those drafts,
-    // it may cause trouble.
-    if (FVersion >= 6)
+    return LocalCanonify(Path);
+  }
+  else
+  {
+    try
     {
-      if (FSecureShell->SshImplementation != sshiProFTPD)
+      FTerminal->LogEvent(0, FORMAT(L"Getting real path for '%s'", (Path)));
+
+      TSFTPPacket Packet(SSH_FXP_REALPATH);
+      AddPathString(Packet, Path);
+
+      // In SFTP-6 new optional field control-byte is added that defaults to
+      // SSH_FXP_REALPATH_NO_CHECK=0x01, meaning it won't fail, if the path does not exist.
+      // That differs from SFTP-5 recommendation that
+      // "The server SHOULD fail the request if the path is not present on the server."
+      // Earlier versions had no recommendation, though canonical SFTP-3 implementation
+      // in OpenSSH fails.
+
+      // While we really do not care much, we anyway set the flag to ~ & 0x01 to make the request fail.
+      // First for consistency.
+      // Second to workaround a bug in ProFTPD/mod_sftp version 1.3.5rc1 through 1.3.5-stable
+      // that sends a completelly malformed response for non-existing paths,
+      // when SSH_FXP_REALPATH_NO_CHECK (even implicitly) is used.
+      // See http://bugs.proftpd.org/show_bug.cgi?id=4160
+
+      // Note that earlier drafts of SFTP-6 (filexfer-07 and -08) had optional compose-path field
+      // before control-byte field. If we ever use this against a server conforming to those drafts,
+      // it may cause trouble.
+      if (FVersion >= 6)
       {
-        Packet.AddByte(SSH_FXP_REALPATH_STAT_ALWAYS);
+        if (FSecureShell->SshImplementation != sshiProFTPD)
+        {
+          Packet.AddByte(SSH_FXP_REALPATH_STAT_ALWAYS);
+        }
+        else
+        {
+          // Cannot use SSH_FXP_REALPATH_STAT_ALWAYS as ProFTPD does wrong bitwise test
+          // so it incorrectly evaluates SSH_FXP_REALPATH_STAT_ALWAYS (0x03) as
+          // SSH_FXP_REALPATH_NO_CHECK (0x01). The only value conforming to the
+          // specification, yet working with ProFTPD is SSH_FXP_REALPATH_STAT_IF (0x02).
+          Packet.AddByte(SSH_FXP_REALPATH_STAT_IF);
+        }
       }
-      else
+      SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_NAME);
+      if (Packet.GetCardinal() != 1)
       {
-        // Cannot use SSH_FXP_REALPATH_STAT_ALWAYS as ProFTPD does wrong bitwise test
-        // so it incorrectly evaluates SSH_FXP_REALPATH_STAT_ALWAYS (0x03) as
-        // SSH_FXP_REALPATH_NO_CHECK (0x01). The only value conforming to the
-        // specification, yet working with ProFTPD is SSH_FXP_REALPATH_STAT_IF (0x02).
-        Packet.AddByte(SSH_FXP_REALPATH_STAT_IF);
+        FTerminal->FatalError(NULL, LoadStr(SFTP_NON_ONE_FXP_NAME_PACKET));
       }
-    }
-    SendPacketAndReceiveResponse(&Packet, &Packet, SSH_FXP_NAME);
-    if (Packet.GetCardinal() != 1)
-    {
-      FTerminal->FatalError(NULL, LoadStr(SFTP_NON_ONE_FXP_NAME_PACKET));
-    }
 
-    UnicodeString RealDir = UnixExcludeTrailingBackslash(Packet.GetPathString(FUtfStrings));
-    RealDir = FTerminal->DecryptFileName(RealDir);
-    // ignore rest of SSH_FXP_NAME packet
+      UnicodeString RealDir = UnixExcludeTrailingBackslash(Packet.GetPathString(FUtfStrings));
+      RealDir = FTerminal->DecryptFileName(RealDir);
+      // ignore rest of SSH_FXP_NAME packet
 
-    FTerminal->LogEvent(0, FORMAT(L"Real path is '%s'", (RealDir)));
+      FTerminal->LogEvent(0, FORMAT(L"Real path is '%s'", (RealDir)));
 
-    return RealDir;
-  }
-  catch(Exception & E)
-  {
-    if (FTerminal->Active)
-    {
-      throw ExtException(&E, FMTLOAD(SFTP_REALPATH_ERROR, (Path)));
+      return RealDir;
     }
-    else
+    catch(Exception & E)
     {
-      throw;
+      if (FTerminal->Active)
+      {
+        throw ExtException(&E, FMTLOAD(SFTP_REALPATH_ERROR, (Path)));
+      }
+      else
+      {
+        throw;
+      }
     }
   }
 }