Browse Source

Session URL can contain any raw session settings using connection parameter. New custom command pattern !E can generate such URL for use in WinSCP extensions.

Source commit: 7c9de42181088d8aaa9a96abaa420534f9e71a27
Martin Prikryl 7 years ago
parent
commit
3267334917

+ 5 - 0
dotnet/SessionOptions.cs

@@ -210,10 +210,15 @@ namespace WinSCP
                     string parameter = CutToChar(ref parameters, ';');
                     string parameterName = CutToChar(ref parameter, '=');
                     parameter = UriUnescape(parameter);
+                    const string RawSettingsPrefix = "x-";
                     if (parameterName.Equals("fingerprint", StringComparison.OrdinalIgnoreCase))
                     {
                         SshHostKeyFingerprint = parameter;
                     }
+                    else if (parameterName.StartsWith(RawSettingsPrefix, StringComparison.OrdinalIgnoreCase))
+                    {
+                        AddRawSettings(UriUnescape(parameterName.Substring(RawSettingsPrefix.Length)), parameter);
+                    }
                     else
                     {
                         throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, "Unsupported connection parameter {0}", parameterName), "url");

+ 3 - 0
dotnet/properties/AssemblyInfo.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Reflection;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 
 // General Information about an assembly is controlled through the following
@@ -25,6 +26,8 @@ using System.Runtime.InteropServices;
 
 [assembly: CLSCompliant(true)]
 
