mirror of
https://github.com/winscp/winscp.git
synced 2026-10-24 02:28:55 +03:00
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
This commit is contained in:
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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"@";
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user