1
0

TerminalManager.cpp 67 KB

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