TerminalManager.cpp 47 KB

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