TerminalManager.cpp 59 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872
  1. //---------------------------------------------------------------------------
  2. #define NO_WIN32_LEAN_AND_MEAN
  3. #include <vcl.h>
  4. #pragma hdrstop
  5. #include "TerminalManager.h"
  6. #include <Authenticate.h>
  7. #include "CustomScpExplorer.h"
  8. #include "NonVisual.h"
  9. #include "WinConfiguration.h"
  10. #include "Tools.h"
  11. #include <Common.h>
  12. #include <CoreMain.h>
  13. #include <GUITools.h>
  14. #include <TextsWin.h>
  15. #include <TextsCore.h>
  16. #include <Progress.h>
  17. #include <Exceptions.h>
  18. #include <VCLCommon.h>
  19. #include <WinApi.h>
  20. #include <PuttyTools.h>
  21. #include <HelpWin.h>
  22. #include <System.IOUtils.hpp>
  23. #include <StrUtils.hpp>
  24. //---------------------------------------------------------------------------
  25. #pragma package(smart_init)
  26. //---------------------------------------------------------------------------
  27. TTerminalManager * TTerminalManager::FInstance = NULL;
  28. //---------------------------------------------------------------------------
  29. __fastcall TManagedTerminal::TManagedTerminal(TSessionData * SessionData,
  30. TConfiguration * Configuration) :
  31. TTerminal(SessionData, Configuration),
  32. LocalExplorerState(NULL), RemoteExplorerState(NULL),
  33. ReopenStart(0), DirectoryLoaded(Now()), TerminalThread(NULL), Disconnected(false)
  34. {
  35. StateData = new TSessionData(L"");
  36. StateData->Assign(SessionData);
  37. StateData->LocalDirectory = ::ExpandFileName(StateData->LocalDirectory);
  38. }
  39. //---------------------------------------------------------------------------
  40. __fastcall TManagedTerminal::~TManagedTerminal()
  41. {
  42. delete StateData;
  43. delete LocalExplorerState;
  44. delete RemoteExplorerState;
  45. }
  46. //---------------------------------------------------------------------------
  47. //---------------------------------------------------------------------------
  48. TTerminalManager * __fastcall TTerminalManager::Instance(bool ForceCreation)
  49. {
  50. if (!FInstance && ForceCreation)
  51. {
  52. FInstance = new TTerminalManager();
  53. }
  54. return FInstance;
  55. }
  56. //---------------------------------------------------------------------------
  57. void __fastcall TTerminalManager::DestroyInstance()
  58. {
  59. DebugAssert(FInstance);
  60. SAFE_DESTROY(FInstance);
  61. }
  62. //---------------------------------------------------------------------------
  63. __fastcall TTerminalManager::TTerminalManager() :
  64. TTerminalList(Configuration)
  65. {
  66. FQueueSection = new TCriticalSection();
  67. FActiveTerminal = NULL;
  68. FScpExplorer = NULL;
  69. FDestroying = false;
  70. FTerminalPendingAction = tpNull;
  71. FDirectoryReadingStart = 0;
  72. FAuthenticateForm = NULL;
  73. FTaskbarList = NULL;
  74. FAuthenticating = 0;
  75. FMainThread = GetCurrentThreadId();
  76. FChangeSection.reset(new TCriticalSection());
  77. FPendingConfigurationChange = 0;
  78. FKeepAuthenticateForm = false;
  79. FApplicationsEvents.reset(new TApplicationEvents(Application));
  80. FApplicationsEvents->OnException = ApplicationException;
  81. FApplicationsEvents->OnShowHint = ApplicationShowHint;
  82. FApplicationsEvents->OnMessage = ApplicationMessage;
  83. FApplicationsEvents->OnModalBegin = ApplicationModalBegin;
  84. FApplicationsEvents->OnModalEnd = ApplicationModalEnd;
  85. DebugAssert(WinConfiguration->OnMasterPasswordPrompt == NULL);
  86. WinConfiguration->OnMasterPasswordPrompt = MasterPasswordPrompt;
  87. InitTaskbarButtonCreatedMessage();
  88. DebugAssert(Configuration && !Configuration->OnChange);
  89. Configuration->OnChange = ConfigurationChange;
  90. FOnLastTerminalClosed = NULL;
  91. FOnTerminalListChanged = NULL;
  92. FTerminalList = new TStringList();
  93. FQueues = new TList();
  94. FTerminationMessages = new TStringList();
  95. std::unique_ptr<TSessionData> DummyData(new TSessionData(L""));
  96. FLocalTerminal = CreateTerminal(DummyData.get());
  97. SetupTerminal(FLocalTerminal);
  98. }
  99. //---------------------------------------------------------------------------
  100. __fastcall TTerminalManager::~TTerminalManager()
  101. {
  102. FreeAll();
  103. DebugAssert(!ScpExplorer);
  104. DebugAssert(Configuration->OnChange == ConfigurationChange);
  105. Configuration->OnChange = NULL;
  106. FApplicationsEvents.reset(NULL);
  107. DebugAssert(WinConfiguration->OnMasterPasswordPrompt == MasterPasswordPrompt);
  108. WinConfiguration->OnMasterPasswordPrompt = NULL;
  109. delete FLocalTerminal;
  110. delete FQueues;
  111. delete FTerminationMessages;
  112. delete FTerminalList;
  113. CloseAutheticateForm();
  114. delete FQueueSection;
  115. ReleaseTaskbarList();
  116. }
  117. //---------------------------------------------------------------------------
  118. void __fastcall TTerminalManager::SetQueueConfiguration(TTerminalQueue * Queue)
  119. {
  120. Queue->TransfersLimit = GUIConfiguration->QueueTransfersLimit;
  121. Queue->KeepDoneItemsFor =
  122. (GUIConfiguration->QueueKeepDoneItems ? GUIConfiguration->QueueKeepDoneItemsFor : 0);
  123. }
  124. //---------------------------------------------------------------------------
  125. TTerminalQueue * __fastcall TTerminalManager::NewQueue(TTerminal * Terminal)
  126. {
  127. TTerminalQueue * Queue = new TTerminalQueue(Terminal, Configuration);
  128. SetQueueConfiguration(Queue);
  129. Queue->Enabled = WinConfiguration->EnableQueueByDefault;
  130. Queue->OnQueryUser = TerminalQueryUser;
  131. Queue->OnPromptUser = TerminalPromptUser;
  132. Queue->OnShowExtendedException = TerminalShowExtendedException;
  133. Queue->OnEvent = QueueEvent;
  134. return Queue;
  135. }
  136. //---------------------------------------------------------------------------
  137. TTerminal * __fastcall TTerminalManager::CreateTerminal(TSessionData * Data)
  138. {
  139. return new TManagedTerminal(Data, Configuration);
  140. }
  141. //---------------------------------------------------------------------------
  142. void __fastcall TTerminalManager::SetupTerminal(TTerminal * Terminal)
  143. {
  144. Terminal->OnQueryUser = TerminalQueryUser;
  145. Terminal->OnPromptUser = TerminalPromptUser;
  146. Terminal->OnDisplayBanner = TerminalDisplayBanner;
  147. Terminal->OnShowExtendedException = TerminalShowExtendedException;
  148. Terminal->OnProgress = OperationProgress;
  149. Terminal->OnFinished = OperationFinished;
  150. Terminal->OnDeleteLocalFile = DeleteLocalFile;
  151. Terminal->OnReadDirectoryProgress = TerminalReadDirectoryProgress;
  152. Terminal->OnInformation = TerminalInformation;
  153. Terminal->OnCustomCommand = TerminalCustomCommand;
  154. }
  155. //---------------------------------------------------------------------------
  156. TTerminal * __fastcall TTerminalManager::DoNewTerminal(TSessionData * Data)
  157. {
  158. TTerminal * Terminal = TTerminalList::NewTerminal(Data);
  159. try
  160. {
  161. FQueues->Add(NewQueue(Terminal));
  162. FTerminationMessages->Add(L"");
  163. SetupTerminal(Terminal);
  164. }
  165. catch(...)
  166. {
  167. if (Terminal != NULL)
  168. {
  169. FreeTerminal(Terminal);
  170. }
  171. throw;
  172. }
  173. return Terminal;
  174. }
  175. //---------------------------------------------------------------------------
  176. TTerminal * __fastcall TTerminalManager::NewTerminal(TSessionData * Data)
  177. {
  178. TTerminal * Terminal = DoNewTerminal(Data);
  179. DoTerminalListChanged();
  180. return Terminal;
  181. }
  182. //---------------------------------------------------------------------------
  183. TTerminal * __fastcall TTerminalManager::NewTerminals(TList * DataList)
  184. {
  185. TTerminal * Result = NULL;
  186. for (int Index = 0; Index < DataList->Count; Index++)
  187. {
  188. TTerminal * Terminal =
  189. DoNewTerminal(reinterpret_cast<TSessionData *>(DataList->Items[Index]));
  190. if (Index == 0)
  191. {
  192. Result = Terminal;
  193. }
  194. }
  195. DoTerminalListChanged();
  196. return Result;
  197. }
  198. //---------------------------------------------------------------------------
  199. void __fastcall TTerminalManager::FreeActiveTerminal()
  200. {
  201. if (FTerminalPendingAction == tpNull)
  202. {
  203. DebugAssert(ActiveTerminal);
  204. FreeTerminal(ActiveTerminal);
  205. }
  206. else
  207. {
  208. DebugAssert(FTerminalPendingAction == ::tpNone);
  209. FTerminalPendingAction = tpFree;
  210. }
  211. }
  212. //---------------------------------------------------------------------------
  213. void __fastcall TTerminalManager::DoConnectTerminal(TTerminal * Terminal, bool Reopen, bool AdHoc)
  214. {
  215. TManagedTerminal * ManagedTerminal = dynamic_cast<TManagedTerminal *>(Terminal);
  216. // it must be managed terminal, unless it is secondary terminal (of managed terminal)
  217. DebugAssert((ManagedTerminal != NULL) || (dynamic_cast<TSecondaryTerminal *>(Terminal) != NULL));
  218. // particularly when we are reconnecting RemoteDirectory of managed terminal
  219. // hold the last used remote directory as opposite to session data, which holds
  220. // the default remote directory.
  221. // make sure the last used directory is used, but the default is preserved too
  222. UnicodeString OrigRemoteDirectory = Terminal->SessionData->RemoteDirectory;
  223. try
  224. {
  225. TTerminalThread * TerminalThread = new TTerminalThread(Terminal);
  226. TerminalThread->AllowAbandon = (Terminal == FActiveTerminal);
  227. try
  228. {
  229. if (ManagedTerminal != NULL)
  230. {
  231. Terminal->SessionData->RemoteDirectory = ManagedTerminal->StateData->RemoteDirectory;
  232. if ((double)ManagedTerminal->ReopenStart == 0)
  233. {
  234. ManagedTerminal->ReopenStart = Now();
  235. }
  236. ManagedTerminal->Disconnected = false;
  237. DebugAssert(ManagedTerminal->TerminalThread == NULL);
  238. ManagedTerminal->TerminalThread = TerminalThread;
  239. }
  240. TNotifyEvent OnIdle;
  241. ((TMethod*)&OnIdle)->Code = TerminalThreadIdle;
  242. TerminalThread->OnIdle = OnIdle;
  243. if (Reopen)
  244. {
  245. TerminalThread->TerminalReopen();
  246. }
  247. else
  248. {
  249. TerminalThread->TerminalOpen();
  250. }
  251. }
  252. __finally
  253. {
  254. TerminalThread->OnIdle = NULL;
  255. if (!TerminalThread->Release())
  256. {
  257. if (!AdHoc && (DebugAlwaysTrue(Terminal == FActiveTerminal)))
  258. {
  259. // terminal was abandoned, must create a new one to replace it
  260. Terminal = CreateTerminal(new TSessionData(L""));
  261. SetupTerminal(Terminal);
  262. OwnsObjects = false;
  263. Items[ActiveTerminalIndex] = Terminal;
  264. OwnsObjects = true;
  265. FActiveTerminal = Terminal;
  266. // Can be NULL, when opening the first session from command-line
  267. if (FScpExplorer != NULL)
  268. {
  269. FScpExplorer->ReplaceTerminal(Terminal);
  270. }
  271. }
  272. // Now we do not have any reference to an abandoned terminal, so we can safely allow the thread
  273. // to complete its task and destroy the terminal afterwards.
  274. TerminalThread->Terminate();
  275. // When abandoning cancelled terminal, DoInformation(Phase = 0) does not make it to TerminalInformation handler.
  276. if (DebugAlwaysTrue(FAuthenticating > 0))
  277. {
  278. FKeepAuthenticateForm = false;
  279. AuthenticatingDone();
  280. }
  281. }
  282. else
  283. {
  284. if (ManagedTerminal != NULL)
  285. {
  286. ManagedTerminal->TerminalThread = NULL;
  287. }
  288. }
  289. }
  290. }
  291. __finally
  292. {
  293. Terminal->SessionData->RemoteDirectory = OrigRemoteDirectory;
  294. if (Terminal->Active && (ManagedTerminal != NULL))
  295. {
  296. ManagedTerminal->ReopenStart = 0;
  297. }
  298. }
  299. if (DebugAlwaysTrue(Terminal->Active) && !Reopen && GUIConfiguration->QueueBootstrap)
  300. {
  301. FindQueueForTerminal(Terminal)->AddItem(new TBootstrapQueueItem());
  302. }
  303. }
  304. //---------------------------------------------------------------------------
  305. void __fastcall TTerminalManager::CloseAutheticateForm()
  306. {
  307. SAFE_DESTROY(FAuthenticateForm);
  308. }
  309. //---------------------------------------------------------------------------
  310. bool __fastcall TTerminalManager::ConnectTerminal(TTerminal * Terminal)
  311. {
  312. bool Result = true;
  313. // were it an active terminal, it would allow abandoning, what this API cannot deal with
  314. DebugAssert(Terminal != FActiveTerminal);
  315. try
  316. {
  317. DoConnectTerminal(Terminal, false, false);
  318. }
  319. catch (Exception & E)
  320. {
  321. ShowExtendedExceptionEx(Terminal, &E);
  322. Result = false;
  323. }
  324. return Result;
  325. }
  326. //---------------------------------------------------------------------------
  327. void __fastcall TTerminalManager::TerminalThreadIdle(void * /*Data*/, TObject * /*Sender*/)
  328. {
  329. Application->ProcessMessages();
  330. }
  331. //---------------------------------------------------------------------------
  332. bool __fastcall TTerminalManager::ConnectActiveTerminalImpl(bool Reopen)
  333. {
  334. TTerminalPendingAction Action;
  335. bool Result;
  336. do
  337. {
  338. Action = tpNull;
  339. Result = false;
  340. try
  341. {
  342. DebugAssert(ActiveTerminal);
  343. DoConnectTerminal(ActiveTerminal, Reopen, false);
  344. if (ScpExplorer)
  345. {
  346. DebugAssert(ActiveTerminal->Status == ssOpened);
  347. TerminalReady();
  348. }
  349. WinConfiguration->ClearTemporaryLoginData();
  350. Result = true;
  351. }
  352. catch (Exception & E)
  353. {
  354. DebugAssert(FTerminalPendingAction == tpNull);
  355. FTerminalPendingAction = ::tpNone;
  356. try
  357. {
  358. DebugAssert(ActiveTerminal != NULL);
  359. ActiveTerminal->ShowExtendedException(&E);
  360. Action = FTerminalPendingAction;
  361. }
  362. __finally
  363. {
  364. FTerminalPendingAction = tpNull;
  365. }
  366. }
  367. }
  368. while (Action == tpReconnect);
  369. if (Action == tpFree)
  370. {
  371. DisconnectActiveTerminal();
  372. }
  373. return Result;
  374. }
  375. //---------------------------------------------------------------------------
  376. bool __fastcall TTerminalManager::ConnectActiveTerminal()
  377. {
  378. ActiveTerminal->CollectUsage();
  379. // add only stored sessions to the jump list,
  380. // ad-hoc session cannot be reproduced from just a session name
  381. if (StoredSessions->FindSame(ActiveTerminal->SessionData) != NULL)
  382. {
  383. WinConfiguration->AddSessionToJumpList(ActiveTerminal->SessionData->SessionName);
  384. }
  385. FAuthenticationCancelled = false;
  386. bool Result = ConnectActiveTerminalImpl(false);
  387. UnicodeString DateStamp = StandardDatestamp();
  388. if (Result)
  389. {
  390. if (Configuration->Usage->Get(L"OpenedSessionsFailedLastDate") == DateStamp)
  391. {
  392. Configuration->Usage->Inc(L"OpenedSessionsFailedRecovered");
  393. }
  394. }
  395. else
  396. {
  397. Configuration->Usage->Inc(L"OpenedSessionsFailed");
  398. Configuration->Usage->Set(L"OpenedSessionsFailedLastDate", DateStamp);
  399. if (FAuthenticationCancelled)
  400. {
  401. Configuration->Usage->Inc(L"OpenedSessionsFailedAfterCancel");
  402. }
  403. }
  404. if (Result && WinConfiguration->AutoOpenInPutty && CanOpenInPutty())
  405. {
  406. try
  407. {
  408. OpenInPutty();
  409. }
  410. catch(Exception & E)
  411. {
  412. ShowExtendedExceptionEx(NULL, &E);
  413. }
  414. }
  415. return Result;
  416. }
  417. //---------------------------------------------------------------------------
  418. void __fastcall TTerminalManager::DisconnectActiveTerminal()
  419. {
  420. DebugAssert(ActiveTerminal);
  421. if (ActiveTerminal->Active)
  422. {
  423. ActiveTerminal->Close();
  424. }
  425. int Index = IndexOf(ActiveTerminal);
  426. TTerminalQueue * OldQueue;
  427. TTerminalQueue * NewQueue;
  428. OldQueue = reinterpret_cast<TTerminalQueue *>(FQueues->Items[Index]);
  429. NewQueue = this->NewQueue(ActiveTerminal);
  430. FQueues->Items[Index] = NewQueue;
  431. ScpExplorer->Queue = NewQueue;
  432. delete OldQueue;
  433. dynamic_cast<TManagedTerminal *>(ActiveTerminal)->Disconnected = true;
  434. if (ScpExplorer != NULL)
  435. {
  436. TerminalReady(); // in case it was never connected
  437. ScpExplorer->TerminalDisconnected();
  438. }
  439. // disconnecting duplidate session removes need to distinguish the only connected session with short path
  440. DoTerminalListChanged();
  441. }
  442. //---------------------------------------------------------------------------
  443. void __fastcall TTerminalManager::ReconnectActiveTerminal()
  444. {
  445. DebugAssert(ActiveTerminal);
  446. if (ScpExplorer)
  447. {
  448. if (ScpExplorer->Terminal == ActiveTerminal)
  449. {
  450. ScpExplorer->UpdateTerminal(ActiveTerminal);
  451. }
  452. }
  453. try
  454. {
  455. if (FTerminalPendingAction == tpNull)
  456. {
  457. ConnectActiveTerminalImpl(true);
  458. }
  459. else
  460. {
  461. FTerminalPendingAction = tpReconnect;
  462. }
  463. }
  464. catch(...)
  465. {
  466. DisconnectActiveTerminal();
  467. throw;
  468. }
  469. }
  470. //---------------------------------------------------------------------------
  471. void __fastcall TTerminalManager::FreeAll()
  472. {
  473. FDestroying = true;
  474. try
  475. {
  476. while (Count)
  477. {
  478. FreeTerminal(Terminals[0]);
  479. }
  480. }
  481. __finally
  482. {
  483. FDestroying = false;
  484. }
  485. }
  486. //---------------------------------------------------------------------------
  487. void __fastcall TTerminalManager::FreeTerminal(TTerminal * Terminal)
  488. {
  489. try
  490. {
  491. // we want the Login dialog to open on auto-workspace name,
  492. // as set in TCustomScpExplorerForm::FormClose
  493. if (!FDestroying || !WinConfiguration->AutoSaveWorkspace)
  494. {
  495. if (StoredSessions->FindSame(Terminal->SessionData) != NULL)
  496. {
  497. WinConfiguration->LastStoredSession = Terminal->SessionData->Name;
  498. }
  499. }
  500. if (ScpExplorer != NULL)
  501. {
  502. ScpExplorer->TerminalRemoved(Terminal);
  503. }
  504. if (Terminal->Active)
  505. {
  506. Terminal->Close();
  507. }
  508. }
  509. __finally
  510. {
  511. int Index = IndexOf(Terminal);
  512. Extract(Terminal);
  513. TTerminalQueue * Queue;
  514. Queue = reinterpret_cast<TTerminalQueue *>(FQueues->Items[Index]);
  515. FQueues->Delete(Index);
  516. FTerminationMessages->Delete(Index);
  517. if (ActiveTerminal && (Terminal == ActiveTerminal))
  518. {
  519. if ((Count > 0) && !FDestroying)
  520. {
  521. ActiveTerminal = Terminals[Index < Count ? Index : Index - 1];
  522. }
  523. else
  524. {
  525. ActiveTerminal = NULL;
  526. }
  527. }
  528. else
  529. {
  530. SaveTerminal(Terminal);
  531. }
  532. // only now all references to/from queue (particularly events to explorer)
  533. // are cleared
  534. delete Queue;
  535. delete Terminal;
  536. DoTerminalListChanged();
  537. }
  538. }
  539. //---------------------------------------------------------------------------
  540. void __fastcall TTerminalManager::SetScpExplorer(TCustomScpExplorerForm * value)
  541. {
  542. if (ScpExplorer != value)
  543. {
  544. // changing explorer is not supported yet
  545. DebugAssert(!ScpExplorer || !value);
  546. FScpExplorer = value;
  547. if (FScpExplorer)
  548. {
  549. FScpExplorer->Terminal = ActiveTerminal;
  550. FScpExplorer->Queue = ActiveQueue;
  551. FOnLastTerminalClosed = FScpExplorer->LastTerminalClosed;
  552. FOnTerminalListChanged = FScpExplorer->TerminalListChanged;
  553. UpdateTaskbarList();
  554. }
  555. else
  556. {
  557. FOnLastTerminalClosed = NULL;
  558. FOnTerminalListChanged = NULL;
  559. }
  560. }
  561. }
  562. //---------------------------------------------------------------------------
  563. void __fastcall TTerminalManager::SetActiveTerminal(TTerminal * value)
  564. {
  565. DoSetActiveTerminal(value, false);
  566. }
  567. //---------------------------------------------------------------------------
  568. void __fastcall TTerminalManager::SetActiveTerminalWithAutoReconnect(TTerminal * value)
  569. {
  570. DoSetActiveTerminal(value, true);
  571. }
  572. //---------------------------------------------------------------------------
  573. void __fastcall TTerminalManager::DoSetActiveTerminal(TTerminal * value, bool AutoReconnect)
  574. {
  575. if (ActiveTerminal != value)
  576. {
  577. // here used to be call to TCustomScpExporer::UpdateSessionData (now UpdateTerminal)
  578. // but it seems to be duplicate to call from TCustomScpExporer::TerminalChanging
  579. TTerminal * PActiveTerminal = ActiveTerminal;
  580. FActiveTerminal = value;
  581. // moved from else block of next if (ActiveTerminal) statement
  582. // so ScpExplorer can update its caption
  583. UpdateAppTitle();
  584. if (ScpExplorer)
  585. {
  586. if (ActiveTerminal && ((ActiveTerminal->Status == ssOpened) || dynamic_cast<TManagedTerminal *>(ActiveTerminal)->Disconnected))
  587. {
  588. TerminalReady();
  589. }
  590. else
  591. {
  592. ScpExplorer->Terminal = NULL;
  593. ScpExplorer->Queue = NULL;
  594. }
  595. }
  596. if (PActiveTerminal && !PActiveTerminal->Active)
  597. {
  598. SaveTerminal(PActiveTerminal);
  599. }
  600. if (ActiveTerminal)
  601. {
  602. int Index = ActiveTerminalIndex;
  603. if (!ActiveTerminal->Active && !FTerminationMessages->Strings[Index].IsEmpty())
  604. {
  605. UnicodeString Message = FTerminationMessages->Strings[Index];
  606. FTerminationMessages->Strings[Index] = L"";
  607. if (AutoReconnect)
  608. {
  609. ReconnectActiveTerminal();
  610. }
  611. else
  612. {
  613. Exception * E = new ESshFatal(NULL, Message);
  614. try
  615. {
  616. // finally show pending terminal message,
  617. // this gives user also possibility to reconnect
  618. ActiveTerminal->ShowExtendedException(E);
  619. }
  620. __finally
  621. {
  622. delete E;
  623. }
  624. }
  625. }
  626. }
  627. else
  628. {
  629. if (OnLastTerminalClosed)
  630. {
  631. OnLastTerminalClosed(this);
  632. }
  633. }
  634. if ((ActiveTerminal != NULL) && !ActiveTerminal->Active &&
  635. !dynamic_cast<TManagedTerminal *>(ActiveTerminal)->Disconnected)
  636. {
  637. ConnectActiveTerminal();
  638. }
  639. }
  640. }
  641. //---------------------------------------------------------------------------
  642. void __fastcall TTerminalManager::QueueStatusUpdated()
  643. {
  644. UpdateAppTitle();
  645. }
  646. //---------------------------------------------------------------------------
  647. bool __fastcall TTerminalManager::ShouldDisplayQueueStatusOnAppTitle()
  648. {
  649. bool Result = IsApplicationMinimized();
  650. if (!Result && (ScpExplorer != NULL))
  651. {
  652. HWND Window = GetActiveWindow();
  653. Window = GetAncestor(Window, GA_ROOTOWNER);
  654. Result = (ScpExplorer->Handle != Window);
  655. }
  656. return Result;
  657. }
  658. //---------------------------------------------------------------------------
  659. UnicodeString __fastcall TTerminalManager::GetAppProgressTitle()
  660. {
  661. UnicodeString Result;
  662. UnicodeString QueueProgressTitle;
  663. UnicodeString ProgressTitle = !FProgressTitle.IsEmpty() ? FProgressTitle : ScpExplorer->GetProgressTitle();
  664. if (!FForegroundProgressTitle.IsEmpty())
  665. {
  666. Result = FForegroundProgressTitle;
  667. }
  668. else if (!ProgressTitle.IsEmpty() && !ForegroundTask())
  669. {
  670. Result = ProgressTitle;
  671. }
  672. else if (ShouldDisplayQueueStatusOnAppTitle() &&
  673. !(QueueProgressTitle = ScpExplorer->GetQueueProgressTitle()).IsEmpty())
  674. {
  675. Result = QueueProgressTitle;
  676. }
  677. return Result;
  678. }
  679. //---------------------------------------------------------------------------
  680. void __fastcall TTerminalManager::UpdateAppTitle()
  681. {
  682. if (ScpExplorer)
  683. {
  684. TForm * MainForm = GetMainForm();
  685. if (MainForm != ScpExplorer)
  686. {
  687. // triggers caption update for some forms
  688. MainForm->Perform(WM_MANAGES_CAPTION, 0, 0);
  689. }
  690. UnicodeString NewTitle = FormatMainFormCaption(GetActiveTerminalTitle(false));
  691. UnicodeString ProgressTitle = GetAppProgressTitle();
  692. if (!ProgressTitle.IsEmpty())
  693. {
  694. NewTitle = ProgressTitle + L" - " + NewTitle;
  695. }
  696. else if (ActiveTerminal && (ScpExplorer != NULL))
  697. {
  698. UnicodeString Path = ScpExplorer->PathForCaption();
  699. if (!Path.IsEmpty())
  700. {
  701. NewTitle = Path + L" - " + NewTitle;
  702. }
  703. }
  704. // Not updating MainForm here, as for all other possible main forms, this code is actually not what we want.
  705. // And they all update their title on their own (some using GetAppProgressTitle()).
  706. ScpExplorer->Caption = NewTitle;
  707. ScpExplorer->ApplicationTitleChanged();
  708. }
  709. }
  710. //---------------------------------------------------------------------------
  711. void __fastcall TTerminalManager::SaveTerminal(TTerminal * Terminal)
  712. {
  713. TSessionData * Data = StoredSessions->FindSame(Terminal->SessionData);
  714. if (Data != NULL)
  715. {
  716. TManagedTerminal * ManagedTerminal = dynamic_cast<TManagedTerminal *>(Terminal);
  717. DebugAssert(ManagedTerminal != NULL);
  718. bool Changed = false;
  719. if (Terminal->SessionData->UpdateDirectories)
  720. {
  721. Data->CopyDirectoriesStateData(ManagedTerminal->StateData);
  722. Changed = true;
  723. }
  724. if (Changed)
  725. {
  726. // modified only, implicit
  727. StoredSessions->Save(false, false);
  728. }
  729. }
  730. }
  731. //---------------------------------------------------------------------------
  732. void __fastcall TTerminalManager::HandleException(Exception * E)
  733. {
  734. // can be null for example when exception is thrown on login dialog
  735. if (ActiveTerminal != NULL)
  736. {
  737. ActiveTerminal->ShowExtendedException(E);
  738. }
  739. else
  740. {
  741. ShowExtendedException(E);
  742. }
  743. }
  744. //---------------------------------------------------------------------------
  745. void __fastcall TTerminalManager::ApplicationException(TObject * /*Sender*/,
  746. Exception * E)
  747. {
  748. HandleException(E);
  749. }
  750. //---------------------------------------------------------------------------
  751. void __fastcall TTerminalManager::ApplicationShowHint(UnicodeString & HintStr,
  752. bool & /*CanShow*/, THintInfo & HintInfo)
  753. {
  754. HintInfo.HintData = HintInfo.HintControl;
  755. if (HasLabelHintPopup(HintInfo.HintControl, HintStr))
  756. {
  757. // Hack for transfer setting labels.
  758. // Should be converted to something like HintLabel()
  759. HintInfo.HideTimeout = 100000; // "almost" never
  760. }
  761. else if (dynamic_cast<TProgressBar *>(HintInfo.HintControl) != NULL)
  762. {
  763. // Hint is forcibly hidden in TProgressForm::FormHide
  764. HintInfo.HideTimeout = 100000; // "almost" never
  765. HintInfo.ReshowTimeout = 500; // updated each 0.5s
  766. }
  767. else
  768. {
  769. int HintMaxWidth = 300;
  770. TControl * ScaleControl = HintInfo.HintControl;
  771. if (DebugAlwaysFalse(HintInfo.HintControl == NULL) ||
  772. (GetParentForm(HintInfo.HintControl) == NULL))
  773. {
  774. ScaleControl = ScpExplorer;
  775. }
  776. HintMaxWidth = ScaleByTextHeight(ScaleControl, HintMaxWidth);
  777. HintInfo.HintMaxWidth = HintMaxWidth;
  778. }
  779. }
  780. //---------------------------------------------------------------------------
  781. bool __fastcall TTerminalManager::HandleMouseWheel(WPARAM WParam, LPARAM LParam)
  782. {
  783. // WORKAROUND This is no longer necessary on Windows 10 (except for WM_WANTS_MOUSEWHEEL_INACTIVE part)
  784. bool Result = false;
  785. if (Application->Active)
  786. {
  787. TPoint Point(LOWORD(LParam), HIWORD(LParam));
  788. TWinControl * Control = FindVCLWindow(Point);
  789. if (Control != NULL)
  790. {
  791. TCustomForm * Form = GetParentForm(Control);
  792. // Only case we expect the parent form to be NULL is on the Find/Replace dialog,
  793. // which is owned by VCL's internal TRedirectorWindow.
  794. DebugAssert((Form != NULL) || (Control->ClassName() == L"TRedirectorWindow"));
  795. if (Form != NULL)
  796. {
  797. // Send it only to windows we tested it with.
  798. // Though we should sooner or later remove this test and pass it to all our windows.
  799. if (Form->Active && (Form->Perform(WM_WANTS_MOUSEWHEEL, 0, 0) == 1))
  800. {
  801. SendMessage(Control->Handle, WM_MOUSEWHEEL, WParam, LParam);
  802. Result = true;
  803. }
  804. else if (!Form->Active && (Form->Perform(WM_WANTS_MOUSEWHEEL_INACTIVE, 0, 0) == 1))
  805. {
  806. TWinControl * Control2;
  807. // FindVCLWindow stops on window level, when the window is not active? or when there's a modal window over it?
  808. // (but in any case, when we have operation running on top of Synchronization checklist).
  809. // WORKAROUND: The while loop does what AllLevels parameter of ControlAtPos should do, but it's broken.
  810. // Based on (now removed) Embarcadero QC 82143.
  811. while ((Control2 = dynamic_cast<TWinControl *>(Control->ControlAtPos(Control->ScreenToClient(Point), false, true))) != NULL)
  812. {
  813. Control = Control2;
  814. }
  815. SendMessage(Control->Handle, WM_MOUSEWHEEL, WParam, LParam);
  816. Result = true;
  817. }
  818. }
  819. }
  820. }
  821. return Result;
  822. }
  823. //---------------------------------------------------------------------------
  824. void __fastcall TTerminalManager::ApplicationMessage(TMsg & Msg, bool & Handled)
  825. {
  826. if (Msg.message == FTaskbarButtonCreatedMessage)
  827. {
  828. CreateTaskbarList();
  829. }
  830. if (Msg.message == WM_MOUSEWHEEL)
  831. {
  832. Handled = HandleMouseWheel(Msg.wParam, Msg.lParam);
  833. }
  834. }
  835. //---------------------------------------------------------------------------
  836. void __fastcall TTerminalManager::ApplicationModalBegin(TObject * /*Sender*/)
  837. {
  838. NonVisualDataModule->StartBusy();
  839. if (ScpExplorer != NULL)
  840. {
  841. ScpExplorer->SuspendWindowLock();
  842. }
  843. }
  844. //---------------------------------------------------------------------------
  845. void __fastcall TTerminalManager::ApplicationModalEnd(TObject * /*Sender*/)
  846. {
  847. NonVisualDataModule->EndBusy();
  848. if (ScpExplorer != NULL)
  849. {
  850. ScpExplorer->ResumeWindowLock();
  851. }
  852. }
  853. //---------------------------------------------------------------------------
  854. void __fastcall TTerminalManager::InitTaskbarButtonCreatedMessage()
  855. {
  856. // XE6 VCL already handles TaskbarButtonCreated, but does not call ChangeWindowMessageFilterEx.
  857. // So we keep our implementation.
  858. // See also https://stackoverflow.com/q/14614823/850848#14618587
  859. FTaskbarButtonCreatedMessage = RegisterWindowMessage(L"TaskbarButtonCreated");
  860. HINSTANCE User32Library = LoadLibrary(L"user32.dll");
  861. ChangeWindowMessageFilterExProc ChangeWindowMessageFilterEx =
  862. (ChangeWindowMessageFilterExProc)GetProcAddress(User32Library, "ChangeWindowMessageFilterEx");
  863. if (ChangeWindowMessageFilterEx != NULL)
  864. {
  865. // without this we won't get TaskbarButtonCreated, when app is running elevated
  866. ChangeWindowMessageFilterEx(
  867. Application->Handle, FTaskbarButtonCreatedMessage, MSGFLT_ALLOW, NULL);
  868. }
  869. }
  870. //---------------------------------------------------------------------------
  871. void __fastcall TTerminalManager::CreateTaskbarList()
  872. {
  873. ReleaseTaskbarList();
  874. if(SUCCEEDED(CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_ALL,
  875. IID_ITaskbarList3, (void **) &FTaskbarList)))
  876. {
  877. if (ScpExplorer != NULL)
  878. {
  879. UpdateTaskbarList();
  880. }
  881. }
  882. }
  883. //---------------------------------------------------------------------------
  884. void __fastcall TTerminalManager::ReleaseTaskbarList()
  885. {
  886. if (FTaskbarList != NULL)
  887. {
  888. FTaskbarList->Release();
  889. }
  890. }
  891. //---------------------------------------------------------------------------
  892. void __fastcall TTerminalManager::UpdateTaskbarList()
  893. {
  894. ScpExplorer->UpdateTaskbarList(FTaskbarList);
  895. }
  896. //---------------------------------------------------------------------------
  897. void __fastcall TTerminalManager::DeleteLocalFile(const UnicodeString FileName, bool Alternative, int & Deleted)
  898. {
  899. Deleted = RecursiveDeleteFileChecked(FileName, (WinConfiguration->DeleteToRecycleBin != Alternative));
  900. }
  901. //---------------------------------------------------------------------------
  902. void __fastcall TTerminalManager::TerminalQueryUser(TObject * Sender,
  903. const UnicodeString Query, TStrings * MoreMessages, unsigned int Answers,
  904. const TQueryParams * Params, unsigned int & Answer, TQueryType Type, void * /*Arg*/)
  905. {
  906. UnicodeString HelpKeyword;
  907. TMessageParams MessageParams(Params);
  908. UnicodeString AQuery = Query;
  909. if (Params != NULL)
  910. {
  911. HelpKeyword = Params->HelpKeyword;
  912. if (FLAGSET(Params->Params, qpFatalAbort))
  913. {
  914. AQuery = FMTLOAD(WARN_FATAL_ERROR, (AQuery));
  915. if (!MessageParams.TimerMessage.IsEmpty())
  916. {
  917. MessageParams.TimerMessage = FMTLOAD(WARN_FATAL_ERROR, (MessageParams.TimerMessage));
  918. }
  919. }
  920. }
  921. if (ScpExplorer)
  922. {
  923. Answer = ScpExplorer->MoreMessageDialog(AQuery, MoreMessages, Type, Answers,
  924. HelpKeyword, &MessageParams, dynamic_cast<TTerminal *>(Sender));
  925. }
  926. else
  927. {
  928. Answer = MoreMessageDialog(AQuery, MoreMessages, Type, Answers, HelpKeyword,
  929. &MessageParams);
  930. }
  931. }
  932. //---------------------------------------------------------------------------
  933. void __fastcall TTerminalManager::AuthenticateFormCancel(TObject * Sender)
  934. {
  935. TAuthenticateForm * Form = dynamic_cast<TAuthenticateForm *>(Sender);
  936. DebugAssert(Form != NULL);
  937. TManagedTerminal * ManagedTerminal = dynamic_cast<TManagedTerminal *>(Form->Terminal);
  938. // will be null e.g. for background transfers
  939. if (ManagedTerminal != NULL)
  940. {
  941. TTerminalThread * TerminalThread = ManagedTerminal->TerminalThread;
  942. // can be NULL for reconnects from transfers
  943. if ((TerminalThread != NULL) && !TerminalThread->Cancelling)
  944. {
  945. Form->Log(LoadStr(AUTH_CANCELLING));
  946. TerminalThread->Cancel();
  947. }
  948. }
  949. FAuthenticationCancelled = true;
  950. }
  951. //---------------------------------------------------------------------------
  952. TAuthenticateForm * __fastcall TTerminalManager::MakeAuthenticateForm(
  953. TTerminal * Terminal)
  954. {
  955. TAuthenticateForm * Dialog = SafeFormCreate<TAuthenticateForm>();
  956. Dialog->Init(Terminal);
  957. DebugAssert(Dialog->OnCancel == NULL);
  958. Dialog->OnCancel = AuthenticateFormCancel;
  959. return Dialog;
  960. }
  961. //---------------------------------------------------------------------------
  962. void __fastcall TTerminalManager::FileNameInputDialogInitializeRenameBaseName(
  963. TObject * /*Sender*/, TInputDialogData * Data)
  964. {
  965. EditSelectBaseName(Data->Edit->Handle);
  966. }
  967. //---------------------------------------------------------------------------
  968. void __fastcall TTerminalManager::TerminalPromptUser(
  969. TTerminal * Terminal, TPromptKind Kind, UnicodeString Name, UnicodeString Instructions,
  970. TStrings * Prompts, TStrings * Results, bool & Result, void * /*Arg*/)
  971. {
  972. if (((Kind == pkPrompt) || (Kind == pkFileName)) && (FAuthenticateForm == NULL) &&
  973. (Terminal->Status != ssOpening))
  974. {
  975. DebugAssert(Instructions.IsEmpty());
  976. DebugAssert(Prompts->Count == 1);
  977. DebugAssert(FLAGSET(int(Prompts->Objects[0]), pupEcho));
  978. UnicodeString AResult = Results->Strings[0];
  979. TInputDialogInitialize InputDialogInitialize = NULL;
  980. if ((Kind == pkFileName) && !WinConfiguration->RenameWholeName)
  981. {
  982. InputDialogInitialize = FileNameInputDialogInitializeRenameBaseName;
  983. }
  984. Result = InputDialog(Name, Prompts->Strings[0], AResult, L"", NULL, false, InputDialogInitialize);
  985. if (Result)
  986. {
  987. Results->Strings[0] = AResult;
  988. }
  989. }
  990. else
  991. {
  992. TAuthenticateForm * AuthenticateForm = FAuthenticateForm;
  993. if (AuthenticateForm == NULL)
  994. {
  995. AuthenticateForm = MakeAuthenticateForm(Terminal);
  996. }
  997. try
  998. {
  999. Result = AuthenticateForm->PromptUser(Kind, Name, Instructions, Prompts, Results,
  1000. (FAuthenticateForm != NULL), Terminal->StoredCredentialsTried);
  1001. }
  1002. __finally
  1003. {
  1004. if (FAuthenticateForm == NULL)
  1005. {
  1006. delete AuthenticateForm;
  1007. }
  1008. }
  1009. }
  1010. }
  1011. //---------------------------------------------------------------------------
  1012. void __fastcall TTerminalManager::TerminalDisplayBanner(
  1013. TTerminal * Terminal, UnicodeString SessionName,
  1014. const UnicodeString & Banner, bool & NeverShowAgain, int Options, unsigned int & Params)
  1015. {
  1016. DebugAssert(FAuthenticateForm != NULL);
  1017. TAuthenticateForm * AuthenticateForm = FAuthenticateForm;
  1018. if (AuthenticateForm == NULL)
  1019. {
  1020. AuthenticateForm = MakeAuthenticateForm(Terminal);
  1021. }
  1022. try
  1023. {
  1024. AuthenticateForm->Banner(Banner, NeverShowAgain, Options, Params);
  1025. }
  1026. __finally
  1027. {
  1028. if (FAuthenticateForm == NULL)
  1029. {
  1030. delete AuthenticateForm;
  1031. }
  1032. }
  1033. }
  1034. //---------------------------------------------------------------------------
  1035. void __fastcall TTerminalManager::TerminalShowExtendedException(
  1036. TTerminal * Terminal, Exception * E, void * /*Arg*/)
  1037. {
  1038. if (ScpExplorer)
  1039. {
  1040. ScpExplorer->ShowExtendedException(Terminal, E);
  1041. }
  1042. else
  1043. {
  1044. ShowExtendedExceptionEx(Terminal, E);
  1045. }
  1046. }
  1047. //---------------------------------------------------------------------------
  1048. static TDateTime DirectoryReadingProgressDelay(0, 0, 1, 500);
  1049. //---------------------------------------------------------------------------
  1050. void __fastcall TTerminalManager::TerminalReadDirectoryProgress(
  1051. TObject * /*Sender*/, int Progress, int ResolvedLinks, bool & Cancel)
  1052. {
  1053. if (Progress == 0)
  1054. {
  1055. if (ScpExplorer != NULL)
  1056. {
  1057. // See also TCustomScpExplorerForm::RemoteDirViewBusy
  1058. ScpExplorer->LockWindow();
  1059. }
  1060. FDirectoryReadingStart = Now();
  1061. if (!FProgressTitle.IsEmpty() || !FForegroundProgressTitle.IsEmpty())
  1062. {
  1063. FProgressTitle = L"";
  1064. FForegroundProgressTitle = L"";
  1065. UpdateAppTitle();
  1066. }
  1067. // Reset "was ESC ever pressed?" state
  1068. GetAsyncKeyState(VK_ESCAPE);
  1069. }
  1070. else if (Progress < 0)
  1071. {
  1072. if (Progress == -2)
  1073. {
  1074. // cancelled
  1075. if (ScpExplorer != NULL)
  1076. {
  1077. ScpExplorer->ReadDirectoryCancelled();
  1078. }
  1079. }
  1080. else
  1081. {
  1082. if (ScpExplorer != NULL)
  1083. {
  1084. ScpExplorer->UnlockWindow();
  1085. }
  1086. FProgressTitle = L"";
  1087. FForegroundProgressTitle = L"";
  1088. UpdateAppTitle();
  1089. }
  1090. }
  1091. else
  1092. {
  1093. // If the least significant bit is set,
  1094. // the key was pressed after the previous call to GetAsyncKeyState.
  1095. int KeyState = GetAsyncKeyState(VK_ESCAPE);
  1096. if (FLAGSET(KeyState, 0x01))
  1097. {
  1098. Cancel = true;
  1099. }
  1100. if ((Now() - FDirectoryReadingStart) >= DirectoryReadingProgressDelay)
  1101. {
  1102. // 4 is arbitrary number
  1103. FForegroundProgressTitle =
  1104. FMTLOAD(ResolvedLinks >= 4 ? DIRECTORY_READING_AND_RESOLVING_PROGRESS : DIRECTORY_READING_PROGRESS,
  1105. (Progress));
  1106. UpdateAppTitle();
  1107. }
  1108. }
  1109. }
  1110. //---------------------------------------------------------------------------
  1111. void __fastcall TTerminalManager::TerminalCustomCommand(
  1112. TTerminal * /*Terminal*/, const UnicodeString & Command, bool & Handled)
  1113. {
  1114. // Implementation has to be thread-safe
  1115. Handled = CopyCommandToClipboard(Command);
  1116. }
  1117. //---------------------------------------------------------------------------
  1118. void __fastcall TTerminalManager::AuthenticatingDone()
  1119. {
  1120. FAuthenticating--;
  1121. if (FAuthenticating == 0)
  1122. {
  1123. BusyEnd(FBusyToken);
  1124. FBusyToken = NULL;
  1125. }
  1126. if (!FKeepAuthenticateForm)
  1127. {
  1128. CloseAutheticateForm();
  1129. }
  1130. }
  1131. //---------------------------------------------------------------------------
  1132. void __fastcall TTerminalManager::TerminalInformation(
  1133. TTerminal * Terminal, const UnicodeString & Str, bool /*Status*/, int Phase)
  1134. {
  1135. if (Phase == 1)
  1136. {
  1137. if (FAuthenticating == 0)
  1138. {
  1139. FBusyToken = BusyStart();
  1140. }
  1141. FAuthenticating++;
  1142. }
  1143. else if (Phase == 0)
  1144. {
  1145. DebugAssert(FAuthenticating > 0);
  1146. AuthenticatingDone();
  1147. }
  1148. else
  1149. {
  1150. if (FAuthenticating > 0)
  1151. {
  1152. bool ShowPending = false;
  1153. if (FAuthenticateForm == NULL)
  1154. {
  1155. FAuthenticateForm = MakeAuthenticateForm(Terminal);
  1156. ShowPending = true;
  1157. }
  1158. FAuthenticateForm->Log(Str);
  1159. if (ShowPending)
  1160. {
  1161. FAuthenticateForm->ShowAsModal();
  1162. }
  1163. }
  1164. }
  1165. }
  1166. //---------------------------------------------------------------------------
  1167. void __fastcall TTerminalManager::OperationFinished(::TFileOperation Operation,
  1168. TOperationSide Side, bool Temp, const UnicodeString & FileName, bool Success,
  1169. TOnceDoneOperation & OnceDoneOperation)
  1170. {
  1171. DebugAssert(ScpExplorer);
  1172. ScpExplorer->OperationFinished(Operation, Side, Temp, FileName, Success,
  1173. OnceDoneOperation);
  1174. }
  1175. //---------------------------------------------------------------------------
  1176. void __fastcall TTerminalManager::OperationProgress(
  1177. TFileOperationProgressType & ProgressData)
  1178. {
  1179. UpdateAppTitle();
  1180. DebugAssert(ScpExplorer);
  1181. ScpExplorer->OperationProgress(ProgressData);
  1182. }
  1183. //---------------------------------------------------------------------------
  1184. void __fastcall TTerminalManager::QueueEvent(TTerminalQueue * Queue, TQueueEvent Event)
  1185. {
  1186. TGuard Guard(FQueueSection);
  1187. FQueueEvents.push_back(std::make_pair(Queue, Event));
  1188. }
  1189. //---------------------------------------------------------------------------
  1190. void __fastcall TTerminalManager::DoConfigurationChange()
  1191. {
  1192. DebugAssert(Configuration);
  1193. DebugAssert(Configuration == WinConfiguration);
  1194. TTerminalQueue * Queue;
  1195. for (int Index = 0; Index < Count; Index++)
  1196. {
  1197. DebugAssert(Terminals[Index]->Log);
  1198. Terminals[Index]->Log->ReflectSettings();
  1199. Terminals[Index]->ActionLog->ReflectSettings();
  1200. Queue = reinterpret_cast<TTerminalQueue *>(FQueues->Items[Index]);
  1201. SetQueueConfiguration(Queue);
  1202. }
  1203. if (ScpExplorer)
  1204. {
  1205. ScpExplorer->ConfigurationChanged();
  1206. }
  1207. }
  1208. //---------------------------------------------------------------------------
  1209. void __fastcall TTerminalManager::ConfigurationChange(TObject * /*Sender*/)
  1210. {
  1211. if (FMainThread == GetCurrentThreadId())
  1212. {
  1213. DoConfigurationChange();
  1214. }
  1215. else
  1216. {
  1217. TGuard Guard(FChangeSection.get());
  1218. FPendingConfigurationChange++;
  1219. }
  1220. }
  1221. //---------------------------------------------------------------------------
  1222. void __fastcall TTerminalManager::TerminalReady()
  1223. {
  1224. ScpExplorer->Terminal = ActiveTerminal;
  1225. ScpExplorer->Queue = ActiveQueue;
  1226. ScpExplorer->TerminalReady();
  1227. }
  1228. //---------------------------------------------------------------------------
  1229. TStrings * __fastcall TTerminalManager::GetTerminalList()
  1230. {
  1231. FTerminalList->Clear();
  1232. for (int i = 0; i < Count; i++)
  1233. {
  1234. TTerminal * Terminal = Terminals[i];
  1235. UnicodeString Name = GetTerminalTitle(Terminal, true);
  1236. FTerminalList->AddObject(Name, Terminal);
  1237. }
  1238. return FTerminalList;
  1239. }
  1240. //---------------------------------------------------------------------------
  1241. int __fastcall TTerminalManager::GetActiveTerminalIndex()
  1242. {
  1243. return ActiveTerminal ? IndexOf(ActiveTerminal) : -1;
  1244. }
  1245. //---------------------------------------------------------------------------
  1246. void __fastcall TTerminalManager::SetActiveTerminalIndex(int value)
  1247. {
  1248. ActiveTerminal = Terminals[value];
  1249. }
  1250. //---------------------------------------------------------------------------
  1251. UnicodeString __fastcall TTerminalManager::GetTerminalShortPath(TTerminal * Terminal)
  1252. {
  1253. UnicodeString Result = UnixExtractFileName(Terminal->CurrentDirectory);
  1254. if (Result.IsEmpty())
  1255. {
  1256. Result = Terminal->CurrentDirectory;
  1257. }
  1258. return Result;
  1259. }
  1260. //---------------------------------------------------------------------------
  1261. UnicodeString __fastcall TTerminalManager::GetTerminalTitle(TTerminal * Terminal, bool Unique)
  1262. {
  1263. UnicodeString Result = Terminal->SessionData->SessionName;
  1264. if (Unique)
  1265. {
  1266. int Index = IndexOf(Terminal);
  1267. // not for background transfer sessions and disconnected sessions
  1268. if ((Index >= 0) && Terminal->Active)
  1269. {
  1270. for (int Index2 = 0; Index2 < Count; Index2++)
  1271. {
  1272. UnicodeString Name = Terminals[Index2]->SessionData->SessionName;
  1273. if ((Terminals[Index2] != Terminal) &&
  1274. Terminals[Index2]->Active &&
  1275. SameText(Name, Result))
  1276. {
  1277. UnicodeString Path = GetTerminalShortPath(Terminal);
  1278. if (!Path.IsEmpty())
  1279. {
  1280. Result = FORMAT(L"%s (%s)", (Result, Path));
  1281. }
  1282. break;
  1283. }
  1284. }
  1285. }
  1286. }
  1287. return Result;
  1288. }
  1289. //---------------------------------------------------------------------------
  1290. UnicodeString __fastcall TTerminalManager::GetActiveTerminalTitle(bool Unique)
  1291. {
  1292. UnicodeString Result;
  1293. if (ActiveTerminal != NULL)
  1294. {
  1295. Result = GetTerminalTitle(ActiveTerminal, Unique);
  1296. }
  1297. return Result;
  1298. }
  1299. //---------------------------------------------------------------------------
  1300. TTerminalQueue * __fastcall TTerminalManager::GetActiveQueue()
  1301. {
  1302. TTerminalQueue * Result = NULL;
  1303. if (ActiveTerminal != NULL)
  1304. {
  1305. Result = reinterpret_cast<TTerminalQueue *>(FQueues->Items[ActiveTerminalIndex]);
  1306. }
  1307. return Result;
  1308. }
  1309. //---------------------------------------------------------------------------
  1310. void __fastcall TTerminalManager::CycleTerminals(bool Forward)
  1311. {
  1312. if (Count > 0)
  1313. {
  1314. int Index = ActiveTerminalIndex;
  1315. Index += Forward ? 1 : -1;
  1316. if (Index < 0)
  1317. {
  1318. Index = Count-1;
  1319. }
  1320. else if (Index >= Count)
  1321. {
  1322. Index = 0;
  1323. }
  1324. ActiveTerminalIndex = Index;
  1325. }
  1326. }
  1327. //---------------------------------------------------------------------------
  1328. bool __fastcall TTerminalManager::CanOpenInPutty()
  1329. {
  1330. return (ActiveTerminal != NULL) && !GUIConfiguration->PuttyPath.Trim().IsEmpty();
  1331. }
  1332. //---------------------------------------------------------------------------
  1333. void __fastcall TTerminalManager::OpenInPutty()
  1334. {
  1335. Configuration->Usage->Inc(L"OpenInPutty");
  1336. TSessionData * Data = NULL;
  1337. try
  1338. {
  1339. // Is NULL on the first session when called from ConnectActiveTerminal()
  1340. // due to WinConfiguration->AutoOpenInPutty
  1341. if (ScpExplorer != NULL)
  1342. {
  1343. Data = ScpExplorer->CloneCurrentSessionData();
  1344. }
  1345. else
  1346. {
  1347. Data = new TSessionData(L"");
  1348. DebugAssert(ActiveTerminal != NULL);
  1349. Data->Assign(ActiveTerminal->SessionData);
  1350. ActiveTerminal->UpdateSessionCredentials(Data);
  1351. }
  1352. // putty does not support resolving environment variables in session settings
  1353. Data->ExpandEnvironmentVariables();
  1354. if (ActiveTerminal->TunnelLocalPortNumber != 0)
  1355. {
  1356. Data->ConfigureTunnel(ActiveTerminal->TunnelLocalPortNumber);
  1357. }
  1358. OpenSessionInPutty(GUIConfiguration->PuttyPath, Data);
  1359. }
  1360. __finally
  1361. {
  1362. delete Data;
  1363. }
  1364. }
  1365. //---------------------------------------------------------------------------
  1366. void __fastcall TTerminalManager::NewSession(bool /*FromSite*/, const UnicodeString & SessionUrl, bool ReloadSessions, TForm * LinkedForm)
  1367. {
  1368. if (ReloadSessions)
  1369. {
  1370. StoredSessions->Reload();
  1371. }
  1372. UnicodeString DownloadFile; // unused
  1373. std::unique_ptr<TObjectList> DataList(new TObjectList());
  1374. GetLoginData(SessionUrl, NULL, DataList.get(), DownloadFile, true, LinkedForm);
  1375. if (DataList->Count > 0)
  1376. {
  1377. ActiveTerminal = NewTerminals(DataList.get());
  1378. }
  1379. }
  1380. //---------------------------------------------------------------------------
  1381. void __fastcall TTerminalManager::Idle(bool SkipCurrentTerminal)
  1382. {
  1383. if (FPendingConfigurationChange > 0) // optimization
  1384. {
  1385. bool Changed = false;
  1386. {
  1387. TGuard Guard(FChangeSection.get());
  1388. if (DebugAlwaysTrue(FPendingConfigurationChange > 0))
  1389. {
  1390. FPendingConfigurationChange--;
  1391. Changed = true;
  1392. }
  1393. }
  1394. if (Changed)
  1395. {
  1396. DoConfigurationChange();
  1397. }
  1398. }
  1399. for (int Index = 0; Index < Count; Index++)
  1400. {
  1401. TTerminal * Terminal = Terminals[Index];
  1402. try
  1403. {
  1404. if (!SkipCurrentTerminal || (Terminal != ActiveTerminal))
  1405. {
  1406. TManagedTerminal * ManagedTerminal = dynamic_cast<TManagedTerminal *>(Terminal);
  1407. DebugAssert(ManagedTerminal != NULL);
  1408. // make sure Idle is called on the thread that runs the terminal
  1409. if (ManagedTerminal->TerminalThread != NULL)
  1410. {
  1411. ManagedTerminal->TerminalThread->Idle();
  1412. }
  1413. else
  1414. {
  1415. if (Terminal->Active)
  1416. {
  1417. Terminal->Idle();
  1418. }
  1419. }
  1420. if (Terminal->Active)
  1421. {
  1422. DebugAssert(Index < FQueues->Count);
  1423. if (Index < FQueues->Count)
  1424. {
  1425. reinterpret_cast<TTerminalQueue *>(FQueues->Items[Index])->Idle();
  1426. }
  1427. }
  1428. }
  1429. }
  1430. catch(Exception & E)
  1431. {
  1432. if (Terminal == ActiveTerminal)
  1433. {
  1434. // throw further, so that the exception is handled in proper place
  1435. // (particularly in broken-transfer reconnect handler, bug 72)
  1436. throw;
  1437. }
  1438. else
  1439. {
  1440. // we may not have inactive terminal, unless there is a explorer,
  1441. // also Idle is called from explorer anyway
  1442. DebugAssert(ScpExplorer != NULL);
  1443. if (ScpExplorer != NULL)
  1444. {
  1445. ScpExplorer->InactiveTerminalException(Terminal, &E);
  1446. }
  1447. if (!Terminal->Active)
  1448. {
  1449. // if session is lost, save the error message and rethrow it
  1450. // once the terminal gets activated
  1451. FTerminationMessages->Strings[Index] = E.Message;
  1452. }
  1453. }
  1454. }
  1455. }
  1456. TTerminalQueue * QueueWithEvent;
  1457. TQueueEvent QueueEvent;
  1458. do
  1459. {
  1460. QueueWithEvent = NULL;
  1461. {
  1462. TGuard Guard(FQueueSection);
  1463. if (!FQueueEvents.empty())
  1464. {
  1465. QueueWithEvent = FQueueEvents[0].first;
  1466. QueueEvent = FQueueEvents[0].second;
  1467. FQueueEvents.erase(FQueueEvents.begin());
  1468. }
  1469. }
  1470. if (QueueWithEvent != NULL)
  1471. {
  1472. int Index = FQueues->IndexOf(QueueWithEvent);
  1473. // the session may not exist anymore
  1474. if (Index >= 0)
  1475. {
  1476. TTerminal * Terminal = Terminals[Index];
  1477. // we can hardly have a queue event without explorer
  1478. DebugAssert(ScpExplorer != NULL);
  1479. if (ScpExplorer != NULL)
  1480. {
  1481. ScpExplorer->QueueEvent(Terminal, QueueWithEvent, QueueEvent);
  1482. }
  1483. }
  1484. }
  1485. }
  1486. while (QueueWithEvent != NULL);
  1487. }
  1488. //---------------------------------------------------------------------------
  1489. void __fastcall TTerminalManager::MasterPasswordPrompt()
  1490. {
  1491. if (GetCurrentThreadId() == MainThreadID)
  1492. {
  1493. if (!DoMasterPasswordDialog())
  1494. {
  1495. Abort();
  1496. }
  1497. }
  1498. else
  1499. {
  1500. // this can happen only when we keep cancelling all master password prompts
  1501. // as long as the sessing finally connects (session password has to be
  1502. // explictly typed in), and background transfer is started
  1503. Abort();
  1504. }
  1505. }
  1506. //---------------------------------------------------------------------------
  1507. void __fastcall TTerminalManager::Move(TTerminal * Source, TTerminal * Target)
  1508. {
  1509. int SourceIndex = IndexOf(Source);
  1510. int TargetIndex = IndexOf(Target);
  1511. TTerminalList::Move(SourceIndex, TargetIndex);
  1512. FQueues->Move(SourceIndex, TargetIndex);
  1513. DoTerminalListChanged();
  1514. // when there are indexed sessions with the same name,
  1515. // the index may change when reordering the sessions
  1516. UpdateAppTitle();
  1517. }
  1518. //---------------------------------------------------------------------------
  1519. void __fastcall TTerminalManager::DoTerminalListChanged()
  1520. {
  1521. if (OnTerminalListChanged)
  1522. {
  1523. OnTerminalListChanged(this);
  1524. }
  1525. }
  1526. //---------------------------------------------------------------------------
  1527. void __fastcall TTerminalManager::SaveWorkspace(TList * DataList)
  1528. {
  1529. for (int Index = 0; Index < Count; Index++)
  1530. {
  1531. TManagedTerminal * ManagedTerminal = dynamic_cast<TManagedTerminal *>(Terminals[Index]);
  1532. TSessionData * Data = StoredSessions->SaveWorkspaceData(ManagedTerminal->StateData, Index);
  1533. DataList->Add(Data);
  1534. }
  1535. }
  1536. //---------------------------------------------------------------------------
  1537. TTerminal * __fastcall TTerminalManager::FindActiveTerminalForSite(TSessionData * Data)
  1538. {
  1539. TTerminal * Result = NULL;
  1540. for (int Index = 0; (Result == NULL) && (Index < Count); Index++)
  1541. {
  1542. TTerminal * Terminal = Terminals[Index];
  1543. if (Terminal->Active &&
  1544. Terminal->SessionData->IsSameSite(Data))
  1545. {
  1546. Result = Terminal;
  1547. }
  1548. }
  1549. return Result;
  1550. }
  1551. //---------------------------------------------------------------------------
  1552. TTerminalQueue * __fastcall TTerminalManager::FindQueueForTerminal(TTerminal * Terminal)
  1553. {
  1554. int Index = IndexOf(Terminal);
  1555. return reinterpret_cast<TTerminalQueue *>(FQueues->Items[Index]);
  1556. }
  1557. //---------------------------------------------------------------------------
  1558. TRemoteFile * __fastcall TTerminalManager::CheckRights(
  1559. TTerminal * Terminal, const UnicodeString & EntryType, const UnicodeString & FileName, bool & WrongRights)
  1560. {
  1561. std::unique_ptr<TRemoteFile> FileOwner;
  1562. TRemoteFile * File;
  1563. try
  1564. {
  1565. Terminal->LogEvent(FORMAT(L"Checking %s \"%s\"...", (LowerCase(EntryType), FileName)));
  1566. Terminal->ReadFile(FileName, File);
  1567. FileOwner.reset(File);
  1568. int ForbiddenRights = TRights::rfGroupWrite | TRights::rfOtherWrite;
  1569. if ((File->Rights->Number & ForbiddenRights) != 0)
  1570. {
  1571. Terminal->LogEvent(FORMAT(L"%s \"%s\" exists, but has incorrect permissions %s.", (EntryType, FileName, File->Rights->Octal)));
  1572. WrongRights = true;
  1573. }
  1574. else
  1575. {
  1576. Terminal->LogEvent(FORMAT(L"%s \"%s\" exists and has correct permissions %s.", (EntryType, FileName, File->Rights->Octal)));
  1577. }
  1578. }
  1579. catch (Exception & E)
  1580. {
  1581. }
  1582. return FileOwner.release();
  1583. }
  1584. //---------------------------------------------------------------------------
  1585. bool __fastcall TTerminalManager::UploadPublicKey(
  1586. TTerminal * Terminal, TSessionData * Data, UnicodeString & FileName)
  1587. {
  1588. std::unique_ptr<TOpenDialog> OpenDialog(new TOpenDialog(Application));
  1589. OpenDialog->Title = LoadStr(LOGIN_PUBLIC_KEY_TITLE);
  1590. OpenDialog->Filter = LoadStr(LOGIN_PUBLIC_KEY_FILTER);
  1591. OpenDialog->DefaultExt = PuttyKeyExt;
  1592. OpenDialog->FileName = FileName;
  1593. bool Result = OpenDialog->Execute();
  1594. if (Result)
  1595. {
  1596. Configuration->Usage->Inc(L"PublicKeyInstallation");
  1597. FileName = OpenDialog->FileName;
  1598. bool AutoReadDirectory;
  1599. bool ExceptionOnFail;
  1600. UnicodeString TemporaryDir;
  1601. const UnicodeString SshFolder = L".ssh";
  1602. const UnicodeString AuthorizedKeysFile = L"authorized_keys";
  1603. UnicodeString AuthorizedKeysFilePath = FORMAT(L"%s/%s", (SshFolder, AuthorizedKeysFile));
  1604. VerifyAndConvertKey(FileName, ssh2only, false);
  1605. UnicodeString Comment;
  1606. UnicodeString Line = GetPublicKeyLine(FileName, Comment);
  1607. bool AdHocTerminal = (Terminal == NULL);
  1608. std::unique_ptr<TTerminal> TerminalOwner;
  1609. if (AdHocTerminal)
  1610. {
  1611. DebugAssert(Data != NULL);
  1612. TAutoFlag KeepAuthenticateFormFlag(FKeepAuthenticateForm);
  1613. try
  1614. {
  1615. TerminalOwner.reset(CreateTerminal(Data));
  1616. Terminal = TerminalOwner.get();
  1617. SetupTerminal(Terminal);
  1618. Terminal->OnProgress = NULL;
  1619. Terminal->OnFinished = NULL;
  1620. DoConnectTerminal(Terminal, false, true);
  1621. }
  1622. catch (Exception & E)
  1623. {
  1624. CloseAutheticateForm();
  1625. throw;
  1626. }
  1627. }
  1628. bool Installed = false;
  1629. bool WrongRights = false;
  1630. AutoReadDirectory = Terminal->AutoReadDirectory;
  1631. ExceptionOnFail = Terminal->ExceptionOnFail;
  1632. try
  1633. {
  1634. Terminal->AutoReadDirectory = false;
  1635. Terminal->ExceptionOnFail = true;
  1636. UnicodeString SshImplementation = Terminal->GetSessionInfo().SshImplementation;
  1637. UnicodeString NotOpenSSHMessage = FMTLOAD(LOGIN_NOT_OPENSSH, (SshImplementation));
  1638. if (IsOpenSSH(SshImplementation) ||
  1639. (MessageDialog(NotOpenSSHMessage, qtConfirmation, qaOK | qaCancel, HELP_LOGIN_AUTHORIZED_KEYS) == qaOK))
  1640. {
  1641. Terminal->Log->AddSeparator();
  1642. Terminal->LogEvent(FORMAT(L"Adding public key line to \"%s\" file:\n%s", (AuthorizedKeysFilePath, Line)));
  1643. // Ad-hoc terminal
  1644. if (FAuthenticateForm != NULL)
  1645. {
  1646. FAuthenticateForm->Log(FMTLOAD(LOGIN_PUBLIC_KEY_UPLOAD, (Comment)));
  1647. }
  1648. UnicodeString SshFolderAbsolutePath = UnixIncludeTrailingBackslash(Terminal->GetHomeDirectory()) + SshFolder;
  1649. std::unique_ptr<TRemoteFile> SshFolderFile(CheckRights(Terminal, L"Folder", SshFolderAbsolutePath, WrongRights));
  1650. if (SshFolderFile.get() == NULL)
  1651. {
  1652. TRights SshFolderRights;
  1653. SshFolderRights.Number = TRights::rfUserRead | TRights::rfUserWrite | TRights::rfUserExec;
  1654. TRemoteProperties SshFolderProperties;
  1655. SshFolderProperties.Rights = SshFolderRights;
  1656. SshFolderProperties.Valid = TValidProperties() << vpRights;
  1657. Terminal->LogEvent(FORMAT(L"Trying to create \"%s\" folder with permissions %s...", (SshFolder, SshFolderRights.Octal)));
  1658. Terminal->CreateDirectory(SshFolderAbsolutePath, &SshFolderProperties);
  1659. }
  1660. TemporaryDir = ExcludeTrailingBackslash(WinConfiguration->TemporaryDir());
  1661. if (!ForceDirectories(ApiPath(TemporaryDir)))
  1662. {
  1663. throw EOSExtException(FMTLOAD(CREATE_TEMP_DIR_ERROR, (TemporaryDir)));
  1664. }
  1665. UnicodeString TemporaryAuthorizedKeysFile = IncludeTrailingBackslash(TemporaryDir) + AuthorizedKeysFile;
  1666. UnicodeString AuthorizedKeysFileAbsolutePath = UnixIncludeTrailingBackslash(SshFolderAbsolutePath) + AuthorizedKeysFile;
  1667. bool Updated = true;
  1668. TCopyParamType CopyParam; // Use factory defaults
  1669. CopyParam.ResumeSupport = rsOff; // not to break the permissions
  1670. CopyParam.PreserveTime = false; // not needed
  1671. UnicodeString AuthorizedKeys;
  1672. std::unique_ptr<TRemoteFile> AuthorizedKeysFileFile(CheckRights(Terminal, L"File", AuthorizedKeysFileAbsolutePath, WrongRights));
  1673. if (AuthorizedKeysFileFile.get() != NULL)
  1674. {
  1675. AuthorizedKeysFileFile->FullFileName = AuthorizedKeysFileAbsolutePath;
  1676. std::unique_ptr<TStrings> Files(new TStringList());
  1677. Files->AddObject(AuthorizedKeysFileAbsolutePath, AuthorizedKeysFileFile.get());
  1678. Terminal->LogEvent(FORMAT(L"Downloading current \"%s\" file...", (AuthorizedKeysFile)));
  1679. Terminal->CopyToLocal(Files.get(), TemporaryDir, &CopyParam, cpNoConfirmation, NULL);
  1680. // Overload with Encoding parameter work incorrectly, when used on a file without BOM
  1681. AuthorizedKeys = TFile::ReadAllText(TemporaryAuthorizedKeysFile);
  1682. std::unique_ptr<TStrings> AuthorizedKeysLines(TextToStringList(AuthorizedKeys));
  1683. int P = Line.Pos(L" ");
  1684. if (DebugAlwaysTrue(P > 0))
  1685. {
  1686. P = PosEx(L" ", Line, P + 1);
  1687. }
  1688. UnicodeString Prefix = Line.SubString(1, P); // including the space
  1689. for (int Index = 0; Index < AuthorizedKeysLines->Count; Index++)
  1690. {
  1691. if (StartsStr(Prefix, AuthorizedKeysLines->Strings[Index]))
  1692. {
  1693. Terminal->LogEvent(FORMAT(L"\"%s\" file already contains public key line:\n%s", (AuthorizedKeysFile, AuthorizedKeysLines->Strings[Index])));
  1694. Updated = false;
  1695. }
  1696. }
  1697. if (Updated)
  1698. {
  1699. Terminal->LogEvent(FORMAT(L"\"%s\" file does not contain the public key line yet.", (AuthorizedKeysFile)));
  1700. if (!EndsStr(L"\n", AuthorizedKeys))
  1701. {
  1702. Terminal->LogEvent(FORMAT(L"Adding missing trailing new line to \"%s\" file...", (AuthorizedKeysFile)));
  1703. AuthorizedKeys += L"\n";
  1704. }
  1705. }
  1706. }
  1707. else
  1708. {
  1709. Terminal->LogEvent(FORMAT(L"Creating new \"%s\" file...", (AuthorizedKeysFile)));
  1710. CopyParam.PreserveRights = true;
  1711. CopyParam.Rights.Number = TRights::rfUserRead | TRights::rfUserWrite;
  1712. }
  1713. if (Updated)
  1714. {
  1715. AuthorizedKeys += Line + L"\n";
  1716. // Overload without Encoding parameter uses TEncoding::UTF8, but does not write BOM, what we want
  1717. TFile::WriteAllText(TemporaryAuthorizedKeysFile, AuthorizedKeys);
  1718. std::unique_ptr<TStrings> Files(new TStringList());
  1719. Files->Add(TemporaryAuthorizedKeysFile);
  1720. Terminal->LogEvent(FORMAT(L"Uploading updated \"%s\" file...", (AuthorizedKeysFile)));
  1721. Terminal->CopyToRemote(Files.get(), SshFolderAbsolutePath, &CopyParam, cpNoConfirmation, NULL);
  1722. }
  1723. Installed = true;
  1724. }
  1725. }
  1726. __finally
  1727. {
  1728. Terminal->AutoReadDirectory = AutoReadDirectory;
  1729. Terminal->ExceptionOnFail = ExceptionOnFail;
  1730. if (!TemporaryDir.IsEmpty())
  1731. {
  1732. RecursiveDeleteFile(ExcludeTrailingBackslash(TemporaryDir), false);
  1733. }
  1734. CloseAutheticateForm(); // When uploading from Login dialog
  1735. }
  1736. if (Installed)
  1737. {
  1738. Terminal->LogEvent(L"Public key installation done.");
  1739. if (AdHocTerminal)
  1740. {
  1741. TerminalOwner.reset(NULL);
  1742. }
  1743. else
  1744. {
  1745. Terminal->Log->AddSeparator();
  1746. }
  1747. UnicodeString Message = FMTLOAD(LOGIN_PUBLIC_KEY_UPLOADED, (Comment));
  1748. if (WrongRights)
  1749. {
  1750. Message += L"\n\n" + FMTLOAD(LOGIN_PUBLIC_KEY_PERMISSIONS, (AuthorizedKeysFilePath));
  1751. }
  1752. MessageDialog(Message, qtInformation, qaOK, HELP_LOGIN_AUTHORIZED_KEYS);
  1753. }
  1754. }
  1755. return Result;
  1756. }