Browse Source

Merge branch 'master' into dev

Source commit: 4f8918904e24c76309c69285030ae553403d8867
Martin Prikryl 9 years ago
parent
commit
4f9e1a8a04

+ 3 - 1
dotnet/OutputDataReceivedEventArgs.cs

@@ -9,10 +9,12 @@ namespace WinSCP
     public sealed class OutputDataReceivedEventArgs : EventArgs
     {
         public string Data { get; private set; }
+        public bool Error { get; private set; }
 
-        internal OutputDataReceivedEventArgs(string data)
+        internal OutputDataReceivedEventArgs(string data, bool error)
         {
             Data = data;
+            Error = error;
         }
 
         public override string ToString()

+ 55 - 19
dotnet/Session.cs

@@ -115,6 +115,7 @@ namespace WinSCP
                 Timeout = new TimeSpan(0, 1, 0);
                 _reconnectTime = new TimeSpan(0, 2, 0); // keep in sync with TScript::OptionImpl
                 Output = new StringCollection();
+                _error = new StringCollection();
                 _operationResults = new List<OperationResultBase>();
                 _events = new List<Action>();
                 _eventsEvent = new AutoResetEvent(false);
@@ -209,18 +210,40 @@ namespace WinSCP
                     log = openCommand + log;
                     WriteCommand(command, log);
 
-                    string logExplanation =
-                        string.Format(CultureInfo.CurrentCulture,
-                            "(response log file {0} was not created). This could indicate lack of write permissions to the log folder or problems starting WinSCP itself.",
-                            XmlLogPath);
-
                     // Wait until the log file gets created or WinSCP terminates (in case of fatal error)
                     do
                     {
+                        string logExplanation;
+                        lock (Output)
+                        {
+                            if (_error.Count > 0)
+                            {
+                                string[] error = new string[_error.Count];
+                                _error.CopyTo(error, 0);
+                                logExplanation =
+                                    string.Format(
+                                        CultureInfo.CurrentCulture, "Error output was \"{0}\". ", string.Join(Environment.NewLine, error));
+                            }
+                            else if (Output.Count > 0)
+                            {
+                                string[] output = new string[Output.Count];
+                                Output.CopyTo(output, 0);
+                                logExplanation =
+                                    string.Format(
+                                        CultureInfo.CurrentCulture, "Output was \"{0}\". ", string.Join(Environment.NewLine, output));
+                            }
+                            else
+                            {
+                                logExplanation = "There was no output. ";
+                            }
+                        }
+                        logExplanation +=
+                            string.Format(CultureInfo.CurrentCulture,
+                                "Response log file {0} was not created. This could indicate lack of write permissions to the log folder or problems starting WinSCP itself.",
+                                XmlLogPath);
+
                         if (_process.HasExited && !File.Exists(XmlLogPath))
                         {
-                            string[] output = new string[Output.Count];
-                            Output.CopyTo(output, 0);
                             Logger.WriteCounters();
                             Logger.WriteProcesses();
                             _process.WriteStatus();
@@ -230,17 +253,15 @@ namespace WinSCP
                                 exitCode = string.Format(CultureInfo.CurrentCulture, "{0} ({1:X})", exitCode, _process.ExitCode);
                             }
                             throw new SessionLocalException(this,
-                                string.Format(CultureInfo.CurrentCulture,
-                                    "WinSCP process terminated with exit code {0} and output \"{1}\", without responding {2}",
-                                    exitCode, string.Join(Environment.NewLine, output), logExplanation));
+                                string.Format(CultureInfo.CurrentCulture, "WinSCP process terminated with exit code {0}. ", exitCode) +
+                                logExplanation);
                         }
 
                         Thread.Sleep(50);
 
                         CheckForTimeout(
-                            string.Format(CultureInfo.CurrentCulture,
-                                "WinSCP has not responded in time {0}",
-                                logExplanation));
+                            string.Format(CultureInfo.CurrentCulture, "WinSCP has not responded in time.") +
+                            logExplanation);
 
                     } while (!File.Exists(XmlLogPath));
 
@@ -1618,12 +1639,26 @@ namespace WinSCP
             else
             {
                 Logger.WriteLine("Scheduling output: [{0}]", e.Data);
-                Output.InternalAdd(e.Data);
-                if (Output.Count > 1000)
+                string s = e.Data.TrimEnd(new[] { '\r' });
+
+                lock (Output)
                 {
-                    Output.InternalRemoveFirst();
+                    Output.InternalAdd(s);
+                    if (Output.Count > 1000)
+                    {
+                        Output.InternalRemoveFirst();
+                    }
+                    if (e.Error)
+                    {
+                        _error.InternalAdd(s);
+                        if (_error.Count > 1000)
+                        {
+                            _error.InternalRemoveFirst();
+                        }
+                    }
                 }
-                ScheduleEvent(() => RaiseOutputDataReceived(e.Data));
+
+                ScheduleEvent(() => RaiseOutputDataReceived(e.Data, e.Error));
             }
 
             GotOutput();
@@ -1710,13 +1745,13 @@ namespace WinSCP
             }
         }
 
-        private void RaiseOutputDataReceived(string data)
+        private void RaiseOutputDataReceived(string data, bool error)
         {
             Logger.WriteLine("Output: [{0}]", data);
 
             if (OutputDataReceived != null)
             {
-                OutputDataReceived(this, new OutputDataReceivedEventArgs(data));
+                OutputDataReceived(this, new OutputDataReceivedEventArgs(data, error));
             }
         }
 
@@ -2142,5 +2177,6 @@ namespace WinSCP
         private string _homePath;
         private string _executableProcessUserName;
         private SecureString _executableProcessPassword;
+        private StringCollection _error;
     }
 }

+ 4 - 2
dotnet/internal/ConsoleCommStruct.cs

@@ -11,7 +11,7 @@ namespace WinSCP
     {
         public uint InputType;
         public uint OutputType;
-        public bool WantsProgress;
+        public bool WantsProgress; // since version 6
     }
 
     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
@@ -21,6 +21,8 @@ namespace WinSCP
         public string Message; // wide since version 4
         [MarshalAs(UnmanagedType.I1)]
         public bool FromBeginning;
+        [MarshalAs(UnmanagedType.I1)]
+        public bool Error; // since version 7
     }
 
     [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
@@ -89,7 +91,7 @@ namespace WinSCP
 
     internal class ConsoleCommStruct : IDisposable
     {
-        public const int CurrentVersion = 0x0006;
+        public const int CurrentVersion = 0x0007;
 
         public ConsoleCommStruct(Session session, SafeFileHandle fileMapping)
         {

+ 9 - 9
dotnet/internal/ExeSessionProcess.cs

@@ -77,7 +77,7 @@ namespace WinSCP
                 string xmlLogSwitch;
                 if (useXmlLog)
                 {
-                    xmlLogSwitch = string.Format(CultureInfo.InvariantCulture, "/xmllog=\"{0}\" /xmlgroups ", LogPathEscape(_session.XmlLogPath));
+                    xmlLogSwitch = string.Format(CultureInfo.InvariantCulture, "/xmllog=\"{0}\" /xmlgroups /xmllogrequired ", LogPathEscape(_session.XmlLogPath));
                 }
                 else
                 {
@@ -364,7 +364,7 @@ namespace WinSCP
                             e.Str = _input[0];
                             e.Result = true;
                             _input.RemoveAt(0);
-                            Print(false, e.Str + "\n");
+                            Print(false, false, e.Str + "\n");
                             return;
                         }
                     }
@@ -374,7 +374,7 @@ namespace WinSCP
             }
         }
 
