Martin Prikryl 22 years ago
parent
commit
a9d3c0af61

+ 6 - 6
DScpComp.bpk

@@ -20,7 +20,7 @@
     <DEBUGLIBPATH value="$(BCB)\lib\debug"/>
     <RELEASELIBPATH value="$(BCB)\lib\release"/>
     <LINKER value="ilink32"/>
-    <USERDEFINES value="OLD_DND;NORTON_LIKE;DESIGN_ONLY;_DEBUG"/>
+    <USERDEFINES value="OLD_DND;NORTON_LIKE;DESIGN_ONLY"/>
     <SYSDEFINES value="NO_STRICT;USEPACKAGES"/>
     <MAINSOURCE value="DScpComp.cpp"/>
     <INCLUDEPATH value="components;core;packages\filemng;packages\dragndrop;packages\my;packages\my\filemng;$(BCB)\include;$(BCB)\include\vcl"/>
@@ -30,12 +30,12 @@
     <OTHERFILES value=""/>
   </MACROS>
   <OPTIONS>
-    <CFLAG1 value="-Od -H=c:\PROGRA~1\borland\CBUILD~1\lib\vcl60.csm -Hc -Vx -Ve -X- -r- -a8 
-      -b- -k -y -v -vi- -c -tWM"/>
-    <PFLAGS value="  -$Y+ -$W -$O- -$C- -$A8 -v -JPHNE -M"/>
+    <CFLAG1 value="-O2 -H=c:\PROGRA~1\borland\CBUILD~1\lib\vcl60.csm -Hc -Vx -Ve -X- -a8 -b- 
+      -k- -vi -c -tWM"/>
+    <PFLAGS value="  -$Y- -$L- -$D- -$C- -$A8 -v -JPHNE -M"/>
     <RFLAGS value=""/>
-    <AFLAGS value="/mx /w2 /zi"/>
-    <LFLAGS value="-I -D&quot;WinSCP components&quot; -aa -Tpp -Gpd -x -Gn -Gl -Gi -v"/>
+    <AFLAGS value="/mx /w2 /zn"/>
+    <LFLAGS value="-I -D&quot;WinSCP components&quot; -aa -Tpp -Gpd -GD -s -Gn -Gl -Gi -M"/>
     <OTHERFILES value=""/>
   </OPTIONS>
   <LINKER>

+ 3 - 0
DragExt.bpf

@@ -0,0 +1,3 @@
+This file is used by the project manager only and should be treated like the project file
+
+
DllMain 

+ 97 - 0
DragExt.bpr

@@ -0,0 +1,97 @@
+<?xml version='1.0' encoding='utf-8' ?>
+<!-- C++Builder XML Project -->
+<PROJECT>
+  <MACROS>
+    <VERSION value="BCB.06.00"/>
+    <PROJECT value="DragExt.dll"/>
+    <OBJFILES value="dragext\DragExt.obj"/>
+    <RESFILES value="DragExt.res"/>
+    <DEFFILE value=""/>
+    <RESDEPEN value="$(RESFILES)"/>
+    <LIBFILES value=""/>
+    <LIBRARIES value=""/>
+    <SPARELIBS value=""/>
+    <PACKAGES value="vcl.bpi rtl.bpi vclx.bpi bcbsmp.bpi dbrtl.bpi adortl.bpi vcldb.bpi 
+      qrpt.bpi bdertl.bpi vcldbx.bpi dsnap.bpi cds.bpi bdecds.bpi teeui.bpi 
+      teedb.bpi tee.bpi teeqr.bpi ibxpress.bpi visualclx.bpi visualdbclx.bpi 
+      vclie.bpi xmlrtl.bpi inet.bpi inetdbbde.bpi inetdbxpress.bpi inetdb.bpi 
+      nmfast.bpi bcbie.bpi soaprtl.bpi dclocx.bpi dbexpress.bpi dbxcds.bpi 
+      indy.bpi bcb2kaxserver.bpi Moje_B5.bpi DragDrop_B5.bpi DriveDir_B5.bpi"/>
+    <PATHCPP value=".;dragext"/>
+    <PATHPAS value=".;"/>
+    <PATHRC value=".;"/>
+    <PATHASM value=".;"/>
+    <DEBUGLIBPATH value="$(BCB)\lib\debug"/>
+    <RELEASELIBPATH value="$(BCB)\lib\release"/>
+    <LINKER value="ilink32"/>
+    <USERDEFINES value="_DEBUG"/>
+    <SYSDEFINES value="NO_STRICT;_NO_VCL"/>
+    <MAINSOURCE value="DragExt.bpf"/>
+    <INCLUDEPATH value="dragext;$(BCB)\include"/>
+    <LIBPATH value="dragext;$(BCB)\lib\obj;$(BCB)\lib"/>
+    <WARNINGS value="-w-par"/>
+    <OTHERFILES value=""/>
+  </MACROS>
+  <OPTIONS>
+    <CFLAG1 value="-WD -Od -H=$(BCB)\lib\vcl60.csm -Hc -Vx -Ve -X- -r- -a8 -b- -k -y -v -vi- 
+      -tWD -tWM -c"/>
+    <PFLAGS value="  -$YD -$W -$O- -$A8 -v -JPHNE -M"/>
+    <RFLAGS value=""/>
+    <AFLAGS value="/mx /w2 /zd"/>
+    <LFLAGS value="-I -D&quot;&quot; -aa -Tpd -x -Gn -Gi -v"/>
+    <OTHERFILES value=""/>
+  </OPTIONS>
+  <LINKER>
+    <ALLOBJ value="c0d32.obj $(OBJFILES)"/>
+    <ALLRES value="$(RESFILES)"/>
+    <ALLLIB value="$(LIBFILES) $(LIBRARIES) import32.lib cw32mt.lib"/>
+    <OTHERFILES value=""/>
+  </LINKER>
+  <FILELIST>
+      <FILE FILENAME="DragExt.bpf" FORMNAME="" UNITNAME="DragExt" CONTAINERID="BPF" DESIGNCLASS="" LOCALCOMMAND=""/>
+      <FILE FILENAME="dragext\DragExt.cpp" FORMNAME="" UNITNAME="DragExt" CONTAINERID="CCompiler" DESIGNCLASS="" LOCALCOMMAND=""/>
+      <FILE FILENAME="DragExt.res" FORMNAME="" UNITNAME="DragExt.res" CONTAINERID="ResTool" DESIGNCLASS="" LOCALCOMMAND=""/>
+  </FILELIST>
+  <BUILDTOOLS>
+  </BUILDTOOLS>
+
+  <IDEOPTIONS>
+[Version Info]
+IncludeVerInfo=1
+AutoIncBuild=1
+MajorVer=1
+MinorVer=0
+Release=1
+Build=28
+Debug=0
+PreRelease=0
+Special=0
+Private=0
+DLL=1
+Locale=1033
+CodePage=1252
+
+[Version Info Keys]
+CompanyName=Martin Prikryl
+FileDescription=Drag&amp;Drop shell extension for WinSCP
+FileVersion=1.0.1.28
+InternalName=dragext
+LegalCopyright=(c) 2004 Martin Prikryl
+LegalTrademarks=
+OriginalFilename=dragext.dll
+ProductName=WinSCP
+ProductVersion=3.5.6.0
+Comments=
+WWW=http://winscp.sourceforge.net/
+
+[Compiler]
+ShowInfoMsgs=0
+LinkDebugVcl=0
+LinkCGLIB=0
+
+[Linker]
+LibPrefix=
+LibSuffix=
+LibVersion=
+  </IDEOPTIONS>
+</PROJECT>

BIN
DragExt.res


+ 6 - 6
WinSCP3.bpr

@@ -95,10 +95,10 @@ IncludeVerInfo=1
 AutoIncBuild=1
 MajorVer=3
 MinorVer=5
-Release=5
-Build=211
+Release=6
+Build=213
 Debug=0
-PreRelease=1
+PreRelease=0
 Special=0
 Private=0
 DLL=0
@@ -108,13 +108,13 @@ CodePage=1252
 [Version Info Keys]
 CompanyName=Martin Prikryl
 FileDescription=Windows SCP/SFTP client
-FileVersion=3.5.5.211
+FileVersion=3.5.6.213
 InternalName=winscp3
 LegalCopyright=(c) 2000-2004 Martin Prikryl
 LegalTrademarks=
-OriginalFilename=winscp355.exe
+OriginalFilename=winscp356.exe
 ProductName=WinSCP
-ProductVersion=3.5.5.0
+ProductVersion=3.5.6.0
 WWW=http://winscp.sourceforge.net/
 
 [Compiler]

BIN
WinSCP3.res


+ 58 - 13
core/Configuration.cpp

@@ -318,18 +318,47 @@ AnsiString __fastcall TConfiguration::ModuleFileName()
   return ParamStr(0);
 }
 //---------------------------------------------------------------------------
-void * __fastcall TConfiguration::GetApplicationInfo()
+void * __fastcall TConfiguration::GetFileApplicationInfo(const AnsiString FileName)
 {
-  if (!FApplicationInfo)
+  void * Result;
+  if (FileName.IsEmpty())
   {
-    FApplicationInfo = CreateFileInfo(ModuleFileName());
+    if (!FApplicationInfo)
+    {
+      FApplicationInfo = CreateFileInfo(ModuleFileName());
+    }
+    Result = FApplicationInfo;
   }
-  return FApplicationInfo;
+  else
+  {
+    Result = CreateFileInfo(FileName);
+  }
+  return Result;
+}
+//---------------------------------------------------------------------------
+void * __fastcall TConfiguration::GetApplicationInfo()
+{
+  return GetFileApplicationInfo("");
+}
+//---------------------------------------------------------------------------
+AnsiString __fastcall TConfiguration::GetFileProductName(const AnsiString FileName)
+{
+  return GetFileFileInfoString("ProductName", FileName);
+}
+//---------------------------------------------------------------------------
+AnsiString __fastcall TConfiguration::GetProductName()
+{
+  return GetFileProductName("");
+}
+//---------------------------------------------------------------------------
+AnsiString __fastcall TConfiguration::GetFileProductVersion(const AnsiString FileName)
+{
+  return TrimVersion(GetFileFileInfoString("ProductVersion", FileName));
 }
 //---------------------------------------------------------------------------
 AnsiString __fastcall TConfiguration::GetProductVersion()
 {
-  return TrimVersion(FileInfoString["ProductVersion"]);
+  return GetFileProductVersion("");
 }
 //---------------------------------------------------------------------------
 AnsiString __fastcall TConfiguration::TrimVersion(AnsiString Version)
@@ -375,23 +404,39 @@ AnsiString __fastcall TConfiguration::GetVersion()
   }
 }
 //---------------------------------------------------------------------------
-AnsiString __fastcall TConfiguration::GetFileInfoString(const AnsiString Key)
+AnsiString __fastcall TConfiguration::GetFileFileInfoString(const AnsiString Key,
+  const AnsiString FileName)
 {
   AnsiString Result;
-  if (GetTranslationCount(ApplicationInfo) > 0)
+  void * Info = GetFileApplicationInfo(FileName);
+  try
   {
-    TTranslation Translation;
-    Translation = GetTranslation(ApplicationInfo, 0);
-    Result = ::GetFileInfoString(ApplicationInfo,
-      Translation, Key);
+    if ((Info != NULL) && (GetTranslationCount(Info) > 0))
+    {
+      TTranslation Translation;
+      Translation = GetTranslation(Info, 0);
+      Result = ::GetFileInfoString(Info, Translation, Key);
+    }
+    else
+    {
+      assert(!FileName.IsEmpty());
+    }
   }
-  else
+  __finally
   {
-    assert(false);
+    if (!FileName.IsEmpty())
+    {
+      FreeFileInfo(Info);
+    }
   }
   return Result;
 }
 //---------------------------------------------------------------------------
