From 6c7f6477d0617fd2539120c5fc9a60b174f5ca6d Mon Sep 17 00:00:00 2001 From: martinprikryl <martinprikryl> Date: Wed, 4 Nov 2015 14:11:29 +0000 Subject: [PATCH] WinSCP 5.7.6 2015-11-04 http://winscp.net/eng/docs/history?a=5.7.6 --- dotnet/Session.cs | 4 + .../ReadOnlyInteropCollectionHelper.cs | 5 + dotnet/interopcollections/StringCollection.cs | 5 + dotnet/properties/AssemblyInfo.cs | 6 +- source/Console.cbproj | 2 +- source/DragExt.cbproj | 2 +- source/DragExt64.rc | 4 +- source/WinSCP.cbproj | 4 +- source/components/UnixDirView.cpp | 36 ++++---- source/components/UnixDriveView.cpp | 21 +++-- source/core/FtpFileSystem.cpp | 92 +++++++++++++------ source/core/FtpFileSystem.h | 1 + source/core/Script.cpp | 12 +++ source/core/SecureShell.cpp | 4 + source/core/SessionData.cpp | 8 +- source/core/SessionData.h | 6 +- source/core/SessionInfo.cpp | 4 +- source/core/WebDAVFileSystem.cpp | 32 ++++--- source/filezilla/FtpControlSocket.cpp | 16 +++- source/filezilla/FtpListResult.cpp | 33 +++++-- source/filezilla/FtpListResult.h | 6 +- source/forms/CustomScpExplorer.cpp | 60 ++++++------ source/resource/TextsCore.h | 1 + source/resource/TextsCore1.rc | 1 + source/windows/TerminalManager.cpp | 7 +- source/windows/WinMain.cpp | 2 +- 26 files changed, 243 insertions(+), 131 deletions(-) diff --git a/dotnet/Session.cs b/dotnet/Session.cs index ed5c0e3e..06f2d628 100644 --- a/dotnet/Session.cs +++ b/dotnet/Session.cs @@ -1239,6 +1239,10 @@ private void ProcessOutputDataReceived(object sender, OutputDataReceivedEventArg { Logger.WriteLine("Scheduling output: [{0}]", e.Data); Output.InternalAdd(e.Data); + if (Output.Count > 1000) + { + Output.InternalRemoveFirst(); + } GotOutput(); ScheduleEvent(() => RaiseOutputDataReceived(e.Data)); } diff --git a/dotnet/internal/ReadOnlyInteropCollectionHelper.cs b/dotnet/internal/ReadOnlyInteropCollectionHelper.cs index b2bf8cb4..f88cf875 100644 --- a/dotnet/internal/ReadOnlyInteropCollectionHelper.cs +++ b/dotnet/internal/ReadOnlyInteropCollectionHelper.cs @@ -11,6 +11,11 @@ public void InternalAdd(T item) _list.Add(item); } + public void InternalRemoveFirst() + { + _list.RemoveAt(0); + } + public T this[int index] { get diff --git a/dotnet/interopcollections/StringCollection.cs b/dotnet/interopcollections/StringCollection.cs index 6ccfa0df..92a90a4e 100644 --- a/dotnet/interopcollections/StringCollection.cs +++ b/dotnet/interopcollections/StringCollection.cs @@ -83,6 +83,11 @@ internal void InternalAdd(string item) _helper.InternalAdd(item); } + internal void InternalRemoveFirst() + { + _helper.InternalRemoveFirst(); + } + private readonly ReadOnlyInteropCollectionHelper<string> _helper = new ReadOnlyInteropCollectionHelper<string>(); } } diff --git a/dotnet/properties/AssemblyInfo.cs b/dotnet/properties/AssemblyInfo.cs index 3a9d817d..fa7320c0 100644 --- a/dotnet/properties/AssemblyInfo.cs +++ b/dotnet/properties/AssemblyInfo.cs @@ -19,9 +19,9 @@ // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("a0b93468-d98a-4845-a234-8076229ad93f")] -[assembly: AssemblyVersion("1.2.9.0")] -[assembly: AssemblyFileVersion("1.2.9.0")] -[assembly: AssemblyInformationalVersionAttribute("5.7.5.0")] +[assembly: AssemblyVersion("1.2.10.0")] +[assembly: AssemblyFileVersion("1.2.10.0")] +[assembly: AssemblyInformationalVersionAttribute("5.7.6.0")] [assembly: CLSCompliant(true)] diff --git a/source/Console.cbproj b/source/Console.cbproj index eb55d6e2..f4bfe9e6 100644 --- a/source/Console.cbproj +++ b/source/Console.cbproj @@ -65,7 +65,7 @@ <ProjectType>CppConsoleApplication</ProjectType> <SanitizedProjectName>Console</SanitizedProjectName> <VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo> - <VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=Console interface for WinSCP;FileVersion=4.1.0.0;InternalName=console;LegalCopyright=(c) 2000-2015 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.com;ProductName=WinSCP;ProductVersion=5.7.5.0;ReleaseType=stable;WWW=http://winscp.net/</VerInfo_Keys> + <VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=Console interface for WinSCP;FileVersion=4.1.0.0;InternalName=console;LegalCopyright=(c) 2000-2015 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.com;ProductName=WinSCP;ProductVersion=5.7.6.0;ReleaseType=stable;WWW=http://winscp.net/</VerInfo_Keys> <VerInfo_Locale>1033</VerInfo_Locale> <VerInfo_MajorVer>4</VerInfo_MajorVer> <VerInfo_MinorVer>1</VerInfo_MinorVer> diff --git a/source/DragExt.cbproj b/source/DragExt.cbproj index cb506b67..a1f283bd 100644 --- a/source/DragExt.cbproj +++ b/source/DragExt.cbproj @@ -66,7 +66,7 @@ <SanitizedProjectName>DragExt</SanitizedProjectName> <VerInfo_DLL>true</VerInfo_DLL> <VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo> - <VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=Drag&Drop shell extension for WinSCP (32-bit);FileVersion=1.2.1.0;InternalName=dragext32;LegalCopyright=(c) 2000-2015 Martin Prikryl;LegalTrademarks=;OriginalFilename=dragext.dll;ProductName=WinSCP;ProductVersion=5.7.5.0;ReleaseType=stable;WWW=http://winscp.net/</VerInfo_Keys> + <VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=Drag&Drop shell extension for WinSCP (32-bit);FileVersion=1.2.1.0;InternalName=dragext32;LegalCopyright=(c) 2000-2015 Martin Prikryl;LegalTrademarks=;OriginalFilename=dragext.dll;ProductName=WinSCP;ProductVersion=5.7.6.0;ReleaseType=stable;WWW=http://winscp.net/</VerInfo_Keys> <VerInfo_Locale>1033</VerInfo_Locale> <VerInfo_MinorVer>2</VerInfo_MinorVer> <VerInfo_Release>1</VerInfo_Release> diff --git a/source/DragExt64.rc b/source/DragExt64.rc index 826ab4e3..ab360e92 100644 --- a/source/DragExt64.rc +++ b/source/DragExt64.rc @@ -1,6 +1,6 @@ 1 VERSIONINFO FILEVERSION 1,2,1,0 -PRODUCTVERSION 5,7,5,0 +PRODUCTVERSION 5,7,6,0 FILEOS 0x4 FILETYPE 0x2 { @@ -16,7 +16,7 @@ FILETYPE 0x2 VALUE "LegalTrademarks", "\0" VALUE "OriginalFilename", "dragext64.dll\0" VALUE "ProductName", "WinSCP\0" - VALUE "ProductVersion", "5.7.5.0\0" + VALUE "ProductVersion", "5.7.6.0\0" VALUE "ReleaseType", "stable\0" VALUE "WWW", "http://winscp.net/\0" } diff --git a/source/WinSCP.cbproj b/source/WinSCP.cbproj index e03e8c58..4753d695 100644 --- a/source/WinSCP.cbproj +++ b/source/WinSCP.cbproj @@ -83,11 +83,11 @@ <SanitizedProjectName>WinSCP</SanitizedProjectName> <UsingDelphiRTL>true</UsingDelphiRTL> <VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo> - <VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=WinSCP: SFTP, FTP and SCP client;FileVersion=5.7.5.0;InternalName=winscp;LegalCopyright=(c) 2000-2015 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.exe;ProductName=WinSCP;ProductVersion=5.7.5.0;ReleaseType=stable;WWW=http://winscp.net/</VerInfo_Keys> + <VerInfo_Keys>CompanyName=Martin Prikryl;FileDescription=WinSCP: SFTP, FTP and SCP client;FileVersion=5.7.6.0;InternalName=winscp;LegalCopyright=(c) 2000-2015 Martin Prikryl;LegalTrademarks=;OriginalFilename=winscp.exe;ProductName=WinSCP;ProductVersion=5.7.6.0;ReleaseType=stable;WWW=http://winscp.net/</VerInfo_Keys> <VerInfo_Locale>1033</VerInfo_Locale> <VerInfo_MajorVer>5</VerInfo_MajorVer> <VerInfo_MinorVer>7</VerInfo_MinorVer> - <VerInfo_Release>5</VerInfo_Release> + <VerInfo_Release>6</VerInfo_Release> </PropertyGroup> <PropertyGroup Condition="'$(Base_Win32)'!=''"> <Defines>IDE;STRICT;$(Defines)</Defines> diff --git a/source/components/UnixDirView.cpp b/source/components/UnixDirView.cpp index 8df1f18c..d3bc4dba 100644 --- a/source/components/UnixDirView.cpp +++ b/source/components/UnixDirView.cpp @@ -429,25 +429,29 @@ void __fastcall TUnixDirView::PerformItemDragDropOperation(TListItem * Item, #ifndef DESIGN_ONLY if (OnDDFileOperation) { - assert(DragDropFilesEx->FileList->Count > 0); + // Could be empty if the source application does not provide any files; + // or if the IDataObject fails GetData, like Visual Studio Code 0.8.0: + // https://code.visualstudio.com/issues/detail/19410 + if (DragDropFilesEx->FileList->Count > 0) + { + UnicodeString SourceDirectory; + UnicodeString TargetDirectory; - UnicodeString SourceDirectory; - UnicodeString TargetDirectory; + SourceDirectory = ExtractFilePath(DragDropFilesEx->FileList->Items[0]->Name); + if (Item) + { + assert(ITEMFILE->IsDirectory && (Terminal->Files->IndexOf(ITEMFILE) >= 0)); + TargetDirectory = ITEMFILE->FullFileName; + } + else + { + TargetDirectory = Path; + } - SourceDirectory = ExtractFilePath(DragDropFilesEx->FileList->Items[0]->Name); - if (Item) - { - assert(ITEMFILE->IsDirectory && (Terminal->Files->IndexOf(ITEMFILE) >= 0)); - TargetDirectory = ITEMFILE->FullFileName; + bool DoFileOperation = true; + OnDDFileOperation(this, Effect, SourceDirectory, TargetDirectory, + DoFileOperation); } - else - { - TargetDirectory = Path; - } - - bool DoFileOperation = true; - OnDDFileOperation(this, Effect, SourceDirectory, TargetDirectory, - DoFileOperation); } #else USEDPARAM(Item); diff --git a/source/components/UnixDriveView.cpp b/source/components/UnixDriveView.cpp index cfbe5127..87a3d021 100644 --- a/source/components/UnixDriveView.cpp +++ b/source/components/UnixDriveView.cpp @@ -528,18 +528,21 @@ void __fastcall TCustomUnixDriveView::PerformDragDropFileOperation( { if (OnDDFileOperation) { - assert(DragDropFilesEx->FileList->Count > 0); - assert(Node != NULL); + // see a comment in TUnixDirView::PerformItemDragDropOperation + if (DragDropFilesEx->FileList->Count > 0) + { + assert(Node != NULL); - UnicodeString SourceDirectory; - UnicodeString TargetDirectory; + UnicodeString SourceDirectory; + UnicodeString TargetDirectory; - SourceDirectory = ExtractFilePath(DragDropFilesEx->FileList->Items[0]->Name); - TargetDirectory = NodeData(Node)->Directory; + SourceDirectory = ExtractFilePath(DragDropFilesEx->FileList->Items[0]->Name); + TargetDirectory = NodeData(Node)->Directory; - bool DoFileOperation = true; - OnDDFileOperation(this, Effect, SourceDirectory, TargetDirectory, - DoFileOperation); + bool DoFileOperation = true; + OnDDFileOperation(this, Effect, SourceDirectory, TargetDirectory, + DoFileOperation); + } } } //--------------------------------------------------------------------------- diff --git a/source/core/FtpFileSystem.cpp b/source/core/FtpFileSystem.cpp index ea2714c7..3eed5fbb 100644 --- a/source/core/FtpFileSystem.cpp +++ b/source/core/FtpFileSystem.cpp @@ -382,6 +382,8 @@ void __fastcall TFTPFileSystem::Open() } } + FTransferActiveImmediately = (Data->FtpTransferActiveImmediately == asOn); + UnicodeString HostName = Data->HostNameExpanded; UnicodeString UserName = Data->UserNameExpanded; UnicodeString Password = Data->Password; @@ -743,6 +745,15 @@ void __fastcall TFTPFileSystem::CollectUsage() { FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPSyncplify"); } + // 220-Idea FTP Server v0.80 (xxx.home.pl) [xxx.xxx.xxx.xxx] + // 220 Ready + // ... + // SYST + // UNIX Type: L8 + else if (ContainsText(FWelcomeMessage, L"Idea FTP Server")) + { + FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPIdea"); + } else { FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPOther"); @@ -2431,7 +2442,7 @@ void __fastcall TFTPFileSystem::AutoDetectTimeDifference(TRemoteFileList * FileL TDateTime UtcModification = UtcFile->Modification; delete UtcFile; - FTimeDifference = SecondsBetween(UtcModification, File->Modification); + FTimeDifference = static_cast<__int64>(SecsPerDay * (UtcModification - File->Modification)); UnicodeString LogMessage; if (FTimeDifference == 0) @@ -2563,10 +2574,9 @@ void __fastcall TFTPFileSystem::DoReadFile(const UnicodeString & AFileName, if (File != NULL) { AFile = File->Duplicate(); + ApplyTimeDifference(AFile); } - ApplyTimeDifference(AFile); - FLastDataSent = Now(); } __finally @@ -2597,41 +2607,50 @@ void __fastcall TFTPFileSystem::ReadFile(const UnicodeString FileName, } else { - // FZAPI does not have efficient way to read properties of one file. - // In case we need properties of set of files from the same directory, - // cache the file list for future - if ((FFileListCache != NULL) && - UnixSamePath(Path, FFileListCache->Directory) && - (UnixIsAbsolutePath(FFileListCache->Directory) || - (FFileListCachePath == CurrentDirectory))) + if (IsUnixRootPath(FileName)) { - AFile = FFileListCache->FindFile(NameOnly); + AFile = new TRemoteDirectoryFile(); + AFile->FullFileName = FileName; + AFile->FileName = L""; } - // if cache is invalid or file is not in cache, (re)read the directory - if (AFile == NULL) + else { - TRemoteFileList * FileListCache = new TRemoteFileList(); - FileListCache->Directory = Path; - try + // FZAPI does not have efficient way to read properties of one file. + // In case we need properties of set of files from the same directory, + // cache the file list for future + if ((FFileListCache != NULL) && + UnixSamePath(Path, FFileListCache->Directory) && + (UnixIsAbsolutePath(FFileListCache->Directory) || + (FFileListCachePath == CurrentDirectory))) { - ReadDirectory(FileListCache); + AFile = FFileListCache->FindFile(NameOnly); } - catch(...) + // if cache is invalid or file is not in cache, (re)read the directory + if (AFile == NULL) { - delete FileListCache; - throw; + TRemoteFileList * FileListCache = new TRemoteFileList(); + FileListCache->Directory = Path; + try + { + ReadDirectory(FileListCache); + } + catch(...) + { + delete FileListCache; + throw; + } + // set only after we successfully read the directory, + // otherwise, when we reconnect from ReadDirectory, + // the FFileListCache is reset from ResetCache. + delete FFileListCache; + FFileListCache = FileListCache; + FFileListCachePath = GetCurrentDirectory(); + + AFile = FFileListCache->FindFile(NameOnly); } - // set only after we successfully read the directory, - // otherwise, when we reconnect from ReadDirectory, - // the FFileListCache is reset from ResetCache. - delete FFileListCache; - FFileListCache = FileListCache; - FFileListCachePath = GetCurrentDirectory(); - AFile = FFileListCache->FindFile(NameOnly); + Own = false; } - - Own = false; } if (AFile == NULL) @@ -2982,7 +3001,7 @@ int __fastcall TFTPFileSystem::GetOptionVal(int OptionID) const break; case OPTION_MPEXT_TRANSFER_ACTIVE_IMMEDIATELY: - Result = Data->FtpTransferActiveImmediately; + Result = FTransferActiveImmediately; break; case OPTION_MPEXT_REMOVE_BOM: @@ -3455,6 +3474,10 @@ void __fastcall TFTPFileSystem::HandleReplyStatus(UnicodeString Response) // SIZE // 211 End + // This format is according to RFC 2228. + // Is used by ProFTPD when MultilineRFC2228 is enabled + // http://www.proftpd.org/docs/directives/linked/config_ref_MultilineRFC2228.html + // 211-Features: // 211-MDTM // 211-REST STREAM @@ -3471,6 +3494,8 @@ void __fastcall TFTPFileSystem::HandleReplyStatus(UnicodeString Response) // MDTM // 211 END + // Partially duplicated in CFtpControlSocket::OnReceive + bool HasCodePrefix = (Response.Length() >= 3) && TryStrToInt(Response.SubString(1, 3), Code) && @@ -3531,6 +3556,13 @@ void __fastcall TFTPFileSystem::HandleReplyStatus(UnicodeString Response) { FTerminal->DisplayBanner(FWelcomeMessage); } + // Idea FTP Server v0.80 + if ((FTerminal->SessionData->FtpTransferActiveImmediately == asAuto) && + FWelcomeMessage.Pos(L"Idea FTP Server") > 0) + { + FTerminal->LogEvent(L"The server requires TLS/SSL handshake on transfer connection before responding 1yz to STOR/APPE"); + FTransferActiveImmediately = true; + } } } else if (FLastCommand == PASS) diff --git a/source/core/FtpFileSystem.h b/source/core/FtpFileSystem.h index dbb3f675..d1b9dd57 100644 --- a/source/core/FtpFileSystem.h +++ b/source/core/FtpFileSystem.h @@ -268,6 +268,7 @@ friend class TFileListHelper; std::unique_ptr<TStrings> FHashAlgs; bool FSupportsAnyChecksumFeature; UnicodeString FLastCommandSent; + bool FTransferActiveImmediately; mutable UnicodeString FOptionScratch; }; //--------------------------------------------------------------------------- diff --git a/source/core/Script.cpp b/source/core/Script.cpp index 6930a872..4772fd85 100644 --- a/source/core/Script.cpp +++ b/source/core/Script.cpp @@ -569,6 +569,12 @@ TStrings * __fastcall TScript::CreateFileList(TScriptProcParams * Parameters, in for (int i = Start; i <= End; i++) { UnicodeString FileName = Parameters->Param[i]; + + if (SimpleUnixExcludeTrailingBackslash(FileName) != FileName) + { + PrintLine(LoadStr(SCRIPT_AMBIGUOUS_SLASH_IN_PATH)); + } + if (FLAGSET(ListType, fltDirectories)) { TRemoteFile * File = new TRemoteFile(); @@ -686,6 +692,12 @@ TStrings * __fastcall TScript::CreateLocalFileList(TScriptProcParams * Parameter // (it actually won't make a difference functionally as we fall back to adding // the path as is in "else" branch, but the comment "let it fail later" won't stand) UnicodeString FileName = ExcludeTrailingBackslash(Parameters->Param[i]); + + if (FileName != Parameters->Param[i]) + { + PrintLine(LoadStr(SCRIPT_AMBIGUOUS_SLASH_IN_PATH)); + } + if (FLAGSET(ListType, fltMask)) { TSearchRecChecked SearchRec; diff --git a/source/core/SecureShell.cpp b/source/core/SecureShell.cpp index 4611ed0a..061efd10 100644 --- a/source/core/SecureShell.cpp +++ b/source/core/SecureShell.cpp @@ -2353,6 +2353,10 @@ void __fastcall TSecureShell::CollectUsage() { Configuration->Usage->Inc(L"OpenedSessionsSSHSyncplify"); } + else if (ContainsText(FSessionInfo.SshImplementation, L"zFTPServer")) + { + Configuration->Usage->Inc(L"OpenedSessionsSSHzFTP"); + } else { Configuration->Usage->Inc(L"OpenedSessionsSSHOther"); diff --git a/source/core/SessionData.cpp b/source/core/SessionData.cpp index e101a888..cdf44d7e 100644 --- a/source/core/SessionData.cpp +++ b/source/core/SessionData.cpp @@ -211,7 +211,7 @@ void __fastcall TSessionData::Default() FtpAccount = L""; FtpPingInterval = 30; FtpPingType = ptDummyCommand; - FtpTransferActiveImmediately = false; + FtpTransferActiveImmediately = asAuto; Ftps = ftpsNone; MinTlsVersion = tls10; MaxTlsVersion = tls12; @@ -657,7 +657,7 @@ void __fastcall TSessionData::DoLoad(THierarchicalStorage * Storage, bool & Rewr FtpAccount = Storage->ReadString(L"FtpAccount", FtpAccount); FtpPingInterval = Storage->ReadInteger(L"FtpPingInterval", FtpPingInterval); FtpPingType = static_cast<TPingType>(Storage->ReadInteger(L"FtpPingType", FtpPingType)); - FtpTransferActiveImmediately = Storage->ReadBool(L"FtpTransferActiveImmediately", FtpTransferActiveImmediately); + FtpTransferActiveImmediately = static_cast<TAutoSwitch>(Storage->ReadInteger(L"FtpTransferActiveImmediately2", FtpTransferActiveImmediately)); Ftps = static_cast<TFtps>(Storage->ReadInteger(L"Ftps", Ftps)); FtpListAll = TAutoSwitch(Storage->ReadInteger(L"FtpListAll", FtpListAll)); FtpHost = TAutoSwitch(Storage->ReadInteger(L"FtpHost", FtpHost)); @@ -920,7 +920,7 @@ void __fastcall TSessionData::Save(THierarchicalStorage * Storage, WRITE_DATA(String, FtpAccount); WRITE_DATA(Integer, FtpPingInterval); WRITE_DATA(Integer, FtpPingType); - WRITE_DATA(Bool, FtpTransferActiveImmediately); + WRITE_DATA_EX(Integer, L"FtpTransferActiveImmediately2", FtpTransferActiveImmediately, ); WRITE_DATA(Integer, Ftps); WRITE_DATA(Integer, FtpListAll); WRITE_DATA(Integer, FtpHost); @@ -2650,7 +2650,7 @@ void __fastcall TSessionData::SetFtpPingType(TPingType value) SET_SESSION_PROPERTY(FtpPingType); } //--------------------------------------------------------------------------- -void __fastcall TSessionData::SetFtpTransferActiveImmediately(bool value) +void __fastcall TSessionData::SetFtpTransferActiveImmediately(TAutoSwitch value) { SET_SESSION_PROPERTY(FtpTransferActiveImmediately); } diff --git a/source/core/SessionData.h b/source/core/SessionData.h index 799485f5..e88bc894 100644 --- a/source/core/SessionData.h +++ b/source/core/SessionData.h @@ -181,7 +181,7 @@ friend class TStoredSessionList; UnicodeString FFtpAccount; int FFtpPingInterval; TPingType FFtpPingType; - bool FFtpTransferActiveImmediately; + TAutoSwitch FFtpTransferActiveImmediately; TFtps FFtps; TTlsVersion FMinTlsVersion; TTlsVersion FMaxTlsVersion; @@ -332,7 +332,7 @@ friend class TStoredSessionList; void __fastcall SetFtpAccount(UnicodeString value); void __fastcall SetFtpPingInterval(int value); void __fastcall SetFtpPingType(TPingType value); - void __fastcall SetFtpTransferActiveImmediately(bool value); + void __fastcall SetFtpTransferActiveImmediately(TAutoSwitch value); void __fastcall SetFtps(TFtps value); void __fastcall SetMinTlsVersion(TTlsVersion value); void __fastcall SetMaxTlsVersion(TTlsVersion value); @@ -522,7 +522,7 @@ friend class TStoredSessionList; __property int FtpPingInterval = { read=FFtpPingInterval, write=SetFtpPingInterval }; __property TDateTime FtpPingIntervalDT = { read=GetFtpPingIntervalDT }; __property TPingType FtpPingType = { read = FFtpPingType, write = SetFtpPingType }; - __property bool FtpTransferActiveImmediately = { read = FFtpTransferActiveImmediately, write = SetFtpTransferActiveImmediately }; + __property TAutoSwitch FtpTransferActiveImmediately = { read = FFtpTransferActiveImmediately, write = SetFtpTransferActiveImmediately }; __property TFtps Ftps = { read = FFtps, write = SetFtps }; __property TTlsVersion MinTlsVersion = { read = FMinTlsVersion, write = SetMinTlsVersion }; __property TTlsVersion MaxTlsVersion = { read = FMaxTlsVersion, write = SetMaxTlsVersion }; diff --git a/source/core/SessionInfo.cpp b/source/core/SessionInfo.cpp index 45e55028..16c20b21 100644 --- a/source/core/SessionInfo.cpp +++ b/source/core/SessionInfo.cpp @@ -1173,9 +1173,9 @@ void __fastcall TSessionLog::DoAddStartupInfo(TSessionData * Data) ADF(L"TLS/SSL versions: %s-%s", (GetTlsVersionName(Data->MinTlsVersion), GetTlsVersionName(Data->MaxTlsVersion))); } // kind of hidden option, so do not reveal it unless it is set - if (Data->FtpTransferActiveImmediately) + if (Data->FtpTransferActiveImmediately != asAuto) { - ADF(L"Transfer active immediately: %s", (BooleanToEngStr(Data->FtpTransferActiveImmediately))); + ADF(L"Transfer active immediately: %s", (BugFlags[Data->FtpTransferActiveImmediately])); } } ADF(L"Local directory: %s, Remote directory: %s, Update: %s, Cache: %s", diff --git a/source/core/WebDAVFileSystem.cpp b/source/core/WebDAVFileSystem.cpp index ff83cd6b..ef805505 100644 --- a/source/core/WebDAVFileSystem.cpp +++ b/source/core/WebDAVFileSystem.cpp @@ -878,15 +878,13 @@ void TWebDAVFileSystem::NeonPropsResult( UTF8String UnescapedUri = PathUnescape(Uri->path).c_str(); UnicodeString Path = StrFromNeon(UnescapedUri); - Path = UnixExcludeTrailingBackslash(Path); - TReadFileData & Data = *static_cast<TReadFileData *>(UserData); if (Data.FileList != NULL) { UnicodeString FileListPath = Data.FileSystem->AbsolutePath(Data.FileList->Directory, false); if (UnixSamePath(Path, FileListPath)) { - Path = UnixIncludeTrailingBackslash(Path) + L".."; + Path = UnixIncludeTrailingBackslash(UnixIncludeTrailingBackslash(Path) + L".."); } std::unique_ptr<TRemoteFile> File(new TRemoteFile(NULL)); File->Terminal = Data.FileSystem->FTerminal; @@ -910,7 +908,10 @@ const char * __fastcall TWebDAVFileSystem::GetProp(const ne_prop_result_set * Re void __fastcall TWebDAVFileSystem::ParsePropResultSet(TRemoteFile * File, const UnicodeString & Path, const ne_prop_result_set * Results) { - File->FullFileName = Path; + File->FullFileName = UnixExcludeTrailingBackslash(Path); + // Some servers do not use DAV:collection tag, but indicate the folder by trailing slash only. + // It seems that all servers actually use the trailing slash, including IIS, mod_Dav, IT Hit, OpenDrive, etc. + bool Collection = (File->FullFileName != Path); File->FileName = UnixExtractFileName(File->FullFileName); const char * ContentLength = GetProp(Results, PROP_CONTENT_LENGTH); // some servers, for example iFiles, do not provide "getcontentlength" for folders @@ -945,18 +946,25 @@ void __fastcall TWebDAVFileSystem::ParsePropResultSet(TRemoteFile * File, } } } - bool Collection = false; - const char * ResourceType = GetProp(Results, PROP_RESOURCE_TYPE); - if (ResourceType != NULL) + + // optimization + if (!Collection) { - // property has XML value - UnicodeString AResourceType = ResourceType; - // this is very poor parsing - if (ContainsText(ResourceType, L"<DAV:collection")) + // This is possibly redundant code as all servers we know (see a comment above) + // indicate the folder by trailing slash too + const char * ResourceType = GetProp(Results, PROP_RESOURCE_TYPE); + if (ResourceType != NULL) { - Collection = true; + // property has XML value + UnicodeString AResourceType = ResourceType; + // this is very poor parsing + if (ContainsText(ResourceType, L"<DAV:collection")) + { + Collection = true; + } } } + File->Type = Collection ? FILETYPE_DIRECTORY : FILETYPE_DEFAULT; // this is MS extension (draft-hopmann-collection-props-00) const char * IsHidden = GetProp(Results, PROP_HIDDEN); diff --git a/source/filezilla/FtpControlSocket.cpp b/source/filezilla/FtpControlSocket.cpp index 5cd0d042..7051ebbb 100644 --- a/source/filezilla/FtpControlSocket.cpp +++ b/source/filezilla/FtpControlSocket.cpp @@ -1226,14 +1226,20 @@ void CFtpControlSocket::OnReceive(int nErrorCode) { ShowStatus(A2CT(m_RecvBuffer.back()), FZ_LOG_REPLY); } - //Check for multi-line responses + // Check for multi-line responses + // Partially duplicated in TFTPFileSystem::HandleReplyStatus if (m_RecvBuffer.back().GetLength() > 3) { if (m_MultiLine != "") { if (m_RecvBuffer.back().Left(4) != m_MultiLine) { - DiscardLine(m_RecvBuffer.back()); + CStringA line = m_RecvBuffer.back(); + if (line.Left(4) == m_MultiLine.Left(3) + '-') + { + line = line.Mid(4, line.GetLength() - 4); + } + DiscardLine(line); m_RecvBuffer.pop_back(); } else // end of multi-line found @@ -1699,7 +1705,7 @@ void CFtpControlSocket::List(BOOL bFinish, int nError /*=FALSE*/, CServerPath pa pData->pDirectoryListing = new t_directory; if (COptions::GetOptionVal(OPTION_DEBUGSHOWLISTING)) m_pTransferSocket->m_pListResult->SendToMessageLog(m_pOwner->m_hOwnerWnd, m_pOwner->m_nReplyMessageID); - pData->pDirectoryListing->direntry = m_pTransferSocket->m_pListResult->getList(num, pData->ListStartTime); + pData->pDirectoryListing->direntry = m_pTransferSocket->m_pListResult->getList(num, pData->ListStartTime, false); pData->pDirectoryListing->num = num; if (m_pTransferSocket->m_pListResult->m_server.nServerType & FZ_SERVERTYPE_SUB_FTP_VMS && m_CurrentServer.nServerType & FZ_SERVERTYPE_FTP) m_CurrentServer.nServerType |= FZ_SERVERTYPE_SUB_FTP_VMS; @@ -2541,7 +2547,7 @@ void CFtpControlSocket::ListFile(CString filename, const CServerPath &path) pListResult->AddData(buffer, size); if (COptions::GetOptionVal(OPTION_DEBUGSHOWLISTING)) pListResult->SendToMessageLog(m_pOwner->m_hOwnerWnd, m_pOwner->m_nReplyMessageID); - pData->direntry = pListResult->getList(num, CTime::GetCurrentTime()); + pData->direntry = pListResult->getList(num, CTime::GetCurrentTime(), true); if (pListResult->m_server.nServerType & FZ_SERVERTYPE_SUB_FTP_VMS && m_CurrentServer.nServerType & FZ_SERVERTYPE_FTP) m_CurrentServer.nServerType |= FZ_SERVERTYPE_SUB_FTP_VMS; delete pListResult; @@ -2893,7 +2899,7 @@ void CFtpControlSocket::FileTransfer(t_transferfile *transferfile/*=0*/,BOOL bFi pData->pDirectoryListing=new t_directory; if (COptions::GetOptionVal(OPTION_DEBUGSHOWLISTING)) m_pTransferSocket->m_pListResult->SendToMessageLog(m_pOwner->m_hOwnerWnd, m_pOwner->m_nReplyMessageID); - pData->pDirectoryListing->direntry=m_pTransferSocket->m_pListResult->getList(num, pData->ListStartTime); + pData->pDirectoryListing->direntry=m_pTransferSocket->m_pListResult->getList(num, pData->ListStartTime, false); pData->pDirectoryListing->num=num; if (m_pTransferSocket->m_pListResult->m_server.nServerType&FZ_SERVERTYPE_SUB_FTP_VMS && m_CurrentServer.nServerType&FZ_SERVERTYPE_FTP) m_CurrentServer.nServerType |= FZ_SERVERTYPE_SUB_FTP_VMS; diff --git a/source/filezilla/FtpListResult.cpp b/source/filezilla/FtpListResult.cpp index cb3c8b9f..8dcfa5c1 100644 --- a/source/filezilla/FtpListResult.cpp +++ b/source/filezilla/FtpListResult.cpp @@ -422,7 +422,7 @@ CFtpListResult::~CFtpListResult() delete [] m_curline; } -t_directory::t_direntry *CFtpListResult::getList(int &num, CTime EntryTime) +t_directory::t_direntry *CFtpListResult::getList(int &num, CTime EntryTime, bool mlst) { #ifdef _DEBUG USES_CONVERSION; @@ -435,7 +435,7 @@ t_directory::t_direntry *CFtpListResult::getList(int &num, CTime EntryTime) char *tmpline = new char[strlen(line) + 1]; strcpy(tmpline, line); t_directory::t_direntry direntry; - if (parseLine(tmpline, strlen(tmpline), direntry, tmp)) + if (parseLine(tmpline, strlen(tmpline), direntry, tmp, mlst)) { delete [] tmpline; if (tmp) @@ -507,14 +507,14 @@ t_directory::t_direntry *CFtpListResult::getList(int &num, CTime EntryTime) return res; } -BOOL CFtpListResult::parseLine(const char *lineToParse, const int linelen, t_directory::t_direntry &direntry, int &nFTPServerType) +BOOL CFtpListResult::parseLine(const char *lineToParse, const int linelen, t_directory::t_direntry &direntry, int &nFTPServerType, bool mlst) { USES_CONVERSION; nFTPServerType = 0; direntry.ownergroup = _T(""); - if (parseAsMlsd(lineToParse, linelen, direntry)) + if (parseAsMlsd(lineToParse, linelen, direntry, mlst)) return TRUE; if (parseAsUnix(lineToParse, linelen, direntry)) @@ -556,6 +556,7 @@ BOOL CFtpListResult::parseLine(const char *lineToParse, const int linelen, t_dir return FALSE; } +// Used only with LISTDEBUG void CFtpListResult::AddData(char *data, int size) { #ifdef _DEBUG @@ -605,7 +606,7 @@ void CFtpListResult::AddData(char *data, int size) int tmp; char *tmpline = new char[strlen(line) + 1]; strcpy(tmpline, line); - if (parseLine(tmpline, strlen(tmpline), direntry, tmp)) + if (parseLine(tmpline, strlen(tmpline), direntry, tmp, false)) { delete [] tmpline; if (tmp) @@ -1288,7 +1289,7 @@ BOOL CFtpListResult::parseAsEPLF(const char *line, const int linelen, t_director return FALSE; } -BOOL CFtpListResult::parseAsMlsd(const char *line, const int linelen, t_directory::t_direntry &direntry) +BOOL CFtpListResult::parseAsMlsd(const char *line, const int linelen, t_directory::t_direntry &direntry, bool mlst) { #ifdef _DEBUG USES_CONVERSION; @@ -1342,7 +1343,9 @@ BOOL CFtpListResult::parseAsMlsd(const char *line, const int linelen, t_director if (factname == _T("type")) { if (!value.CompareNoCase(_T("dir"))) + { direntry.dir = TRUE; + } // This is syntax used by proftpd by default // http://www.proftpd.org/docs/modules/mod_facts.html // They claim it's the correct one. @@ -1367,9 +1370,23 @@ BOOL CFtpListResult::parseAsMlsd(const char *line, const int linelen, t_director if ((value.GetLength() > 14) && (value[13] == ':')) direntry.linkTarget = value.Mid(14); } - else if (!value.CompareNoCase(_T("cdir")) || - !value.CompareNoCase(_T("pdir"))) + else if (!value.CompareNoCase(L"cdir")) + { + // ProFTPD up to 1.3.6rc1 and 1.3.5a incorrectly uses "cdir" for the current working directory. + // So at least in MLST, where this would be the only entry, we treat it like "dir". + if (mlst) + { + direntry.dir = TRUE; + } + else + { + return FALSE; + } + } + else if (!value.CompareNoCase(L"pdir")) + { return FALSE; + } } else if (factname == _T("size")) { diff --git a/source/filezilla/FtpListResult.h b/source/filezilla/FtpListResult.h index d4ea06f2..32de64d8 100644 --- a/source/filezilla/FtpListResult.h +++ b/source/filezilla/FtpListResult.h @@ -61,17 +61,17 @@ class CFtpListResult : public CApiLog void AddData(char *data,int size); CFtpListResult(t_server server, bool *bUTF8 = 0); virtual ~CFtpListResult(); - t_directory::t_direntry *getList(int &num, CTime EntryTime); + t_directory::t_direntry *getList(int &num, CTime EntryTime, bool mlst); private: typedef std::list<t_directory::t_direntry> tEntryList; tEntryList m_EntryList; - BOOL parseLine(const char *lineToParse, const int linelen, t_directory::t_direntry &direntry, int &nFTPServerType); + BOOL parseLine(const char *lineToParse, const int linelen, t_directory::t_direntry &direntry, int &nFTPServerType, bool mlst); BOOL parseAsVMS(const char *line, const int linelen, t_directory::t_direntry &direntry); BOOL parseAsEPLF(const char *line, const int linelen, t_directory::t_direntry &direntry); - BOOL parseAsMlsd(const char *line, const int linelen, t_directory::t_direntry &direntry); + BOOL parseAsMlsd(const char *line, const int linelen, t_directory::t_direntry &direntry, bool mlst); BOOL parseAsUnix(const char *line, const int linelen, t_directory::t_direntry &direntry); BOOL parseAsDos(const char *line, const int linelen, t_directory::t_direntry &direntry); BOOL parseAsOther(const char *line, const int linelen, t_directory::t_direntry &direntry); diff --git a/source/forms/CustomScpExplorer.cpp b/source/forms/CustomScpExplorer.cpp index b741ce0e..8bc96064 100644 --- a/source/forms/CustomScpExplorer.cpp +++ b/source/forms/CustomScpExplorer.cpp @@ -474,6 +474,9 @@ bool __fastcall TCustomScpExplorerForm::CommandLineFromAnotherInstance( // so it's likely that our window is not visible, // and user won't see what is going on Application->BringToFront(); + // reload sessions as we may be asked to open a session + // just stored by another instance + StoredSessions->Load(); UnicodeString SessionName = Params.Param[1]; std::unique_ptr<TObjectList> DataList(new TObjectList()); UnicodeString DownloadFile; // unused @@ -5735,7 +5738,7 @@ void __fastcall TCustomScpExplorerForm::WMQueryEndSession(TMessage & Message) // handle the abrupt termination caused by subsequent WM_ENDSESSION cleanly. // Hence the process termination might be safer :) if ((Message.LParam != ENDSESSION_CRITICAL) && - (!FQueue->IsEmpty || (FProgressForm != NULL))) + (((FQueue != NULL) && !FQueue->IsEmpty) || (FProgressForm != NULL))) { Message.Result = FALSE; } @@ -6763,39 +6766,42 @@ void __fastcall TCustomScpExplorerForm::RemoteFileControlDragDropFileOperation( break; }; - TStrings * FileList = new TStringList(); - try + TDragDropFilesEx * DragDropFilesEx = DragDropFiles(Sender); + // see a comment in TUnixDirView::PerformItemDragDropOperation + if (DragDropFilesEx->FileList->Count > 0) { - TDragDropFilesEx * DragDropFilesEx = DragDropFiles(Sender); - - for (int Index = 0; Index < DragDropFilesEx->FileList->Count; Index++) - { - FileList->Add(DragDropFilesEx->FileList->Items[Index]->Name); - } - - FDragDropOperation = true; - TTransferOperationParam Param; - Param.TargetDirectory = TargetPath; - // upload, no temp dirs - Param.Temp = false; - Param.DragDrop = true; - if (ExecuteFileOperation(Operation, osLocal, FileList, - (WinConfiguration->DDTransferConfirmation == asOff), &Param)) + TStrings * FileList = new TStringList(); + try { - if (IsFileControl(DropSourceControl, osLocal)) + for (int Index = 0; Index < DragDropFilesEx->FileList->Count; Index++) { - Configuration->Usage->Inc(L"UploadsDragDropInternal"); + FileList->Add(DragDropFilesEx->FileList->Items[Index]->Name); } - else + + FDragDropOperation = true; + TTransferOperationParam Param; + Param.TargetDirectory = TargetPath; + // upload, no temp dirs + Param.Temp = false; + Param.DragDrop = true; + if (ExecuteFileOperation(Operation, osLocal, FileList, + (WinConfiguration->DDTransferConfirmation == asOff), &Param)) { - Configuration->Usage->Inc(L"UploadsDragDropExternal"); + if (IsFileControl(DropSourceControl, osLocal)) + { + Configuration->Usage->Inc(L"UploadsDragDropInternal"); + } + else + { + Configuration->Usage->Inc(L"UploadsDragDropExternal"); + } } } - } - __finally - { - FDragDropOperation = false; - delete FileList; + __finally + { + FDragDropOperation = false; + delete FileList; + } } } //--------------------------------------------------------------------------- diff --git a/source/resource/TextsCore.h b/source/resource/TextsCore.h index 75dc29a6..3e0972fb 100644 --- a/source/resource/TextsCore.h +++ b/source/resource/TextsCore.h @@ -240,6 +240,7 @@ #define UNKNOWN_CHECKSUM 715 #define CIPHER_NOT_VERIFIED 716 #define KEX_NOT_VERIFIED 717 +#define SCRIPT_AMBIGUOUS_SLASH_IN_PATH 729 #define CORE_CONFIRMATION_STRINGS 300 #define CONFIRM_PROLONG_TIMEOUT3 301 diff --git a/source/resource/TextsCore1.rc b/source/resource/TextsCore1.rc index 5d103077..e26996a3 100644 --- a/source/resource/TextsCore1.rc +++ b/source/resource/TextsCore1.rc @@ -210,6 +210,7 @@ BEGIN UNKNOWN_CHECKSUM, "Checksum algorithm '%s' is not supported." CIPHER_NOT_VERIFIED, "Cipher %s was not verified!" KEX_NOT_VERIFIED, "Key-exchange algorithm %s was not verified!" + SCRIPT_AMBIGUOUS_SLASH_IN_PATH, "Selecting files using a path ending with slash is ambiguous. Remove the slash to select the folder. Append * mask to select all files in the folder." CORE_CONFIRMATION_STRINGS, "CORE_CONFIRMATION" CONFIRM_PROLONG_TIMEOUT3, "Host is not communicating for %d seconds.\n\nWait for another %0:d seconds?" diff --git a/source/windows/TerminalManager.cpp b/source/windows/TerminalManager.cpp index 637cb660..68c37843 100644 --- a/source/windows/TerminalManager.cpp +++ b/source/windows/TerminalManager.cpp @@ -791,8 +791,11 @@ bool __fastcall TTerminalManager::HandleMouseWheel(WPARAM WParam, LPARAM LParam) TWinControl * Control = FindVCLWindow(Point); if (Control != NULL) { - TCustomForm * Form = ValidParentForm(Control); - if (Form->Active) + TCustomForm * Form = GetParentForm(Control); + // Only case we expect the parent form to be NULL is on the Find/Replace dialog, + // which is owned by VCL's internal TRedirectorWindow. + assert((Form != NULL) || (Control->ClassName() == L"TRedirectorWindow")); + if ((Form != NULL) && Form->Active) { // Send it only to windows we tested it with. // Though we should sooner or later remote this test and pass it to all our windows. diff --git a/source/windows/WinMain.cpp b/source/windows/WinMain.cpp index 2ecb48a8..744cd9a4 100644 --- a/source/windows/WinMain.cpp +++ b/source/windows/WinMain.cpp @@ -350,7 +350,7 @@ void __fastcall UpdateStaticUsage() { unsigned int DpiX; unsigned int DpiY; - GetDpiForMonitor(Screen->Monitors[0]->Handle, MDT_Default, &DpiX, &DpiY); + GetDpiForMonitor(Screen->Monitors[Index]->Handle, MDT_Default, &DpiX, &DpiY); if (DpiX != DpiY) {