-        private void Print(bool fromBeginning, string message)
+        private void Print(bool fromBeginning, bool error, string message)
         {
             if (fromBeginning && ((message.Length == 0) || (message[0] != '\n')))
             {
@@ -390,24 +390,24 @@ namespace WinSCP
             {
                 if (!string.IsNullOrEmpty(_lastFromBeginning))
                 {
-                    AddToOutput(_lastFromBeginning);
+                    AddToOutput(_lastFromBeginning, false);
                     _lastFromBeginning = null;
                 }
 
                 if (fromBeginning && (message.Length > 0) && (message[0] == '\n'))
                 {
-                    AddToOutput("\n");
+                    AddToOutput("\n", false);
                     _lastFromBeginning = message.Substring(1);
                     _logger.WriteLine("Buffered from-beginning message [{0}]", _lastFromBeginning);
                 }
                 else
                 {
-                    AddToOutput(message);
+                    AddToOutput(message, error);
                 }
             }
         }
 
-        private void AddToOutput(string message)
+        private void AddToOutput(string message, bool error)
         {
             string[] lines = (_incompleteLine + message).Split(new[] { '\n' });
 
@@ -417,7 +417,7 @@ namespace WinSCP
             {
                 if (OutputDataReceived != null)
                 {
-                    OutputDataReceived(this, new OutputDataReceivedEventArgs(lines[i]));
+                    OutputDataReceived(this, new OutputDataReceivedEventArgs(lines[i], error));
                 }
             }
         }
@@ -425,7 +425,7 @@ namespace WinSCP
         private void ProcessPrintEvent(ConsolePrintEventStruct e)
         {
             _logger.WriteLineLevel(1, string.Format(CultureInfo.CurrentCulture, "Print: {0}", e.Message));
-            Print(e.FromBeginning, e.Message);
+            Print(e.FromBeginning, e.Error, e.Message);
         }
 
         private void ProcessInitEvent(ConsoleInitEventStruct e)

+ 3 - 2
source/console/Console.h

@@ -12,8 +12,8 @@ struct TConsoleCommStruct
 {
   enum TVersion
   {
-    CurrentVersion =          0x0006,
-    CurrentVersionConfirmed = 0x0106
+    CurrentVersion =          0x0007,
+    CurrentVersionConfirmed = 0x0107
   };
 
   struct TInitEvent
@@ -27,6 +27,7 @@ struct TConsoleCommStruct
   {
     wchar_t Message[10240]; // wide since version 4
     bool FromBeginning;
+    bool Error; // since vesion 7
   };
 
   struct TInputEvent

+ 1 - 0
source/core/Configuration.cpp

@@ -115,6 +115,7 @@ void __fastcall TConfiguration::Default()
   UpdateActualLogProtocol();
   FLogActions = false;
   FPermanentLogActions = false;
+  FLogActionsRequired = false;
   FActionsLogFileName = L"%TEMP%\\!S.xml";
   FPermanentActionsLogFileName = FActionsLogFileName;
   FProgramIniPathWrittable = -1;

+ 2 - 0
source/core/Configuration.h

@@ -42,6 +42,7 @@ private:
   int FActualLogProtocol;
   bool FLogActions;
   bool FPermanentLogActions;
+  bool FLogActionsRequired;
   UnicodeString FActionsLogFileName;
   UnicodeString FPermanentActionsLogFileName;
   bool FConfirmOverwriting;
@@ -243,6 +244,7 @@ public:
   __property int LogProtocol  = { read=FLogProtocol, write=SetLogProtocol };
   __property int ActualLogProtocol  = { read=FActualLogProtocol };
   __property bool LogActions  = { read=FLogActions, write=SetLogActions };
+  __property bool LogActionsRequired  = { read=FLogActionsRequired, write=FLogActionsRequired };
   __property UnicodeString ActionsLogFileName  = { read=FActionsLogFileName, write=SetActionsLogFileName };
   __property int LogWindowLines  = { read=FLogWindowLines, write=SetLogWindowLines };
   __property bool LogWindowComplete  = { read=GetLogWindowComplete, write=SetLogWindowComplete };

+ 13 - 2
source/core/FileMasks.cpp

@@ -789,6 +789,11 @@ void __fastcall TFileMasks::SetStr(const UnicodeString Str, bool SingleMask)
 const wchar_t TCustomCommand::NoQuote = L'\0';
 const UnicodeString TCustomCommand::Quotes = L"\"'";
 //---------------------------------------------------------------------------
+UnicodeString __fastcall TCustomCommand::Escape(const UnicodeString & S)
+{
+  return ReplaceStr(S, L"!", L"!!");
+}
+//---------------------------------------------------------------------------
 TCustomCommand::TCustomCommand()
 {
 }
@@ -915,7 +920,7 @@ UnicodeString __fastcall TCustomCommand::Complete(const UnicodeString & Command,
       {
         if (!LastPass)
         {
-          Replacement = ReplaceStr(Replacement, L"!", L"!!");
+          Replacement = Escape(Replacement);
         }
         if (Delimit)
         {
@@ -975,7 +980,8 @@ bool __fastcall TCustomCommand::FindPattern(const UnicodeString & Command,
     wchar_t APatternCmd;
     GetToken(Command, Index, Len, APatternCmd);
     if (((PatternCmd != L'!') && (tolower(PatternCmd) == tolower(APatternCmd))) ||
-        ((PatternCmd == L'!') && (Len == 1) && (APatternCmd != TEXT_TOKEN)))
+        ((PatternCmd == L'!') && (Len == 1) && (APatternCmd != TEXT_TOKEN)) ||
+        ((PatternCmd == L'\0') && (APatternCmd != TEXT_TOKEN)))
     {
       Result = true;
     }
@@ -986,6 +992,11 @@ bool __fastcall TCustomCommand::FindPattern(const UnicodeString & Command,
   return Result;
 }
 //---------------------------------------------------------------------------
+bool __fastcall TCustomCommand::HasAnyPatterns(const UnicodeString & Command)
+{
+  return FindPattern(Command, L'\0');
+}
+//---------------------------------------------------------------------------
 void __fastcall TCustomCommand::ValidatePattern(const UnicodeString & /*Command*/,
   int /*Index*/, int /*Len*/, wchar_t /*PatternCmd*/, void * /*Arg*/)
 {

+ 3 - 0
source/core/FileMasks.h

@@ -138,6 +138,9 @@ public:
 
   UnicodeString __fastcall Complete(const UnicodeString & Command, bool LastPass);
   virtual void __fastcall Validate(const UnicodeString & Command);
+  bool __fastcall HasAnyPatterns(const UnicodeString & Command);
+
+  static UnicodeString __fastcall Escape(const UnicodeString & S);
 
 protected:
   static const wchar_t NoQuote;

+ 14 - 4
source/core/FtpFileSystem.cpp

@@ -369,6 +369,7 @@ void __fastcall TFTPFileSystem::Open()
   TSessionData * Data = FTerminal->SessionData;
 
   FWindowsServer = false;
+  FMVS = false;
   FTransferActiveImmediately = (Data->FtpTransferActiveImmediately == asOn);
 
   FSessionInfo.LoginTime = Now();
@@ -761,6 +762,14 @@ void __fastcall TFTPFileSystem::CollectUsage()
   {
     FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPIdea");
   }
+  // 220-FTPD1 IBM FTP CS V2R1 at name.test.com, 13:49:38 on 2016-01-28.
+  // ...
+  // SYST
+  // 215 MVS is the operating system of this server. FTP Server is running on z/OS.
+  else if (FMVS)
+  {
+    FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPMVS");
+  }
   else
   {
     FTerminal->Configuration->Usage->Inc(L"OpenedSessionsFTPOther");
@@ -3789,13 +3798,14 @@ void __fastcall TFTPFileSystem::HandleReplyStatus(UnicodeString Response)
       if (FLastCode == 215)
       {
         FSystem = FLastResponse->Text.TrimRight();
+        // full name is "MVS is the operating system of this server. FTP Server is running on ..."
+        // (the ... can be "z/OS")
+        // https://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.cs3cod0/ftp215-02.htm
+        FMVS = (FSystem.SubString(1, 3) == L"MVS");
         if ((FListAll == asAuto) &&
              // full name is "Personal FTP Server PRO K6.0"
             ((FSystem.Pos(L"Personal FTP Server") > 0) ||
-             // full name is "MVS is the operating system of this server. FTP Server is running on ..."
-             // (the ... can be "z/OS")
-             // https://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.cs3cod0/ftp215-02.htm
-             (FSystem.SubString(1, 3) == L"MVS")))
+             FMVS))
         {
           FTerminal->LogEvent(L"Server is known not to support LIST -a");
           FListAll = asOff;

+ 1 - 0
source/core/FtpFileSystem.h

@@ -289,6 +289,7 @@ private:
   bool FWindowsServer;
   __int64 FBytesAvailable;
   bool FBytesAvailableSuppoted;
+  bool FMVS;
   mutable UnicodeString FOptionScratch;
 };
 //---------------------------------------------------------------------------

+ 13 - 4
source/core/Script.cpp

@@ -861,18 +861,18 @@ void __fastcall TScript::ConnectTerminal(TTerminal * ATerminal)
   ATerminal->Open();
 }
 //---------------------------------------------------------------------------
-void __fastcall TScript::Print(const UnicodeString Str)
+void __fastcall TScript::Print(const UnicodeString Str, bool Error)
 {
   if (FOnPrint != NULL)
   {
-    FOnPrint(this, Str);
+    FOnPrint(this, Str, Error);
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TScript::PrintLine(const UnicodeString Str)
+void __fastcall TScript::PrintLine(const UnicodeString Str, bool Error)
 {
   Log(llOutput, Str);
-  Print(Str + L"\n");
+  Print(Str + L"\n", Error);
 }
 //---------------------------------------------------------------------------
 bool __fastcall TScript::HandleExtendedException(Exception * E, TTerminal * ATerminal)
@@ -2599,6 +2599,7 @@ void __fastcall TManagementScript::Connect(const UnicodeString Session,
         DebugAssert(Data->CanLogin);
       }
 
+      bool WasLogActions = Configuration->LogActions;
       TTerminal * ATerminal = FTerminalList->NewTerminal(Data);
       try
       {
@@ -2616,6 +2617,14 @@ void __fastcall TManagementScript::Connect(const UnicodeString Session,
       }
       catch(Exception & E)
       {
+        // fatal error, most probably caused by XML logging failure (as it has been turned off),
+        // and XML log is required => abort
+        if ((dynamic_cast<EFatal *>(&E) != NULL) &&
+            WasLogActions && !Configuration->LogActions &&
+            Configuration->LogActionsRequired)
+        {
+          FContinue = false;
+        }
         // make sure errors (mainly fatal ones) are associated
         // with this terminal, not the last active one
         bool Handled = HandleExtendedException(&E, ATerminal);

+ 3 - 3
source/core/Script.h

@@ -25,7 +25,7 @@ public:
   unsigned int CPS;
 };
 //---------------------------------------------------------------------------
-typedef void __fastcall (__closure *TScriptPrintEvent)(TScript * Script, const UnicodeString Str);
+typedef void __fastcall (__closure *TScriptPrintEvent)(TScript * Script, const UnicodeString Str, bool Error);
 typedef void __fastcall (__closure *TScriptSynchronizeStartStop)(TScript * Script,
   const UnicodeString LocalDirectory, const UnicodeString RemoteDirectory,
   const TCopyParamType & CopyParam, int SynchronizeParams);
@@ -54,7 +54,7 @@ public:
 
   void __fastcall Command(UnicodeString Cmd);
   void __fastcall Log(TLogLineType Type, UnicodeString Str);
-  void __fastcall PrintLine(const UnicodeString Str);
+  void __fastcall PrintLine(const UnicodeString Str, bool Error = false);
   void __fastcall StartInteractive();
 
   void __fastcall Synchronize(const UnicodeString LocalDirectory,
@@ -105,7 +105,7 @@ protected:
   virtual void __fastcall ConnectTerminal(TTerminal * ATerminal);
   bool __fastcall EnsureCommandSessionFallback(
     TFSCapability Capability, TSessionAction & Action);
-  void __fastcall Print(const UnicodeString Str);
+  void __fastcall Print(const UnicodeString Str, bool Error = false);
   void __fastcall CheckSession();
   void __fastcall CheckParams(TScriptProcParams * Parameters);
   void __fastcall CopyParamParams(TCopyParamType & CopyParam, TScriptProcParams * Parameters);

+ 11 - 7
source/core/SessionData.cpp

@@ -493,7 +493,7 @@ bool __fastcall TSessionData::IsInFolderOrWorkspace(UnicodeString AFolder)
   return StartsText(UnixIncludeTrailingBackslash(AFolder), Name);
 }
 //---------------------------------------------------------------------
-void __fastcall TSessionData::DoLoad(THierarchicalStorage * Storage, bool & RewritePassword)
+void __fastcall TSessionData::DoLoad(THierarchicalStorage * Storage, bool PuttyImport, bool & RewritePassword)
 {
   // Make sure we only ever use methods supported by TOptionsStorage
   // (implemented by TOptionsIniFile)
@@ -603,7 +603,11 @@ void __fastcall TSessionData::DoLoad(THierarchicalStorage * Storage, bool & Rewr
   TrimVMSVersions = Storage->ReadBool(L"TrimVMSVersions", TrimVMSVersions);
   NotUtf = TAutoSwitch(Storage->ReadInteger(L"Utf", Storage->ReadInteger(L"SFTPUtfBug", NotUtf)));
 
-  TcpNoDelay = Storage->ReadBool(L"TcpNoDelay", TcpNoDelay);
+  // PuTTY defaults to TcpNoDelay, but the psftp/pscp ignores this preference, and always set this to off (what is our default too)
+  if (!PuttyImport)
+  {
+    TcpNoDelay = Storage->ReadBool(L"TcpNoDelay", TcpNoDelay);
+  }
   SendBuf = Storage->ReadInteger(L"SendBuf", Storage->ReadInteger("SshSendBuf", SendBuf));
   SshSimple = Storage->ReadBool(L"SshSimple", SshSimple);
 
@@ -760,7 +764,7 @@ void __fastcall TSessionData::DoLoad(THierarchicalStorage * Storage, bool & Rewr
 #endif
 }
 //---------------------------------------------------------------------
-void __fastcall TSessionData::Load(THierarchicalStorage * Storage)
+void __fastcall TSessionData::Load(THierarchicalStorage * Storage, bool PuttyImport)
 {
   bool RewritePassword = false;
   if (Storage->OpenSubKey(InternalStorageKey, False))
@@ -773,7 +777,7 @@ void __fastcall TSessionData::Load(THierarchicalStorage * Storage)
     ClearSessionPasswords();
     FProxyPassword = L"";
 
-    DoLoad(Storage, RewritePassword);
+    DoLoad(Storage, PuttyImport, RewritePassword);
 
     Storage->CloseSubKey();
   }
@@ -1927,7 +1931,7 @@ bool __fastcall TSessionData::ParseUrl(UnicodeString Url, TOptions * Options,
 void __fastcall TSessionData::ApplyRawSettings(THierarchicalStorage * Storage)
 {
   bool Dummy;
-  DoLoad(Storage, Dummy);
+  DoLoad(Storage, false, Dummy);
 }
 //---------------------------------------------------------------------
 void __fastcall TSessionData::ConfigureTunnel(int APortNumber)
@@ -3631,7 +3635,7 @@ __fastcall TStoredSessionList::~TStoredSessionList()
 }
 //---------------------------------------------------------------------
 void __fastcall TStoredSessionList::Load(THierarchicalStorage * Storage,
-  bool AsModified, bool UseDefaults)
+  bool AsModified, bool UseDefaults, bool PuttyImport)
 {
   TStringList *SubKeys = new TStringList();
   TList * Loaded = new TList;
@@ -3692,7 +3696,7 @@ void __fastcall TStoredSessionList::Load(THierarchicalStorage * Storage,
             Add(SessionData);
           }
           Loaded->Add(SessionData);
-          SessionData->Load(Storage);
+          SessionData->Load(Storage, PuttyImport);
           if (AsModified)
           {
             SessionData->Modified = true;

+ 3 - 3
source/core/SessionData.h

@@ -368,7 +368,7 @@ private:
   UnicodeString __fastcall GetFolderName();
   void __fastcall Modify();
   UnicodeString __fastcall GetSource();
-  void __fastcall DoLoad(THierarchicalStorage * Storage, bool & RewritePassword);
+  void __fastcall DoLoad(THierarchicalStorage * Storage, bool PuttyImport, bool & RewritePassword);
   void __fastcall DoSave(THierarchicalStorage * Storage,
     bool PuttyExport, const TSessionData * Default, bool DoNotEncryptPasswords);
   UnicodeString __fastcall ReadXmlNode(_di_IXMLNode Node, const UnicodeString & Name, const UnicodeString & Default);
@@ -417,7 +417,7 @@ public:
   TSessionData * __fastcall Clone();
   void __fastcall Default();
   void __fastcall NonPersistant();
-  void __fastcall Load(THierarchicalStorage * Storage);
+  void __fastcall Load(THierarchicalStorage * Storage, bool PuttyImport);
   void __fastcall ApplyRawSettings(THierarchicalStorage * Storage);
   void __fastcall ImportFromFilezilla(_di_IXMLNode Node, const UnicodeString & Path, _di_IXMLNode SettingsNode);
   void __fastcall Save(THierarchicalStorage * Storage, bool PuttyExport,
@@ -613,7 +613,7 @@ public:
   void __fastcall ImportFromFilezilla(const UnicodeString FileName, const UnicodeString ConfigurationFileName);
   void __fastcall Export(const UnicodeString FileName);
   void __fastcall Load(THierarchicalStorage * Storage, bool AsModified = false,
-    bool UseDefaults = false);
+    bool UseDefaults = false, bool PuttyImport = false);
   void __fastcall Save(THierarchicalStorage * Storage, bool All = false);
   void __fastcall SelectAll(bool Select);
   void __fastcall Import(TStoredSessionList * From, bool OnlySelected, TList * Imported);

+ 39 - 23
source/core/SessionInfo.cpp

@@ -1360,34 +1360,43 @@ void __fastcall TActionLog::Add(const UnicodeString & Line)
   DebugAssert(FConfiguration);
   if (FLogging)
   {
-    try
+    TGuard Guard(FCriticalSection);
+    if (FFile == NULL)
     {
-      TGuard Guard(FCriticalSection);
-      if (FFile == NULL)
-      {
-        OpenLogFile();
-      }
+      OpenLogFile();
+    }
 
-      if (FFile != NULL)
+    if (FFile != NULL)
+    {
+      try
       {
         UTF8String UtfLine = UTF8String(Line);
         fwrite(UtfLine.c_str(), 1, UtfLine.Length(), (FILE *)FFile);
         fwrite("\n", 1, 1, (FILE *)FFile);
       }
-    }
-    catch (Exception &E)
-    {
-      // We failed logging, turn it off and notify user.
-      FConfiguration->LogActions = false;
-      try
-      {
-        throw ExtException(&E, LoadStr(LOG_GEN_ERROR));
-      }
       catch (Exception &E)
       {
-        if (FUI != NULL)
+        FCriticalSection->Release();
+
+        // We failed logging, turn it off and notify user.
+        FConfiguration->LogActions = false;
+        if (FConfiguration->LogActionsRequired)
         {
-          FUI->HandleExtendedException(&E);
+          throw EFatal(&E, LoadStr(LOG_FATAL_ERROR));
+        }
+        else
+        {
+          try
+          {
+            throw ExtException(&E, LoadStr(LOG_GEN_ERROR));
+          }
+          catch (Exception &E)
+          {
+            if (FUI != NULL)
+            {
+              FUI->HandleExtendedException(&E);
+            }
+          }
         }
       }
     }
@@ -1490,15 +1499,22 @@ void __fastcall TActionLog::OpenLogFile()
     FCurrentLogFileName = L"";
     FCurrentFileName = L"";
     FConfiguration->LogActions = false;
-    try
+    if (FConfiguration->LogActionsRequired)
     {
-      throw ExtException(&E, LoadStr(LOG_GEN_ERROR));
+      throw EFatal(&E, LoadStr(LOG_FATAL_ERROR));
     }
-    catch (Exception & E)
+    else
     {
-      if (FUI != NULL)
+      try
       {
-        FUI->HandleExtendedException(&E);
+        throw ExtException(&E, LoadStr(LOG_GEN_ERROR));
+      }
+      catch (Exception & E)
+      {
+        if (FUI != NULL)
+        {
+          FUI->HandleExtendedException(&E);
+        }
       }
     }
   }

+ 3 - 21
source/filezilla/FtpControlSocket.cpp

@@ -1949,13 +1949,7 @@ void CFtpControlSocket::List(BOOL bFinish, int nError /*=FALSE*/, CServerPath pa
             break;
           }
 
-          unsigned int tmpPort;
-          if (!GetPeerName(pData->host, tmpPort))
-          {
-            LogMessage(FZ_LOG_WARNING, L"GetPeerName failed");
-            error = TRUE;
-            break;
-          }
+          pData->host = m_CurrentServer.host;
         }
         else
         {
@@ -3214,13 +3208,7 @@ void CFtpControlSocket::FileTransfer(t_transferfile *transferfile/*=0*/,BOOL bFi
             break;
           }
 
-          unsigned int tmpPort;
-          if (!GetPeerName(pData->host, tmpPort))
-          {
-            LogMessage(FZ_LOG_WARNING, L"GetPeerName failed");
-            nReplyError = FZ_REPLY_ERROR;
-            break;
-          }
+          pData->host = m_CurrentServer.host;
         }
 
         m_pTransferSocket = new CTransferSocket(this, CSMODE_LIST);
@@ -3490,13 +3478,7 @@ void CFtpControlSocket::FileTransfer(t_transferfile *transferfile/*=0*/,BOOL bFi
               break;
             }
 
-            unsigned int tmpPort;
-            if (!GetPeerName(pData->host, tmpPort))
-            {
-              LogMessage(FZ_LOG_WARNING, L"GetPeerName failed");
-              nReplyError = FZ_REPLY_ERROR;
-              break;
-            }
+            pData->host = m_CurrentServer.host;
           }
           else
           {

+ 18 - 7
source/forms/Custom.cpp

@@ -837,7 +837,8 @@ class TCustomCommandOptionsDialog : public TCustomDialog
 {
 public:
   __fastcall TCustomCommandOptionsDialog(
-    const TCustomCommandType * Command, TStrings * CustomCommandOptions, unsigned int Flags);
+    const TCustomCommandType * Command, TStrings * CustomCommandOptions, unsigned int Flags,
+    TCustomCommand * CustomCommandForOptions);
 
   bool __fastcall Execute();
 
@@ -866,7 +867,8 @@ private:
 };
 //---------------------------------------------------------------------------
 __fastcall TCustomCommandOptionsDialog::TCustomCommandOptionsDialog(
-    const TCustomCommandType * Command, TStrings * CustomCommandOptions, unsigned int Flags) :
+    const TCustomCommandType * Command, TStrings * CustomCommandOptions,
+    unsigned int Flags, TCustomCommand * CustomCommandForOptions) :
   TCustomDialog(HELP_EXTENSION_OPTIONS)
 {
   FCommand = Command;
@@ -884,13 +886,21 @@ __fastcall TCustomCommandOptionsDialog::TCustomCommandOptionsDialog(
     {
       UnicodeString OptionKey = FCommand->GetOptionKey(Option);
       UnicodeString Value;
-      if (FCustomCommandOptions->IndexOfName(OptionKey) >= 0)
+      if ((CustomCommandForOptions != NULL) &&
+          Option.HasPatterns(CustomCommandForOptions))
       {
-        Value = FCustomCommandOptions->Values[OptionKey];
+        Value = CustomCommandForOptions->Complete(Option.Default, true);
       }
       else
       {
-        Value = Option.Default;
+        if (FCustomCommandOptions->IndexOfName(OptionKey) >= 0)
+        {
+          Value = FCustomCommandOptions->Values[OptionKey];
+        }
+        else
+        {
+          Value = Option.Default;
+        }
       }
 
       int Tag = (OptionIndex << 16) + ControlIndex;
@@ -1280,9 +1290,10 @@ void __fastcall TCustomCommandOptionsDialog::DoShow()
 }
 //---------------------------------------------------------------------------
 bool __fastcall DoCustomCommandOptionsDialog(
-  const TCustomCommandType * Command, TStrings * CustomCommandOptions, unsigned int Flags)
+  const TCustomCommandType * Command, TStrings * CustomCommandOptions,
+  unsigned int Flags, TCustomCommand * CustomCommandForOptions)
 {
   std::unique_ptr<TCustomCommandOptionsDialog> Dialog(
-    new TCustomCommandOptionsDialog(Command, CustomCommandOptions, Flags));
+    new TCustomCommandOptionsDialog(Command, CustomCommandOptions, Flags, CustomCommandForOptions));
   return Dialog->Execute();
 }

+ 19 - 15
source/forms/CustomScpExplorer.cpp

@@ -497,7 +497,7 @@ bool __fastcall TCustomScpExplorerForm::CommandLineFromAnotherInstance(
       UnicodeString SessionName = Params.Param[1];
       std::unique_ptr<TObjectList> DataList(new TObjectList());
       UnicodeString DownloadFile; // unused
-      GetLoginData(SessionName, &Params, DataList.get(), DownloadFile, true);
+      GetLoginData(SessionName, &Params, DataList.get(), DownloadFile, true, this);
       if (DataList->Count > 0)
       {
         TTerminalManager * Manager = TTerminalManager::Instance();
@@ -1583,7 +1583,7 @@ bool __fastcall TCustomScpExplorerForm::CustomCommandRemoteAllowed()
 }
 //---------------------------------------------------------------------------
 int __fastcall TCustomScpExplorerForm::CustomCommandState(
-  const TCustomCommandType & Command, bool /*OnFocused*/, TCustomCommandListType ListType)
+  const TCustomCommandType & Command, bool OnFocused, TCustomCommandListType ListType)
 {
   int Result;
 
@@ -1614,7 +1614,7 @@ int __fastcall TCustomScpExplorerForm::CustomCommandState(
     {
       if ((ListType == ccltAll) || (ListType == ccltFile))
       {
-        Result = ((FCurrentSide == osRemote) && EnableSelectedOperation[osRemote]) ? AllowedState : 0;
+        Result = ((FCurrentSide == osRemote) && DirView(osRemote)->AnyFileSelected(OnFocused, false, true)) ? AllowedState : 0;
       }
       else
       {
@@ -1655,7 +1655,7 @@ int __fastcall TCustomScpExplorerForm::CustomCommandState(
     {
       if ((ListType == ccltAll) || (ListType == ccltFile))
       {
-        Result = EnableSelectedOperation[FCurrentSide] ? 1 : 0;
+        Result = DirView(FCurrentSide)->AnyFileSelected(OnFocused, false, true) ? 1 : 0;
       }
       else
       {
@@ -1671,10 +1671,21 @@ void __fastcall TCustomScpExplorerForm::CustomCommand(TStrings * FileList,
   const TCustomCommandType & ACommand, TStrings * ALocalFileList)
 {
 
+  TCustomCommandData Data(Terminal);
+  std::unique_ptr<TCustomCommand> CustomCommandForOptions;
+  if (FLAGCLEAR(ACommand.Params, ccLocal))
+  {
+    CustomCommandForOptions.reset(new TRemoteCustomCommand(Data, Terminal->CurrentDirectory));
+  }
+  else
+  {
+    CustomCommandForOptions.reset(new TLocalCustomCommand(Data, Terminal->CurrentDirectory, DefaultDownloadTargetDirectory()));
+  }
+
   std::unique_ptr<TStrings> CustomCommandOptions(CloneStrings(WinConfiguration->CustomCommandOptions));
   if (ACommand.AnyOptionWithFlag(TCustomCommandType::ofRun))
   {
-    if (!DoCustomCommandOptionsDialog(&ACommand, CustomCommandOptions.get(), TCustomCommandType::ofRun))
+    if (!DoCustomCommandOptionsDialog(&ACommand, CustomCommandOptions.get(), TCustomCommandType::ofRun, CustomCommandForOptions.get()))
     {
       Abort();
     }
@@ -1686,7 +1697,6 @@ void __fastcall TCustomScpExplorerForm::CustomCommand(TStrings * FileList,
   {
     if (EnsureCommandSessionFallback(fcShellAnyCommand))
     {
-      TCustomCommandData Data(Terminal);
       TRemoteCustomCommand RemoteCustomCommand(Data, Terminal->CurrentDirectory);
       TWinInteractiveCustomCommand InteractiveCustomCommand(
         &RemoteCustomCommand, ACommand.Name, ACommand.HomePage);
@@ -1738,7 +1748,6 @@ void __fastcall TCustomScpExplorerForm::CustomCommand(TStrings * FileList,
   }
   else
   {
-    TCustomCommandData Data(Terminal);
     TLocalCustomCommand LocalCustomCommand(Data, Terminal->CurrentDirectory, DefaultDownloadTargetDirectory());
     TWinInteractiveCustomCommand InteractiveCustomCommand(
       &LocalCustomCommand, ACommand.Name, ACommand.HomePage);
@@ -1851,7 +1860,6 @@ void __fastcall TCustomScpExplorerForm::CustomCommand(TStrings * FileList,
                 LocalFile = LocalFileList->Strings[0];
               }
 
-              TCustomCommandData Data(FTerminal);
               TLocalCustomCommand CustomCommand(Data,
                 Terminal->CurrentDirectory, DefaultDownloadTargetDirectory(), L"", LocalFile, FileList);
               UnicodeString ShellCommand = CustomCommand.Complete(Command, true);
@@ -1874,7 +1882,6 @@ void __fastcall TCustomScpExplorerForm::CustomCommand(TStrings * FileList,
                 for (int Index = 0; Index < RemoteFileList->Count; Index++)
                 {
                   UnicodeString FileName = RemoteFileList->Strings[Index];
-                  TCustomCommandData Data(FTerminal);
                   TLocalCustomCommand CustomCommand(Data,
                     Terminal->CurrentDirectory, DefaultDownloadTargetDirectory(), FileName, LocalFile, L"");
                   ExecuteShellAndWait(CustomCommand.Complete(Command, true));
@@ -1886,7 +1893,6 @@ void __fastcall TCustomScpExplorerForm::CustomCommand(TStrings * FileList,
 
                 for (int Index = 0; Index < LocalFileList->Count; Index++)
                 {
-                  TCustomCommandData Data(FTerminal);
                   TLocalCustomCommand CustomCommand(
                     Data, Terminal->CurrentDirectory, DefaultDownloadTargetDirectory(),
                     FileName, LocalFileList->Strings[Index], L"");
@@ -1903,7 +1909,6 @@ void __fastcall TCustomScpExplorerForm::CustomCommand(TStrings * FileList,
                 for (int Index = 0; Index < LocalFileList->Count; Index++)
                 {
                   UnicodeString FileName = RemoteFileList->Strings[Index];
-                  TCustomCommandData Data(FTerminal);
                   TLocalCustomCommand CustomCommand(
                     Data, Terminal->CurrentDirectory, DefaultDownloadTargetDirectory(),
                     FileName, LocalFileList->Strings[Index], L"");
@@ -1915,7 +1920,6 @@ void __fastcall TCustomScpExplorerForm::CustomCommand(TStrings * FileList,
             {
               for (int Index = 0; Index < RemoteFileList->Count; Index++)
               {
-                TCustomCommandData Data(FTerminal);
                 TLocalCustomCommand CustomCommand(Data,
                   Terminal->CurrentDirectory, DefaultDownloadTargetDirectory(),
                   RemoteFileList->Strings[Index], L"", L"");
@@ -2026,7 +2030,6 @@ void __fastcall TCustomScpExplorerForm::CustomCommand(TStrings * FileList,
       if (FileListCommand)
       {
         UnicodeString FileList = MakeFileList(LocalFileList.get());
-        TCustomCommandData Data(FTerminal);
         TLocalCustomCommand CustomCommand(
           Data, Terminal->CurrentDirectory, DefaultDownloadTargetDirectory(),
           L"", L"", FileList);
@@ -2045,7 +2048,6 @@ void __fastcall TCustomScpExplorerForm::CustomCommand(TStrings * FileList,
           for (int Index = 0; Index < LocalFileList->Count; Index++)
           {
             UnicodeString FileName = LocalFileList->Strings[Index];
-            TCustomCommandData Data(FTerminal);
             TLocalCustomCommand CustomCommand(
               Data, Terminal->CurrentDirectory, DefaultDownloadTargetDirectory(),
               FileName, L"", L"");
@@ -5798,7 +5800,7 @@ void __fastcall TCustomScpExplorerForm::NeedSession(bool ReloadSessions)
 {
   try
   {
-    TTerminalManager::Instance()->NewSession(false, L"", ReloadSessions);
+    TTerminalManager::Instance()->NewSession(false, L"", ReloadSessions, this);
   }
   __finally
   {
@@ -6631,6 +6633,8 @@ bool __fastcall TCustomScpExplorerForm::DDGetTarget(
   }
   else
   {
+    ForceQueue = false;
+
     Enabled = false;
     try
     {

+ 24 - 4
source/forms/Login.cpp

@@ -34,14 +34,14 @@ const int WorkspaceImageIndex = 4;
 const int NewSiteImageIndex = 6;
 const int SiteColorMaskImageIndex = 8;
 //---------------------------------------------------------------------------
-bool __fastcall DoLoginDialog(TStoredSessionList *SessionList, TList * DataList)
+bool __fastcall DoLoginDialog(TStoredSessionList *SessionList, TList * DataList, TForm * LinkedForm)
 {
   DebugAssert(DataList != NULL);
   TLoginDialog * LoginDialog = SafeFormCreate<TLoginDialog>();
   bool Result;
   try
   {
-    LoginDialog->Init(SessionList);
+    LoginDialog->Init(SessionList, LinkedForm);
     Result = LoginDialog->Execute(DataList);
   }
   __finally
@@ -71,6 +71,7 @@ __fastcall TLoginDialog::TLoginDialog(TComponent* AOwner)
   FLoading = false;
   FSortEnablePending = false;
   FSiteSearch = ssSiteName;
+  FLinkedForm = NULL;
 
   // we need to make sure that window procedure is set asap
   // (so that CM_SHOWINGCHANGED handling is applied)
@@ -94,9 +95,10 @@ void __fastcall TLoginDialog::InvalidateSessionData()
   FSessionData = NULL;
 }
 //---------------------------------------------------------------------
-void __fastcall TLoginDialog::Init(TStoredSessionList *SessionList)
+void __fastcall TLoginDialog::Init(TStoredSessionList *SessionList, TForm * LinkedForm)
 {
   FStoredSessions = SessionList;
+  FLinkedForm = LinkedForm;
   LoadSessions();
   UnicodeString Dummy;
   RunPageantAction->Visible = FindTool(PageantTool, Dummy);
@@ -1517,6 +1519,20 @@ void __fastcall TLoginDialog::CMDialogKey(TWMKeyDown & Message)
   TForm::Dispatch(&Message);
 }
 //---------------------------------------------------------------------------
+void __fastcall TLoginDialog::WMMoving(TMessage & Message)
+{
+  TForm::Dispatch(&Message);
+
+  if (FLinkedForm != NULL)
+  {
+    RECT & Rect = *reinterpret_cast<RECT*>(Message.LParam);
+    FLinkedForm->SetBounds(
+      FLinkedForm->Left + (Rect.left - Left),
+      FLinkedForm->Top + (Rect.top - Top),
+      FLinkedForm->Width, FLinkedForm->Height);
+  }
+}
+//---------------------------------------------------------------------------
 void __fastcall TLoginDialog::Dispatch(void * Message)
 {
   TMessage * M = reinterpret_cast<TMessage*>(Message);
@@ -1551,6 +1567,10 @@ void __fastcall TLoginDialog::Dispatch(void * Message)
       TForm::Dispatch(Message);
     }
   }
+  else if (M->Msg == WM_MOVING)
+  {
+    WMMoving(*M);
+  }
   else
   {
     TForm::Dispatch(Message);
@@ -1765,7 +1785,7 @@ void __fastcall TLoginDialog::CheckIsSessionFolder(TTreeNode * Node)
 void __fastcall TLoginDialog::SessionTreeEdited(TObject * /*Sender*/,
   TTreeNode * Node, UnicodeString & S)
 {
-  if (Node->Text != S)
+  if ((Node->Text != S) && !S.IsEmpty())
   {
     TSessionData * Session = SelectedSession;
 

+ 3 - 1
source/forms/Login.h

@@ -307,6 +307,7 @@ private:
   bool FSortEnablePending;
   std::unique_ptr<TImageList> FButtonImageList;
   TSiteSearch FSiteSearch;
+  TForm * FLinkedForm;
 
   void __fastcall LoadSession(TSessionData * SessionData);
   void __fastcall LoadContents();
@@ -388,6 +389,7 @@ private:
   void __fastcall ResetNewSiteData();
   TModalResult __fastcall DefaultResult();
   int __fastcall AddLoginButtonImage(bool Enabled);
+  void __fastcall WMMoving(TMessage & Message);
 
 protected:
   void __fastcall Default();
@@ -406,7 +408,7 @@ protected:
 public:
   virtual __fastcall TLoginDialog(TComponent* AOwner);
   __fastcall ~TLoginDialog();
-  void __fastcall Init(TStoredSessionList *SessionList);
+  void __fastcall Init(TStoredSessionList *SessionList, TForm * LinkedForm);
   bool __fastcall Execute(TList * DataList);
 };
 //----------------------------------------------------------------------------

+ 12 - 10
source/forms/NonVisual.dfm

@@ -136,8 +136,8 @@ object NonVisualDataModule: TNonVisualDataModule
       Caption = 'Download in &Background...'
       HelpKeyword = 'task_download'
       Hint = 
-        'Download selected remote file(s) to local directory in ' +
-        'background'
+        'Download selected remote file(s) to local directory in backgroun' +
+        'd'
       ImageIndex = 107
     end
     object RemoteCopyFocusedQueueAction: TAction
@@ -146,8 +146,8 @@ object NonVisualDataModule: TNonVisualDataModule
       Caption = 'Download in &Background...'
       HelpKeyword = 'task_download'
       Hint = 
-        'Download selected remote file(s) to local directory in ' +
-        'background'
+        'Download selected remote file(s) to local directory in backgroun' +
+        'd'
       ImageIndex = 107
     end
     object LocalCopyQueueAction: TAction
@@ -155,9 +155,7 @@ object NonVisualDataModule: TNonVisualDataModule
       Category = 'Local Selected Operation'
       Caption = 'Upload in &Background...'
       HelpKeyword = 'task_upload'
-      Hint = 
-        'Upload selected local file(s) to remote directory in back' +
-        'ground'
+      Hint = 'Upload selected local file(s) to remote directory in background'
       ImageIndex = 108
     end
     object LocalCopyFocusedQueueAction: TAction
@@ -165,9 +163,7 @@ object NonVisualDataModule: TNonVisualDataModule
       Category = 'Local Focused Operation'
       Caption = 'Upload in &Background...'
       HelpKeyword = 'task_upload'
-      Hint = 
-        'Upload selected local file(s) to remote directory in back' +
-        'ground'
+      Hint = 'Upload selected local file(s) to remote directory in background'
       ImageIndex = 108
     end
     object RemoteCopyNonQueueAction: TAction
@@ -2880,6 +2876,9 @@ object NonVisualDataModule: TNonVisualDataModule
         Action = NewLinkAction
       end
     end
+    object TBXItem75: TTBXItem
+      Action = PasteAction2
+    end
     object RemoteDirViewPopupCustomCommandsMenu: TTBXSubmenuItem
       Action = CustomCommandsNonFileAction
     end
@@ -2945,6 +2944,9 @@ object NonVisualDataModule: TNonVisualDataModule
         Action = NewDirAction
       end
     end
+    object TBXItem76: TTBXItem
+      Action = PasteAction2
+    end
   end
   object RemoteAddressPopup: TTBXPopupMenu
     Images = GlyphsModule.ExplorerImages

+ 2 - 0
source/forms/NonVisual.h

@@ -615,6 +615,8 @@ __published:    // IDE-managed Components
   TAction *TipsAction;
   TAction *CustomCommandsNonFileAction;
   TTBXSubmenuItem *RemoteDirViewPopupCustomCommandsMenu;
+  TTBXItem *TBXItem75;
+  TTBXItem *TBXItem76;
   void __fastcall LogActionsUpdate(TBasicAction *Action, bool &Handled);
   void __fastcall LogActionsExecute(TBasicAction *Action, bool &Handled);
   void __fastcall ExplorerActionsUpdate(TBasicAction *Action, bool &Handled);

+ 1 - 1
source/forms/Preferences.cpp

@@ -2660,7 +2660,7 @@ void __fastcall TPreferencesDialog::ConfigureCommand()
   int Index = CustomCommandsView->ItemIndex;
   const TCustomCommandType * Command = GetCommandList(Index)->Commands[GetCommandIndex(Index)];
 
-  DoCustomCommandOptionsDialog(Command, FCustomCommandOptions.get(), TCustomCommandType::ofConfig);
+  DoCustomCommandOptionsDialog(Command, FCustomCommandOptions.get(), TCustomCommandType::ofConfig, NULL);
   UpdateCustomCommandsView();
 }
 //---------------------------------------------------------------------------

+ 1 - 0
source/resource/TextsCore.h

@@ -260,6 +260,7 @@
 #define FILEZILLA_NO_SITES      735
 #define FILEZILLA_SITE_NOT_EXIST 736
 #define SFTP_AS_FTP_ERROR       737
+#define LOG_FATAL_ERROR         738
 
 #define CORE_CONFIRMATION_STRINGS 300
 #define CONFIRM_PROLONG_TIMEOUT3 301

+ 1 - 0
source/resource/TextsCore1.rc

@@ -230,6 +230,7 @@ BEGIN
   FILEZILLA_NO_SITES, "No sites found in FileZilla site manager file (%s)."
   FILEZILLA_SITE_NOT_EXIST, "FileZilla site \"%s\" was not found."
   SFTP_AS_FTP_ERROR, "You cannot connect to an SFTP server using an FTP protocol. Please select the correct protocol."
+  LOG_FATAL_ERROR, "Error occurred during logging. Cannot continue."
 
   CORE_CONFIRMATION_STRINGS, "CORE_CONFIRMATION"
   CONFIRM_PROLONG_TIMEOUT3, "Host is not communicating for %d seconds.\n\nWait for another %0:d seconds?"

+ 27 - 26
source/windows/ConsoleRunner.cpp

@@ -43,7 +43,7 @@ class TConsole
 {
 public:
   virtual __fastcall ~TConsole() {};
-  virtual void __fastcall Print(UnicodeString Str, bool FromBeginning = false) = 0;
+  virtual void __fastcall Print(UnicodeString Str, bool FromBeginning = false, bool Error = false) = 0;
   virtual bool __fastcall Input(UnicodeString & Str, bool Echo, unsigned int Timer) = 0;
   virtual int __fastcall Choice(UnicodeString Options, int Cancel, int Break,
     int Timeouted, bool Timeouting, unsigned int Timer) = 0;
@@ -64,7 +64,7 @@ class TOwnConsole : public TConsole
 public:
   static TOwnConsole * __fastcall Instance();
 
-  virtual void __fastcall Print(UnicodeString Str, bool FromBeginning = false);
+  virtual void __fastcall Print(UnicodeString Str, bool FromBeginning = false, bool Error = false);
   virtual bool __fastcall Input(UnicodeString & Str, bool Echo, unsigned int Timer);
   virtual int __fastcall Choice(UnicodeString Options, int Cancel, int Break,
     int Timeouted, bool Timeouting, unsigned int Timer);
@@ -269,7 +269,7 @@ bool __fastcall TOwnConsole::PendingAbort()
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TOwnConsole::Print(UnicodeString Str, bool FromBeginning)
+void __fastcall TOwnConsole::Print(UnicodeString Str, bool FromBeginning, bool /*Error*/)
 {
   if (FromBeginning)
   {
@@ -535,7 +535,7 @@ public:
   __fastcall TExternalConsole(const UnicodeString Instance, bool NoInteractiveInput);
   virtual __fastcall ~TExternalConsole();
 
-  virtual void __fastcall Print(UnicodeString Str, bool FromBeginning = false);
+  virtual void __fastcall Print(UnicodeString Str, bool FromBeginning = false, bool Error = false);
   virtual bool __fastcall Input(UnicodeString & Str, bool Echo, unsigned int Timer);
   virtual int __fastcall Choice(UnicodeString Options, int Cancel, int Break,
     int Timeouted, bool Timeouting, unsigned int Timer);
@@ -684,7 +684,7 @@ UnicodeString __fastcall TExternalConsole::FinalLogMessage()
   return FORMAT(L"Max roundtrip: %d", (static_cast<int>(FMaxSend)));
 }
 //---------------------------------------------------------------------------
-void __fastcall TExternalConsole::Print(UnicodeString Str, bool FromBeginning)
+void __fastcall TExternalConsole::Print(UnicodeString Str, bool FromBeginning, bool Error)
 {
   // need to do at least one iteration, even when Str is empty (new line)
   do
@@ -699,6 +699,7 @@ void __fastcall TExternalConsole::Print(UnicodeString Str, bool FromBeginning)
       CommStruct->Event = TConsoleCommStruct::PRINT;
       wcscpy(CommStruct->PrintEvent.Message, Piece.c_str());
       CommStruct->PrintEvent.FromBeginning = FromBeginning;
+      CommStruct->PrintEvent.Error = Error;
 
       // In the next iteration we need to append never overwrite.
       // Note that this won't work properly for disk/pipe outputs,
@@ -934,7 +935,7 @@ class TNullConsole : public TConsole
 public:
   __fastcall TNullConsole();
 
-  virtual void __fastcall Print(UnicodeString Str, bool FromBeginning = false);
+  virtual void __fastcall Print(UnicodeString Str, bool FromBeginning = false, bool Error = false);
   virtual bool __fastcall Input(UnicodeString & Str, bool Echo, unsigned int Timer);
   virtual int __fastcall Choice(UnicodeString Options, int Cancel, int Break,
     int Timeouted, bool Timeouting, unsigned int Timer);
@@ -955,7 +956,7 @@ __fastcall TNullConsole::TNullConsole()
 {
 }
 //---------------------------------------------------------------------------
-void __fastcall TNullConsole::Print(UnicodeString /*Str*/, bool /*FromBeginning*/)
+void __fastcall TNullConsole::Print(UnicodeString /*Str*/, bool /*FromBeginning*/, bool /*Error*/)
 {
   // noop
 }
@@ -1047,15 +1048,15 @@ public:
   int __fastcall Run(const UnicodeString Session, TOptions * Options,
     TStrings * ScriptCommands, TStrings * ScriptParameters);
   void __fastcall ShowException(Exception * E);
-  inline void __fastcall PrintMessage(const UnicodeString & Str);
+  inline void __fastcall PrintMessage(const UnicodeString & Str, bool Error = false);
 
 protected:
   bool __fastcall DoInput(UnicodeString & Str, bool Echo, unsigned int Timer,
     bool Interactive);
   void __fastcall Input(const UnicodeString Prompt, UnicodeString & Str,
     bool Echo, bool Interactive);
-  inline void __fastcall Print(const UnicodeString & Str, bool FromBeginning = false);
-  inline void __fastcall PrintLine(const UnicodeString & Str);
+  inline void __fastcall Print(const UnicodeString & Str, bool FromBeginning = false, bool Error = false);
+  inline void __fastcall PrintLine(const UnicodeString & Str, bool Error = false);
   void __fastcall UpdateTitle();
   inline bool __fastcall NotifyAbort();
   inline bool __fastcall Aborted(bool AllowCompleteAbort = true);
@@ -1074,7 +1075,7 @@ private:
   TTimer * Timer;
   bool FExternalTimestampVar;
 
-  void __fastcall ScriptPrint(TScript * Script, const UnicodeString Str);
+  void __fastcall ScriptPrint(TScript * Script, const UnicodeString Str, bool Error);
   void __fastcall ScriptPrintProgress(TScript * Script, bool First, const UnicodeString Str);
   void __fastcall ScriptInput(TScript * Script, const UnicodeString Prompt, UnicodeString & Str);
   void __fastcall ScriptTerminalPromptUser(TTerminal * Terminal,
@@ -1167,36 +1168,36 @@ void __fastcall TConsoleRunner::ScriptInput(TScript * /*Script*/,
   Input(Prompt, Str, true, true);
 }
 //---------------------------------------------------------------------------
-void __fastcall TConsoleRunner::Print(const UnicodeString & Str, bool FromBeginning)
+void __fastcall TConsoleRunner::Print(const UnicodeString & Str, bool FromBeginning, bool Error)
 {
   if (FLastProgressLen > 0)
   {
-    FConsole->Print(L"\n" + Str, FromBeginning);
+    FConsole->Print(L"\n" + Str, FromBeginning, Error);
     FLastProgressLen = 0;
   }
   else
   {
-    FConsole->Print(Str, FromBeginning);
+    FConsole->Print(Str, FromBeginning, Error);
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TConsoleRunner::PrintLine(const UnicodeString & Str)
+void __fastcall TConsoleRunner::PrintLine(const UnicodeString & Str, bool Error)
 {
-  Print(Str + L"\n");
+  Print(Str + L"\n", false, Error);
 }
 //---------------------------------------------------------------------------
-void __fastcall TConsoleRunner::PrintMessage(const UnicodeString & Str)
+void __fastcall TConsoleRunner::PrintMessage(const UnicodeString & Str, bool Error)
 {
   UnicodeString Line = RemoveEmptyLines(Str);
 
   if (FScript != NULL)
   {
     // this also logs the message
-    FScript->PrintLine(Line);
+    FScript->PrintLine(Line, Error);
   }
   else
   {
-    PrintLine(Line);
+    PrintLine(Line, Error);
   }
 }
 //---------------------------------------------------------------------------
@@ -1222,7 +1223,7 @@ bool __fastcall TConsoleRunner::Aborted(bool AllowCompleteAbort)
     Result = FConsole->PendingAbort();
     if (Result)
     {
-      PrintMessage(LoadStr(USER_TERMINATED));
+      PrintMessage(LoadStr(USER_TERMINATED), true);
 
       if (AllowCompleteAbort && NotifyAbort())
       {
@@ -1238,9 +1239,9 @@ bool __fastcall TConsoleRunner::Aborted(bool AllowCompleteAbort)
 }
 //---------------------------------------------------------------------------
 void __fastcall TConsoleRunner::ScriptPrint(TScript * /*Script*/,
-  const UnicodeString Str)
+  const UnicodeString Str, bool Error)
 {
-  Print(Str);
+  Print(Str, false, Error);
 }
 //---------------------------------------------------------------------------
 void __fastcall TConsoleRunner::ScriptPrintProgress(TScript * /*Script*/,
@@ -1737,16 +1738,16 @@ void __fastcall TConsoleRunner::SynchronizeControllerSynchronizeInvalid(
 {
   if (!Directory.IsEmpty())
   {
-    PrintMessage(FMTLOAD(WATCH_ERROR_DIRECTORY, (Directory)));
+    PrintMessage(FMTLOAD(WATCH_ERROR_DIRECTORY, (Directory)), true);
   }
   else
   {
-    PrintMessage(LoadStr(WATCH_ERROR_GENERAL));
+    PrintMessage(LoadStr(WATCH_ERROR_GENERAL), true);
   }
 
   if (!ErrorStr.IsEmpty())
   {
-    PrintMessage(ErrorStr);
+    PrintMessage(ErrorStr, true);
   }
 }
 //---------------------------------------------------------------------------
@@ -1784,7 +1785,7 @@ void __fastcall TConsoleRunner::DoShowException(TTerminal * Terminal, Exception
   if (ExceptionFullMessage(E, Message))
   {
     FCommandError = true;
-    PrintMessage(Message);
+    PrintMessage(Message, true);
   }
 
   TTerminal * LoggingTerminal = Terminal;

+ 1 - 1
source/windows/GUIConfiguration.cpp

@@ -1238,7 +1238,7 @@ TStoredSessionList * __fastcall TGUIConfiguration::SelectPuttySessionsForImport(
   Storage->ForceAnsi = true;
   if (Storage->OpenRootKey(false))
   {
-    ImportSessionList->Load(Storage.get(), false, true);
+    ImportSessionList->Load(Storage.get(), false, true, true);
   }
 
   TSessionData * PuttySessionData =

+ 2 - 2
source/windows/TerminalManager.cpp

@@ -1317,7 +1317,7 @@ void __fastcall TTerminalManager::OpenInPutty()
   }
 }
 //---------------------------------------------------------------------------
-void __fastcall TTerminalManager::NewSession(bool /*FromSite*/, const UnicodeString & SessionUrl, bool ReloadSessions)
+void __fastcall TTerminalManager::NewSession(bool /*FromSite*/, const UnicodeString & SessionUrl, bool ReloadSessions, TForm * LinkedForm)
 {
   if (ReloadSessions)
   {
@@ -1327,7 +1327,7 @@ void __fastcall TTerminalManager::NewSession(bool /*FromSite*/, const UnicodeStr
   UnicodeString DownloadFile; // unused
   std::unique_ptr<TObjectList> DataList(new TObjectList());
 
-  GetLoginData(SessionUrl, NULL, DataList.get(), DownloadFile, true);
+  GetLoginData(SessionUrl, NULL, DataList.get(), DownloadFile, true, LinkedForm);
 
   if (DataList->Count > 0)
   {

+ 1 - 1
source/windows/TerminalManager.h

@@ -53,7 +53,7 @@ public:
   void __fastcall UpdateAppTitle();
   bool __fastcall CanOpenInPutty();
   void __fastcall OpenInPutty();
-  void __fastcall NewSession(bool FromSite, const UnicodeString & SessionUrl, bool ReloadSessions = true);
+  void __fastcall NewSession(bool FromSite, const UnicodeString & SessionUrl, bool ReloadSessions = true, TForm * LinkedForm = NULL);
   void __fastcall Idle();
   UnicodeString __fastcall TerminalTitle(TTerminal * Terminal);
   void __fastcall HandleException(Exception * E);

+ 5 - 0
source/windows/UserInterface.cpp

@@ -1293,6 +1293,11 @@ bool __fastcall CheckXmlLogParam(TProgramParams * Params)
   {
     Configuration->Usage->Inc(L"ScriptXmlLog");
     Configuration->TemporaryActionsLogging(LogFile);
+
+    if (Params->FindSwitch(L"XmlLogRequired"))
+    {
+      Configuration->LogActionsRequired = true;
+    }
   }
   return Result;
 }

+ 24 - 0
source/windows/WinConfiguration.cpp

@@ -3142,6 +3142,7 @@ UnicodeString __fastcall TCustomCommandType::GetCommandWithExpandedOptions(TStri
         OptionValue = Option.Default;
       }
       UnicodeString OptionCommand = GetOptionCommand(Option, OptionValue);
+      OptionCommand = TCustomCommand::Escape(OptionCommand);
       Result = ReplaceText(Result, FORMAT(L"%%%s%%", (Option.Id)), OptionCommand);
     }
   }
@@ -3183,6 +3184,29 @@ bool __fastcall TCustomCommandType::TOption::GetIsControl() const
   return (Id != L"-");
 }
 //---------------------------------------------------------------------------
+bool TCustomCommandType::TOption::HasPatterns(TCustomCommand * CustomCommandForOptions) const
+{
+  bool CanHavePatterns;
+  switch (Kind)
+  {
+    case okTextBox:
+    case okFile:
+      CanHavePatterns = true;
+      break;
+
+    default:
+      CanHavePatterns = false;
+      break;
+  }
+
+  bool Result =
+    CanHavePatterns &&
+    FLAGSET(Flags, TCustomCommandType::ofRun) &&
+    FLAGCLEAR(Flags, TCustomCommandType::ofConfig) &&
+    CustomCommandForOptions->HasAnyPatterns(Default);
+  return Result;
+}
+//---------------------------------------------------------------------------
 bool TCustomCommandType::TOption::operator==(const TCustomCommandType::TOption & Other) const
 {
   // needed by vector<> but probably never used

+ 1 - 0
source/windows/WinConfiguration.h

@@ -734,6 +734,7 @@ public:
 
     bool operator==(const TOption & Other) const;
     __property bool IsControl = { read = GetIsControl };
+    bool HasPatterns(TCustomCommand * CustomCommandForOptions) const;
 
   private:
     bool __fastcall GetIsControl() const;

+ 4 - 3
source/windows/WinInterface.h

@@ -132,7 +132,8 @@ class TShortCuts;
 bool __fastcall DoShortCutDialog(TShortCut & ShortCut,
   const TShortCuts & ShortCuts, UnicodeString HelpKeyword);
 bool __fastcall DoCustomCommandOptionsDialog(
-  const TCustomCommandType * Command, TStrings * CustomCommandOptions, unsigned int Flags);
+  const TCustomCommandType * Command, TStrings * CustomCommandOptions, unsigned int Flags,
+  TCustomCommand * CustomCommandForOptions);
 
 // windows\UserInterface.cpp
 bool __fastcall DoMasterPasswordDialog();
@@ -141,7 +142,7 @@ bool __fastcall DoChangeMasterPasswordDialog(UnicodeString & NewPassword);
 // windows\WinMain.cpp
 int __fastcall Execute();
 void __fastcall GetLoginData(UnicodeString SessionName, TOptions * Options,
-  TObjectList * DataList, UnicodeString & DownloadFile, bool NeedSession);
+  TObjectList * DataList, UnicodeString & DownloadFile, bool NeedSession, TForm * LinkedForm);
 
 // forms\InputDlg.cpp
 struct TInputDialogData
@@ -210,7 +211,7 @@ bool __fastcall DoImportSessionsDialog(TList * Imported);
 enum TLicense { lcNoLicense = -1, lcWinScp, lcExpat };
 void __fastcall DoLicenseDialog(TLicense License);
 
-bool __fastcall DoLoginDialog(TStoredSessionList * SessionList, TList * DataList);
+bool __fastcall DoLoginDialog(TStoredSessionList * SessionList, TList * DataList, TForm * LinkedForm);
 
   // forms\SiteAdvanced.cpp
 bool __fastcall DoSiteAdvancedDialog(TSessionData * SessionData);

+ 3 - 3
source/windows/WinMain.cpp

@@ -24,7 +24,7 @@
 #pragma package(smart_init)
 //---------------------------------------------------------------------------
 void __fastcall GetLoginData(UnicodeString SessionName, TOptions * Options,
-  TObjectList * DataList, UnicodeString & DownloadFile, bool NeedSession)
+  TObjectList * DataList, UnicodeString & DownloadFile, bool NeedSession, TForm * LinkedForm)
 {
   bool DefaultsOnly = false;
 
@@ -85,7 +85,7 @@ void __fastcall GetLoginData(UnicodeString SessionName, TOptions * Options,
     // - the specified session does not contain enough information to login [= not even hostname]
 
     DebugAssert(DataList->Count <= 1);
-    if (!DoLoginDialog(StoredSessions, DataList))
+    if (!DoLoginDialog(StoredSessions, DataList, LinkedForm))
     {
       Abort();
     }
@@ -979,7 +979,7 @@ int __fastcall Execute()
         TObjectList * DataList = new TObjectList();
         try
         {
-          GetLoginData(AutoStartSession, Params, DataList, DownloadFile, NeedSession);
+          GetLoginData(AutoStartSession, Params, DataList, DownloadFile, NeedSession, NULL);
           // GetLoginData now Aborts when session is needed and none is selected
           if (DebugAlwaysTrue(!NeedSession || (DataList->Count > 0)))
           {