1
0

GenerateUrl.cpp 34 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
  1. //---------------------------------------------------------------------------
  2. #include <vcl.h>
  3. #pragma hdrstop
  4. #include "VCLCommon.h"
  5. #include "GenerateUrl.h"
  6. #include "CoreMain.h"
  7. #include "WinConfiguration.h"
  8. #include <StrUtils.hpp>
  9. #include <Tools.h>
  10. #include <PuttyTools.h>
  11. #include <TextsWin.h>
  12. #include <ProgParams.h>
  13. #include <GUITools.h>
  14. //---------------------------------------------------------------------------
  15. #pragma package(smart_init)
  16. #pragma resource "*.dfm"
  17. //---------------------------------------------------------------------------
  18. const UnicodeString AllFilesMask(L"*");
  19. const UnicodeString NoOpOperationMask(L"*");
  20. //---------------------------------------------------------------------------
  21. static UnicodeString ExcludeTrailingBackslashUnlessRoot(const UnicodeString & Path)
  22. {
  23. UnicodeString Result = ExcludeTrailingBackslash(Path);
  24. if (SameText(Result, ExtractFileDrive(Result)))
  25. {
  26. Result = IncludeTrailingBackslash(Path);
  27. }
  28. return Result;
  29. }
  30. //---------------------------------------------------------------------------
  31. void __fastcall DoGenerateUrlDialog(TSessionData * Data, TStrings * Paths)
  32. {
  33. std::unique_ptr<TGenerateUrlDialog> Dialog(
  34. new TGenerateUrlDialog(GetFormOwner(), Data, fsList, Paths, false, false, false, 0, UnicodeString(), TCopyParamType()));
  35. Dialog->Execute();
  36. }
  37. //---------------------------------------------------------------------------
  38. void __fastcall DoGenerateTransferCodeDialog(
  39. bool ToRemote, bool Move, int CopyParamAttrs, TSessionData * Data, TFilesSelected FilesSelected, TStrings * FileList, const UnicodeString & Path,
  40. const TCopyParamType & CopyParam)
  41. {
  42. std::unique_ptr<TGenerateUrlDialog> Dialog(
  43. new TGenerateUrlDialog(GetFormOwner(), Data, FilesSelected, FileList, true, ToRemote, Move, CopyParamAttrs, Path, CopyParam));
  44. Dialog->Execute();
  45. }
  46. //---------------------------------------------------------------------------
  47. class TRichEditWithLinks : public TRichEdit
  48. {
  49. public:
  50. virtual __fastcall TRichEditWithLinks(TComponent * AOwner);
  51. protected:
  52. virtual void __fastcall CreateWnd();
  53. void __fastcall Dispatch(void * Message);
  54. };
  55. //---------------------------------------------------------------------------
  56. __fastcall TRichEditWithLinks::TRichEditWithLinks(TComponent * AOwner) :
  57. TRichEdit(AOwner)
  58. {
  59. }
  60. //---------------------------------------------------------------------------
  61. void __fastcall TRichEditWithLinks::CreateWnd()
  62. {
  63. TRichEdit::CreateWnd();
  64. int Mask = SendMessage(Handle, EM_GETEVENTMASK, 0, 0);
  65. SendMessage(Handle, EM_SETEVENTMASK, 0, Mask | ENM_LINK);
  66. SendMessage(Handle, EM_SETEDITSTYLEEX, 0, SES_EX_HANDLEFRIENDLYURL);
  67. }
  68. //---------------------------------------------------------------------------
  69. void __fastcall TRichEditWithLinks::Dispatch(void * AMessage)
  70. {
  71. TMessage & Message = *reinterpret_cast<TMessage *>(AMessage);
  72. if (Message.Msg == CN_NOTIFY)
  73. {
  74. TWMNotify & WMNotify = *reinterpret_cast<TWMNotify *>(AMessage);
  75. if (WMNotify.NMHdr->code == EN_LINK)
  76. {
  77. TENLink & ENLink = *reinterpret_cast<TENLink *>(Message.LParam);
  78. if (ENLink.msg == WM_LBUTTONDOWN)
  79. {
  80. UnicodeString AText = Text;
  81. // The cpMin and cpMax refer to indexes in a script with a single-byte EOL,
  82. // while the Text (GetWindowText) uses two-byte EOL
  83. AText = ReplaceStr(AText, L"\r\n", L"\n");
  84. if (DebugAlwaysTrue(ENLink.chrg.cpMax < AText.Length()))
  85. {
  86. UnicodeString Url = AText.SubString(ENLink.chrg.cpMin + 1, ENLink.chrg.cpMax - ENLink.chrg.cpMin);
  87. if (IsHttpOrHttpsUrl(Url))
  88. {
  89. OpenBrowser(Url);
  90. }
  91. else
  92. {
  93. ShowHelp(Url);
  94. }
  95. }
  96. }
  97. }
  98. TRichEdit::Dispatch(AMessage);
  99. }
  100. else
  101. {
  102. TRichEdit::Dispatch(AMessage);
  103. }
  104. }
  105. //---------------------------------------------------------------------------
  106. //---------------------------------------------------------------------------
  107. __fastcall TGenerateUrlDialog::TGenerateUrlDialog(
  108. TComponent * Owner, TSessionData * Data, TFilesSelected FilesSelected, TStrings * Paths,
  109. bool Transfer, bool ToRemote, bool Move, int CopyParamAttrs, const UnicodeString & Path, const TCopyParamType & CopyParam)
  110. : TForm(Owner)
  111. {
  112. UseSystemSettings(this);
  113. FData = Data;
  114. if (Paths != NULL)
  115. {
  116. FPaths.reset(new TStringList());
  117. FPaths->AddStrings(Paths);
  118. }
  119. FTransfer = Transfer;
  120. FToRemote = ToRemote;
  121. FMove = Move;
  122. FCopyParamAttrs = CopyParamAttrs;
  123. FCopyParam = CopyParam;
  124. FFilesSelected = FilesSelected;
  125. FPathsSample = false;
  126. FUrlCounted = false;
  127. FScriptCounted = false;
  128. FAssemblyCounted = false;
  129. if (FTransfer)
  130. {
  131. DebugAssert(FPaths.get() != NULL);
  132. const int MaxSample = 3;
  133. if ((FFilesSelected == fsList) && (FPaths->Count > MaxSample))
  134. {
  135. FPathsSample = true;
  136. while (FPaths->Count > MaxSample)
  137. {
  138. FPaths->Delete(FPaths->Count - 1);
  139. }
  140. }
  141. if (FToRemote)
  142. {
  143. UnicodeString FirstPath = Paths->Strings[0];
  144. FSourcePath = FToRemote ? ExcludeTrailingBackslashUnlessRoot(ExtractFilePath(FirstPath)) : UnixExtractFilePath(FirstPath);
  145. for (int Index = 0; Index < FPaths->Count; Index++)
  146. {
  147. FPaths->Strings[Index] = ExtractFileName(FPaths->Strings[Index]);
  148. }
  149. }
  150. else
  151. {
  152. FSourcePath = Data->RemoteDirectory;
  153. // should be noop as we get only file names for remote files
  154. for (int Index = 0; Index < FPaths->Count; Index++)
  155. {
  156. FPaths->Strings[Index] = UnixExtractFileName(FPaths->Strings[Index]);
  157. }
  158. }
  159. }
  160. FPath = Path;
  161. FChanging = false;
  162. FResultMemoWithLinks = new TRichEditWithLinks(this);
  163. FResultMemoWithLinks->Parent = ResultMemo->Parent;
  164. FResultMemoWithLinks->SetBounds(ResultMemo->Left, ResultMemo->Top, ResultMemo->Width, ResultMemo->Height);
  165. FResultMemoWithLinks->Anchors = ResultMemo->Anchors;
  166. FResultMemoWithLinks->BevelInner = ResultMemo->BevelInner;
  167. FResultMemoWithLinks->BevelOuter = ResultMemo->BevelOuter;
  168. FResultMemoWithLinks->BorderStyle = ResultMemo->BorderStyle;
  169. FResultMemoWithLinks->PopupMenu = ResultMemo->PopupMenu;
  170. FResultMemoWithLinks->TabOrder = ResultMemo->TabOrder;
  171. FResultMemoWithLinks->PlainText = false;
  172. FResultMemoWithLinks->WantReturns = false; // affects Esc too, what we want
  173. ResultMemo->Visible = false;
  174. ReadOnlyControl(FResultMemoWithLinks);
  175. }
  176. //---------------------------------------------------------------------------
  177. bool __fastcall TGenerateUrlDialog::IsFileUrl()
  178. {
  179. return (FPaths.get() != NULL) && !FTransfer;
  180. }
  181. //---------------------------------------------------------------------------
  182. UnicodeString __fastcall TGenerateUrlDialog::GenerateUrl(UnicodeString Path)
  183. {
  184. UnicodeString Url =
  185. FData->GenerateSessionUrl(
  186. FLAGMASK(WinSCPSpecificCheck->Checked, sufSpecific) |
  187. FLAGMASK(UserNameCheck->Enabled && UserNameCheck->Checked, sufUserName) |
  188. FLAGMASK(PasswordCheck->Enabled && PasswordCheck->Checked, sufPassword) |
  189. FLAGMASK(HostKeyCheck->Enabled && HostKeyCheck->Checked, sufHostKey) |
  190. FLAGMASK(RawSettingsCheck->Enabled && RawSettingsCheck->Checked, sufRawSettings) |
  191. FLAGMASK(CustomWinConfiguration->HttpForWebDAV, sufHttpForWebDAV));
  192. if ((RemoteDirectoryCheck->Enabled && RemoteDirectoryCheck->Checked) ||
  193. IsFileUrl())
  194. {
  195. if (StartsStr(L"/", Path));
  196. {
  197. Path.Delete(1, 1);
  198. }
  199. Url += EncodeUrlPath(Path);
  200. }
  201. if (SaveExtensionCheck->Enabled && SaveExtensionCheck->Checked)
  202. {
  203. Url += UnicodeString(UrlParamSeparator) + UrlSaveParamName;
  204. }
  205. return Url;
  206. }
  207. //---------------------------------------------------------------------------
  208. static UnicodeString __fastcall RtfColorEntry(int Color)
  209. {
  210. return FORMAT(L"\\red%d\\green%d\\blue%d;", ((Color & 0xFF0000) >> 16, (Color & 0x00FF00) >> 8, (Color & 0x0000FF) >> 0));
  211. }
  212. //---------------------------------------------------------------------
  213. static UnicodeString __fastcall RtfScriptComment(const UnicodeString & Text)
  214. {
  215. return RtfColorItalicText(7, Text);
  216. }
  217. //---------------------------------------------------------------------
  218. static UnicodeString __fastcall RtfScriptPlaceholder(const UnicodeString & Text)
  219. {
  220. return RtfColorText(7, Text);
  221. }
  222. //---------------------------------------------------------------------
  223. static UnicodeString __fastcall RtfScriptCommand(const UnicodeString & Command)
  224. {
  225. return RtfLink(ScriptCommandLink(Command), RtfKeyword(Command));
  226. }
  227. //---------------------------------------------------------------------
  228. UnicodeString __fastcall RtfCommandlineSwitch(const UnicodeString & Switch, const UnicodeString & Anchor)
  229. {
  230. return RtfLink(L"commandline#" + Anchor, RtfParameter(TProgramParams::FormatSwitch(Switch.LowerCase())));
  231. }
  232. //---------------------------------------------------------------------------
  233. static UnicodeString __fastcall QuoteStringParam(UnicodeString S)
  234. {
  235. return AddQuotes(RtfEscapeParam(S, false));
  236. }
  237. //---------------------------------------------------------------------------
  238. // Keep in sync with .NET Session.EscapeFileMask
  239. static UnicodeString __fastcall EscapeFileMask(UnicodeString S)
  240. {
  241. return
  242. ReplaceStr(ReplaceStr(ReplaceStr(ReplaceStr(ReplaceStr(
  243. S, L"[", L"[[]"), L"*", L"[*]"), L"?", L"[?]"), L">", L">>"), L"<", L"<<");
  244. }
  245. //---------------------------------------------------------------------------
  246. UnicodeString __fastcall TGenerateUrlDialog::GenerateUrl()
  247. {
  248. UnicodeString Result;
  249. if (!IsFileUrl())
  250. {
  251. UnicodeString Path = FData->RemoteDirectory;
  252. if (!Path.IsEmpty() && !EndsStr(L"/", Path))
  253. {
  254. Path += L"/";
  255. }
  256. Result = RtfText(GenerateUrl(Path));
  257. }
  258. else
  259. {
  260. for (int Index = 0; Index < FPaths->Count; Index++)
  261. {
  262. UnicodeString Url = GenerateUrl(FPaths->Strings[Index]);
  263. Result += RtfText(Url) + RtfPara;
  264. }
  265. }
  266. return Result;
  267. }
  268. //---------------------------------------------------------------------------
  269. void __fastcall TGenerateUrlDialog::AddSampleDescription(UnicodeString & Description)
  270. {
  271. if (FPathsSample)
  272. {
  273. Description += LoadStr(GENERATE_URL_FILE_SAMPLE) + L"\n";
  274. }
  275. }
  276. //---------------------------------------------------------------------------
  277. UnicodeString __fastcall TGenerateUrlDialog::GenerateScript(UnicodeString & ScriptDescription)
  278. {
  279. UnicodeString Result;
  280. UnicodeString ExeName = Application->ExeName;
  281. UnicodeString BaseExeName = ExtractFileBaseName(ExeName);
  282. UnicodeString OpenCommand = FData->GenerateOpenCommandArgs(true);
  283. UnicodeString CommandPlaceholder1 = FMTLOAD(GENERATE_URL_COMMAND, (1));
  284. UnicodeString CommandPlaceholder2 = FMTLOAD(GENERATE_URL_COMMAND, (2));
  285. UnicodeString LogPath = LoadStr(GENERATE_URL_WRITABLE_PATH_TO_LOG) + RtfText(BaseExeName + L".log");
  286. UnicodeString LogParameter =
  287. RtfCommandlineSwitch(LOG_SWITCH, L"logging") + RtfText(L"=") +
  288. RtfScriptPlaceholder(L"\"" + LogPath + L"\"");
  289. UnicodeString IniParameter =
  290. RtfCommandlineSwitch(INI_SWITCH, L"configuration") + RtfText(UnicodeString(L"=") + INI_NUL);
  291. UnicodeString CommandParameter = RtfCommandlineSwitch(COMMAND_SWITCH, L"scripting");
  292. typedef std::vector<UnicodeString> TCommands;
  293. TCommands Commands;
  294. Commands.push_back(RtfScriptCommand(L"open") + L" " + OpenCommand);
  295. Commands.push_back(UnicodeString());
  296. if (FTransfer)
  297. {
  298. UnicodeString TransferCommand;
  299. if (FToRemote)
  300. {
  301. Commands.push_back(RtfScriptCommand(L"lcd") + L" " + RtfText(QuoteStringParam(FSourcePath)));
  302. Commands.push_back(RtfScriptCommand(L"cd") + L" " + RtfText(QuoteStringParam(UnixExcludeTrailingBackslash(FPath))));
  303. TransferCommand = L"put";
  304. }
  305. else
  306. {
  307. Commands.push_back(RtfScriptCommand(L"cd") + L" " + RtfText(QuoteStringParam(FSourcePath)));
  308. Commands.push_back(RtfScriptCommand(L"lcd") + L" " + RtfText(QuoteStringParam(ExcludeTrailingBackslashUnlessRoot(FPath))));
  309. TransferCommand = L"get";
  310. }
  311. Commands.push_back(UnicodeString());
  312. UnicodeString TransferCommandLink = ScriptCommandLink(TransferCommand);
  313. UnicodeString TransferCommandArgs;
  314. if (FMove)
  315. {
  316. TransferCommandArgs += RtfSwitch(DELETE_SWITCH, TransferCommandLink);
  317. }
  318. TransferCommandArgs += FCopyParam.GenerateTransferCommandArgs(FCopyParamAttrs, TransferCommandLink);
  319. AddSampleDescription(ScriptDescription);
  320. TransferCommand = RtfScriptCommand(TransferCommand) + TransferCommandArgs;
  321. if (FFilesSelected == fsList)
  322. {
  323. for (int Index = 0; Index < FPaths->Count; Index++)
  324. {
  325. UnicodeString Path = ExtractFileName(FPaths->Strings[Index], !FToRemote);
  326. if (!FToRemote)
  327. {
  328. Path = EscapeFileMask(Path);
  329. }
  330. Commands.push_back(TransferCommand + L" " + RtfText(QuoteStringParam(Path)));
  331. }
  332. }
  333. else
  334. {
  335. Commands.push_back(TransferCommand + L" " + RtfText(AllFilesMask));
  336. }
  337. }
  338. else
  339. {
  340. Commands.push_back(L"# " + CommandPlaceholder1);
  341. Commands.push_back(L"# " + CommandPlaceholder2);
  342. }
  343. Commands.push_back(UnicodeString());
  344. Commands.push_back(RtfScriptCommand(L"exit"));
  345. UnicodeString ComExeName = ChangeFileExt(ExeName, L".com");
  346. if (ScriptFormatCombo->ItemIndex == sfScriptFile)
  347. {
  348. for (TCommands::const_iterator I = Commands.begin(); I != Commands.end(); I++)
  349. {
  350. UnicodeString Command = *I;
  351. if (!Command.IsEmpty())
  352. {
  353. if (Command[1] == L'#')
  354. {
  355. Result += RtfScriptComment(Command);
  356. }
  357. else
  358. {
  359. Result += Command;
  360. }
  361. }
  362. Result += RtfPara;
  363. }
  364. UnicodeString ScriptCommandLine =
  365. FORMAT("\"%s\" /%s=\"%s\" /%s=%s /%s=\"%s\"",
  366. (ExeName, LowerCase(LOG_SWITCH), LogPath, LowerCase(INI_SWITCH), INI_NUL, LowerCase(SCRIPT_SWITCH), LoadStr(GENERATE_URL_PATH_TO_SCRIPT)));
  367. Result +=
  368. RtfPara +
  369. RtfScriptComment(L"# " + LoadStr(GENERATE_URL_SCRIPT_DESC)) + RtfPara +
  370. RtfScriptComment(L"# " + ScriptCommandLine) + RtfPara;
  371. }
  372. else if (ScriptFormatCombo->ItemIndex == sfBatchFile)
  373. {
  374. Result =
  375. RtfScriptPlaceholder(L"@echo off") + RtfPara +
  376. RtfPara +
  377. RtfText(L"\"" + ComExeName + "\" ^") + RtfPara +
  378. RtfText(L" ") + LogParameter + L" " + IniParameter + RtfText(L" ^") + RtfPara +
  379. RtfText(L" ") + CommandParameter;
  380. for (TCommands::const_iterator I = Commands.begin(); I != Commands.end(); I++)
  381. {
  382. UnicodeString Command = *I;
  383. if (!Command.IsEmpty())
  384. {
  385. Result +=
  386. RtfText(L" ^") + RtfPara +
  387. RtfText(L" \"");
  388. if (Command[1] == L'#')
  389. {
  390. Command.Delete(1, 1);
  391. Result += RtfScriptPlaceholder(Command.TrimLeft());
  392. }
  393. else
  394. {
  395. Result += RtfEscapeParam(ReplaceStr(Command, L"%", L"%%"), false);
  396. }
  397. Result += L"\"";
  398. }
  399. }
  400. Result +=
  401. RtfPara +
  402. RtfPara +
  403. RtfKeyword(L"set") + RtfText(L" WINSCP_RESULT=%ERRORLEVEL%") + RtfPara +
  404. RtfKeyword(L"if") + RtfText(L" %WINSCP_RESULT% ") + RtfKeyword(L"equ") + RtfText(L" 0 (") + RtfPara +
  405. RtfText(L" ") + RtfKeyword(L"echo") + RtfText(L" Success") + RtfPara +
  406. RtfText(L") ") + RtfKeyword(L"else") + RtfText(L" (") + RtfPara +
  407. RtfText(L" ") + RtfKeyword(L"echo") + RtfText(L" Error") + RtfPara +
  408. RtfText(L")") + RtfPara +
  409. RtfPara +
  410. RtfKeyword(L"exit") + RtfText(L" /b %WINSCP_RESULT%") + RtfPara;
  411. }
  412. else if (ScriptFormatCombo->ItemIndex == sfCommandLine)
  413. {
  414. Result =
  415. LogParameter + L" " +
  416. IniParameter + L" " +
  417. CommandParameter;
  418. for (TCommands::const_iterator I = Commands.begin(); I != Commands.end(); I++)
  419. {
  420. UnicodeString Command = *I;
  421. if (!Command.IsEmpty())
  422. {
  423. Result += RtfText(L" \"");
  424. if (Command[1] == L'#')
  425. {
  426. Command.Delete(1, 1);
  427. Result += RtfScriptPlaceholder(Command.TrimLeft());
  428. }
  429. else
  430. {
  431. Result += RtfEscapeParam(Command, false);
  432. }
  433. Result += L"\"";
  434. }
  435. }
  436. }
  437. else if (ScriptFormatCombo->ItemIndex == sfPowerShell)
  438. {
  439. // https://stackoverflow.com/q/74440303/850848
  440. UnicodeString PsArgPassingLink(L"https://learn.microsoft.com/en-us/powershell/module/microsoft.powershell.core/about/about_preference_variables#psnativecommandargumentpassing");
  441. Result =
  442. RtfLink(PsArgPassingLink, RtfScriptComment(FORMAT(L"# %s", (LoadStr(GENERATE_URL_PS_ARG_PASSING))))) + RtfPara +
  443. RtfLink(PsArgPassingLink, L"$PSNativeCommandArgumentPassing") + L" = " + AssemblyString(alPowerShell, L"Legacy") + RtfPara +
  444. RtfPara +
  445. RtfText(L"& \"" + ComExeName + "\" `") + RtfPara +
  446. RtfText(L" ") + LogParameter + L" " + IniParameter + RtfText(L" `") + RtfPara +
  447. RtfText(L" ") + CommandParameter;
  448. for (TCommands::const_iterator I = Commands.begin(); I != Commands.end(); I++)
  449. {
  450. UnicodeString Command = *I;
  451. if (!Command.IsEmpty())
  452. {
  453. Result += RtfText(L" `") + RtfPara + RtfText(L" \"");
  454. if (Command[1] == L'#')
  455. {
  456. Command.Delete(1, 1);
  457. Result += RtfScriptPlaceholder(Command.TrimLeft());
  458. }
  459. else
  460. {
  461. Command = ReplaceStr(Command, L"`", L"``");
  462. Command = ReplaceStr(Command, L"$", L"`$");
  463. Result += RtfEscapeParam(Command, true);
  464. }
  465. Result += L"\"";
  466. }
  467. }
  468. Result +=
  469. RtfPara +
  470. RtfPara +
  471. RtfText(L"$winscpResult = $LastExitCode") + RtfPara +
  472. RtfKeyword(L"if") + RtfText(L" ($winscpResult -eq 0)") + RtfPara +
  473. RtfText(L"{") + RtfPara +
  474. RtfText(L" ") + RtfKeyword(L"Write-Host") + L" " + RtfString(L"\"Success\"") + RtfPara +
  475. RtfText(L"}") + RtfPara +
  476. RtfText(L"else") + RtfPara +
  477. RtfText(L"{") + RtfPara +
  478. RtfText(L" ") + RtfKeyword(L"Write-Host") + L" " + RtfString(L"\"Error\"") + RtfPara +
  479. RtfText(L"}") + RtfPara +
  480. RtfPara +
  481. RtfKeyword(L"exit") + RtfText(L" $winscpResult") + RtfPara;
  482. }
  483. return Result;
  484. }
  485. //---------------------------------------------------------------------------
  486. UnicodeString __fastcall TGenerateUrlDialog::GenerateAssemblyCode(UnicodeString & AssemblyDescription)
  487. {
  488. TAssemblyLanguage Language = static_cast<TAssemblyLanguage>(AssemblyLanguageCombo->ItemIndex);
  489. UnicodeString Head;
  490. UnicodeString Tail;
  491. int Indent;
  492. FData->GenerateAssemblyCode(Language, Head, Tail, Indent);
  493. UnicodeString Result = Head;
  494. UnicodeString Code;
  495. if (FTransfer)
  496. {
  497. UnicodeString CopyParamProperties = FCopyParam.GenerateAssemblyCode(Language, FCopyParamAttrs);
  498. bool HasTransferOptions = !CopyParamProperties.IsEmpty();
  499. if (HasTransferOptions)
  500. {
  501. Code +=
  502. AssemblyCommentLine(Language, LoadStr(GENERATE_URL_COPY_PARAM)) +
  503. CopyParamProperties +
  504. RtfPara;
  505. }
  506. Code += AssemblyCommentLine(Language, LoadStr(GENERATE_URL_TRANSFER_FILES));
  507. AddSampleDescription(AssemblyDescription);
  508. UnicodeString DestPath = FPath;
  509. UnicodeString TransferMethodName;
  510. if (FToRemote)
  511. {
  512. TransferMethodName = L"PutFiles";
  513. DestPath = UnixIncludeTrailingBackslash(DestPath);
  514. }
  515. else
  516. {
  517. TransferMethodName = L"GetFiles";
  518. DestPath = IncludeTrailingBackslash(DestPath);
  519. }
  520. DestPath += NoOpOperationMask;
  521. UnicodeString DestPathVariableName = AssemblyVariableName(Language, L"remotePath");
  522. UnicodeString StatementSeparator = AssemblyStatementSeparator(Language);
  523. UnicodeString DestPathString = AssemblyString(Language, DestPath);
  524. UnicodeString DestPathCode;
  525. if ((FFilesSelected != fsList) || (FPaths->Count == 1))
  526. {
  527. DestPathCode = DestPathString;
  528. }
  529. else
  530. {
  531. switch (Language)
  532. {
  533. case alCSharp:
  534. Code += RtfKeyword(L"const") + L" " + RtfKeyword(L"string") + L" " + DestPathVariableName;
  535. break;
  536. case alVBNET:
  537. Code += RtfKeyword(L"Const") + L" " + DestPathVariableName;
  538. break;
  539. case alPowerShell:
  540. Code += DestPathVariableName;
  541. break;
  542. }
  543. Code += L" = " + DestPathString + StatementSeparator + RtfPara;
  544. DestPathCode = DestPathVariableName;
  545. }
  546. UnicodeString TransferMethodCallStart =
  547. AssemblyVariableName(Language, SessionClassName) + L"." +
  548. RtfLibraryMethod(SessionClassName, TransferMethodName, false) + L"(";
  549. const UnicodeString ParameterSeparator = L", ";
  550. UnicodeString TransferMethodCallEnd = ParameterSeparator + DestPathCode;
  551. if (FMove || HasTransferOptions)
  552. {
  553. TransferMethodCallEnd += ParameterSeparator + AssemblyBoolean(Language, FMove);
  554. }
  555. if (HasTransferOptions)
  556. {
  557. TransferMethodCallEnd += ParameterSeparator + AssemblyVariableName(Language, TransferOptionsClassName);
  558. }
  559. TransferMethodCallEnd +=
  560. L")." + RtfLibraryMethod(L"OperationResultBase", L"Check", true) + L"()" +
  561. StatementSeparator + RtfPara;
  562. if (FFilesSelected == fsList)
  563. {
  564. for (int Index = 0; Index < FPaths->Count; Index++)
  565. {
  566. UnicodeString FileName = FPaths->Strings[Index];
  567. UnicodeString Path;
  568. if (!FToRemote)
  569. {
  570. Path = UnixIncludeTrailingBackslash(FSourcePath) + FileName;
  571. }
  572. else
  573. {
  574. Path = IncludeTrailingBackslash(FSourcePath) + FileName;
  575. }
  576. UnicodeString PathCode = AssemblyString(Language, Path);
  577. if (!FToRemote && (FileName != EscapeFileMask(FileName)))
  578. {
  579. PathCode =
  580. AssemblyVariableName(Language, SessionClassName) + L"." +
  581. RtfLibraryMethod(SessionClassName, L"EscapeFileMask", false) + L"(" + PathCode + L")";
  582. }
  583. Code += TransferMethodCallStart + PathCode + TransferMethodCallEnd;
  584. }
  585. }
  586. else
  587. {
  588. UnicodeString SourcePath = FSourcePath;
  589. if (FToRemote)
  590. {
  591. SourcePath = IncludeTrailingBackslash(SourcePath);
  592. }
  593. else
  594. {
  595. SourcePath = UnixIncludeTrailingBackslash(SourcePath);
  596. }
  597. SourcePath += AllFilesMask;
  598. Code += TransferMethodCallStart + AssemblyString(Language, SourcePath) + TransferMethodCallEnd;
  599. }
  600. }
  601. else
  602. {
  603. Code = AssemblyCommentLine(Language, LoadStr(GENERATE_URL_YOUR_CODE));
  604. }
  605. UnicodeString Indentation = UnicodeString::StringOfChar(L' ', Indent);
  606. Code = Indentation + ReplaceStr(Code, RtfPara, RtfPara + Indentation);
  607. if (DebugAlwaysTrue(Code.SubString(Code.Length() - Indentation.Length() + 1, Indentation.Length()) == Indentation))
  608. {
  609. Code.SetLength(Code.Length() - Indentation.Length());
  610. }
  611. Result += Code;
  612. Result += Tail;
  613. return Result;
  614. }
  615. //---------------------------------------------------------------------------
  616. void __fastcall TGenerateUrlDialog::UpdateControls()
  617. {
  618. if (!FChanging)
  619. {
  620. int CaptionId;
  621. if (FTransfer)
  622. {
  623. CaptionId = GENERATE_URL_TRANSFER_TITLE;
  624. }
  625. else
  626. {
  627. CaptionId = IsFileUrl() ? GENERATE_URL_FILE_TITLE : GENERATE_URL_SESSION_TITLE;
  628. }
  629. Caption = LoadStr(CaptionId);
  630. UrlSheet->TabVisible = !FTransfer;
  631. ScriptSheet->TabVisible = !IsFileUrl();
  632. AssemblySheet->TabVisible = !IsFileUrl();
  633. bool HostKeyUnknown = FData->UsesSsh && FData->HostKey.IsEmpty();
  634. UnicodeString ResultGroupCaption;
  635. if (OptionsPageControl->ActivePage == UrlSheet)
  636. {
  637. ResultGroupCaption = LoadStr(GENERATE_URL_URL);
  638. }
  639. else if (OptionsPageControl->ActivePage == ScriptSheet)
  640. {
  641. if (ScriptFormatCombo->ItemIndex == sfCommandLine)
  642. {
  643. ResultGroupCaption = LoadStr(GENERATE_URL_COMMANDLINE_LABEL);
  644. }
  645. else
  646. {
  647. ResultGroupCaption = ScriptFormatCombo->Items->Strings[ScriptFormatCombo->ItemIndex];
  648. }
  649. }
  650. else if (DebugAlwaysTrue(OptionsPageControl->ActivePage == AssemblySheet))
  651. {
  652. ResultGroupCaption = LoadStr(GENERATE_URL_CODE);
  653. }
  654. ResultGroup->Caption = ResultGroupCaption;
  655. EnableControl(UserNameCheck, !FData->UserNameExpanded.IsEmpty());
  656. bool UserNameIncluded = UserNameCheck->Enabled && UserNameCheck->Checked;
  657. EnableControl(PasswordCheck, UserNameIncluded && FData->HasPassword());
  658. EnableControl(HostKeyCheck, UserNameIncluded && !FData->HostKey.IsEmpty());
  659. EnableControl(RemoteDirectoryCheck, !FData->RemoteDirectory.IsEmpty() && !IsFileUrl());
  660. EnableControl(SaveExtensionCheck, !IsFileUrl());
  661. EnableControl(RawSettingsCheck, UserNameIncluded && FData->HasRawSettingsForUrl());
  662. UnicodeString Result;
  663. bool * Counted = NULL;
  664. UnicodeString CounterName;
  665. bool WordWrap = false; // shut up
  666. bool FixedWidth = false; // shut up
  667. if (OptionsPageControl->ActivePage == UrlSheet)
  668. {
  669. Counted = &FUrlCounted;
  670. CounterName = L"GeneratedUrls";
  671. Result = GenerateUrl();
  672. WordWrap = true;
  673. FixedWidth = false;
  674. }
  675. else if (OptionsPageControl->ActivePage == ScriptSheet)
  676. {
  677. Counted = &FScriptCounted;
  678. CounterName = FTransfer ? L"GeneratedScriptsTransfer" : L"GeneratedScripts";
  679. UnicodeString ScriptDescription;
  680. if (ScriptFormatCombo->ItemIndex == sfScriptFile)
  681. {
  682. WordWrap = false;
  683. FixedWidth = true;
  684. }
  685. else if (ScriptFormatCombo->ItemIndex == sfBatchFile)
  686. {
  687. WordWrap = false;
  688. FixedWidth = true;
  689. }
  690. else if (ScriptFormatCombo->ItemIndex == sfCommandLine)
  691. {
  692. WordWrap = true;
  693. FixedWidth = false;
  694. ScriptDescription = FMTLOAD(GENERATE_URL_COMMANDLINE_DESC, (FORMAT("\"%s\"", (Application->ExeName)))) + L"\n";
  695. }
  696. else if (ScriptFormatCombo->ItemIndex == sfPowerShell)
  697. {
  698. WordWrap = false;
  699. FixedWidth = true;
  700. }
  701. if (HostKeyUnknown)
  702. {
  703. ScriptDescription += LoadStr(GENERATE_URL_HOSTKEY_UNKNOWN) + L"\n";
  704. }
  705. Result = GenerateScript(ScriptDescription);
  706. ScriptDescriptionLabel->Caption = ScriptDescription;
  707. }
  708. else if (DebugAlwaysTrue(OptionsPageControl->ActivePage == AssemblySheet))
  709. {
  710. Counted = &FAssemblyCounted;
  711. CounterName = FTransfer ? L"GeneratedCodesTransfer" : L"GeneratedCodes";
  712. UnicodeString AssemblyDescription;
  713. if (HostKeyUnknown)
  714. {
  715. AssemblyDescription += LoadStr(GENERATE_URL_HOSTKEY_UNKNOWN) + L"\n";
  716. }
  717. Result = GenerateAssemblyCode(AssemblyDescription);
  718. AssemblyDescriptionLabel->Caption = AssemblyDescription;
  719. WordWrap = false;
  720. FixedWidth = true;
  721. }
  722. if (FixedWidth)
  723. {
  724. FResultMemoWithLinks->Font->Name = CustomWinConfiguration->DefaultFixedWidthFontName;
  725. }
  726. else
  727. {
  728. FResultMemoWithLinks->Font->Name = Font->Name;
  729. }
  730. if (!CounterName.IsEmpty() && !(*Counted))
  731. {
  732. (*Counted) = true;
  733. Configuration->Usage->Inc(CounterName);
  734. }
  735. Result =
  736. L"{\\rtf1\n"
  737. "{\\colortbl ;" +
  738. // The same RGB as on wiki
  739. RtfColorEntry(0x010101) + // near-black fake color to be used with no-style link to override the default blue underline
  740. RtfColorEntry(0x008000) + // code comment (green)
  741. RtfColorEntry(0x008080) + // class (teal)
  742. RtfColorEntry(0x800000) + // string (maroon)
  743. RtfColorEntry(0x0000FF) + // keyword (blue)
  744. RtfColorEntry(0x993333) + // command-line argument (reddish)
  745. RtfColorEntry(0x808080) + // script command (gray)
  746. L"}\n"
  747. "{\\fonttbl{\\f0\\fnil\\fcharset0 " + FResultMemoWithLinks->Font->Name + L";}}\n"
  748. "\\f0\\fs" + IntToStr(FResultMemoWithLinks->Font->Size * 2) + L" " +
  749. Result +
  750. "}";
  751. FResultMemoWithLinks->WordWrap = WordWrap;
  752. FResultMemoWithLinks->ScrollBars = WordWrap ? ssVertical : ssBoth;
  753. std::unique_ptr<TMemoryStream> Stream(new TMemoryStream());
  754. UTF8String ResultUtf = Result;
  755. Stream->Write(ResultUtf.c_str(), ResultUtf.Length());
  756. Stream->Position = 0;
  757. FResultMemoWithLinks->Perform(WM_VSCROLL, SB_TOP, 0);
  758. FResultMemoWithLinks->Lines->LoadFromStream(Stream.get(), TEncoding::UTF8);
  759. }
  760. }
  761. //---------------------------------------------------------------------------
  762. void __fastcall TGenerateUrlDialog::Execute()
  763. {
  764. int Components = WinConfiguration->GenerateUrlComponents;
  765. if (Components < 0)
  766. {
  767. Components = UserNameCheck->Tag | RemoteDirectoryCheck->Tag;
  768. }
  769. TGenerateUrlCodeTarget Target = WinConfiguration->GenerateUrlCodeTarget;
  770. {
  771. TAutoFlag ChangingFlag(FChanging);
  772. if (IsFileUrl())
  773. {
  774. OptionsPageControl->ActivePage = UrlSheet;
  775. }
  776. else
  777. {
  778. switch (Target)
  779. {
  780. case guctUrl:
  781. OptionsPageControl->ActivePage = UrlSheet;
  782. break;
  783. case guctScript:
  784. OptionsPageControl->ActivePage = ScriptSheet;
  785. break;
  786. case guctAssembly:
  787. OptionsPageControl->ActivePage = AssemblySheet;
  788. break;
  789. default:
  790. DebugFail();
  791. }
  792. }
  793. for (int Index = 0; Index < UrlSheet->ControlCount; Index++)
  794. {
  795. TCheckBox * CheckBox = dynamic_cast<TCheckBox *>(UrlSheet->Controls[Index]);
  796. if (DebugAlwaysTrue((CheckBox != NULL) && (CheckBox->Tag != 0)))
  797. {
  798. CheckBox->Checked = FLAGSET(Components, CheckBox->Tag);
  799. }
  800. }
  801. ScriptFormatCombo->ItemIndex = WinConfiguration->GenerateUrlScriptFormat;
  802. AssemblyLanguageCombo->ItemIndex = WinConfiguration->GenerateUrlAssemblyLanguage;
  803. }
  804. UpdateControls();
  805. if (OptionsPageControl->ActivePage != UrlSheet)
  806. {
  807. ClientWidth = ScaleByTextHeightRunTime(this, 777);
  808. ClientHeight = ScaleByTextHeightRunTime(this, 666);
  809. }
  810. ShowModal();
  811. // Do not save the selection for files as the "URL" was selected implicitly
  812. if (!IsFileUrl())
  813. {
  814. if (OptionsPageControl->ActivePage == UrlSheet)
  815. {
  816. Target = guctUrl;
  817. }
  818. else if (OptionsPageControl->ActivePage == ScriptSheet)
  819. {
  820. Target = guctScript;
  821. }
  822. else if (OptionsPageControl->ActivePage == AssemblySheet)
  823. {
  824. Target = guctAssembly;
  825. }
  826. else
  827. {
  828. DebugFail();
  829. }
  830. WinConfiguration->GenerateUrlCodeTarget = Target;
  831. }
  832. if (Target == guctUrl)
  833. {
  834. Components = 0;
  835. for (int Index = 0; Index < UrlSheet->ControlCount; Index++)
  836. {
  837. TCheckBox * CheckBox = dynamic_cast<TCheckBox *>(UrlSheet->Controls[Index]);
  838. if (DebugAlwaysTrue((CheckBox != NULL) && (CheckBox->Tag != 0)) &&
  839. CheckBox->Checked)
  840. {
  841. Components |= CheckBox->Tag;
  842. }
  843. }
  844. WinConfiguration->GenerateUrlComponents = Components;
  845. }
  846. else if (Target == guctScript)
  847. {
  848. WinConfiguration->GenerateUrlScriptFormat = static_cast<TScriptFormat>(ScriptFormatCombo->ItemIndex);
  849. }
  850. else if (Target == guctAssembly)
  851. {
  852. WinConfiguration->GenerateUrlAssemblyLanguage = static_cast<TAssemblyLanguage>(AssemblyLanguageCombo->ItemIndex);
  853. }
  854. }
  855. //---------------------------------------------------------------------------
  856. void __fastcall TGenerateUrlDialog::ControlChange(TObject * /*Sender*/)
  857. {
  858. UpdateControls();
  859. }
  860. //---------------------------------------------------------------------------
  861. void __fastcall TGenerateUrlDialog::ClipboardButtonClick(TObject * /*Sender*/)
  862. {
  863. TInstantOperationVisualizer Visualizer;
  864. // Cannot read the text from FResultMemoWithLinks->Lines as TRichEdit (as opposite to TMemo)
  865. // breaks wrapped lines
  866. UnicodeString Text = FResultMemoWithLinks->Text;
  867. UnicodeString EOL = sLineBreak;
  868. int P = Pos(EOL, Text);
  869. // Trim the EOL of the only string, what CopyToClipboard(FResultMemoWithLinks->Lines) would have done.
  870. // It probably never happens as rich edit does not return EOL on the last line.
  871. if (DebugAlwaysFalse(P == Text.Length() - EOL.Length() + 1))
  872. {
  873. Text.SetLength(Text.Length() - EOL.Length());
  874. }
  875. // Add trailing EOL, if there are multiple lines (see above)
  876. else if ((P > 0) && !EndsStr(EOL, Text))
  877. {
  878. Text += EOL;
  879. }
  880. Text = RtfRemoveHyperlinks(Text);
  881. CopyToClipboard(Text);
  882. }
  883. //---------------------------------------------------------------------------
  884. void __fastcall TGenerateUrlDialog::HelpButtonClick(TObject * /*Sender*/)
  885. {
  886. FormHelp(this);
  887. }
  888. //---------------------------------------------------------------------------
  889. void __fastcall TGenerateUrlDialog::WMNCCreate(TWMNCCreate & Message)
  890. {
  891. // bypass TForm::WMNCCreate to prevent disabling "resize"
  892. // (which is done for bsDialog, see comments in CreateParams)
  893. DefaultHandler(&Message);
  894. }
  895. //---------------------------------------------------------------------------
  896. void __fastcall TGenerateUrlDialog::Dispatch(void * AMessage)
  897. {
  898. TMessage & Message = *reinterpret_cast<TMessage *>(AMessage);
  899. if (Message.Msg == WM_NCCREATE)
  900. {
  901. WMNCCreate(*reinterpret_cast<TWMNCCreate *>(AMessage));
  902. }
  903. else
  904. {
  905. TForm::Dispatch(AMessage);
  906. }
  907. }
  908. //---------------------------------------------------------------------------
  909. void __fastcall TGenerateUrlDialog::CreateParams(TCreateParams & Params)
  910. {
  911. TForm::CreateParams(Params);
  912. // Allow resizing of the window, even if this is bsDialog.
  913. // This makes it more close to bsSizeable, but bsSizeable cannot for some
  914. // reason receive focus, if window is shown atop non-main window
  915. // (like editor)
  916. Params.Style = Params.Style | WS_THICKFRAME;
  917. }
  918. //---------------------------------------------------------------------------
  919. void __fastcall TGenerateUrlDialog::FormShow(TObject * /*Sender*/)
  920. {
  921. UpdateControls();
  922. }
  923. //---------------------------------------------------------------------------