//--------------------------------------------------------------------------- #define NO_WIN32_LEAN_AND_MEAN #include #pragma hdrstop #include "TerminalManager.h" #include #include "CustomScpExplorer.h" #include "LogMemo.h" #include "NonVisual.h" #include "WinConfiguration.h" #include "Tools.h" #include #include #include #include #include #include #include #include #include #include //--------------------------------------------------------------------------- #pragma package(smart_init) //--------------------------------------------------------------------------- TTerminalManager * TTerminalManager::FInstance = NULL; //--------------------------------------------------------------------------- __fastcall TManagedTerminal::TManagedTerminal(TSessionData * SessionData, TConfiguration * Configuration) : TTerminal(SessionData, Configuration), LocalExplorerState(NULL), RemoteExplorerState(NULL), ReopenStart(0), DirectoryLoaded(Now()), TerminalThread(NULL) { StateData = new TSessionData(L""); StateData->Assign(SessionData); StateData->LocalDirectory = ::ExpandFileName(StateData->LocalDirectory); } //--------------------------------------------------------------------------- __fastcall TManagedTerminal::~TManagedTerminal() { delete StateData; delete LocalExplorerState; delete RemoteExplorerState; } //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- TTerminalManager * __fastcall TTerminalManager::Instance(bool ForceCreation) { if (!FInstance && ForceCreation) { FInstance = new TTerminalManager(); } return FInstance; } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::DestroyInstance() { DebugAssert(FInstance); SAFE_DESTROY(FInstance); } //--------------------------------------------------------------------------- __fastcall TTerminalManager::TTerminalManager() : TTerminalList(Configuration) { FQueueSection = new TCriticalSection(); FLogMemo = NULL; FActiveTerminal = NULL; FScpExplorer = NULL; FDestroying = false; FTerminalPendingAction = tpNull; FDirectoryReadingStart = 0; FAuthenticateForm = NULL; FTaskbarList = NULL; FAuthenticating = 0; FApplicationsEvents.reset(new TApplicationEvents(Application)); FApplicationsEvents->OnException = ApplicationException; FApplicationsEvents->OnShowHint = ApplicationShowHint; FApplicationsEvents->OnMessage = ApplicationMessage; FApplicationsEvents->OnModalBegin = ApplicationModalBegin; FApplicationsEvents->OnModalEnd = ApplicationModalEnd; DebugAssert(WinConfiguration->OnMasterPasswordPrompt == NULL); WinConfiguration->OnMasterPasswordPrompt = MasterPasswordPrompt; InitTaskbarButtonCreatedMessage(); DebugAssert(Configuration && !Configuration->OnChange); Configuration->OnChange = ConfigurationChange; FOnLastTerminalClosed = NULL; FOnTerminalListChanged = NULL; FTerminalList = new TStringList(); FQueues = new TList(); FTerminationMessages = new TStringList(); } //--------------------------------------------------------------------------- __fastcall TTerminalManager::~TTerminalManager() { FreeAll(); DebugAssert(!ScpExplorer); DebugAssert(Configuration->OnChange == ConfigurationChange); Configuration->OnChange = NULL; FApplicationsEvents.reset(NULL); DebugAssert(WinConfiguration->OnMasterPasswordPrompt == MasterPasswordPrompt); WinConfiguration->OnMasterPasswordPrompt = NULL; delete FQueues; delete FTerminationMessages; delete FTerminalList; delete FAuthenticateForm; delete FQueueSection; ReleaseTaskbarList(); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::SetQueueConfiguration(TTerminalQueue * Queue) { Queue->TransfersLimit = GUIConfiguration->QueueTransfersLimit; Queue->KeepDoneItemsFor = (GUIConfiguration->QueueKeepDoneItems ? GUIConfiguration->QueueKeepDoneItemsFor : 0); } //--------------------------------------------------------------------------- TTerminalQueue * __fastcall TTerminalManager::NewQueue(TTerminal * Terminal) { TTerminalQueue * Queue = new TTerminalQueue(Terminal, Configuration); SetQueueConfiguration(Queue); Queue->Enabled = WinConfiguration->EnableQueueByDefault; Queue->OnQueryUser = TerminalQueryUser; Queue->OnPromptUser = TerminalPromptUser; Queue->OnShowExtendedException = TerminalShowExtendedException; Queue->OnEvent = QueueEvent; return Queue; } //--------------------------------------------------------------------------- TTerminal * __fastcall TTerminalManager::CreateTerminal(TSessionData * Data) { return new TManagedTerminal(Data, Configuration); } //--------------------------------------------------------------------------- TTerminal * __fastcall TTerminalManager::DoNewTerminal(TSessionData * Data) { FTerminalList->Clear(); TTerminal * Terminal = TTerminalList::NewTerminal(Data); try { FQueues->Add(NewQueue(Terminal)); FTerminationMessages->Add(L""); Terminal->OnQueryUser = TerminalQueryUser; Terminal->OnPromptUser = TerminalPromptUser; Terminal->OnDisplayBanner = TerminalDisplayBanner; Terminal->OnShowExtendedException = TerminalShowExtendedException; Terminal->OnProgress = OperationProgress; Terminal->OnFinished = OperationFinished; Terminal->OnDeleteLocalFile = DeleteLocalFile; Terminal->OnReadDirectoryProgress = TerminalReadDirectoryProgress; Terminal->OnInformation = TerminalInformation; } catch(...) { if (Terminal != NULL) { FreeTerminal(Terminal); } throw; } return Terminal; } //--------------------------------------------------------------------------- TTerminal * __fastcall TTerminalManager::NewTerminal(TSessionData * Data) { TTerminal * Terminal = DoNewTerminal(Data); DoTerminalListChanged(); return Terminal; } //--------------------------------------------------------------------------- TTerminal * __fastcall TTerminalManager::NewTerminals(TList * DataList) { TTerminal * Result = NULL; for (int Index = 0; Index < DataList->Count; Index++) { TTerminal * Terminal = NewTerminal(reinterpret_cast(DataList->Items[Index])); if (Index == 0) { Result = Terminal; } } DoTerminalListChanged(); return Result; } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::FreeActiveTerminal() { if (FTerminalPendingAction == tpNull) { DebugAssert(ActiveTerminal); FreeTerminal(ActiveTerminal); } else { DebugAssert(FTerminalPendingAction == ::tpNone); FTerminalPendingAction = tpFree; } } //--------------------------------------------------------------------------- void TTerminalManager::ConnectTerminal(TTerminal * Terminal, bool Reopen) { TManagedTerminal * ManagedTerminal = dynamic_cast(Terminal); // it must be managed terminal, unless it is secondary terminal (of managed terminal) DebugAssert((ManagedTerminal != NULL) || (dynamic_cast(Terminal) != NULL)); // particularly when we are reconnecting RemoteDirectory of managed terminal // hold the last used remote directory as opposite to session data, which holds // the default remote directory. // make sure the last used directory is used, but the default is preserved too UnicodeString OrigRemoteDirectory = Terminal->SessionData->RemoteDirectory; try { TTerminalThread * TerminalThread = new TTerminalThread(Terminal); try { if (ManagedTerminal != NULL) { Terminal->SessionData->RemoteDirectory = ManagedTerminal->StateData->RemoteDirectory; if ((double)ManagedTerminal->ReopenStart == 0) { ManagedTerminal->ReopenStart = Now(); } DebugAssert(ManagedTerminal->TerminalThread == NULL); ManagedTerminal->TerminalThread = TerminalThread; } TNotifyEvent OnIdle; ((TMethod*)&OnIdle)->Code = TerminalThreadIdle; TerminalThread->OnIdle = OnIdle; if (Reopen) { TerminalThread->TerminalReopen(); } else { TerminalThread->TerminalOpen(); } } __finally { if (ManagedTerminal != NULL) { ManagedTerminal->TerminalThread = NULL; } delete TerminalThread; } } __finally { Terminal->SessionData->RemoteDirectory = OrigRemoteDirectory; if (Terminal->Active && (ManagedTerminal != NULL)) { ManagedTerminal->ReopenStart = 0; } } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::TerminalThreadIdle(void * /*Data*/, TObject * /*Sender*/) { Application->ProcessMessages(); } //--------------------------------------------------------------------------- bool __fastcall TTerminalManager::ConnectActiveTerminalImpl(bool Reopen) { TTerminalPendingAction Action; bool Result; do { Action = tpNull; Result = false; try { DebugAssert(ActiveTerminal); ConnectTerminal(ActiveTerminal, Reopen); if (ScpExplorer) { DebugAssert(ActiveTerminal->Status == ssOpened); TerminalReady(); } WinConfiguration->ClearTemporaryLoginData(); Result = true; } catch(Exception & E) { DebugAssert(FTerminalPendingAction == tpNull); FTerminalPendingAction = ::tpNone; try { DebugAssert(ActiveTerminal != NULL); ActiveTerminal->ShowExtendedException(&E); Action = FTerminalPendingAction; } __finally { FTerminalPendingAction = tpNull; } } } while (Action == tpReconnect); if (Action == tpFree) { FreeActiveTerminal(); } return Result; } //--------------------------------------------------------------------------- bool __fastcall TTerminalManager::ConnectActiveTerminal() { ActiveTerminal->CollectUsage(); // add only stored sessions to the jump list, // ad-hoc session cannot be reproduced from just a session name if (StoredSessions->FindSame(ActiveTerminal->SessionData) != NULL) { WinConfiguration->AddSessionToJumpList(ActiveTerminal->SessionData->SessionName); } FAuthenticationCancelled = false; bool Result = ConnectActiveTerminalImpl(false); UnicodeString DateStamp = StandardDatestamp(); if (Result) { if (Configuration->Usage->Get(L"OpenedSessionsFailedLastDate") == DateStamp) { Configuration->Usage->Inc(L"OpenedSessionsFailedRecovered"); } } else { Configuration->Usage->Inc(L"OpenedSessionsFailed"); Configuration->Usage->Set(L"OpenedSessionsFailedLastDate", DateStamp); if (FAuthenticationCancelled) { Configuration->Usage->Inc(L"OpenedSessionsFailedAfterCancel"); } } if (Result && WinConfiguration->AutoOpenInPutty && CanOpenInPutty()) { try { OpenInPutty(); } catch(Exception & E) { ShowExtendedExceptionEx(NULL, &E); } } return Result; } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::DisconnectActiveTerminal() { DebugAssert(ActiveTerminal); int Index = IndexOf(ActiveTerminal); TTerminalQueue * OldQueue; TTerminalQueue * NewQueue; OldQueue = reinterpret_cast(FQueues->Items[Index]); NewQueue = this->NewQueue(ActiveTerminal); FQueues->Items[Index] = NewQueue; ScpExplorer->Queue = NewQueue; delete OldQueue; } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::ReconnectActiveTerminal() { DebugAssert(ActiveTerminal); if (ScpExplorer) { if (ScpExplorer->Terminal == ActiveTerminal) { ScpExplorer->UpdateTerminal(ActiveTerminal); } } try { if (FTerminalPendingAction == tpNull) { ConnectActiveTerminalImpl(true); } else { FTerminalPendingAction = tpReconnect; } } catch(...) { FreeActiveTerminal(); throw; } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::FreeAll() { FDestroying = true; try { while (Count) { FreeTerminal(Terminals[0]); } } __finally { FDestroying = false; } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::FreeTerminal(TTerminal * Terminal) { try { // we want the Login dialog to open on auto-workspace name, // as set in TCustomScpExplorerForm::FormClose if (!FDestroying || !WinConfiguration->AutoSaveWorkspace) { if (StoredSessions->FindSame(Terminal->SessionData) != NULL) { WinConfiguration->LastStoredSession = Terminal->SessionData->Name; } } if (ScpExplorer != NULL) { ScpExplorer->TerminalRemoved(Terminal); } if (Terminal->Active) { Terminal->Close(); } } __finally { int Index = IndexOf(Terminal); FTerminalList->Clear(); Extract(Terminal); TTerminalQueue * Queue; Queue = reinterpret_cast(FQueues->Items[Index]); FQueues->Delete(Index); FTerminationMessages->Delete(Index); try { if (ActiveTerminal && (Terminal == ActiveTerminal)) { if ((Count > 0) && !FDestroying) { ActiveTerminal = Terminals[Index < Count ? Index : Index - 1]; } else { ActiveTerminal = NULL; } } else { SaveTerminal(Terminal); } } // ActiveTerminal = NULL throws EAbort when Login dialog is closed __finally { // only now all references to/from queue (particularly events to explorer) // are cleared delete Queue; delete Terminal; DoTerminalListChanged(); } } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::SetScpExplorer(TCustomScpExplorerForm * value) { if (ScpExplorer != value) { // changing explorer is not supported yet DebugAssert(!ScpExplorer || !value); FScpExplorer = value; if (FScpExplorer) { FScpExplorer->Terminal = ActiveTerminal; FScpExplorer->Queue = ActiveQueue; FOnLastTerminalClosed = FScpExplorer->LastTerminalClosed; FOnTerminalListChanged = FScpExplorer->TerminalListChanged; UpdateTaskbarList(); } else { FOnLastTerminalClosed = NULL; FOnTerminalListChanged = NULL; } } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::SetActiveTerminal(TTerminal * value) { DoSetActiveTerminal(value, false); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::SetActiveTerminalWithAutoReconnect(TTerminal * value) { DoSetActiveTerminal(value, true); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::DoSetActiveTerminal(TTerminal * value, bool AutoReconnect) { if (ActiveTerminal != value) { // here used to be call to TCustomScpExporer::UpdateSessionData (now UpdateTerminal) // but it seems to be duplicate to call from TCustomScpExporer::TerminalChanging TTerminal * PActiveTerminal = ActiveTerminal; FActiveTerminal = value; // moved from else block of next if (ActiveTerminal) statement // so ScpExplorer can update its caption UpdateAppTitle(); if (ScpExplorer) { if (ActiveTerminal && (ActiveTerminal->Status == ssOpened)) { TerminalReady(); } else { ScpExplorer->Terminal = NULL; ScpExplorer->Queue = NULL; } } if (PActiveTerminal && !PActiveTerminal->Active) { SaveTerminal(PActiveTerminal); } if (ActiveTerminal) { if (!PActiveTerminal) { CreateLogMemo(); } DebugAssert(LogMemo); LogMemo->SessionLog = ActiveTerminal->Log; SwitchLogFormSessionLog(); int Index = ActiveTerminalIndex; if (!ActiveTerminal->Active && !FTerminationMessages->Strings[Index].IsEmpty()) { UnicodeString Message = FTerminationMessages->Strings[Index]; FTerminationMessages->Strings[Index] = L""; if (AutoReconnect) { ReconnectActiveTerminal(); } else { Exception * E = new ESshFatal(NULL, Message); try { // finally show pending terminal message, // this gives user also possibility to reconnect ActiveTerminal->ShowExtendedException(E); } __finally { delete E; } } } } else { FreeLogMemo(); if (OnLastTerminalClosed) { OnLastTerminalClosed(this); } } if ((ActiveTerminal != NULL) && !ActiveTerminal->Active) { ConnectActiveTerminal(); } } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::QueueStatusUpdated() { UpdateAppTitle(); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::UpdateAppTitle() { if (ScpExplorer) { UnicodeString NewTitle; if (ActiveTerminal) { NewTitle = FormatMainFormCaption(ActiveTerminalTitle); } else { NewTitle = FormatMainFormCaption(L""); } UnicodeString QueueProgressTitle; if (!FForegroundProgressTitle.IsEmpty()) { NewTitle = FForegroundProgressTitle + L" - " + NewTitle; } else if (!FProgressTitle.IsEmpty() && !ForegroundTask()) { NewTitle = FProgressTitle + L" - " + NewTitle; } else if ((ScpExplorer != NULL) && (ScpExplorer->Handle != GetAncestor(GetActiveWindow(), GA_ROOTOWNER)) && !(QueueProgressTitle = ScpExplorer->GetQueueProgressTitle()).IsEmpty()) { NewTitle = QueueProgressTitle + L" - " + NewTitle; } else if (ActiveTerminal && (ScpExplorer != NULL)) { UnicodeString Path = ScpExplorer->PathForCaption(); if (!Path.IsEmpty()) { NewTitle = Path + L" - " + NewTitle; } } ScpExplorer->Caption = NewTitle; ScpExplorer->ApplicationTitleChanged(); } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::SaveTerminal(TTerminal * Terminal) { TSessionData * Data = StoredSessions->FindSame(Terminal->SessionData); if (Data != NULL) { TManagedTerminal * ManagedTerminal = dynamic_cast(Terminal); DebugAssert(ManagedTerminal != NULL); bool Changed = false; if (Terminal->SessionData->UpdateDirectories) { Data->CopyDirectoriesStateData(ManagedTerminal->StateData); Changed = true; } if (Changed) { // modified only, implicit StoredSessions->Save(false, false); } } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::CreateLogMemo() { DebugAssert(!FLogMemo); DebugAssert(ActiveTerminal); FLogMemo = new TLogMemo(Application); try { FLogMemo->SessionLog = ActiveTerminal->Log; FLogMemo->PopupMenu = NonVisualDataModule->LogMemoPopup; } catch (...) { delete FLogMemo; throw; } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::FreeLogMemo() { DebugAssert(LogMemo); LogMemo->PopupMenu = NULL; SAFE_DESTROY(FLogMemo); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::HandleException(Exception * E) { // can be null for example when exception is thrown on login dialog if (ActiveTerminal != NULL) { ActiveTerminal->ShowExtendedException(E); } else { ShowExtendedException(E); } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::ApplicationException(TObject * /*Sender*/, Exception * E) { HandleException(E); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::ApplicationShowHint(UnicodeString & HintStr, bool & /*CanShow*/, THintInfo & HintInfo) { HintInfo.HintData = HintInfo.HintControl; TLabel * HintLabel = dynamic_cast(HintInfo.HintControl); if ((HintLabel != NULL) && HasLabelHintPopup(HintLabel, HintStr)) { // Hack for transfer setting labels. // Should be converted to something like HintLabel() HintInfo.HideTimeout = 100000; // "almost" never } else if (dynamic_cast(HintInfo.HintControl) != NULL) { // Hint is forcibly hidden in TProgressForm::FormHide HintInfo.HideTimeout = 100000; // "almost" never HintInfo.ReshowTimeout = 500; // updated each 0.5s } else { int HintMaxWidth = 300; TControl * ScaleControl = HintInfo.HintControl; if (DebugAlwaysFalse(HintInfo.HintControl == NULL) || (GetParentForm(HintInfo.HintControl) == NULL)) { ScaleControl = ScpExplorer; } HintMaxWidth = ScaleByTextHeight(ScaleControl, HintMaxWidth); HintInfo.HintMaxWidth = HintMaxWidth; } } //--------------------------------------------------------------------------- bool __fastcall TTerminalManager::HandleMouseWheel(WPARAM WParam, LPARAM LParam) { // WORKAROUND This is no longer necessary on Windows 10 bool Result = false; if (Application->Active) { TPoint Point(LOWORD(LParam), HIWORD(LParam)); TWinControl * Control = FindVCLWindow(Point); if (Control != NULL) { TCustomForm * Form = GetParentForm(Control); // Only case we expect the parent form to be NULL is on the Find/Replace dialog, // which is owned by VCL's internal TRedirectorWindow. DebugAssert((Form != NULL) || (Control->ClassName() == L"TRedirectorWindow")); if ((Form != NULL) && Form->Active) { // Send it only to windows we tested it with. // Though we should sooner or later remove this test and pass it to all our windows. if (Form->Perform(WM_WANTS_MOUSEWHEEL, 0, 0) == 1) { SendMessage(Control->Handle, WM_MOUSEWHEEL, WParam, LParam); Result = true; } } } } return Result; } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::ApplicationMessage(TMsg & Msg, bool & Handled) { if (Msg.message == FTaskbarButtonCreatedMessage) { CreateTaskbarList(); } if (Msg.message == WM_MOUSEWHEEL) { Handled = HandleMouseWheel(Msg.wParam, Msg.lParam); } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::ApplicationModalBegin(TObject * /*Sender*/) { NonVisualDataModule->StartBusy(); if (ScpExplorer != NULL) { ScpExplorer->SuspendWindowLock(); } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::ApplicationModalEnd(TObject * /*Sender*/) { NonVisualDataModule->EndBusy(); if (ScpExplorer != NULL) { ScpExplorer->ResumeWindowLock(); } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::InitTaskbarButtonCreatedMessage() { // XE6 VCL already handles TaskbarButtonCreated, but does not call ChangeWindowMessageFilterEx. // So we keep our implementation. // See also http://stackoverflow.com/a/14618587/850848 FTaskbarButtonCreatedMessage = RegisterWindowMessage(L"TaskbarButtonCreated"); HINSTANCE User32Library = LoadLibrary(L"user32.dll"); ChangeWindowMessageFilterExProc ChangeWindowMessageFilterEx = (ChangeWindowMessageFilterExProc)GetProcAddress(User32Library, "ChangeWindowMessageFilterEx"); if (ChangeWindowMessageFilterEx != NULL) { // without this we won't get TaskbarButtonCreated, when app is running elevated ChangeWindowMessageFilterEx( Application->Handle, FTaskbarButtonCreatedMessage, MSGFLT_ALLOW, NULL); } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::CreateTaskbarList() { ReleaseTaskbarList(); if(SUCCEEDED(CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_ALL, IID_ITaskbarList3, (void **) &FTaskbarList))) { if (ScpExplorer != NULL) { UpdateTaskbarList(); } } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::ReleaseTaskbarList() { if (FTaskbarList != NULL) { FTaskbarList->Release(); } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::UpdateTaskbarList() { ScpExplorer->UpdateTaskbarList(FTaskbarList); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::DeleteLocalFile(const UnicodeString FileName, bool Alternative) { RecursiveDeleteFileChecked(FileName, (WinConfiguration->DeleteToRecycleBin != Alternative)); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::TerminalQueryUser(TObject * Sender, const UnicodeString Query, TStrings * MoreMessages, unsigned int Answers, const TQueryParams * Params, unsigned int & Answer, TQueryType Type, void * /*Arg*/) { UnicodeString HelpKeyword; TMessageParams MessageParams(Params); UnicodeString AQuery = Query; if (Params != NULL) { HelpKeyword = Params->HelpKeyword; if (FLAGSET(Params->Params, qpFatalAbort)) { AQuery = FMTLOAD(WARN_FATAL_ERROR, (AQuery)); if (!MessageParams.TimerMessage.IsEmpty()) { MessageParams.TimerMessage = FMTLOAD(WARN_FATAL_ERROR, (MessageParams.TimerMessage)); } } } if (ScpExplorer) { Answer = ScpExplorer->MoreMessageDialog(AQuery, MoreMessages, Type, Answers, HelpKeyword, &MessageParams, dynamic_cast(Sender)); } else { Answer = MoreMessageDialog(AQuery, MoreMessages, Type, Answers, HelpKeyword, &MessageParams); } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::AuthenticateFormCancel(TObject * Sender) { TAuthenticateForm * Form = dynamic_cast(Sender); DebugAssert(Form != NULL); TManagedTerminal * ManagedTerminal = dynamic_cast(Form->Terminal); // will be null e.g. for background transfers if (ManagedTerminal != NULL) { TTerminalThread * TerminalThread = ManagedTerminal->TerminalThread; // can be NULL for reconnects from transfers if ((TerminalThread != NULL) && !TerminalThread->Cancelling) { Form->Log(LoadStr(AUTH_CANCELLING)); TerminalThread->Cancel(); } } FAuthenticationCancelled = true; } //--------------------------------------------------------------------------- TAuthenticateForm * __fastcall TTerminalManager::MakeAuthenticateForm( TTerminal * Terminal) { TAuthenticateForm * Dialog = SafeFormCreate(); Dialog->Init(Terminal); DebugAssert(Dialog->OnCancel == NULL); Dialog->OnCancel = AuthenticateFormCancel; return Dialog; } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::FileNameInputDialogInitializeRenameBaseName( TObject * /*Sender*/, TInputDialogData * Data) { EditSelectBaseName(Data->Edit->Handle); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::TerminalPromptUser( TTerminal * Terminal, TPromptKind Kind, UnicodeString Name, UnicodeString Instructions, TStrings * Prompts, TStrings * Results, bool & Result, void * /*Arg*/) { if (((Kind == pkPrompt) || (Kind == pkFileName)) && (FAuthenticateForm == NULL) && (Terminal->Status != ssOpening)) { DebugAssert(Instructions.IsEmpty()); DebugAssert(Prompts->Count == 1); DebugAssert(FLAGSET(int(Prompts->Objects[0]), pupEcho)); UnicodeString AResult = Results->Strings[0]; TInputDialogInitialize InputDialogInitialize = NULL; if ((Kind == pkFileName) && !WinConfiguration->RenameWholeName) { InputDialogInitialize = FileNameInputDialogInitializeRenameBaseName; } Result = InputDialog(Name, Prompts->Strings[0], AResult, L"", NULL, false, InputDialogInitialize); if (Result) { Results->Strings[0] = AResult; } } else { TAuthenticateForm * AuthenticateForm = FAuthenticateForm; if (AuthenticateForm == NULL) { AuthenticateForm = MakeAuthenticateForm(Terminal); } try { Result = AuthenticateForm->PromptUser(Kind, Name, Instructions, Prompts, Results, (FAuthenticateForm != NULL), Terminal->StoredCredentialsTried); } __finally { if (FAuthenticateForm == NULL) { delete AuthenticateForm; } } } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::TerminalDisplayBanner( TTerminal * Terminal, UnicodeString SessionName, const UnicodeString & Banner, bool & NeverShowAgain, int Options) { DebugAssert(FAuthenticateForm != NULL); TAuthenticateForm * AuthenticateForm = FAuthenticateForm; if (AuthenticateForm == NULL) { AuthenticateForm = MakeAuthenticateForm(Terminal); } try { AuthenticateForm->Banner(Banner, NeverShowAgain, Options); } __finally { if (FAuthenticateForm == NULL) { delete AuthenticateForm; } } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::TerminalShowExtendedException( TTerminal * Terminal, Exception * E, void * /*Arg*/) { if (ScpExplorer) { ScpExplorer->ShowExtendedException(Terminal, E); } else { ShowExtendedExceptionEx(Terminal, E); } } //--------------------------------------------------------------------------- static TDateTime DirectoryReadingProgressDelay(0, 0, 1, 500); //--------------------------------------------------------------------------- void __fastcall TTerminalManager::TerminalReadDirectoryProgress( TObject * /*Sender*/, int Progress, int ResolvedLinks, bool & Cancel) { if (Progress == 0) { if (ScpExplorer != NULL) { // See also TCustomScpExplorerForm::RemoteDirViewBusy ScpExplorer->LockWindow(); } FDirectoryReadingStart = Now(); if (!FProgressTitle.IsEmpty() || !FForegroundProgressTitle.IsEmpty()) { FProgressTitle = L""; FForegroundProgressTitle = L""; UpdateAppTitle(); } // Reset "was ESC ever pressed?" state GetAsyncKeyState(VK_ESCAPE); } else if (Progress < 0) { if (Progress == -2) { // cancelled if (ScpExplorer != NULL) { ScpExplorer->ReadDirectoryCancelled(); } } else { if (ScpExplorer != NULL) { ScpExplorer->UnlockWindow(); } FProgressTitle = L""; FForegroundProgressTitle = L""; UpdateAppTitle(); } } else { // If the least significant bit is set, // the key was pressed after the previous call to GetAsyncKeyState. int KeyState = GetAsyncKeyState(VK_ESCAPE); if (FLAGSET(KeyState, 0x01)) { Cancel = true; } if ((Now() - FDirectoryReadingStart) >= DirectoryReadingProgressDelay) { // 4 is arbitrary number FForegroundProgressTitle = FMTLOAD(ResolvedLinks >= 4 ? DIRECTORY_READING_AND_RESOLVING_PROGRESS : DIRECTORY_READING_PROGRESS, (Progress)); UpdateAppTitle(); } } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::TerminalInformation( TTerminal * Terminal, const UnicodeString & Str, bool /*Status*/, int Phase) { if (Phase == 1) { if (FAuthenticating == 0) { FBusyToken = BusyStart(); } FAuthenticating++; } else if (Phase == 0) { DebugAssert(FAuthenticating > 0); FAuthenticating--; if (FAuthenticating == 0) { BusyEnd(FBusyToken); FBusyToken = NULL; } SAFE_DESTROY(FAuthenticateForm); } else { if (FAuthenticating > 0) { bool ShowPending = false; if (FAuthenticateForm == NULL) { FAuthenticateForm = MakeAuthenticateForm(Terminal); ShowPending = true; } FAuthenticateForm->Log(Str); if (ShowPending) { FAuthenticateForm->ShowAsModal(); } } } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::OperationFinished(::TFileOperation Operation, TOperationSide Side, bool Temp, const UnicodeString & FileName, bool Success, TOnceDoneOperation & OnceDoneOperation) { DebugAssert(ScpExplorer); ScpExplorer->OperationFinished(Operation, Side, Temp, FileName, Success, OnceDoneOperation); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::OperationProgress( TFileOperationProgressType & ProgressData) { if (ProgressData.InProgress) { FProgressTitle = TProgressForm::ProgressStr(&ProgressData); } else { FProgressTitle = L""; } UpdateAppTitle(); DebugAssert(ScpExplorer); ScpExplorer->OperationProgress(ProgressData); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::QueueEvent(TTerminalQueue * Queue, TQueueEvent Event) { TGuard Guard(FQueueSection); FQueueEvents.push_back(std::make_pair(Queue, Event)); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::ConfigurationChange(TObject * /*Sender*/) { DebugAssert(Configuration); DebugAssert(Configuration == WinConfiguration); TTerminalQueue * Queue; for (int Index = 0; Index < Count; Index++) { DebugAssert(Terminals[Index]->Log); Terminals[Index]->Log->ReflectSettings(); Terminals[Index]->ActionLog->ReflectSettings(); Queue = reinterpret_cast(FQueues->Items[Index]); SetQueueConfiguration(Queue); } if (ScpExplorer) { ScpExplorer->ConfigurationChanged(); } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::TerminalReady() { ScpExplorer->Terminal = ActiveTerminal; ScpExplorer->Queue = ActiveQueue; ScpExplorer->TerminalReady(); } //--------------------------------------------------------------------------- TStrings * __fastcall TTerminalManager::GetTerminalList() { if (FTerminalList->Count != Count) { for (int i = 0; i < Count; i++) { UnicodeString NameN; UnicodeString Name = Terminals[i]->SessionData->SessionName; int Number = 1; NameN = Name; while (FTerminalList->IndexOf(NameN) >= 0) { Number++; NameN = FORMAT(L"%s (%d)", (Name, Number)); } if (Number > 1) { Name = FORMAT(L"%s (%d)", (Name, Number)); } FTerminalList->AddObject(Name, Terminals[i]); } } return FTerminalList; } //--------------------------------------------------------------------------- int __fastcall TTerminalManager::GetActiveTerminalIndex() { return ActiveTerminal ? IndexOf(ActiveTerminal) : -1; } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::SetActiveTerminalIndex(int value) { ActiveTerminal = Terminals[value]; } //--------------------------------------------------------------------------- UnicodeString __fastcall TTerminalManager::TerminalTitle(TTerminal * Terminal) { int Index = IndexOf(Terminal); UnicodeString Result; if (Index >= 0) { Result = TerminalList->Strings[Index]; } else { // this is the case of background transfer sessions Result = Terminal->SessionData->SessionName; } return Result; } //--------------------------------------------------------------------------- UnicodeString __fastcall TTerminalManager::GetActiveTerminalTitle() { UnicodeString Result = ActiveTerminal ? TerminalTitle(ActiveTerminal) : UnicodeString(L""); return Result; } //--------------------------------------------------------------------------- TTerminalQueue * __fastcall TTerminalManager::GetActiveQueue() { TTerminalQueue * Result = NULL; if (ActiveTerminal != NULL) { Result = reinterpret_cast(FQueues->Items[ActiveTerminalIndex]); } return Result; } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::CycleTerminals(bool Forward) { int Index = ActiveTerminalIndex; Index += Forward ? 1 : -1; if (Index < 0) { Index = Count-1; } else if (Index >= Count) { Index = 0; } ActiveTerminalIndex = Index; } //--------------------------------------------------------------------------- bool __fastcall TTerminalManager::CanOpenInPutty() { return (ActiveTerminal != NULL) && !GUIConfiguration->PuttyPath.Trim().IsEmpty(); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::UpdateSessionCredentials(TSessionData * Data) { Data->UserName = ActiveTerminal->UserName; Data->Password = ActiveTerminal->Password; } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::OpenInPutty() { Configuration->Usage->Inc(L"OpenInPutty"); TSessionData * Data = NULL; try { // Is NULL on the first session when called from ConnectActiveTerminal() // due to WinConfiguration->AutoOpenInPutty if (ScpExplorer != NULL) { Data = ScpExplorer->CloneCurrentSessionData(); } else { Data = new TSessionData(L""); DebugAssert(ActiveTerminal != NULL); Data->Assign(ActiveTerminal->SessionData); UpdateSessionCredentials(Data); } // putty does not support resolving environment variables in session settings Data->ExpandEnvironmentVariables(); if (ActiveTerminal->TunnelLocalPortNumber != 0) { Data->ConfigureTunnel(ActiveTerminal->TunnelLocalPortNumber); } OpenSessionInPutty(GUIConfiguration->PuttyPath, Data); } __finally { delete Data; } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::NewSession(bool /*FromSite*/, const UnicodeString & SessionUrl, bool ReloadSessions) { if (ReloadSessions) { StoredSessions->Load(); } UnicodeString DownloadFile; // unused std::unique_ptr DataList(new TObjectList()); GetLoginData(SessionUrl, NULL, DataList.get(), DownloadFile, true); if (DataList->Count > 0) { ActiveTerminal = NewTerminals(DataList.get()); } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::Idle() { for (int Index = 0; Index < Count; Index++) { TTerminal * Terminal = Terminals[Index]; try { TManagedTerminal * ManagedTerminal = dynamic_cast(Terminal); DebugAssert(ManagedTerminal != NULL); // make sure Idle is called on the thread that runs the terminal if (ManagedTerminal->TerminalThread != NULL) { ManagedTerminal->TerminalThread->Idle(); } else { if (Terminal->Active) { Terminal->Idle(); } } if (Terminal->Active) { DebugAssert(Index < FQueues->Count); if (Index < FQueues->Count) { reinterpret_cast(FQueues->Items[Index])->Idle(); } } } catch(Exception & E) { if (Terminal == ActiveTerminal) { // throw further, so that the exception is handled in proper place // (particularly in broken-transfer reconnect handler, bug 72) throw; } else { // we may not have inactive terminal, unless there is a explorer, // also Idle is called from explorer anyway DebugAssert(ScpExplorer != NULL); if (ScpExplorer != NULL) { ScpExplorer->InactiveTerminalException(Terminal, &E); } if (!Terminal->Active) { // if session is lost, save the error message and rethrow it // once the terminal gets activated FTerminationMessages->Strings[Index] = E.Message; } } } } TTerminalQueue * QueueWithEvent; TQueueEvent QueueEvent; do { QueueWithEvent = NULL; { TGuard Guard(FQueueSection); if (!FQueueEvents.empty()) { QueueWithEvent = FQueueEvents[0].first; QueueEvent = FQueueEvents[0].second; FQueueEvents.erase(FQueueEvents.begin()); } } if (QueueWithEvent != NULL) { int Index = FQueues->IndexOf(QueueWithEvent); // the session may not exist anymore if (Index >= 0) { TTerminal * Terminal = Terminals[Index]; // we can hardly have a queue event without explorer DebugAssert(ScpExplorer != NULL); if (ScpExplorer != NULL) { ScpExplorer->QueueEvent(Terminal, QueueWithEvent, QueueEvent); } } } } while (QueueWithEvent != NULL); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::MasterPasswordPrompt() { if (GetCurrentThreadId() == MainThreadID) { if (!DoMasterPasswordDialog()) { Abort(); } } else { // this can happen only when we keep cancelling all master password prompts // as long as the sessing finally connects (session password has to be // explictly typed in), and background transfer is started Abort(); } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::Move(TTerminal * Source, TTerminal * Target) { FTerminalList->Clear(); int SourceIndex = IndexOf(Source); int TargetIndex = IndexOf(Target); TTerminalList::Move(SourceIndex, TargetIndex); FQueues->Move(SourceIndex, TargetIndex); DoTerminalListChanged(); // when there are indexed sessions with the same name, // the index may change when reordering the sessions UpdateAppTitle(); } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::DoTerminalListChanged() { if (OnTerminalListChanged) { OnTerminalListChanged(this); } } //--------------------------------------------------------------------------- void __fastcall TTerminalManager::SaveWorkspace(TList * DataList) { for (int Index = 0; Index < Count; Index++) { TManagedTerminal * ManagedTerminal = dynamic_cast(Terminals[Index]); TSessionData * Data = StoredSessions->SaveWorkspaceData(ManagedTerminal->StateData); DataList->Add(Data); Data->Name = IntToHex(Index, 4); } } //--------------------------------------------------------------------------- TTerminal * __fastcall TTerminalManager::FindActiveTerminalForSite(TSessionData * Data) { TTerminal * Result = NULL; for (int Index = 0; (Result == NULL) && (Index < Count); Index++) { TTerminal * Terminal = Terminals[Index]; if (Terminal->Active && Terminal->SessionData->IsSameSite(Data)) { Result = Terminal; } } return Result; } //--------------------------------------------------------------------------- TTerminalQueue * __fastcall TTerminalManager::FindQueueForTerminal(TTerminal * Terminal) { int Index = IndexOf(Terminal); return reinterpret_cast(FQueues->Items[Index]); }