+[assembly: InternalsVisibleTo("Tests")]
+
 namespace WinSCP
 {
     internal static class AssemblyConstants

+ 8 - 0
source/core/FileMasks.cpp

@@ -1185,6 +1185,7 @@ int __fastcall TFileCustomCommand::PatternLen(const UnicodeString & Command, int
   switch (PatternCmd)
   {
     case L's':
+    case L'e':
     case L'@':
     case L'u':
     case L'p':
@@ -1208,6 +1209,13 @@ bool __fastcall TFileCustomCommand::PatternReplacement(
   // keep consistent with TSessionLog::OpenLogFile
 
   if (SameText(Pattern, L"!s"))
+  {
+    if (FData.SessionData != NULL)
+    {
+      Replacement = FData.SessionData->GenerateSessionUrl(sufSession);
+    }
+  }
+  else if (SameText(Pattern, L"!e"))
   {
     if (FData.SessionData != NULL)
     {

+ 56 - 15
source/core/SessionData.cpp

@@ -64,6 +64,7 @@ const wchar_t UrlParamSeparator = L';';
 const wchar_t UrlParamValueSeparator = L'=';
 const UnicodeString UrlHostKeyParamName(L"fingerprint");
 const UnicodeString UrlSaveParamName(L"save");
+const UnicodeString UrlRawSettingsParamNamePrefix(L"x-");
 const UnicodeString PassphraseOption(L"passphrase");
 const UnicodeString RawSettingsOption(L"rawsettings");
 const UnicodeString S3HostName(S3LibDefaultHostName());
@@ -1918,6 +1919,8 @@ bool __fastcall TSessionData::ParseUrl(UnicodeString Url, TOptions * Options,
       UnicodeString ConnectionParams = UserInfo;
       UserInfo = UserInfoWithoutConnectionParams;
 
+      std::unique_ptr<TStrings> RawSettings(new TStringList());
+
       while (!ConnectionParams.IsEmpty())
       {
         UnicodeString ConnectionParam = CutToChar(ConnectionParams, UrlParamSeparator, false);
@@ -1927,6 +1930,17 @@ bool __fastcall TSessionData::ParseUrl(UnicodeString Url, TOptions * Options,
           HostKey = DecodeUrlChars(ConnectionParam);
           FOverrideCachedHostKey = false;
         }
+        else if (StartsText(UrlRawSettingsParamNamePrefix, ConnectionParamName))
+        {
+          UnicodeString Name = RightStr(ConnectionParamName, ConnectionParamName.Length() - UrlRawSettingsParamNamePrefix.Length());
+          Name = DecodeUrlChars(Name);
+          RawSettings->Values[Name] = DecodeUrlChars(ConnectionParam);
+        }
+      }
+
+      if (RawSettings->Count > 0) // optimization
+      {
+        ApplyRawSettings(RawSettings.get());
       }
 
       bool HasPassword = (UserInfo.Pos(L':') > 0);
@@ -2072,22 +2086,10 @@ bool __fastcall TSessionData::ParseUrl(UnicodeString Url, TOptions * Options,
     }
     if (Options->FindSwitch(RawSettingsOption))
     {
-      TStrings * RawSettings = NULL;
-      TOptionsStorage * OptionsStorage = NULL;
-      try
+      std::unique_ptr<TStrings> RawSettings(new TStringList());
+      if (Options->FindSwitch(RawSettingsOption, RawSettings.get()))
       {
-        RawSettings = new TStringList();
-
-        if (Options->FindSwitch(RawSettingsOption, RawSettings))
-        {
-          OptionsStorage = new TOptionsStorage(RawSettings, false);
-          ApplyRawSettings(OptionsStorage);
-        }
-      }
-      __finally
-      {
-        delete RawSettings;
-        delete OptionsStorage;
+        ApplyRawSettings(RawSettings.get());
       }
     }
   }
@@ -2095,6 +2097,12 @@ bool __fastcall TSessionData::ParseUrl(UnicodeString Url, TOptions * Options,
   return true;
 }
 //---------------------------------------------------------------------
+void __fastcall TSessionData::ApplyRawSettings(TStrings * RawSettings)
+{
+  std::unique_ptr<TOptionsStorage> OptionsStorage(new TOptionsStorage(RawSettings, false));
+  ApplyRawSettings(OptionsStorage.get());
+}
+//---------------------------------------------------------------------
 void __fastcall TSessionData::ApplyRawSettings(THierarchicalStorage * Storage)
 {
   bool Dummy;
@@ -2900,6 +2908,27 @@ UnicodeString __fastcall EscapeIPv6Literal(const UnicodeString & IP)
   return L"[" + IP + L"]";
 }
 //---------------------------------------------------------------------
+TStrings * __fastcall TSessionData::GetRawSettingsForUrl()
+{
+  std::unique_ptr<TSessionData> FactoryDefaults(new TSessionData(L""));
+  std::unique_ptr<TSessionData> SessionData(Clone());
+  SessionData->FSProtocol = FactoryDefaults->FSProtocol;
+  SessionData->HostName = FactoryDefaults->HostName;
+  SessionData->PortNumber = FactoryDefaults->PortNumber;
+  SessionData->UserName = FactoryDefaults->UserName;
+  SessionData->Password = FactoryDefaults->Password;
+  SessionData->Ftps = FactoryDefaults->Ftps;
+  SessionData->HostKey = FactoryDefaults->HostKey;
+  SessionData->CopyNonCoreData(FactoryDefaults.get());
+  return SessionData->SaveToOptions(FactoryDefaults.get());
+}
+//---------------------------------------------------------------------
+bool __fastcall TSessionData::HasRawSettingsForUrl()
+{
+  std::unique_ptr<TStrings> RawSettings(GetRawSettingsForUrl());
+  return (RawSettings->Count > 0);
+}
+//---------------------------------------------------------------------
 UnicodeString __fastcall TSessionData::GenerateSessionUrl(unsigned int Flags)
 {
   UnicodeString Url;
@@ -2931,6 +2960,18 @@ UnicodeString __fastcall TSessionData::GenerateSessionUrl(unsigned int Flags)
         UnicodeString(UrlParamValueSeparator) + S;
     }
 
+    if (FLAGSET(Flags, sufRawSettings))
+    {
+      std::unique_ptr<TStrings> RawSettings(GetRawSettingsForUrl());
+      for (int Index = 0; Index < RawSettings->Count; Index++)
+      {
+        Url +=
+          UnicodeString(UrlParamSeparator) +
+          UrlRawSettingsParamNamePrefix + EncodeUrlString(LowerCase(RawSettings->Names[Index])) +
+          UnicodeString(UrlParamValueSeparator) + EncodeUrlString(RawSettings->ValueFromIndex[Index]);
+      }
+    }
+
     Url += L"@";
   }
 

+ 7 - 1
source/core/SessionData.h

@@ -46,7 +46,9 @@ enum TSessionUrlFlags
   sufUserName = 0x02,
   sufPassword = 0x04,
   sufHostKey = 0x08,
-  sufComplete = sufUserName | sufPassword | sufHostKey,
+  sufRawSettings = 0x10,
+  sufSession = sufUserName | sufPassword | sufHostKey,
+  sufComplete = sufSession | sufRawSettings,
   sufOpen = sufUserName | sufPassword
 };
 //---------------------------------------------------------------------------
@@ -431,6 +433,8 @@ private:
     UnicodeString & Result, TAssemblyLanguage Language,
     const UnicodeString & Name, bool Value);
   TStrings * __fastcall SaveToOptions(const TSessionData * Default);
+  void __fastcall ApplyRawSettings(TStrings * RawSettings);
+  TStrings * __fastcall GetRawSettingsForUrl();
   template<class AlgoT>
   void __fastcall SetAlgoList(AlgoT * List, const AlgoT * DefaultList, const UnicodeString * Names,
     int Count, AlgoT WarnAlgo, UnicodeString value);
@@ -473,6 +477,8 @@ public:
   bool __fastcall IsSameSite(const TSessionData * Default);
   bool __fastcall IsInFolderOrWorkspace(UnicodeString Name);
   UnicodeString __fastcall GenerateSessionUrl(unsigned int Flags);
+  bool __fastcall TSessionData::HasRawSettingsForUrl();
+
   UnicodeString __fastcall GenerateOpenCommandArgs(bool Rtf);
   void __fastcall GenerateAssemblyCode(TAssemblyLanguage Language, UnicodeString & Head, UnicodeString & Tail, int & Indent);
   void __fastcall LookupLastFingerprint();

+ 1 - 1
source/forms/CustomCommand.cpp

@@ -47,7 +47,7 @@ __fastcall TCustomCommandDialog::TCustomCommandDialog(TComponent* Owner,
   FCustomCommandList = CustomCommandList;
   FMode = Mode;
   FOnValidate = OnValidate;
-  HintLabel(HintText, LoadStr(CUSTOM_COMMAND_PATTERNS_HINT5));
+  HintLabel(HintText, LoadStr(CUSTOM_COMMAND_PATTERNS_HINT6));
 
   int CaptionRes;
   switch (FMode)

+ 3 - 1
source/forms/GenerateUrl.cpp

@@ -181,7 +181,8 @@ UnicodeString __fastcall TGenerateUrlDialog::GenerateUrl(UnicodeString Path)
       FLAGMASK(WinSCPSpecificCheck->Checked, sufSpecific) |
       FLAGMASK(UserNameCheck->Enabled && UserNameCheck->Checked, sufUserName) |
       FLAGMASK(PasswordCheck->Enabled && PasswordCheck->Checked, sufPassword) |
-      FLAGMASK(HostKeyCheck->Enabled && HostKeyCheck->Checked, sufHostKey));
+      FLAGMASK(HostKeyCheck->Enabled && HostKeyCheck->Checked, sufHostKey) |
+      FLAGMASK(RawSettingsCheck->Enabled && RawSettingsCheck->Checked, sufRawSettings));
 
   if ((RemoteDirectoryCheck->Enabled && RemoteDirectoryCheck->Checked) ||
       IsFileUrl())
@@ -663,6 +664,7 @@ void __fastcall TGenerateUrlDialog::UpdateControls()
     EnableControl(HostKeyCheck, UserNameIncluded && !FData->HostKey.IsEmpty());
     EnableControl(RemoteDirectoryCheck, !FData->RemoteDirectory.IsEmpty() && !IsFileUrl());
     EnableControl(SaveExtensionCheck, !IsFileUrl());
+    EnableControl(RawSettingsCheck, UserNameIncluded && FData->HasRawSettingsForUrl());
 
     UnicodeString Result;
 

+ 29 - 19
source/forms/GenerateUrl.dfm

@@ -35,7 +35,7 @@ object GenerateUrlDialog: TGenerateUrlDialog
         Tag = 1
         Left = 11
         Top = 8
-        Width = 216
+        Width = 144
         Height = 17
         Caption = '&User name'
         TabOrder = 0
@@ -43,29 +43,29 @@ object GenerateUrlDialog: TGenerateUrlDialog
       end
       object HostKeyCheck: TCheckBox
         Tag = 4
-        Left = 11
-        Top = 31
-        Width = 216
+        Left = 161
+        Top = 8
+        Width = 144
         Height = 17
         Caption = 'SSH &host Key'
-        TabOrder = 1
+        TabOrder = 3
         OnClick = ControlChange
       end
       object WinSCPSpecificCheck: TCheckBox
         Tag = 16
-        Left = 11
-        Top = 54
-        Width = 216
+        Left = 161
+        Top = 31
+        Width = 144
         Height = 17
         Caption = '&WinSCP-specific'
-        TabOrder = 2
+        TabOrder = 4
         OnClick = ControlChange
       end
       object SaveExtensionCheck: TCheckBox
         Tag = 32
-        Left = 235
+        Left = 161
         Top = 54
-        Width = 216
+        Width = 144
         Height = 17
         Caption = '&Save extension'
         TabOrder = 5
@@ -73,23 +73,33 @@ object GenerateUrlDialog: TGenerateUrlDialog
       end
       object RemoteDirectoryCheck: TCheckBox
         Tag = 8
-        Left = 235
-        Top = 31
-        Width = 216
+        Left = 11
+        Top = 54
+        Width = 144
         Height = 17
         Caption = 'Initial &directory'
-        TabOrder = 4
+        TabOrder = 2
         OnClick = ControlChange
       end
       object PasswordCheck: TCheckBox
         Tag = 2
-        Left = 235
-        Top = 8
-        Width = 216
+        Left = 11
+        Top = 31
+        Width = 144
         Height = 17
         HelpType = htKeyword
         Caption = '&Password'
-        TabOrder = 3
+        TabOrder = 1
+        OnClick = ControlChange
+      end
+      object RawSettingsCheck: TCheckBox
+        Tag = 64
+        Left = 311
+        Top = 8
+        Width = 144
+        Height = 17
+        Caption = '&Advanced settings'
+        TabOrder = 6
         OnClick = ControlChange
       end
     end

+ 1 - 0
source/forms/GenerateUrl.h

@@ -39,6 +39,7 @@ __published:
   TComboBox *AssemblyLanguageCombo;
   TLabel *ScriptDescriptionLabel;
   TLabel *AssemblyDescriptionLabel;
+  TCheckBox *RawSettingsCheck;
   void __fastcall ControlChange(TObject *Sender);
   void __fastcall ClipboardButtonClick(TObject *Sender);
   void __fastcall HelpButtonClick(TObject *Sender);

+ 1 - 1
source/resource/TextsWin.h

@@ -200,7 +200,7 @@
 #define EXTERNAL_EDITOR_HINT    1488
 #define MASK_HINT2              1489
 #define PATH_MASK_HINT2         1490
-#define CUSTOM_COMMAND_PATTERNS_HINT5 1491
+#define CUSTOM_COMMAND_PATTERNS_HINT6 1491
 #define CLOSED_ON_QUEUE_EMPTY   1492
 #define DIRECTORY_READING_CANCELLED 1493
 #define SYNC_DIR_BROWSE_TOGGLE  1494

+ 1 - 1
source/resource/TextsWin1.rc

@@ -203,7 +203,7 @@ BEGIN
         EXTERNAL_EDITOR_HINT, "Edit (external)|Edit selected file(s) using external editor '%s'"
         MASK_HINT2, "* matches any number of characters.\n? matches exactly one character.\n[abc] matches one character from the set.\n[a-z] matches one character from the range.\nExample: *.html; photo??.png"
         PATH_MASK_HINT2, "Mask can be extended with path mask.\nExample: */public_html/*.html"
-        CUSTOM_COMMAND_PATTERNS_HINT5, "Patterns:\n!! expands to exclamation mark\n! expands to file name\n!& expands to list of selected files (quoted, space-delimited)\n!/ expands to current remote path\n!S expands to current session URL\n!@ expands to current session hostname\n!U expands to current session username\n!P expands to current session password\n!# expands to current session port number\n!N expands to current session name\n!?prompt[\\]?default! expands to user-entered value with given prompt and default (optional \\ avoids escaping)\n!`command` expands to output of local command\n \nLocal command patterns:\n!^! expands to file name from local panel\n \nExample:\ngrep \"!?Pattern:?!\" !&"
+        CUSTOM_COMMAND_PATTERNS_HINT6, "Patterns:\n!! expands to exclamation mark\n! expands to file name\n!& expands to list of selected files (quoted, space-delimited)\n!/ expands to current remote path\n!S expands to current session URL\n!E expands to serialized connection data of current session\n!@ expands to current session hostname\n!U expands to current session username\n!P expands to current session password\n!# expands to current session port number\n!N expands to current session name\n!?prompt[\\]?default! expands to user-entered value with given prompt and default (optional \\ avoids escaping)\n!`command` expands to output of local command\n \nLocal command patterns:\n!^! expands to file name from local panel\n \nExample:\ngrep \"!?Pattern:?!\" !&"
         CLOSED_ON_QUEUE_EMPTY, "All background transfers were finished. Connection was closed."
         DIRECTORY_READING_CANCELLED, "Reading of remote directory was cancelled."
         SYNC_DIR_BROWSE_TOGGLE, "Synchronized browsing was turned %s.|on|off"