+AnsiString __fastcall TConfiguration::GetFileInfoString(const AnsiString Key)
+{
+  return GetFileFileInfoString(Key, "");
+} 
+//---------------------------------------------------------------------------
 AnsiString __fastcall TConfiguration::GetRegistryStorageKey()
 {
   return GetRegistryKey();

+ 10 - 1
core/Configuration.h

@@ -36,6 +36,7 @@ private:
   virtual AnsiString __fastcall GetVersionStr();
   virtual AnsiString __fastcall GetVersion();
   AnsiString __fastcall GetProductVersion();
+  AnsiString __fastcall GetProductName();
   AnsiString __fastcall TrimVersion(AnsiString Version);
   AnsiString __fastcall GetStoredSessionsSubKey();
   AnsiString __fastcall GetPuttySessionsKey();
@@ -67,7 +68,7 @@ private:
   AnsiString __fastcall GetPartialExt() const;
   AnsiString __fastcall GetFileInfoString(const AnsiString Key);
   AnsiString __fastcall GetLocalInvalidChars();
-  
+
 protected:
   TStorage FStorage;
 
@@ -84,6 +85,13 @@ protected:
 
   virtual AnsiString __fastcall ModuleFileName();
 
+  AnsiString __fastcall GetFileFileInfoString(const AnsiString Key,
+    const AnsiString FileName);
+  void * __fastcall GetFileApplicationInfo(const AnsiString FileName);
+  AnsiString __fastcall GetFileProductVersion(const AnsiString FileName);
+  AnsiString __fastcall TConfiguration::GetFileProductName(
+    const AnsiString FileName);
+
 public:
   __fastcall TConfiguration();
   __fastcall ~TConfiguration();
@@ -119,6 +127,7 @@ public:
   __property AnsiString VersionStr = { read=GetVersionStr };
   __property AnsiString Version = { read=GetVersion };
   __property AnsiString ProductVersion = { read=GetProductVersion };
+  __property AnsiString ProductName = { read=GetProductName };
   __property AnsiString FileInfoString[AnsiString Key] = { read = GetFileInfoString };
   __property bool Logging  = { read=FLogging, write=SetLogging };
   __property AnsiString LogFileName  = { read=FLogFileName, write=SetLogFileName };

+ 1 - 0
core/Net.cpp

@@ -81,6 +81,7 @@ int from_backend(void * frontend, int is_stderr, char * data, int datalen)
 //---------------------------------------------------------------------------
 static int get_line(const char * prompt, char * str, int maxlen, int is_pw)
 {
+  USEDPARAM(is_pw);
   assert(is_pw);
   assert(CurrentSSH);
 

+ 1 - 1
core/RemoteFiles.cpp

@@ -345,7 +345,7 @@ void __fastcall TRemoteFile::SetListingStr(AnsiString value)
     {
       FSize = ASize;
 
-      Word Day = 0, Month, Year, Hour, Min, P;
+      Word Day, Month, Year, Hour, Min, P;
 
       GETCOL;
       Day = (Word)StrToIntDef(Col, 0);

+ 3 - 0
core/ScpFileSystem.cpp

@@ -941,6 +941,8 @@ void __fastcall TSCPFileSystem::CustomReadFile(const AnsiString FileName,
 void __fastcall TSCPFileSystem::DeleteFile(const AnsiString FileName,
   const TRemoteFile * File, bool Recursive)
 {
+  USEDPARAM(Recursive);
+  USEDPARAM(File);
   assert(Recursive || (File && File->IsSymLink));
   ExecCommand(fsDeleteFile, ARRAYOFCONST((DelimitStr(FileName))));
 }
@@ -954,6 +956,7 @@ void __fastcall TSCPFileSystem::RenameFile(const AnsiString FileName,
 void __fastcall TSCPFileSystem::CreateDirectory(const AnsiString DirName,
   const TRemoteProperties * Properties)
 {
+  USEDPARAM(Properties);
   assert(!Properties); // not implemented yet
   ExecCommand(fsCreateDirectory, ARRAYOFCONST((DelimitStr(DirName))));
 }

+ 108 - 48
core/SftpFileSystem.cpp

@@ -66,6 +66,8 @@ const int asAll = 0xFFFF;
 //---------------------------------------------------------------------------
 #define SFTP_PACKET_ALLOC_DELTA 256
 //---------------------------------------------------------------------------
+#pragma warn -inl
+//---------------------------------------------------------------------------
 class TSFTPPacket
 {
 public:
@@ -150,38 +152,92 @@ public:
     Add(Value.c_str(), Value.Length());
   }
 
-  void AddProperties(const TRemoteProperties * Properties,
-    unsigned short BaseRights, bool IsDirectory, int Version)
+  void AddProperties(unsigned short * Rights, AnsiString * Owner,
+    AnsiString * Group, unsigned long * MTime, unsigned long * ATime,
+    bool IsDirectory, int Version)
   {
     int Flags = 0;
-    if (Properties)
+    if (Rights != NULL)
     {
-      if (Properties->Valid.Contains(vpOwner) || Properties->Valid.Contains(vpGroup))
-      {
-        Flags |= SSH_FILEXFER_ATTR_OWNERGROUP;
-      }
-      if (Properties->Valid.Contains(vpRights))
-      {
-        Flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
-      }
+      Flags |= SSH_FILEXFER_ATTR_PERMISSIONS;
+    }
+    if ((Owner != NULL) || (Group != NULL))
+    {
+      Flags |= SSH_FILEXFER_ATTR_OWNERGROUP;
+    }
+    if ((Version < 4) && ((MTime != NULL) || (ATime != NULL)))
+    {
+      Flags |= SSH_FILEXFER_ATTR_ACMODTIME;
+    }
+    if ((Version >= 4) && (ATime != NULL))
+    {
+      Flags |= SSH_FILEXFER_ATTR_ACCESSTIME;
+    }
+    if ((Version >= 4) && (MTime != NULL))
+    {
+      Flags |= SSH_FILEXFER_ATTR_MODIFYTIME;
     }
     AddCardinal(Flags);
+
     if (Version >= 4)
     {
-      AddByte((unsigned char)(IsDirectory ? SSH_FILEXFER_TYPE_DIRECTORY : SSH_FILEXFER_TYPE_REGULAR));
+      AddByte(static_cast<unsigned char>(IsDirectory ?
+        SSH_FILEXFER_TYPE_DIRECTORY : SSH_FILEXFER_TYPE_REGULAR));
+    }
+
+    if ((Owner != NULL) || (Group != NULL))
+    {
+      assert(Version >= 4);
+      AddString(Owner != NULL ? *Owner : AnsiString());
+      AddString(Group != NULL ? *Group : AnsiString());
+    }
+
+    if (Rights != NULL)
+    {
+      AddCardinal(*Rights);
+    }
+
+    if ((Version < 4) && ((MTime != NULL) || (ATime != NULL)))
+    {
+      AddCardinal(ATime != NULL ? *ATime : *MTime);
+      AddCardinal(MTime != NULL ? *MTime : *ATime);
+    }
+    if ((Version >= 4) && (ATime != NULL))
+    {
+      AddInt64(*ATime);
     }
-    if (Properties)
+    if ((Version >= 4) && (MTime != NULL))
     {
-      if (Flags & SSH_FILEXFER_ATTR_OWNERGROUP)
+      AddInt64(*MTime);
+    }
+  }
+
+  void AddProperties(const TRemoteProperties * Properties,
+    unsigned short BaseRights, bool IsDirectory, int Version)
+  {
+    enum { valNone = 0, valRights = 0x01, valOwner = 0x02, valGroup = 0x04 }
+      Valid = valNone;
+    unsigned short RightsNum = 0;
+    AnsiString Owner;
+    AnsiString Group;
+
+    if (Properties != NULL)
+    {
+      if (Properties->Valid.Contains(vpGroup))
+      {
+        Valid |= valGroup;
+        Group = Properties->Group;
+      }
+
+      if (Properties->Valid.Contains(vpOwner))
       {
-        assert(Version >= 4);
-        AddString(Properties->Valid.Contains(vpOwner) ?
-          Properties->Owner : AnsiString());
-        AddString(Properties->Valid.Contains(vpGroup) ?
-          Properties->Group : AnsiString());
+        Valid |= valOwner;
+        Owner = Properties->Owner;
       }
-      if (Flags & SSH_FILEXFER_ATTR_PERMISSIONS)
+
+      if (Properties->Valid.Contains(vpRights))
       {
+        Valid |= valRights;
         TRights Rights = BaseRights;
         Rights |= Properties->Rights.NumberSet;
         Rights &= (unsigned short)~Properties->Rights.NumberUnset;
@@ -189,9 +245,15 @@ public:
         {
           Rights.AddExecute();
         }
-        AddCardinal(Rights);
+        RightsNum = Rights;
       }
     }
+
+    AddProperties(
+      Valid & valRights ? &RightsNum : NULL,
+      Valid & valOwner ? &Owner : NULL,
+      Valid & valGroup ? &Group : NULL,
+      NULL, NULL, IsDirectory, Version); 
   }
 
   char GetByte()
@@ -848,6 +910,8 @@ private:
   unsigned long FLastBlockSize;
 };
 //---------------------------------------------------------------------------
+#pragma warn .inl
+//---------------------------------------------------------------------------
 class TSFTPBusy
 {
 public:
@@ -1433,7 +1497,7 @@ AnsiString __fastcall TSFTPFileSystem::RealPath(const AnsiString Path,
   return RealPath(APath);
 }
 //---------------------------------------------------------------------------
-AnsiString __fastcall inline TSFTPFileSystem::LocalCanonify(const AnsiString Path)
+AnsiString __fastcall TSFTPFileSystem::LocalCanonify(const AnsiString & Path)
 {
   // TODO: improve (handle .. etc.)
   if (TTerminal::IsAbsolutePath(Path)) return Path;
@@ -1443,7 +1507,7 @@ AnsiString __fastcall inline TSFTPFileSystem::LocalCanonify(const AnsiString Pat
   }
 }
 //---------------------------------------------------------------------------
-AnsiString __fastcall inline TSFTPFileSystem::Canonify(AnsiString Path)
+AnsiString __fastcall TSFTPFileSystem::Canonify(AnsiString Path)
 {
   // inspired by canonify() from PSFTP.C
   AnsiString Result;
@@ -1848,7 +1912,7 @@ void __fastcall TSFTPFileSystem::ReadFile(const AnsiString FileName,
   CustomReadFile(FileName, File, SSH_FXP_LSTAT);
 }
 //---------------------------------------------------------------------------
-bool __fastcall inline TSFTPFileSystem::RemoteFileExists(const AnsiString FullPath,
+bool __fastcall TSFTPFileSystem::RemoteFileExists(const AnsiString FullPath,
   TRemoteFile ** File)
 {
   bool Result;
@@ -1954,6 +2018,7 @@ void __fastcall TSFTPFileSystem::CreateDirectory(const AnsiString DirName,
 void __fastcall TSFTPFileSystem::CreateLink(const AnsiString FileName,
   const AnsiString PointTo, bool Symbolic)
 {
+  USEDPARAM(Symbolic);
   assert(Symbolic); // only symlinks are supported by SFTP
   assert(FVersion >= 3); // symlinks are supported with SFTP version 3 and later
   TSFTPPacket Packet(SSH_FXP_SYMLINK);
@@ -2230,6 +2295,8 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
       bool ResumeAllowed;
       bool ResumeTransfer = false;
       bool DestFileExists = false;
+      TRights DestRights;
+
       __int64 ResumeOffset;
 
       FTerminal->LogEvent(FORMAT("Copying \"%s\" to remote directory started.", (FileName)));
@@ -2268,6 +2335,7 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
           OpenParams.DestFileSize = File->Size;
           FileParams.DestSize = OpenParams.DestFileSize;
           FileParams.DestTimestamp = File->Modification;
+          DestRights = *File->Rights;
           delete File;
           File = NULL;
         }
@@ -2396,31 +2464,32 @@ void __fastcall TSFTPFileSystem::SFTPSource(const AnsiString FileName,
           if (DestFileExists)
           {
             DeleteFile(DestFullName);
-            DestFileExists = false;
+            // DestFileExists used to be set to false here. Had it any reason?
           }
           RenameFile(OpenParams.RemoteFileName, DestFileName);
         );
       }
 
-      if (CopyParam->PreserveTime)
+      bool SetRights = ((DoResume && DestFileExists) || CopyParam->PreserveRights);
+      if (CopyParam->PreserveTime || SetRights)
       {
-        FILE_OPERATION_LOOP(FMTLOAD(PRESERVE_TIME_ERROR, (DestFileName)),
+        FILE_OPERATION_LOOP(FMTLOAD(PRESERVE_TIME_PERM_ERROR, (DestFileName)),
           TSFTPPacket Packet(SSH_FXP_SETSTAT);
           Packet.AddString(DestFullName);
-          if (FVersion >= 4)
+          unsigned short Rights = 0;
+          if (CopyParam->PreserveRights)
           {
-            Packet.AddCardinal(SSH_FILEXFER_ATTR_ACCESSTIME |
-              SSH_FILEXFER_ATTR_MODIFYTIME);
-            Packet.AddByte(SSH_FILEXFER_TYPE_REGULAR);
-            Packet.AddInt64(ATime);
-            Packet.AddInt64(MTime);
+            Rights = CopyParam->RemoteFileRights(OpenParams.LocalFileAttrs);
           }
-          else
+          else if (DoResume && DestFileExists)
           {
-            Packet.AddCardinal(SSH_FILEXFER_ATTR_ACMODTIME);
-            Packet.AddCardinal(ATime);
-            Packet.AddCardinal(MTime);
+            Rights = DestRights.NumberSet;
           }
+
+          Packet.AddProperties(
+            SetRights ? &Rights : NULL, NULL, NULL,
+            CopyParam->PreserveTime ? &MTime : NULL,
+            CopyParam->PreserveTime ? &ATime : NULL, false, FVersion);
           SendPacketAndReceiveResponse(&Packet, NULL, SSH_FXP_STATUS);
         );
       }
@@ -2472,17 +2541,8 @@ int __fastcall TSFTPFileSystem::SFTPOpenRemote(void * AOpenParams, void * /*Para
       OpenRequest.AddString(OpenParams->RemoteFileName);
       OpenRequest.AddCardinal(SSH_FXF_WRITE | SSH_FXF_CREAT | OpenType );
 
-      OpenRequest.AddCardinal(
-        OpenParams->CopyParam->PreserveRights ? SSH_FILEXFER_ATTR_PERMISSIONS : 0);
-      if (FVersion >= 4)
-      {
-        OpenRequest.AddByte(SSH_FILEXFER_TYPE_REGULAR);
-      }
-      if (OpenParams->CopyParam->PreserveRights)
-      {
-        OpenRequest.AddCardinal(
-          OpenParams->CopyParam->RemoteFileRights(OpenParams->LocalFileAttrs));
-      }
+      OpenRequest.AddProperties(NULL, NULL, NULL, NULL, NULL, false, FVersion);
+        
       SendPacketAndReceiveResponse(&OpenRequest, &OpenRequest, SSH_FXP_HANDLE);
       OpenParams->RemoteFileHandle = OpenRequest.GetString();
       Success = true;

+ 2 - 2
core/SftpFileSystem.h

@@ -76,10 +76,10 @@ protected:
   AnsiString __fastcall GetHomeDirectory();
   unsigned long __fastcall GotStatusPacket(TSFTPPacket * Packet, int AllowStatus);
   bool __fastcall inline IsAbsolutePath(const AnsiString Path);
-  bool __fastcall inline RemoteFileExists(const AnsiString FullPath, TRemoteFile ** File = NULL);
+  bool __fastcall RemoteFileExists(const AnsiString FullPath, TRemoteFile ** File = NULL);
   TRemoteFile * __fastcall LoadFile(TSFTPPacket * Packet,
     TRemoteFile * ALinkedByFile);
-  AnsiString __fastcall inline LocalCanonify(const AnsiString Path);
+  AnsiString __fastcall LocalCanonify(const AnsiString & Path);
   AnsiString __fastcall Canonify(AnsiString Path);
   AnsiString __fastcall RealPath(const AnsiString Path);
   AnsiString __fastcall RealPath(const AnsiString Path, const AnsiString BaseDir);

+ 6 - 0
core/Terminal.cpp

@@ -1182,6 +1182,12 @@ void __fastcall TTerminal::DoRenameFile(const AnsiString FileName,
 void __fastcall TTerminal::MoveFile(const AnsiString FileName,
   const TRemoteFile * File, /*const TMoveFileParams*/ void * Param)
 {
+  if (OperationProgress && (OperationProgress->Operation == foRemoteMove))
+  {
+    if (OperationProgress->Cancel != csContinue) Abort();
+    OperationProgress->SetFile(FileName);
+  }
+  
   assert(Param != NULL);
   const TMoveFileParams & Params = *static_cast<const TMoveFileParams*>(Param);
   AnsiString NewName = UnixIncludeTrailingBackslash(Params.Target) +

+ 768 - 0
dragext/DragExt.cpp

@@ -0,0 +1,768 @@
+//---------------------------------------------------------------------------
+#pragma hdrstop
+//---------------------------------------------------------------------------
+#ifndef STRICT
+#define STRICT
+#endif
+#define DLLEXPORT extern "C" __declspec(dllexport)
+//---------------------------------------------------------------------------
+#include <initguid.h>
+#include <shlguid.h>
+#include <stdio.h>
+#include <shlobj.h>
+#include <olectl.h>
+#include <time.h>
+#include "DragExt.h"
+//---------------------------------------------------------------------------
+#define DEBUG(MSG) \
+  if (GLogOn) \
+  { \
+    Debug(MSG); \
+  }
+//---------------------------------------------------------------------------
+#define DRAG_EXT_REG_KEY "Software\\Martin Prikryl\\WinSCP 2\\DragExt"
+#define DRAG_EXT_NAME "WinSCP Shell Extension"
+#define CLSID_SIZE 39
+//---------------------------------------------------------------------------
+class CShellExtClassFactory : public IClassFactory
+{
+public:
+  CShellExtClassFactory();
+  ~CShellExtClassFactory();
+
+  // IUnknown members
+  STDMETHODIMP         QueryInterface(REFIID, LPVOID FAR*);
+  STDMETHODIMP_(ULONG) AddRef();
+  STDMETHODIMP_(ULONG) Release();
+
+  // IClassFactory members
+  STDMETHODIMP CreateInstance(LPUNKNOWN, REFIID, LPVOID FAR*);
+  STDMETHODIMP LockServer(BOOL);
+
+protected:
+  unsigned long FReferenceCounter;
+};
+//---------------------------------------------------------------------------
+class CShellExt : public IShellExtInit, ICopyHook
+{
+public:
+  CShellExt();
+  ~CShellExt();
+
+  // IUnknown members
+  STDMETHODIMP         QueryInterface(REFIID, LPVOID FAR*);
+  STDMETHODIMP_(ULONG) AddRef();
+  STDMETHODIMP_(ULONG) Release();
+
+  // IShellExtInit methods
+  STDMETHODIMP Initialize(LPCITEMIDLIST pIDFolder, LPDATAOBJECT pDataObj, HKEY hKeyID);
+
+  // ICopyHook method
+  STDMETHODIMP_(UINT) CopyCallback(HWND Hwnd, UINT Func, UINT Flags,
+    LPCSTR SrcFile, DWORD SrcAttribs, LPCSTR DestFile, DWORD DestAttribs);
+
+protected:
+  unsigned long FReferenceCounter;
+  LPDATAOBJECT FDataObj;
+  HANDLE FMutex;
+  unsigned long FLastTicks;
+};
+//---------------------------------------------------------------------------
+unsigned int GRefThisDll = 0;
+bool GEnabled = false;
+char GLogFile[MAX_PATH] = "";
+bool GLogOn = false;
+FILE* GLogHandle = NULL;
+HANDLE GLogMutex;
+HINSTANCE GInstance;
+//---------------------------------------------------------------------------
+void Debug(const char* Message)
+{
+  if (GLogOn)
+  {
+    unsigned long WaitResult = WaitForSingleObject(GLogMutex, 1000);
+    if (WaitResult != WAIT_TIMEOUT)
+    {
+      try
+      {
+        if (GLogHandle == NULL)
+        {
+          if (strlen(GLogFile) == 0)
+          {
+            GLogOn = false;
+          }
+          else
+          {
+            GLogHandle = fopen(GLogFile, "at");
+            if (GLogHandle == NULL)
+            {
+              GLogOn = false;
+            }
+            else
+            {
+              setbuf(GLogHandle, NULL);
+              fprintf(GLogHandle, "----------------------------\n");
+            }
+          }
+        }
+
+        if (GLogOn)
+        {
+          SYSTEMTIME Time;
+          GetSystemTime(&Time);
+
+          fprintf(GLogHandle, "[%2d/%2d/%4d %2d:%02d:%02d.%03d][%04x] %s\n",
+            Time.wDay, Time.wMonth, Time.wYear, Time.wHour, Time.wMinute,
+            Time.wSecond, Time.wMilliseconds, GetCurrentThreadId(), Message);
+        }
+      }
+      catch(...)
+      {
+      }
+      ReleaseMutex(GLogMutex);
+    }
+  }
+}
+//---------------------------------------------------------------------------
+void LogVersion(HINSTANCE HInstance)
+{
+  if (GLogOn)
+  {
+    char FileName[MAX_PATH];
+    if (GetModuleFileName(HInstance, FileName, sizeof(FileName)) > 0)
+    {
+      Debug(FileName);
+      
+      unsigned long InfoHandle, Size;
+      Size = GetFileVersionInfoSize(FileName, &InfoHandle);
+      if (Size > 0)
+      {
+        void * Info;
+        Info = new char[Size];
+        if (GetFileVersionInfo(FileName, InfoHandle, Size, Info) != 0)
+        {
+          VS_FIXEDFILEINFO * VersionInfo;
+          unsigned int VersionInfoSize;
+          if (VerQueryValue(Info, "\\", reinterpret_cast<void**>(&VersionInfo),
+                &VersionInfoSize) != 0)
+          {
+            char VersionStr[100];
+            snprintf(VersionStr, sizeof(VersionStr), "LogVersion %d.%d.%d.%d",
+              HIWORD(VersionInfo->dwFileVersionMS),
+              LOWORD(VersionInfo->dwFileVersionMS),
+              HIWORD(VersionInfo->dwFileVersionLS),
+              LOWORD(VersionInfo->dwFileVersionLS));
+            Debug(VersionStr);
+          }
+          else
+          {
+            Debug("LogVersion no fixed version info");
+          }
+        }
+        else
+        {
+          Debug("LogVersion cannot read version info");
+        }
+      }
+      else
+      {
+        Debug("LogVersion no version info");
+      }
+    }
+  }
+}
+//---------------------------------------------------------------------------
+extern "C" int APIENTRY
+DllMain(HINSTANCE HInstance, DWORD Reason, LPVOID Reserved)
+{
+  if (Reason == DLL_PROCESS_ATTACH)
+  {
+    GInstance = HInstance;
+  }
+
+  if (GRefThisDll == 0)
+  {
+    GLogMutex = CreateMutex(NULL, false, "WinSCPDragExtLogMutex");
+
+    for (int Root = 0; Root < 3; Root++)
+    {
+      HKEY Key;
+      if (RegOpenKeyEx(Root == 0 ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
+            DRAG_EXT_REG_KEY, 0,
+            STANDARD_RIGHTS_READ | KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
+            &Key) == ERROR_SUCCESS)
+      {
+        unsigned long Type;
+        unsigned long Value;
+        unsigned long Size;
+        char Buf[MAX_PATH];
+
+        Size = sizeof(Value);
+        if ((RegQueryValueEx(Key, "Enable", NULL, &Type,
+              reinterpret_cast<char*>(&Value), &Size) == ERROR_SUCCESS) &&
+            (Type == REG_DWORD))
+        {
+          GEnabled = (Value != 0);
+        }
+
+        Size = sizeof(Buf);
+        if ((RegQueryValueEx(Key, "LogFile", NULL, &Type,
+              reinterpret_cast<char*>(&Buf), &Size) == ERROR_SUCCESS) &&
+            (Type == REG_SZ))
+        {
+          strncpy(GLogFile, Buf, sizeof(GLogFile));
+          GLogFile[sizeof(GLogFile) - 1] = '\0';
+          GLogOn = true;
+        }
+
+        RegCloseKey(Key);
+      }
+    }
+    DEBUG("DllMain loaded settings");
+    DEBUG(GEnabled ? "DllMain enabled" : "DllMain disabled");
+    LogVersion(HInstance);
+  }
+  else
+  {
+    DEBUG("DllMain settings already loaded");
+  }
+
+  DEBUG("DllMain leave");
+
+  return 1;   // ok
+}
+//---------------------------------------------------------------------------
+DLLEXPORT STDMETHODIMP DllCanUnloadNow(void)
+{
+  bool CanUnload = (GRefThisDll == 0);
+  DEBUG(CanUnload ? "DllCanUnloadNow can" : "DllCanUnloadNow cannot");
+  return (CanUnload ? S_OK : S_FALSE);
+}
+//---------------------------------------------------------------------------
+DLLEXPORT STDMETHODIMP DllGetClassObject(REFCLSID Rclsid, REFIID Riid,
+  LPVOID* PpvOut)
+{
+  DEBUG("DllGetClassObject");
+
+  *PpvOut = NULL;
+
+  if (IsEqualIID(Rclsid, CLSID_ShellExtension))
+  {
+    DEBUG("DllGetClassObject is ShellExtension");
+
+    CShellExtClassFactory* Pcf = new CShellExtClassFactory;
+
+    return Pcf->QueryInterface(Riid, PpvOut);
+  }
+
+  return CLASS_E_CLASSNOTAVAILABLE;
+}
+//---------------------------------------------------------------------------
+bool RegisterServer(bool AllUsers)
+{
+  DEBUG("RegisterServer enter");
+
+  DEBUG(AllUsers ? "RegisterServer all users" : "RegisterServer current users");
+
+  bool Result = false;
+  HKEY RootKey = AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+  HKEY HKey;
+  DWORD Unused;
+  wchar_t ClassID[CLSID_SIZE];
+
+  StringFromGUID2(CLSID_ShellExtension, ClassID, CLSID_SIZE);
+
+  if ((RegOpenKeyEx(RootKey, "Software\\Classes", 0, KEY_WRITE, &HKey) ==
+         ERROR_SUCCESS) &&
+      (RegCreateKeyEx(HKey, "CLSID", 0, NULL,
+         REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &HKey, &Unused) ==
+           ERROR_SUCCESS))
+  {
+    if (RegCreateKeyW(HKey, ClassID, &HKey) == ERROR_SUCCESS)
+    {
+      RegSetValueEx(HKey, NULL, 0, REG_SZ, DRAG_EXT_NAME,
+        strlen(DRAG_EXT_NAME) + 1);
+
+      if (RegCreateKey(HKey, "InProcServer32", &HKey) == ERROR_SUCCESS)
+      {
+        char Filename[MAX_PATH];
+        GetModuleFileName(GInstance, Filename, sizeof(Filename));
+        RegSetValueEx(HKey, NULL, 0, REG_SZ, Filename,
+          strlen(Filename) + 1);
+
+        const char* ThreadingModel = "Apartment";
+        RegSetValueEx(HKey, "ThreadingModel", 0, REG_SZ, ThreadingModel,
+          strlen(ThreadingModel) + 1);
+      }
+    }
+    RegCloseKey(HKey);
+
+    if ((RegOpenKeyEx(RootKey, "Software\\Classes",
+           0, KEY_WRITE, &HKey) == ERROR_SUCCESS) &&
+        (RegCreateKeyEx(HKey,
+           "directory\\shellex\\CopyHookHandlers\\WinSCPCopyHook",
+           0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &HKey,
+           &Unused) == ERROR_SUCCESS))
+    {
+      int Len = wcslen(ClassID);
+      char* MBClassID = new char[Len + 1];
+      wcstombs(MBClassID, ClassID, Len);
+      
+      RegSetValueEx(HKey, NULL, 0, REG_SZ,
+        MBClassID, strlen(MBClassID) + 1);
+      delete MBClassID;
+
+      RegCloseKey(HKey);
+
+      if ((RegCreateKeyEx(RootKey, DRAG_EXT_REG_KEY,
+             0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &HKey,
+             &Unused) == ERROR_SUCCESS))
+      {
+        unsigned long Value = 1;
+        RegSetValueEx(HKey, "Enable", 0, REG_DWORD,
+          reinterpret_cast<char*>(&Value), sizeof(Value));
+        
+        RegCloseKey(HKey);
+        
+        Result = true;
+      }
+      
+      SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
+    }
+  }
+
+  DEBUG("RegisterServer leave");
+
+  return Result;
+}
+//---------------------------------------------------------------------------
+DLLEXPORT STDMETHODIMP DllRegisterServer()
+{
+  DEBUG("DllRegisterServer enter");
+
+  HRESULT Result;
+  if (RegisterServer(true) || RegisterServer(false))
+  {
+    Result = S_OK;
+  }
+  else
+  {
+    Result = SELFREG_E_CLASS;
+  }
+  
+  DEBUG("DllRegisterServer leave");
+
+  return Result;
+}
+//---------------------------------------------------------------------------
+bool UnregisterServer(bool AllUsers)
+{
+  DEBUG("UnregisterServer enter");
+
+  DEBUG(AllUsers ? "UnregisterServer all users" : "UnregisterServer current users");
+
+  bool Result = false;
+  OLECHAR ClassID[CLSID_SIZE];
+
+  StringFromGUID2(CLSID_ShellExtension, ClassID, CLSID_SIZE);
+
+  HKEY RootKey = AllUsers ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+  HKEY HKey;
+
+  if ((RegOpenKeyEx(RootKey, "Software\\Classes", 0, KEY_WRITE, &HKey) ==
+        ERROR_SUCCESS) &&
+      (RegOpenKeyEx(HKey, "directory\\shellex\\CopyHookHandlers",
+        0, KEY_WRITE, &HKey) == ERROR_SUCCESS))
+  {
+    RegDeleteKey(HKey, "WinSCPCopyHook");
+
+    RegCloseKey(HKey);
+  }
+
+  if ((RegOpenKeyEx(RootKey, "Software\\Classes", 0, KEY_WRITE, &HKey) ==
+        ERROR_SUCCESS) &&
+      (RegOpenKeyEx(HKey, "CLSID", 0, KEY_WRITE, &HKey) ==
+        ERROR_SUCCESS))
+  {
+    if (RegOpenKeyExW(HKey, ClassID, 0, KEY_WRITE, &HKey) ==
+          ERROR_SUCCESS)
+    {
+      RegDeleteKey(HKey, "InProcServer32");
+
+      RegCloseKey(HKey);
+
+      if ((RegOpenKeyEx(RootKey, "Software\\Classes", 0, KEY_WRITE, &HKey) ==
+             ERROR_SUCCESS) &&
+          (RegOpenKeyEx(HKey, "CLSID", 0, KEY_WRITE, &HKey) ==
+             ERROR_SUCCESS))
+      {
+        RegDeleteKeyW(HKey, ClassID);
+
+        RegCloseKey(HKey);
+
+        Result = true;
+      }
+    }
+  }
+
+  if ((RegOpenKeyEx(RootKey, DRAG_EXT_REG_KEY, 0, KEY_WRITE, &HKey) ==
+        ERROR_SUCCESS))
+  {
+    unsigned long Value = 0;
+    RegSetValueEx(HKey, "Enable", 0, REG_DWORD,
+      reinterpret_cast<char*>(&Value), sizeof(Value));
+
+    RegCloseKey(HKey);
+
+    Result = true;
+  }
+  
+  SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, 0, 0);
+
+  DEBUG("UnregisterServer leave");
+
+  return Result;
+}
+//---------------------------------------------------------------------------
+DLLEXPORT STDMETHODIMP DllUnregisterServer()
+{
+  DEBUG("DllUnregisterServer enter");
+
+  HRESULT Result = SELFREG_E_CLASS;
+  if (UnregisterServer(true))
+  {
+    Result = S_OK;
+  }
+
+  if (UnregisterServer(false))
+  {
+    Result = S_OK;
+  }
+
+  DEBUG("DllUnregisterServer leave");
+
+  return Result;
+}
+//---------------------------------------------------------------------------
+CShellExtClassFactory::CShellExtClassFactory()
+{
+  DEBUG("CShellExtClassFactory");
+
+  FReferenceCounter = 0;
+
+  GRefThisDll++;
+}
+//---------------------------------------------------------------------------
+CShellExtClassFactory::~CShellExtClassFactory()
+{
+  DEBUG("~CShellExtClassFactory");
+
+  GRefThisDll--;
+}
+//---------------------------------------------------------------------------
+STDMETHODIMP CShellExtClassFactory::QueryInterface(REFIID Riid, LPVOID FAR * Ppv)
+{
+  DEBUG("QueryInterface");
+
+  *Ppv = NULL;
+
+  // Any interface on this object is the object pointer
+
+  if (IsEqualIID(Riid, IID_IUnknown) || IsEqualIID(Riid, IID_IClassFactory))
+  {
+    DEBUG("QueryInterface is IUnknown or IClassFactory");
+
+    *Ppv = (LPCLASSFACTORY)this;
+
+    AddRef();
+
+    return NOERROR;
+  }
+
+  return E_NOINTERFACE;
+}
+//---------------------------------------------------------------------------
+STDMETHODIMP_(ULONG) CShellExtClassFactory::AddRef()
+{
+  DEBUG("AddRef");
+  return ++FReferenceCounter;
+}
+//---------------------------------------------------------------------------
+STDMETHODIMP_(ULONG) CShellExtClassFactory::Release()
+{
+  DEBUG("Release");
+
+  if (--FReferenceCounter)
+  {
+    return FReferenceCounter;
+  }
+
+  delete this;
+
+  return 0;
+}
+//---------------------------------------------------------------------------
+STDMETHODIMP CShellExtClassFactory::CreateInstance(LPUNKNOWN UnkOuter,
+  REFIID Riid, LPVOID * PpvObj)
+{
+  DEBUG("CreateInstance");
+
+  *PpvObj = NULL;
+
+  // Shell extensions typically don't support aggregation (inheritance)
+
+  if (UnkOuter)
+  {
+    return CLASS_E_NOAGGREGATION;
+  }
+
+  // Create the main shell extension object.  The shell will then call
+  // QueryInterface with IID_IShellExtInit--this is how shell extensions are
+  // initialized.
+
+  CShellExt * ShellExt = new CShellExt();  //Create the CShellExt object
+
+  if (NULL == ShellExt)
+  {
+    return E_OUTOFMEMORY;
+  }
+
+  return ShellExt->QueryInterface(Riid, PpvObj);
+}
+//---------------------------------------------------------------------------
+STDMETHODIMP CShellExtClassFactory::LockServer(BOOL Lock)
+{
+  DEBUG("LockServer");
+
+  return NOERROR;
+}
+//---------------------------------------------------------------------------
+// CShellExt
+CShellExt::CShellExt()
+{
+  DEBUG("CShellExt enter");
+
+  FReferenceCounter = 0L;
+  FDataObj = NULL;
+
+  FMutex = CreateMutex(NULL, false, DRAG_EXT_MUTEX);
+  FLastTicks = 0;
+
+  GRefThisDll++;
+
+  DEBUG("CShellExt leave");
+}
+//---------------------------------------------------------------------------
+CShellExt::~CShellExt()
+{
+  DEBUG("~CShellExt enter");
+
+  if (FDataObj)
+  {
+    FDataObj->Release();
+  }
+
+  CloseHandle(FMutex);
+
+  GRefThisDll--;
+
+  DEBUG("~CShellExt leave");
+}
+//---------------------------------------------------------------------------
+STDMETHODIMP CShellExt::QueryInterface(REFIID Riid, LPVOID FAR * Ppv)
+{
+  DEBUG("CShellExt::QueryInterface enter");
+
+  STDMETHODIMP Result = E_NOINTERFACE;
+  *Ppv = NULL;
+
+  if (!GEnabled)
+  {
+    DEBUG("CShellExt::QueryInterface shelext disabled");
+  }
+  else
+  {
+    if (IsEqualIID(Riid, IID_IShellExtInit) || IsEqualIID(Riid, IID_IUnknown))
+    {
+      DEBUG("CShellExt::QueryInterface is IShellExtInit or IUnknown");
+      *Ppv = (LPSHELLEXTINIT)this;
+    }
+    else if (IsEqualIID(Riid, IID_IShellCopyHook))
+    {
+      DEBUG("CShellExt::QueryInterface is IShellCopyHook");
+      *Ppv = (LPCOPYHOOK)this;
+    }
+
+    if (*Ppv)
+    {
+      AddRef();
+
+      Result = NOERROR;
+    }
+  }
+
+  DEBUG("CShellExt::QueryInterface leave");
+
+  return Result;
+}
+//---------------------------------------------------------------------------
+STDMETHODIMP_(ULONG) CShellExt::AddRef()
+{
+  DEBUG("CShellExt::AddRef");
+
+  return ++FReferenceCounter;
+}
+//---------------------------------------------------------------------------
+STDMETHODIMP_(ULONG) CShellExt::Release()
+{
+  DEBUG("CShellExt::Release");
+  if (--FReferenceCounter)
+  {
+    return FReferenceCounter;
+  }
+
+  delete this;
+
+  return 0;
+}
+//---------------------------------------------------------------------------
+STDMETHODIMP CShellExt::Initialize(LPCITEMIDLIST IDFolder,
+  LPDATAOBJECT DataObj, HKEY RegKey)
+{
+  DEBUG("CShellExt::Initialize enter");
+
+  if (FDataObj != NULL)
+  {
+    FDataObj->Release();
+    FDataObj = NULL;
+  }
+
+  // duplicate the object pointer and registry handle
+
+  if (DataObj != NULL)
+  {
+    FDataObj = DataObj;
+    DataObj->AddRef();
+  }
+
+  DEBUG("CShellExt::Initialize leave");
+
+  return NOERROR;
+}
+//---------------------------------------------------------------------------
+STDMETHODIMP_(UINT) CShellExt::CopyCallback(HWND Hwnd, UINT Func, UINT Flags,
+  LPCSTR SrcFile, DWORD SrcAttribs, LPCSTR DestFile, DWORD DestAttribs)
+{
+  DEBUG("CShellExt::CopyCallback enter");
+
+  UINT Result = IDYES;
+
+  if (GEnabled && ((Func == FO_COPY) || (Func == FO_MOVE)))
+  {
+    DEBUG("CShellExt::CopyCallback copy or move");
+
+    unsigned long Ticks = GetTickCount();
+    if (((Ticks - FLastTicks) >= 100) ||
+        (FLastTicks > Ticks))
+    {
+      DEBUG("CShellExt::CopyCallback interval elapsed");
+
+      DEBUG("CShellExt::CopyCallback source / dest:");
+      DEBUG(SrcFile);
+      DEBUG(DestFile);
+
+      FLastTicks = Ticks;
+      const char* BackPtr = strrchr(SrcFile, '\\');
+
+      if ((BackPtr != NULL) &&
+          (strncmp(BackPtr + 1, DRAG_EXT_DUMMY_DIR_PREFIX,
+            DRAG_EXT_DUMMY_DIR_PREFIX_LEN) == 0))
+      {
+        DEBUG("CShellExt::CopyCallback filename has prefix");
+
+        HANDLE MapFile = OpenFileMapping(FILE_MAP_ALL_ACCESS,
+          false, DRAG_EXT_MAPPING);
+
+        if (MapFile != NULL)
+        {
+          DEBUG("CShellExt::CopyCallback mapfile found");
+
+          TDragExtCommStruct* CommStruct;
+          CommStruct = static_cast<TDragExtCommStruct*>(MapViewOfFile(MapFile,
+            FILE_MAP_ALL_ACCESS, 0, 0, 0));
+
+          if (CommStruct != NULL)
+          {
+            DEBUG("CShellExt::CopyCallback mapview created");
+            unsigned long WaitResult = WaitForSingleObject(FMutex, 1000);
+            if (WaitResult != WAIT_TIMEOUT)
+            {
+              DEBUG("CShellExt::CopyCallback mutex got");
+              if (CommStruct->Version >= TDragExtCommStruct::MinVersion)
+              {
+                DEBUG("CShellExt::CopyCallback supported structure version");
+                if (CommStruct->Dragging)
+                {
+                  DEBUG("CShellExt::CopyCallback dragging");
+                  if (strcmp(CommStruct->DropDest, SrcFile) == 0)
+                  {
+                    CommStruct->Dragging = false;
+                    strncpy(CommStruct->DropDest, DestFile, sizeof(CommStruct->DropDest));
+                    CommStruct->DropDest[sizeof(CommStruct->DropDest)-1] = '\0';
+                    Result = IDNO;
+                    DEBUG("CShellExt::CopyCallback dragging refused");
+                  }
+                  else
+                  {
+                    DEBUG("CShellExt::CopyCallback dragged file does NOT match");
+                  }
+                }
+                else
+                {
+                  DEBUG("CShellExt::CopyCallback NOT dragging");
+                }
+              }
+              else
+              {
+                DEBUG("CShellExt::CopyCallback unsupported structure version");
+              }
+              ReleaseMutex(FMutex);
+              DEBUG("CShellExt::CopyCallback mutex released");
+            }
+            else
+            {
+              DEBUG("CShellExt::CopyCallback mutex timeout");
+            }
+            UnmapViewOfFile(CommStruct);
+          }
+          else
+          {
+            DEBUG("CShellExt::CopyCallback mapview NOT created");
+          }
+
+          CloseHandle(MapFile);
+        }
+        else
+        {
+          DEBUG("CShellExt::CopyCallback mapfile NOT found");
+        }
+      }
+      else
+      {
+        DEBUG("CShellExt::CopyCallback filename has NOT prefix");
+      }
+    }
+    else
+    {
+      DEBUG("CShellExt::CopyCallback interval NOT elapsed");
+    }
+  }
+  else
+  {
+    DEBUG("CShellExt::CopyCallback NOT copy nor move");
+  }
+
+  DEBUG("CShellExt::CopyCallback leave");
+
+  return Result;
+}

+ 28 - 0
dragext/DragExt.h

@@ -0,0 +1,28 @@
+//---------------------------------------------------------------------------
+#ifndef DragExtH
+#define DragExtH
+//---------------------------------------------------------------------------
+#define DRAG_EXT_MAPPING "WinSCPDragExtMapping"
+#define DRAG_EXT_MUTEX "WinSCPDragExtMutex"
+#define DRAG_EXT_DUMMY_DIR_PREFIX "scp"
+#define DRAG_EXT_DUMMY_DIR_PREFIX_LEN 3
+//---------------------------------------------------------------------------
+DEFINE_GUID(CLSID_ShellExtension, 0xe15e1d68, 0x0d1c, 0x49f7,
+  0xbe, 0xb8, 0x81, 0x2b, 0x1e, 0x00, 0xfa, 0x60 );
+//---------------------------------------------------------------------------
+struct TDragExtCommStruct
+{
+  enum TVersion
+  {
+    Version0 = 0,
+    CurrentVersion = Version0,
+    MinVersion = Version0,
+    MaxVersion = CurrentVersion
+  };
+
+  int Version;
+  bool Dragging;
+  char DropDest[MAX_PATH];
+};
+//---------------------------------------------------------------------------
+#endif // DragExtH

+ 52 - 6
forms/CustomScpExplorer.cpp

@@ -106,11 +106,15 @@ __fastcall TCustomScpExplorerForm::TCustomScpExplorerForm(TComponent* Owner):
   FErrorList = NULL;
   FSynchronizeProgressForm = NULL;
   FProgressForm = NULL;
-  FDDExtDropEffect = DROPEFFECT_NONE;
+  FDDExtCopySlipped = false;
   FDDExtMapFile = NULL;
   FDDExtMutex = CreateMutex(NULL, false, DRAG_EXT_MUTEX);
   assert(FDDExtMutex != NULL);
 
+  FOle32Library = LoadLibrary("Ole32.dll");
+  FDragCopyCursor = FOle32Library != NULL ?
+    LoadCursor(FOle32Library, MAKEINTRESOURCE(3)) : NULL;
+
   UseSystemSettings(this);
 
   TComboBox * SessionCombo = dynamic_cast<TComboBox*>(GetComponent(fcSessionCombo));
@@ -137,6 +141,10 @@ __fastcall TCustomScpExplorerForm::~TCustomScpExplorerForm()
   CloseHandle(FDDExtMutex);
   FDDExtMutex = NULL;
 
+  FreeLibrary(FOle32Library);
+  FOle32Library = NULL;
+  FDragCopyCursor = NULL;
+
   assert(!FErrorList);
   StoreParams();
   Terminal = NULL;
@@ -1916,7 +1924,7 @@ void __fastcall TCustomScpExplorerForm::DDExtInitDrag(TFileList * FileList,
     UnmapViewOfFile(CommStruct);
   }
 
-  FDDExtDropEffect = DROPEFFECT_NONE;
+  FDDExtCopySlipped = false;
 }
 //---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::RemoteDirViewDDEnd(TObject * /*Sender*/)
@@ -1925,14 +1933,30 @@ void __fastcall TCustomScpExplorerForm::RemoteDirViewDDEnd(TObject * /*Sender*/)
   {
     try
     {
-      if (FDDExtDropEffect != DROPEFFECT_NONE)
+      if ((RemoteDirView->LastDDResult == drCopy) ||
+          (RemoteDirView->LastDDResult == drMove))
       {
         AnsiString TargetDirectory;
+        TFileOperation Operation;
+
+        Operation = (RemoteDirView->LastDDResult == drMove) ? foMove : foCopy;
+
+        // Heuristics: if we slipped copy operation, but interval between
+        // DDTargetDrop() and DDEnd() is greater than some interval,
+        // we suppose that target (explorer) has shown the drop menu.
+        // If such case we use actual operation returned by target application.
+        if (FDDExtCopySlipped)
+        {
+          int SlipInterval = MSecsPerDay * (Now() - FDDDropTime);
+          if (SlipInterval < WinConfiguration->DDExtCopySlipTimeout)
+          {
+            Operation = foCopy;
+          }
+        }
 
         DDGetTarget(TargetDirectory);
 
-        ExecuteFileOperation(
-          FDDExtDropEffect == DROPEFFECT_MOVE ? foMove : foCopy, osRemote, true,
+        ExecuteFileOperation(Operation, osRemote, true,
           !WinConfiguration->DDTransferConfirmation, &TargetDirectory);
       }
     }
@@ -1946,6 +1970,28 @@ void __fastcall TCustomScpExplorerForm::RemoteDirViewDDEnd(TObject * /*Sender*/)
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TCustomScpExplorerForm::RemoteDirViewDDGiveFeedback(
+  TObject * /*Sender*/, int dwEffect, HRESULT & /*Result*/)
+{
+  HCURSOR SlippedMoveCursor;
+
+  // When dragging outside of winscp using shellext, move operation is usually
+  // selected as default (by explorer). We want copy instead. As the operation is
+  // finally hold by us, we can slip "copy" cursor in place of "move" cursor.
+  // Thus explorer thinks that file is moving, but we display copy cursor
+  // and we do actually copy, when shellext steals processing from explorer.
+  // See TCustomScpExplorerForm::DDGetTarget
+  FDDExtCopySlipped =
+    (FDDExtMapFile != NULL) && (FDDTargetDirView == NULL) &&
+    (dwEffect == DROPEFFECT_Move) && (FDragCopyCursor != NULL) &&
+    (GetKeyState(VK_SHIFT) >= 0) && (GetKeyState(VK_CONTROL) >= 0);
+
+  SlippedMoveCursor = FDDExtCopySlipped ? FDragCopyCursor : Dragdrop::DefaultCursor;
+
+  RemoteDirView->DragDropFilesEx->CHMove = SlippedMoveCursor;
+  RemoteDirView->DragDropFilesEx->CHScrollMove = SlippedMoveCursor;
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomScpExplorerForm::DDGetTarget(AnsiString & Directory)
 {
   bool Result = false;
@@ -1999,7 +2045,7 @@ void __fastcall TCustomScpExplorerForm::RemoteDirViewDDTargetDrop(
   else if (FDDExtMapFile != NULL)
   {
     Continue = false;
-    FDDExtDropEffect = DropEffect;
+    FDDDropTime = Now();
   }
 }
 //---------------------------------------------------------------------------

+ 2 - 1
forms/CustomScpExplorer.dfm

@@ -70,8 +70,9 @@ object CustomScpExplorerForm: TCustomScpExplorerForm
       OnExecFile = DirViewExecFile
       OnDDDragEnter = DirViewDDDragEnter
       OnDDDragLeave = DirViewDDDragLeave
-      OnDDCreateDragFileList = RemoteDirViewDDCreateDragFileList
+      OnDDGiveFeedback = RemoteDirViewDDGiveFeedback
       OnDDEnd = RemoteDirViewDDEnd
+      OnDDCreateDragFileList = RemoteDirViewDDCreateDragFileList
       OnDDCreateDataObject = RemoteDirViewDDCreateDataObject
       OnContextPopup = RemoteDirViewContextPopup
       OnDisplayProperties = RemoteDirViewDisplayProperties

+ 6 - 1
forms/CustomScpExplorer.h

@@ -68,6 +68,8 @@ __published:
     int DropEffect, bool &Continue);
   void __fastcall RemoteDirViewDDCreateDataObject(TObject *Sender,
     TDataObject *&DataObject);
+  void __fastcall RemoteDirViewDDGiveFeedback(TObject *Sender,
+    int dwEffect, HRESULT &Result);
   
 private:
   TTerminal * FTerminal;
@@ -83,6 +85,8 @@ private:
   TStringList * FErrorList;
   HANDLE FDDExtMutex;
   AnsiString FDragExtFakeDirectory;
+  HINSTANCE FOle32Library;
+  HCURSOR FDragCopyCursor;
 
   bool __fastcall GetEnableFocusedOperation(TOperationSide Side);
   bool __fastcall GetEnableSelectedOperation(TOperationSide Side);
@@ -101,7 +105,8 @@ protected:
   AnsiString FCustomCommandName;
   TSynchronizeProgressForm * FSynchronizeProgressForm;
   HANDLE FDDExtMapFile;
-  int FDDExtDropEffect;
+  bool FDDExtCopySlipped;
+  TDateTime FDDDropTime;
 
   virtual bool __fastcall CopyParamDialog(TTransferDirection Direction,
     TTransferType Type, bool DragDrop, TStrings * FileList,

+ 10 - 10
forms/Login.dfm

@@ -83,11 +83,11 @@ object LoginDialog: TLoginDialog
       Top = 0
       Width = 361
       Height = 324
-      ActivePage = EnvironmentSheet
+      ActivePage = SessionListSheet
       Align = alClient
       MultiLine = True
       Style = tsButtons
-      TabIndex = 3
+      TabIndex = 0
       TabOrder = 1
       OnChange = PageControlChange
       object SessionListSheet: TTabSheet
@@ -1320,7 +1320,7 @@ object LoginDialog: TLoginDialog
             Height = 21
             Style = csDropDownList
             Anchors = [akLeft, akTop, akRight]
-            ItemHeight = 0
+            ItemHeight = 13
             TabOrder = 0
           end
           object BugPlainPW1Combo: TComboBox
@@ -1330,7 +1330,7 @@ object LoginDialog: TLoginDialog
             Height = 21
             Style = csDropDownList
             Anchors = [akLeft, akTop, akRight]
-            ItemHeight = 0
+            ItemHeight = 13
             TabOrder = 1
           end
           object BugRSA1Combo: TComboBox
@@ -1340,7 +1340,7 @@ object LoginDialog: TLoginDialog
             Height = 21
             Style = csDropDownList
             Anchors = [akLeft, akTop, akRight]
-            ItemHeight = 0
+            ItemHeight = 13
             TabOrder = 2
           end
           object BugHMAC2Combo: TComboBox
@@ -1350,7 +1350,7 @@ object LoginDialog: TLoginDialog
             Height = 21
             Style = csDropDownList
             Anchors = [akLeft, akTop, akRight]
-            ItemHeight = 0
+            ItemHeight = 13
             TabOrder = 3
           end
           object BugDeriveKey2Combo: TComboBox
@@ -1360,7 +1360,7 @@ object LoginDialog: TLoginDialog
             Height = 21
             Style = csDropDownList
             Anchors = [akLeft, akTop, akRight]
-            ItemHeight = 0
+            ItemHeight = 13
             TabOrder = 4
           end
           object BugRSAPad2Combo: TComboBox
@@ -1370,7 +1370,7 @@ object LoginDialog: TLoginDialog
             Height = 21
             Style = csDropDownList
             Anchors = [akLeft, akTop, akRight]
-            ItemHeight = 0
+            ItemHeight = 13
             TabOrder = 5
           end
           object BugDHGEx2Combo: TComboBox
@@ -1380,7 +1380,7 @@ object LoginDialog: TLoginDialog
             Height = 21
             Style = csDropDownList
             Anchors = [akLeft, akTop, akRight]
-            ItemHeight = 0
+            ItemHeight = 13
             TabOrder = 6
           end
           object BugPKSessID2Combo: TComboBox
@@ -1390,7 +1390,7 @@ object LoginDialog: TLoginDialog
             Height = 21
             Style = csDropDownList
             Anchors = [akLeft, akTop, akRight]
-            ItemHeight = 0
+            ItemHeight = 13
             TabOrder = 7
           end
         end

+ 2 - 0
forms/Preferences.cpp

@@ -130,6 +130,7 @@ void __fastcall TPreferencesDialog::LoadConfiguration()
   BOOLPROP(ConfirmOverwriting);
   BOOLPROP(ConfirmDeleting);
   BOOLPROP(ConfirmClosingSession);
+  BOOLPROP(ConfirmExitOnCompletion);
   BOOLPROP(UseLocationProfiles);
   BOOLPROP(ContinueOnError);
   #undef BOOLPROP
@@ -229,6 +230,7 @@ void __fastcall TPreferencesDialog::SaveConfiguration()
     BOOLPROP(ConfirmOverwriting);
     BOOLPROP(ConfirmDeleting);
     BOOLPROP(ConfirmClosingSession);
+    BOOLPROP(ConfirmExitOnCompletion);
     BOOLPROP(UseLocationProfiles);
     BOOLPROP(ContinueOnError);
     #undef BOOLPROP

+ 27 - 17
forms/Preferences.dfm

@@ -52,11 +52,11 @@ object PreferencesDialog: TPreferencesDialog
       Top = 0
       Width = 386
       Height = 390
-      ActivePage = CustomCommandsSheet
+      ActivePage = PreferencesSheet
       Align = alClient
       MultiLine = True
       Style = tsButtons
-      TabIndex = 9
+      TabIndex = 0
       TabOrder = 0
       OnChange = PageControlChange
       object PreferencesSheet: TTabSheet
@@ -69,7 +69,7 @@ object PreferencesDialog: TPreferencesDialog
           335)
         object RandomSeedFileLabel: TLabel
           Left = 16
-          Top = 276
+          Top = 297
           Width = 82
           Height = 13
           Caption = '&Random seed file'
@@ -79,31 +79,31 @@ object PreferencesDialog: TPreferencesDialog
           Left = 8
           Top = 8
           Width = 362
-          Height = 181
+          Height = 202
           Anchors = [akLeft, akTop, akRight]
           Caption = 'Confirmations'
           TabOrder = 0
           DesignSize = (
             362
-            181)
+            202)
           object CopyOnDoubleClickCheck: TCheckBox
             Left = 16
-            Top = 109
+            Top = 131
             Width = 330
             Height = 17
             Anchors = [akLeft, akTop, akRight]
             Caption = '&Copy files using double-click'
-            TabOrder = 4
+            TabOrder = 5
             OnClick = ControlChange
           end
           object CopyOnDoubleClickConfirmationCheck: TCheckBox
             Left = 32
-            Top = 131
+            Top = 153
             Width = 314
             Height = 17
             Anchors = [akLeft, akTop, akRight]
             Caption = 'Co&nfirm copy on double-click operation'
-            TabOrder = 5
+            TabOrder = 6
             OnClick = ControlChange
           end
           object ConfirmOverwritingCheck: TCheckBox
@@ -132,34 +132,44 @@ object PreferencesDialog: TPreferencesDialog
             Width = 330
             Height = 17
             Anchors = [akLeft, akTop, akRight]
-            Caption = 'Closing &session'
+            Caption = 'E&xiting application'
             TabOrder = 2
             OnClick = ControlChange
           end
           object DDTransferConfirmationCheck: TCheckBox
             Left = 16
-            Top = 87
+            Top = 109
             Width = 338
             Height = 17
             Anchors = [akLeft, akTop, akRight]
             Caption = '&Drag && drop operations'
-            TabOrder = 3
+            TabOrder = 4
             OnClick = ControlChange
           end
           object ContinueOnErrorCheck: TCheckBox
             Left = 16
-            Top = 153
+            Top = 175
             Width = 330
             Height = 17
             Anchors = [akLeft, akTop, akRight]
             Caption = 'Continue on &error (advanced users)'
-            TabOrder = 6
+            TabOrder = 7
+            OnClick = ControlChange
+          end
+          object ConfirmExitOnCompletionCheck: TCheckBox
+            Left = 16
+            Top = 87
+            Width = 330
+            Height = 17
+            Anchors = [akLeft, akTop, akRight]
+            Caption = 'Exiting application on &operation completion'
+            TabOrder = 3
             OnClick = ControlChange
           end
         end
         object RandomSeedFileEdit: TFilenameEdit
           Left = 136
-          Top = 272
+          Top = 293
           Width = 234
           Height = 21
           AcceptFiles = True
@@ -175,7 +185,7 @@ object PreferencesDialog: TPreferencesDialog
         end
         object StorageGroup: TXPGroupBox
           Left = 8
-          Top = 195
+          Top = 216
           Width = 362
           Height = 68
           Anchors = [akLeft, akTop, akRight]
@@ -822,7 +832,7 @@ object PreferencesDialog: TPreferencesDialog
             OnClick = RegisterAsUrlHandlerButtonClick
           end
         end
-        object XPGroupBox1: TXPGroupBox
+        object ExternalAppsGroup: TXPGroupBox
           Left = 8
           Top = 248
           Width = 362

+ 2 - 1
forms/Preferences.h

@@ -97,7 +97,7 @@ __published:
   TButton *QuickLaunchIconButton;
   TButton *DesktopIconAllUsersButton;
   TButton *SendToHookButton;
-  TXPGroupBox *XPGroupBox1;
+  TXPGroupBox *ExternalAppsGroup;
   TLabel *Label2;
   TFilenameEdit *PuttyPathEdit;
   TTabSheet *CustomCommandsSheet;
@@ -131,6 +131,7 @@ __published:
   TDirectoryEdit *DDTemporaryDirectoryEdit;
   TCheckBox *DDWarnLackOfTempSpaceCheck;
   TCheckBox *DDWarnOnMoveCheck;
+  TCheckBox *ConfirmExitOnCompletionCheck;
   void __fastcall FormShow(TObject *Sender);
   void __fastcall ControlChange(TObject *Sender);
   void __fastcall EditorFontButtonClick(TObject *Sender);

+ 5 - 1
makefile

@@ -6,7 +6,7 @@ ROOT = $(MAKEDIR)\..
 MAKE = $(ROOT)\bin\make.exe -$(MAKEFLAGS) -f$&.mak
 BPR2MAK = $(ROOT)\bin\bpr2mak
 #------------------------------------------------------------------------------
-default: WinSCP3.exe
+default: WinSCP3.exe DragExt.dll
 
 WinSCP3.exe: WinSCP3.bpr
 WinSCP3.exe: lib\Moje_B5.lib lib\DragDrop_B5.lib lib\DriveDir_B5.lib
@@ -14,6 +14,10 @@ WinSCP3.exe: lib\Putty.lib lib\ScpCore.lib lib\RScpComp.lib lib\ScpForms.lib
  $(BPR2MAK) WinSCP3.bpr
  $(MAKE)
 
+DragExt.dll: DragExt.bpr
+ $(BPR2MAK) DragExt.bpr
+ $(MAKE)
+
 {general}.bpk{lib}.lib:
  cd general
  $(BPR2MAK) $.

+ 1 - 1
putty/TESTBACK.C

@@ -1,4 +1,4 @@
-/* $Id: testback.c,v 1.9 2003/05/10 11:57:55 ben Exp $ */
+/* $Id: TESTBACK.C,v 1.3 2003/12/30 23:22:26 martinprikryl Exp $ */
 /*
  * Copyright (c) 1999 Simon Tatham
  * Copyright (c) 1999 Ben Harris

+ 1 - 1
putty/charset/MACENC.C

@@ -1,4 +1,4 @@
-/* $Id: macenc.c,v 1.2 2003/01/25 19:21:56 ben Exp $ */
+/* $Id: MACENC.C,v 1.3 2003/11/05 21:28:21 martinprikryl Exp $ */
 /*
  * Copyright (c) 2003 Ben Harris
  * All rights reserved.

+ 3 - 4
release/winscpsetup.iss

@@ -53,7 +53,6 @@ AppVersion={#Version}
 AppVerName=WinSCP {#Version}
 OutputBaseFilename=winscp{#Major}{#Minor}{#Rev}setup{#SetupExt}
 SolidCompression=yes
-Compression=lzma
 ShowTasksTreeLines=yes
 
 #define FindHandle
@@ -154,9 +153,9 @@ Name: main; Description: {#Transl("ApplicationComponent")}; \
   Types: {#FullLangs} full custom compact; Flags: fixed; Languages: {#Lang}
 Name: shellext; Description: {#Transl("ShellExtComponent")}; \
   Types: {#FullLangs} compact full; Languages: {#Lang}
-Name: pageant; Description: {#Transl("PuTTYgenComponent")}; \
+Name: pageant; Description: {#Transl("PageantComponent")}; \
   Types: {#FullLangs} full; Languages: {#Lang}
-Name: puttygen; Description: {#Transl("PageantComponent")}; \
+Name: puttygen; Description: {#Transl("PuTTYgenComponent")}; \
   Types: {#FullLangs} full; Languages: {#Lang}
 #ifdef INTL
 Name: transl; Description: {#Transl("TranslationsComponent")}; \
@@ -328,7 +327,7 @@ Root: HKCU; SubKey: "{#RegistryKey}\Configuration\Interface"; \
 #endif
 
 [UninstallRun]
-Filename: "{app}\WinSCP3.exe"; Parameters: "/RandomSeedFileCleanup"; RunOnceId: "RandomSeedFileCleanup"
+Filename: "{app}\WinSCP3.exe"; Parameters: "/UninstallCleanup"; RunOnceId: "UninstallCleanup"
 
 [Code]
 var

+ 143 - 143
resource/TextsCore.h

@@ -1,149 +1,149 @@
 #ifndef TextsCoreH
 #define TextsCoreH
 
-#define UNKNOWN_KEY     1
-#define DIFFERENT_KEY   2
-#define OLD_KEY         3
+#define UNKNOWN_KEY     1
+#define DIFFERENT_KEY   2
+#define OLD_KEY         3
 
-#define CORE_ERROR_STRINGS      100
-#define KEY_NOT_VERIFIED        101
-#define CONNECTION_FAILED       102
-#define USER_TERMINATED         103
-#define LOST_CONNECTION         104
-#define CANT_DETECT_RETURN_CODE 105
-#define COMMAND_FAILED          106
-#define COMMAND_FAILED_CODEONLY 107
-#define INVALID_OUTPUT_ERROR    108
-#define READ_CURRENT_DIR_ERROR  109
-#define SKIP_STARTUP_MESSAGE_ERROR 110
-#define CHANGE_DIR_ERROR        111
-#define LIST_DIR_ERROR          113
-#define LIST_LINE_ERROR         114
-#define RIGHTS_ERROR            115
-#define CLEANUP_CONFIG_ERROR    116
-#define CLEANUP_HOSTKEYS_ERROR  117
-#define CLEANUP_SEEDFILE_ERROR  118
-#define CLEANUP_SESSIONS_ERROR  119
-#define DETECT_RETURNVAR_ERROR  120
-#define LOOKUP_GROUPS_ERROR     121
-#define FILE_NOT_EXISTS         122
-#define CANT_GET_ATTRS          123
-#define OPENFILE_ERROR          124
-#define READ_ERROR              125
-#define COPY_FATAL              126
-#define TOREMOTE_COPY_ERROR     127
-#define TOLOCAL_COPY_ERROR      128
-#define SCP_EMPTY_LINE          129
-#define SCP_ILLEGAL_TIME_FORMAT 130
-#define SCP_INVALID_CONTROL_RECORD 131
-#define COPY_ERROR              132
-#define SCP_ILLEGAL_FILE_DESCRIPTOR 133
-#define NOT_DIRECTORY_ERROR     134
-#define CREATE_DIR_ERROR        135
-#define CREATE_FILE_ERROR       136
-#define WRITE_ERROR             137
-#define CANT_SET_ATTRS          138
-#define REMOTE_ERROR            139
-#define DELETE_FILE_ERROR       140
-#define LOG_ERROR               141
-#define LOG_OPENERROR           142
-#define RENAME_FILE_ERROR       143
-#define RENAME_CREATE_FILE_EXISTS 144
-#define RENAME_CREATE_DIR_EXISTS 145
-#define CHANGE_HOMEDIR_ERROR    146
-#define UNALIAS_ALL_ERROR       147
-#define ALIAS_GROUPLIST_ERROR   148
-#define UNSET_NATIONAL_ERROR    149
-#define FIRST_LINE_EXPECTED     150
-#define CLEANUP_INIFILE_ERROR   151
-#define ATTEMPT_TO_WRITE_TO_PARENT_DIR  152
-#define AUTHENTICATION_LOG      153
-#define AUTHENTICATION_FAILED   154
-#define NOT_CONNECTED           155
-#define SAVE_KEY_ERROR          156
-#define SSH_EXITCODE            158
-#define SFTP_INVALID_TYPE       159
-#define SFTP_VERSION_NOT_SUPPORTED 160
-#define SFTP_MESSAGE_NUMBER     161
-#define SFTP_STATUS_OK          162
-#define SFTP_STATUS_EOF         163
-#define SFTP_STATUS_NO_SUCH_FILE 164
-#define SFTP_STATUS_PERMISSION_DENIED 165
-#define SFTP_STATUS_FAILURE     166
-#define SFTP_STATUS_BAD_MESSAGE 167
-#define SFTP_STATUS_NO_CONNECTION 168
-#define SFTP_STATUS_CONNECTION_LOST 169
-#define SFTP_STATUS_OP_UNSUPPORTED 170
-#define SFTP_ERROR_FORMAT       171
-#define SFTP_STATUS_UNKNOWN     172
-#define READ_SYMLINK_ERROR      173
-#define EMPTY_DIRECTORY         174
-#define SFTP_NON_ONE_FXP_NAME_PACKET 175
-#define SFTP_REALPATH_ERROR     176
-#define CHANGE_PROPERTIES_ERROR 177
-#define SFTP_INITIALIZE_ERROR   178
-#define TIMEZONE_ERROR          179
-#define SFTP_CREATE_FILE_ERROR  180
-#define SFTP_OPEN_FILE_ERROR    181
-#define SFTP_CLOSE_FILE_ERROR   182
-#define NOT_FILE_ERROR          183
-#define RENAME_AFTER_RESUME_ERROR 184
-#define CREATE_LINK_ERROR       185
-#define INVALID_SHELL_COMMAND   186
-#define SFTP_SERVER_MESSAGE_UNSUPPORTED 187
-#define INVALID_OCTAL_PERMISSIONS 188
-#define SFTP_INVALID_EOL        189
-#define SFTP_UNKNOWN_FILE_TYPE  190
-#define SFTP_STATUS_INVALID_HANDLE 191
-#define SFTP_STATUS_NO_SUCH_PATH 192
-#define SFTP_STATUS_FILE_ALREADY_EXISTS 193
-#define SFTP_STATUS_WRITE_PROTECT 194
-#define SFTP_STATUS_NO_MEDIA    195
-#define DECODE_UTF_ERROR        196
-#define CUSTOM_COMMAND_ERROR    197
-#define LOCALE_LOAD_ERROR       198
-#define SFTP_INCOMPLETE_BEFORE_EOF 199
-#define CALCULATE_SIZE_ERROR    200
-#define SFTP_PACKET_TOO_BIG     201
-#define SCP_INIT_ERROR          202
-#define DUPLICATE_BOOKMARK      203
-#define MOVE_FILE_ERROR         204
-#define SFTP_PACKET_TOO_BIG_INIT_EXPLAIN 205
-#define PRESERVE_TIME_ERROR     206
-
-#define CORE_CONFIRMATION_STRINGS 300
-#define CONFIRM_PROLONG_TIMEOUT 301
-#define PROMPT_SESSION_PASSWORD 302
-#define PROMPT_KEY_PASSPHRASE   303
-#define FILE_OVERWRITE          304
-#define DIRECTORY_OVERWRITE     305
-#define CIPHER_BELOW_TRESHOLD   306
-#define CIPHER_TYPE_BOTH        307
-#define CIPHER_TYPE_CS          308
-#define CIPHER_TYPE_SC          309
-#define RESUME_TRANSFER         310
-#define PARTIAL_BIGGER_THAN_SOURCE 311
-#define APPEND_OR_RESUME        312
-#define FILE_OVERWRITE_DETAILS  313
-#define READ_ONLY_OVERWRITE     314
-
-#define CORE_INFORMATION_STRINGS 400
-#define YES_STR                 401
-#define NO_STR                  402
-#define SESSION_INFO_TIP        403
-#define VERSION                 404
-#define CLOSED_ON_COMPLETION    405
-#define SFTP_PROTOCOL_NAME      406
-#define FS_RENAME_NOT_SUPPORTED 407
-#define SFTP_NO_EXTENSION_INFO  408
-#define SFTP_EXTENSION_INFO     409
-#define SCP_UNIX_NAME           410
-#define SCP_NO_UNIX_NAME        411
-
-#define CORE_VARIABLE_STRINGS   600
-#define PUTTY_BASED_ON          601
-#define PUTTY_VERSION           602
-#define PUTTY_COPYRIGHT         603
-#define PUTTY_URL               604
+#define CORE_ERROR_STRINGS      100
+#define KEY_NOT_VERIFIED        101
+#define CONNECTION_FAILED       102
+#define USER_TERMINATED         103
+#define LOST_CONNECTION         104
+#define CANT_DETECT_RETURN_CODE 105
+#define COMMAND_FAILED          106
+#define COMMAND_FAILED_CODEONLY 107
+#define INVALID_OUTPUT_ERROR    108
+#define READ_CURRENT_DIR_ERROR  109
+#define SKIP_STARTUP_MESSAGE_ERROR 110
+#define CHANGE_DIR_ERROR        111
+#define LIST_DIR_ERROR          113
+#define LIST_LINE_ERROR         114
+#define RIGHTS_ERROR            115
+#define CLEANUP_CONFIG_ERROR    116
+#define CLEANUP_HOSTKEYS_ERROR  117
+#define CLEANUP_SEEDFILE_ERROR  118
+#define CLEANUP_SESSIONS_ERROR  119
+#define DETECT_RETURNVAR_ERROR  120
+#define LOOKUP_GROUPS_ERROR     121
+#define FILE_NOT_EXISTS         122
+#define CANT_GET_ATTRS          123
+#define OPENFILE_ERROR          124
+#define READ_ERROR              125
+#define COPY_FATAL              126
+#define TOREMOTE_COPY_ERROR     127
+#define TOLOCAL_COPY_ERROR      128
+#define SCP_EMPTY_LINE          129
+#define SCP_ILLEGAL_TIME_FORMAT 130
+#define SCP_INVALID_CONTROL_RECORD 131
+#define COPY_ERROR              132
+#define SCP_ILLEGAL_FILE_DESCRIPTOR 133
+#define NOT_DIRECTORY_ERROR     134
+#define CREATE_DIR_ERROR        135
+#define CREATE_FILE_ERROR       136
+#define WRITE_ERROR             137
+#define CANT_SET_ATTRS          138
+#define REMOTE_ERROR            139
+#define DELETE_FILE_ERROR       140
+#define LOG_ERROR               141
+#define LOG_OPENERROR           142
+#define RENAME_FILE_ERROR       143
+#define RENAME_CREATE_FILE_EXISTS 144
+#define RENAME_CREATE_DIR_EXISTS 145
+#define CHANGE_HOMEDIR_ERROR    146
+#define UNALIAS_ALL_ERROR       147
+#define ALIAS_GROUPLIST_ERROR   148
+#define UNSET_NATIONAL_ERROR    149
+#define FIRST_LINE_EXPECTED     150
+#define CLEANUP_INIFILE_ERROR   151
+#define ATTEMPT_TO_WRITE_TO_PARENT_DIR  152
+#define AUTHENTICATION_LOG      153
+#define AUTHENTICATION_FAILED   154
+#define NOT_CONNECTED           155
+#define SAVE_KEY_ERROR          156
+#define SSH_EXITCODE            158
+#define SFTP_INVALID_TYPE       159
+#define SFTP_VERSION_NOT_SUPPORTED 160
+#define SFTP_MESSAGE_NUMBER     161
+#define SFTP_STATUS_OK          162
+#define SFTP_STATUS_EOF         163
+#define SFTP_STATUS_NO_SUCH_FILE 164
+#define SFTP_STATUS_PERMISSION_DENIED 165
+#define SFTP_STATUS_FAILURE     166
+#define SFTP_STATUS_BAD_MESSAGE 167
+#define SFTP_STATUS_NO_CONNECTION 168
+#define SFTP_STATUS_CONNECTION_LOST 169
+#define SFTP_STATUS_OP_UNSUPPORTED 170
+#define SFTP_ERROR_FORMAT       171
+#define SFTP_STATUS_UNKNOWN     172
+#define READ_SYMLINK_ERROR      173
+#define EMPTY_DIRECTORY         174
+#define SFTP_NON_ONE_FXP_NAME_PACKET 175
+#define SFTP_REALPATH_ERROR     176
+#define CHANGE_PROPERTIES_ERROR 177
+#define SFTP_INITIALIZE_ERROR   178
+#define TIMEZONE_ERROR          179
+#define SFTP_CREATE_FILE_ERROR  180
+#define SFTP_OPEN_FILE_ERROR    181
+#define SFTP_CLOSE_FILE_ERROR   182
+#define NOT_FILE_ERROR          183
+#define RENAME_AFTER_RESUME_ERROR 184
+#define CREATE_LINK_ERROR       185
+#define INVALID_SHELL_COMMAND   186
+#define SFTP_SERVER_MESSAGE_UNSUPPORTED 187
+#define INVALID_OCTAL_PERMISSIONS 188
+#define SFTP_INVALID_EOL        189
+#define SFTP_UNKNOWN_FILE_TYPE  190
+#define SFTP_STATUS_INVALID_HANDLE 191
+#define SFTP_STATUS_NO_SUCH_PATH 192
+#define SFTP_STATUS_FILE_ALREADY_EXISTS 193
+#define SFTP_STATUS_WRITE_PROTECT 194
+#define SFTP_STATUS_NO_MEDIA    195
+#define DECODE_UTF_ERROR        196
+#define CUSTOM_COMMAND_ERROR    197
+#define LOCALE_LOAD_ERROR       198
+#define SFTP_INCOMPLETE_BEFORE_EOF 199
+#define CALCULATE_SIZE_ERROR    200
+#define SFTP_PACKET_TOO_BIG     201
+#define SCP_INIT_ERROR          202
+#define DUPLICATE_BOOKMARK      203
+#define MOVE_FILE_ERROR         204
+#define SFTP_PACKET_TOO_BIG_INIT_EXPLAIN 205
+#define PRESERVE_TIME_PERM_ERROR 206
+
+#define CORE_CONFIRMATION_STRINGS 300
+#define CONFIRM_PROLONG_TIMEOUT 301
+#define PROMPT_SESSION_PASSWORD 302
+#define PROMPT_KEY_PASSPHRASE   303
+#define FILE_OVERWRITE          304
+#define DIRECTORY_OVERWRITE     305
+#define CIPHER_BELOW_TRESHOLD   306
+#define CIPHER_TYPE_BOTH        307
+#define CIPHER_TYPE_CS          308
+#define CIPHER_TYPE_SC          309
+#define RESUME_TRANSFER         310
+#define PARTIAL_BIGGER_THAN_SOURCE 311
+#define APPEND_OR_RESUME        312
+#define FILE_OVERWRITE_DETAILS  313
+#define READ_ONLY_OVERWRITE     314
+
+#define CORE_INFORMATION_STRINGS 400
+#define YES_STR                 401
+#define NO_STR                  402
+#define SESSION_INFO_TIP        403
+#define VERSION                 404
+#define CLOSED_ON_COMPLETION    405
+#define SFTP_PROTOCOL_NAME      406
+#define FS_RENAME_NOT_SUPPORTED 407
+#define SFTP_NO_EXTENSION_INFO  408
+#define SFTP_EXTENSION_INFO     409
+#define SCP_UNIX_NAME           410
+#define SCP_NO_UNIX_NAME        411
+
+#define CORE_VARIABLE_STRINGS   600
+#define PUTTY_BASED_ON          601
+#define PUTTY_VERSION           602
+#define PUTTY_COPYRIGHT         603
+#define PUTTY_URL               604
 
 #endif // TextsCore

+ 1 - 1
resource/TextsCore1.rc

@@ -106,7 +106,7 @@ BEGIN
   DUPLICATE_BOOKMARK, "Location Profile with name '%s' already exists."
   MOVE_FILE_ERROR, "Error moving file '%s' to '%s'."
   SFTP_PACKET_TOO_BIG_INIT_EXPLAIN, "%s\n \nThe error is typically caused by message printed from startup script (like .profile). The message may start with \"%s\"."
-  PRESERVE_TIME_ERROR, "Upload of file '%s' was successful, but error occurred while setting the timestamp. If the problem persists, turn off 'Preserve timestamp' option."
+  PRESERVE_TIME_PERM_ERROR, "Upload of file '%s' was successful, but error occurred while setting the permissions and/or timestamp. If the problem persists, turn off 'Set permissions' and/or 'Preserve timestamp' option."
 
   CORE_CONFIRMATION_STRINGS, "CORE_CONFIRMATION"
   CONFIRM_PROLONG_TIMEOUT, "Host hasn't answered for %d seconds.\n\nWait for another %0:d seconds? Pressing 'Abort' button will close session."

+ 5 - 1
resource/TextsWin.h

@@ -49,11 +49,12 @@
 #define EXECUTE_APP_ERROR       1127
 #define FILE_NOT_FOUND          1128
 #define ABSOLUTE_PATH_REQUIRED  1129
-#define UNKNOWN_PROTOCOL        1130
 #define REGISTER_URL_ERROR      1131
 #define MUTEX_RELEASE_TIMEOUT   1132
 #define DRAGEXT_MUTEX_RELEASE_TIMEOUT 1133
 #define DRAGEXT_TARGET_UNKNOWN  1134
+#define UNKNOWN_TRANSLATION     1135
+#define INCOMPATIBLE_TRANSLATION 1136
 
 #define WIN_CONFIRMATION_STRINGS 1300
 #define CONFIRM_OVERWRITE_SESSION 1301
@@ -80,6 +81,9 @@
 #define NEXT_BUTTON             1322
 #define APPEND_BUTTON           1323
 #define CONFIRM_REGISTER_URL    1324
+#define UNINSTALL_CLEANUP       1325
+#define EXIT_ON_COMPLETION      1326
+#define DISCONNECT_ON_COMPLETION 1327
 
 #define WIN_INFORMATION_STRINGS 1400
 #define APP_CAPTION             1401

+ 5 - 4
resource/TextsWin1.rc

@@ -12,10 +12,7 @@ BEGIN
         DELETE_LOCAL_FILE_ERROR, "Error deleting file '%s'."
         MASK_ERROR, "'%s' is invalid mask"
         WARN_FATAL_ERROR, "%s\n \nWarning: Aborting this operation will close connection!"
-//        NEWSESSION_ERROR, "Can't open new session."
-//        LOADSESSION_ERROR, "Can't open stored session '%s'."
         SESSION_NOT_EXISTS_ERROR, "Session named '%s' doesn't exist."
-//        RECONNECT_ERROR, "Cannot launch another instance of %s to reconnect session."
         CREATE_SHORTCUT_ERROR, "Cannot create shortcut."
         CANNOT_OVERWRITE_SPECIAL_SESSION, "Cannot overwrite special session '%s'."
         EXPLORE_LOCAL_DIR_ERROR, "Cannot explore directory '%s'."
@@ -36,10 +33,11 @@ BEGIN
         EXECUTE_APP_ERROR, "Cannot execute '%s'."
         FILE_NOT_FOUND, "File '%s' not found."
         ABSOLUTE_PATH_REQUIRED, "Only full (absolute) path may be used."
-        UNKNOWN_PROTOCOL, "Unknown or undefined protocol (%s)"
         MUTEX_RELEASE_TIMEOUT, "Mutex was not released in required interval."
         DRAGEXT_MUTEX_RELEASE_TIMEOUT, "Shell drag extension mutex was not released in required interval."
         DRAGEXT_TARGET_UNKNOWN, "WinSCP was not able to detect folder, where the dragged file(s) was dropped. Either you have not dropped the file(s) to regular folder (e.g. Windows Explorer) or WinSCP shell drag extension is not installed. Install the extension or switch to compatible drag&&drop mode (from Preferences window), which uses temporary folder for downloads. It allows you to drop files to any destination."
+        UNKNOWN_TRANSLATION, "File '%s' does not contain translation for this product version."
+        INCOMPATIBLE_TRANSLATION, "File '%s' contains translation for %s version %s."
 
         REGISTER_URL_ERROR, "Cannot register application to handle scp:// and sftp:// addresses."
 
@@ -65,6 +63,9 @@ BEGIN
         NEXT_BUTTON, "&Next"
         APPEND_BUTTON, "A&ppend"
         CONFIRM_REGISTER_URL, "Do you want to register application to handle scp:// and sftp:// addresses?"
+        UNINSTALL_CLEANUP, "Do you want to cleanup data stored by the application on this computer?"
+        EXIT_ON_COMPLETION, "%s\n\nDo you want to close application?"
+        DISCONNECT_ON_COMPLETION, "%%s\n\nDo you want to terminate %d remaining session(s) and close the application?"
 
         WIN_INFORMATION_STRINGS, "WIN_INFORMATION"
         APP_CAPTION, "%s - %s"

+ 71 - 19
windows/GUIConfiguration.cpp

@@ -19,6 +19,9 @@ struct TPasLibModule {
   void * ResInstance;
 };
 //---------------------------------------------------------------------------
+static const unsigned int AdditionaLanguageMask = 0xFFFFFF00;
+static const AnsiString AdditionaLanguagePrefix("XX");
+//---------------------------------------------------------------------------
 __fastcall TGUIConfiguration::TGUIConfiguration(): TConfiguration()
 {
   FLocale = 0;
@@ -85,7 +88,7 @@ void __fastcall TGUIConfiguration::LoadSpecial(THierarchicalStorage * Storage)
 }
 //---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
-TPasLibModule * TGUIConfiguration::FindModule(void * Instance)
+TPasLibModule * __fastcall TGUIConfiguration::FindModule(void * Instance)
 {
   TPasLibModule * CurModule;
   CurModule = reinterpret_cast<TPasLibModule*>(LibModuleList);
@@ -104,20 +107,30 @@ TPasLibModule * TGUIConfiguration::FindModule(void * Instance)
   return CurModule;
 }
 //---------------------------------------------------------------------------
-HANDLE TGUIConfiguration::LoadNewResourceModule(LCID ALocale)
+HANDLE __fastcall TGUIConfiguration::LoadNewResourceModule(LCID ALocale,
+  AnsiString * FileName)
 {
+  AnsiString LibraryFileName;
   HANDLE NewInstance = 0;
   bool Internal = (ALocale == InternalLocale());
   if (!Internal)
   {
     AnsiString Module;
     AnsiString LocaleName;
-    char LocaleStr[4];
 
     Module = ModuleFileName();
-    GetLocaleInfo(ALocale, LOCALE_SABBREVLANGNAME, LocaleStr, sizeof(LocaleStr));
-    LocaleName = LocaleStr;
-    assert(!LocaleName.IsEmpty());
+    if ((ALocale & AdditionaLanguageMask) != AdditionaLanguageMask)
+    {
+      char LocaleStr[4];
+      GetLocaleInfo(ALocale, LOCALE_SABBREVLANGNAME, LocaleStr, sizeof(LocaleStr));
+      LocaleName = LocaleStr;
+      assert(!LocaleName.IsEmpty());
+    }
+    else
+    {
+      LocaleName = AdditionaLanguagePrefix +
+        char(ALocale & ~AdditionaLanguageMask);
+    }
 
     Module = ChangeFileExt(Module, AnsiString(".") + LocaleName);
     // Look for a potential language/country translation
@@ -127,6 +140,14 @@ HANDLE TGUIConfiguration::LoadNewResourceModule(LCID ALocale)
       // Finally look for a language only translation
       Module.SetLength(Module.Length() - 1);
       NewInstance = LoadLibraryEx(Module.c_str(), 0, LOAD_LIBRARY_AS_DATAFILE);
+      if (NewInstance)
+      {
+        LibraryFileName = Module;
+      }
+    }
+    else
+    {
+      LibraryFileName = Module;
     }
   }
 
@@ -136,17 +157,18 @@ HANDLE TGUIConfiguration::LoadNewResourceModule(LCID ALocale)
   }
   else
   {
-    TPasLibModule * MainModule = FindModule(HInstance);
-    if (MainModule->ResInstance != MainModule->Instance)
-    {
-      FreeLibrary(static_cast<HMODULE>(MainModule->ResInstance));
-    }
-    MainModule->ResInstance = Internal ? MainModule->Instance : NewInstance;
     if (Internal)
     {
+      TPasLibModule * MainModule = FindModule(HInstance);
       NewInstance = MainModule->Instance;
     }
   }
+
+  if (FileName != NULL)
+  {
+    *FileName = LibraryFileName;
+  }
+
   return NewInstance;
 }
 //---------------------------------------------------------------------------
@@ -180,10 +202,11 @@ void __fastcall TGUIConfiguration::SetLocale(LCID value)
 {
   if (Locale != value)
   {
-    if (LoadNewResourceModule(value))
+    HANDLE Module = LoadNewResourceModule(value);
+    if (Module != NULL)
     {
       FLocale = value;
-      ReinitLocale();
+      SetResourceModule(Module);
     }
     else
     {
@@ -196,27 +219,40 @@ void __fastcall TGUIConfiguration::SetLocaleSafe(LCID value)
 {
   if (Locale != value)
   {
-    bool Result;
+    HANDLE Module;
 
     try
     {
-      Result = LoadNewResourceModule(value);
+      Module = LoadNewResourceModule(value);
     }
     catch(...)
     {
       // ignore any exception while loading locale
+      Module = NULL;
     }
 
-    if (Result)
+    if (Module != NULL)
     {
       FLocale = value;
-      ReinitLocale();
+      SetResourceModule(Module);
     }
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TGUIConfiguration::ReinitLocale()
+void __fastcall TGUIConfiguration::FreeResourceModule(HANDLE Instance)
+{
+  TPasLibModule * MainModule = FindModule(HInstance);
+  if (Instance != MainModule->Instance)
+  {
+    FreeLibrary(Instance);
+  }
+}
+//---------------------------------------------------------------------------
+void __fastcall TGUIConfiguration::SetResourceModule(HANDLE Instance)
 {
+  TPasLibModule * MainModule = FindModule(HInstance);
+  FreeResourceModule(MainModule->ResInstance);
+  MainModule->ResInstance = Instance;
 }
 //---------------------------------------------------------------------------
 TStrings * __fastcall TGUIConfiguration::GetLocales()
@@ -311,6 +347,22 @@ TStrings * __fastcall TGUIConfiguration::GetLocales()
         }
         Index++;
       }
+
+      for (int Index = 0; Index < Exts->Count; Index++)
+      {
+        if ((Exts->Objects[Index] == NULL) &&
+            (Exts->Strings[Index].Length() == 3) && 
+            SameText(Exts->Strings[Index].SubString(1, 2), AdditionaLanguagePrefix))
+        {
+          AnsiString LangName = GetFileFileInfoString("LangName",
+            ChangeFileExt(ModuleFileName(), AnsiString(".") + Exts->Strings[Index]));
+          if (!LangName.IsEmpty())
+          {
+            FLocales->AddObject(LangName, reinterpret_cast<TObject*>(
+              AdditionaLanguageMask + Exts->Strings[Index][3]));
+          }
+        }
+      }
     }
   }
   __finally

+ 5 - 3
windows/GUIConfiguration.h

@@ -28,11 +28,13 @@ protected:
   virtual LCID __fastcall GetLocale();
   void __fastcall SetLocale(LCID value);
   void __fastcall SetLocaleSafe(LCID value);
-  HANDLE LoadNewResourceModule(LCID Locale);
-  virtual void __fastcall ReinitLocale();
+  virtual HANDLE __fastcall LoadNewResourceModule(LCID Locale,
+    AnsiString * FileName = NULL);
+  virtual void __fastcall SetResourceModule(HANDLE Instance);
   TStrings * __fastcall GetLocales();
   LCID __fastcall InternalLocale();
-  TPasLibModule * FindModule(void * Instance);
+  TPasLibModule * __fastcall FindModule(void * Instance);
+  void __fastcall FreeResourceModule(HANDLE Instance);
 
 public:
   __fastcall TGUIConfiguration();

+ 38 - 5
windows/UserInterface.cpp

@@ -67,18 +67,51 @@ void __fastcall ShowExtendedExceptionEx(Exception * E, TObject * Sender,
       if (!E->InheritsFrom(__classid(EAbort)))
       {
         TQueryType Type;
-        Type = (E->InheritsFrom(__classid(ESshTerminate)) ?
-          qtInformation : qtError);
+        bool CloseOnCompletion = E->InheritsFrom(__classid(ESshTerminate));
+        Type = CloseOnCompletion ? qtInformation : qtError;
 
         if (E->InheritsFrom(__classid(EFatal)) && !NoReconnect)
         {
-          if (FatalExceptionMessageDialog(E, Type) == qaRetry)
+          TTerminalManager * Manager = TTerminalManager::Instance();
+
+          int Result;
+          if (CloseOnCompletion)
+          {
+            if (WinConfiguration->ConfirmExitOnCompletion)
+            {
+              Result = FatalExceptionMessageDialog(E, Type,
+                Manager->Count > 1 ?
+                  FMTLOAD(DISCONNECT_ON_COMPLETION, (Manager->Count - 1)) :
+                  LoadStr(EXIT_ON_COMPLETION),
+                qaYes | qaNo, 0, mpNeverAskAgainCheck);
+
+              if (Result == qaNeverAskAgain)
+              {
+                Result = qaYes;
+                WinConfiguration->ConfirmExitOnCompletion = false;
+              }
+            }
+            else
+            {
+              Result = qaYes;
+            }
+          }
+          else
+          {
+            Result = FatalExceptionMessageDialog(E, Type);
+          }
+
+          if (Result == qaYes)
+          {
+            Application->Terminate();
+          }
+          else if (Result == qaRetry)
           {
-            TTerminalManager::Instance()->ReconnectActiveTerminal();
+            Manager->ReconnectActiveTerminal();
           }
           else
           {
-            TTerminalManager::Instance()->FreeActiveTerminal();
+            Manager->FreeActiveTerminal();
           }
         }
         else

+ 70 - 3
windows/WinConfiguration.cpp

@@ -23,6 +23,15 @@ __fastcall TWinConfiguration::TWinConfiguration(): TCustomWinConfiguration()
   FBookmarks = new TBookmarks();
   FCustomCommands = new TCustomCommands();
   Default();
+
+  try
+  {
+    CheckTranslationVersion(GetResourceModule(ModuleFileName().c_str()));
+  }
+  catch(...)
+  {
+    LocaleSafe = InternalLocale(); 
+  }
 }
 //---------------------------------------------------------------------------
 __fastcall TWinConfiguration::~TWinConfiguration()
@@ -44,7 +53,8 @@ void __fastcall TWinConfiguration::Default()
   FDDWarnLackOfTempSpace = true;
   FDDWarnLackOfTempSpaceRatio = 1.1;
   FDDExtEnabled = true;
-  FDDExtTimeout = 1000; 
+  FDDExtTimeout = 1000;
+  FDDExtCopySlipTimeout = 100;  
   FDeleteToRecycleBin = true;
   FSelectDirectories = false;
   FSelectMask = "*.*";
@@ -52,6 +62,7 @@ void __fastcall TWinConfiguration::Default()
   FShowInaccesibleDirectories = true;
   FConfirmDeleting = true;
   FConfirmClosingSession = true;
+  FConfirmExitOnCompletion = true;
   FForceDeleteTempFolder = true;
   FCopyOnDoubleClick = false;
   FCopyOnDoubleClickConfirmation = false;
@@ -177,12 +188,14 @@ THierarchicalStorage * TWinConfiguration::CreateScpStorage(bool SessionList)
     KEY(Bool,     ShowInaccesibleDirectories); \
     KEY(Bool,     ConfirmDeleting); \
     KEY(Bool,     ConfirmClosingSession); \
+    KEY(Bool,     ConfirmExitOnCompletion); \
     KEY(String,   AutoStartSession); \
     KEY(Bool,     UseLocationProfiles); \
     KEY(Bool,     ForceDeleteTempFolder); \
     KEY(Integer,  LocaleSafe); \
     KEY(Bool,     DDExtEnabled); \
     KEY(Integer,  DDExtTimeout); \
+    KEY(Integer,  DDExtCopySlipTimeout); \
   ); \
   BLOCK("Interface\\Editor", CANCREATE, \
     KEY(Integer,  Editor.Editor); \
@@ -460,6 +473,11 @@ void __fastcall TWinConfiguration::SetDDExtTimeout(int value)
   SET_CONFIG_PROPERTY(DDExtTimeout);
 }
 //---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::SetDDExtCopySlipTimeout(int value)
+{
+  SET_CONFIG_PROPERTY(DDExtCopySlipTimeout);
+}
+//---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::SetDDWarnLackOfTempSpace(bool value)
 {
   SET_CONFIG_PROPERTY(DDWarnLackOfTempSpace);
@@ -520,6 +538,11 @@ void __fastcall TWinConfiguration::SetConfirmClosingSession(bool value)
   SET_CONFIG_PROPERTY(ConfirmClosingSession);
 }
 //---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::SetConfirmExitOnCompletion(bool value)
+{
+  SET_CONFIG_PROPERTY(ConfirmExitOnCompletion);
+}
+//---------------------------------------------------------------------------
 void __fastcall TWinConfiguration::SetForceDeleteTempFolder(bool value)
 {
   SET_CONFIG_PROPERTY(ForceDeleteTempFolder);
@@ -716,9 +739,34 @@ LCID __fastcall TWinConfiguration::GetLocale()
   return TCustomWinConfiguration::GetLocale();
 }
 //---------------------------------------------------------------------------
-void __fastcall TWinConfiguration::ReinitLocale()
+HANDLE __fastcall TWinConfiguration::LoadNewResourceModule(LCID ALocale,
+  AnsiString * FileName)
+{
+  AnsiString FileNameStorage;
+  if (FileName == NULL)
+  {
+    FileName = &FileNameStorage;
+  }
+
+  HANDLE Instance = TCustomWinConfiguration::LoadNewResourceModule(ALocale, FileName);
+  if (Instance != NULL)
+  {
+    try
+    {
+      CheckTranslationVersion(*FileName);
+    }
+    catch(...)
+    {
+      FreeResourceModule(Instance);
+      throw;
+    }
+  }
+  return Instance;
+}
+//---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::SetResourceModule(HANDLE Instance)
 {
-  TCustomWinConfiguration::ReinitLocale();
+  TCustomWinConfiguration::SetResourceModule(Instance);
 
   Busy(true);
   try
@@ -781,6 +829,25 @@ void __fastcall TWinConfiguration::ReinitLocale()
   }
 }
 //---------------------------------------------------------------------------
+void __fastcall TWinConfiguration::CheckTranslationVersion(const AnsiString FileName)
+{
+  AnsiString TranslationProductVersion = GetFileProductVersion(FileName);
+  AnsiString TranslationProductName = GetFileProductName(FileName);
+  if ((ProductName != TranslationProductName) ||
+      (ProductVersion != TranslationProductVersion))
+  {
+    if (TranslationProductName.IsEmpty() || TranslationProductVersion.IsEmpty())
+    {
+      throw Exception(FMTLOAD(UNKNOWN_TRANSLATION, (FileName)));
+    }
+    else
+    {
+      throw Exception(FMTLOAD(INCOMPATIBLE_TRANSLATION,
+        (FileName, TranslationProductName, TranslationProductVersion)));
+    }
+  }
+}
+//---------------------------------------------------------------------------
 //---------------------------------------------------------------------------
 int __fastcall TCustomCommands::GetParam(const AnsiString & Name)
 {

+ 10 - 1
windows/WinConfiguration.h

@@ -114,7 +114,9 @@ private:
   bool FDDExtEnabled;
   int FDDExtInstalled;
   int FDDExtTimeout;
+  int FDDExtCopySlipTimeout;
   bool FConfirmClosingSession;
+  bool FConfirmExitOnCompletion;
   double FDDWarnLackOfTempSpaceRatio;
   AnsiString FTemporarySessionFile;
   AnsiString FTemporaryKeyFile;
@@ -146,7 +148,9 @@ private:
   void __fastcall SetDDWarnLackOfTempSpace(bool value);
   void __fastcall SetDDExtEnabled(bool value);
   void __fastcall SetDDExtTimeout(int value);
+  void __fastcall SetDDExtCopySlipTimeout(int value);
   void __fastcall SetConfirmClosingSession(bool value);
+  void __fastcall SetConfirmExitOnCompletion(bool value);
   void __fastcall SetForceDeleteTempFolder(bool value);
   void __fastcall SetDDWarnLackOfTempSpaceRatio(double value);
   void __fastcall SetBookmarks(AnsiString Key, TBookmarkList * value);
@@ -172,8 +176,11 @@ protected:
     HANDLE HInst, TComponent * Instance);
   bool __fastcall InitComponent(TComponent * Instance,
     TClass RootAncestor, TClass ClassType);
-  virtual void __fastcall ReinitLocale();
+  virtual HANDLE __fastcall LoadNewResourceModule(LCID Locale,
+    AnsiString * FileName = NULL);
+  virtual void __fastcall SetResourceModule(HANDLE Instance);
   virtual LCID __fastcall GetLocale();
+  void __fastcall CheckTranslationVersion(const AnsiString FileName);
 
 public:
   __fastcall TWinConfiguration();
@@ -208,7 +215,9 @@ public:
   __property bool DDExtEnabled = { read=FDDExtEnabled, write=SetDDExtEnabled };
   __property bool DDExtInstalled = { read=GetDDExtInstalled };
   __property int DDExtTimeout = { read=FDDExtTimeout, write=SetDDExtTimeout };
+  __property int DDExtCopySlipTimeout = { read=FDDExtCopySlipTimeout, write=SetDDExtCopySlipTimeout };
   __property bool ConfirmClosingSession  = { read=FConfirmClosingSession, write=SetConfirmClosingSession };
+  __property bool ConfirmExitOnCompletion  = { read=FConfirmExitOnCompletion, write=SetConfirmExitOnCompletion };
   __property bool ForceDeleteTempFolder  = { read=FForceDeleteTempFolder, write=SetForceDeleteTempFolder };
   __property double DDWarnLackOfTempSpaceRatio  = { read=FDDWarnLackOfTempSpaceRatio, write=SetDDWarnLackOfTempSpaceRatio };
   __property TBookmarkList * Bookmarks[AnsiString Key] = { read = GetBookmarks, write = SetBookmarks };

+ 8 - 7
windows/WinInterface.cpp

@@ -362,7 +362,7 @@ int __fastcall ExceptionMessageDialog(Exception * E,
 }
 //---------------------------------------------------------------------------
 int __fastcall FatalExceptionMessageDialog(Exception * E,
-  TQueryType Type, int HelpCtx)
+  TQueryType Type, AnsiString MessageFormat, int Answers, int HelpCtx, int Params)
 {
   TStrings * MoreMessages = NULL;
   if (E->InheritsFrom(__classid(ExtException)))
@@ -370,19 +370,20 @@ int __fastcall FatalExceptionMessageDialog(Exception * E,
     MoreMessages = ((ExtException *)E)->MoreMessages;
   }
 
-  int Result, Answers;
-
-  Answers = qaOK | qaRetry;
+  assert((Answers & qaRetry) == 0);
+  Answers |= qaRetry;
+  int Result;
 
-  TForm * Dialog = CreateMoreMessageDialog(E->Message, MoreMessages, Type,
-    Answers, HelpCtx, 0);
+  TForm * Dialog = CreateMoreMessageDialog(
+    FORMAT(MessageFormat, (E->Message)), MoreMessages, Type,
+    Answers, HelpCtx, Params);
   try
   {
     TButton * RetryButton = dynamic_cast<TButton *>(Dialog->FindComponent("Retry"));
     assert(RetryButton);
     RetryButton->Caption = LoadStr(RECONNECT_BUTTON);
 
-    Result = ExecuteMessageDialog(Dialog, Answers, 0);
+    Result = ExecuteMessageDialog(Dialog, Answers, Params);
   }
   __finally
   {

+ 2 - 1
windows/WinInterface.h

@@ -39,7 +39,8 @@ int __fastcall MoreMessageDialog(const AnsiString Message,
   int HelpCtx, int Params = 0);
 
 int __fastcall FatalExceptionMessageDialog(Exception * E,
-  TQueryType Type, int HelpCtx = 0);
+  TQueryType Type, AnsiString MessageFormat = "%s", int Answers = qaOK,
+  int HelpCtx = 0, int Params = 0);
 int __fastcall ExceptionMessageDialog(Exception * E,
   TQueryType Type, int Answers, int HelpCtx = 0);
 

+ 6 - 2
windows/WinMain.cpp

@@ -325,9 +325,13 @@ void __fastcall Execute(TProgramParams * Params)
 
     Application->HintHidePause = 1000;
 
-    if (Params->FindSwitch("RandomSeedFileCleanup"))
+    if (Params->FindSwitch("UninstallCleanup"))
     {
-      Configuration->CleanupRandomSeedFile();
+      if (MessageDialog(LoadStr(UNINSTALL_CLEANUP), qtConfirmation,
+            qaOK | qaCancel, 0) == qaOK)
+      {
+        DoCleanupDialog(StoredSessions, Configuration);
+      }
     }
     else if (Params->FindSwitch("RegisterAsUrlHandler"))
     {