From 40abac87255068764fd31ade27a0deb42f1e5c7b Mon Sep 17 00:00:00 2001 From: NickVs2015 Date: Fri, 29 May 2026 15:51:22 +0300 Subject: [PATCH] - SshSession::resetConnection(): force-disconnect before each new top-level backup operation so ssh_scp_new() doesn't fail on a reused stale session (reproduces as 'Failed to open channel for scp' right after restore) - Call resetConnection() at entry of createBackup() and uploadBackup() - Replace recursive findStackView() in PageSetupWizardEasy with upward parent traversal (depth+push check) to avoid JS stack overflow on iOS when component tree is large (71 VPN managers in test) --- client/core/utils/selfhosted/sshSession.cpp | 5 ++ client/core/utils/selfhosted/sshSession.h | 3 ++ .../controllers/serversBackupController.cpp | 4 ++ client/ui/qml/Pages2/PageSetupWizardEasy.qml | 46 ++++++------------- 4 files changed, 25 insertions(+), 33 deletions(-) diff --git a/client/core/utils/selfhosted/sshSession.cpp b/client/core/utils/selfhosted/sshSession.cpp index 363745fd5..0fc7ad8a7 100644 --- a/client/core/utils/selfhosted/sshSession.cpp +++ b/client/core/utils/selfhosted/sshSession.cpp @@ -44,6 +44,11 @@ SshSession::~SshSession() m_sshClient.disconnectFromHost(); } +void SshSession::resetConnection() +{ + m_sshClient.disconnectFromHost(); +} + ErrorCode SshSession::runScript(const ServerCredentials &credentials, QString script, const std::function &cbReadStdOut, const std::function &cbReadStdErr) diff --git a/client/core/utils/selfhosted/sshSession.h b/client/core/utils/selfhosted/sshSession.h index 2a738cb95..2f182b034 100644 --- a/client/core/utils/selfhosted/sshSession.h +++ b/client/core/utils/selfhosted/sshSession.h @@ -47,6 +47,9 @@ public: ErrorCode uploadFileToHost(const ServerCredentials &credentials, const QByteArray &data, const QString &remotePath, libssh::ScpOverwriteMode overwriteMode = libssh::ScpOverwriteMode::ScpOverwriteExisting); + /** Force-close the current SSH connection so the next operation starts fresh. */ + void resetConnection(); + private: libssh::Client m_sshClient; }; diff --git a/client/ui/controllers/serversBackupController.cpp b/client/ui/controllers/serversBackupController.cpp index 4a01897c8..f1a2cf4ff 100644 --- a/client/ui/controllers/serversBackupController.cpp +++ b/client/ui/controllers/serversBackupController.cpp @@ -62,6 +62,8 @@ void ServersBackupController::createBackup(const ServerCredentials &credentials) return; } + m_sshSession.resetConnection(); + setStatus(InProgress); setProgress(0, tr("Starting backup creation...")); @@ -482,6 +484,8 @@ void ServersBackupController::uploadBackup(const ServerCredentials &credentials, return; } + m_sshSession.resetConnection(); + // Save restore mode for later use m_restoreReplaceMode = replaceMode; diff --git a/client/ui/qml/Pages2/PageSetupWizardEasy.qml b/client/ui/qml/Pages2/PageSetupWizardEasy.qml index d04f9c521..00f54252d 100644 --- a/client/ui/qml/Pages2/PageSetupWizardEasy.qml +++ b/client/ui/qml/Pages2/PageSetupWizardEasy.qml @@ -193,45 +193,25 @@ PageType { // Server already added, as we waited for onInstallServerFinished Qt.callLater(function() { var pagePath = "qrc:/ui/qml/Pages2/PageSettingsServerRestoreMode.qml" - - // Find main application window - var item = root - while (item.parent) { - item = item.parent + + // Traverse upward from root to find the containing StackView. + // StackView has both `push` function and `depth` property. + // This avoids a recursive downward search that causes stack overflow + // on iOS when the component tree is large (many VPN managers). + var stackView = root.parent + while (stackView) { + if (typeof stackView.push === "function" && stackView.hasOwnProperty("depth")) { + break + } + stackView = stackView.parent } - - // Find StackView recursively - function findStackView(obj) { - if (!obj) return null - - // Check if object is StackView - if (obj.toString().indexOf("StackView") !== -1 || typeof obj.push === "function") { - return obj - } - - // Check children - if (obj.children) { - for (var i = 0; i < obj.children.length; i++) { - var result = findStackView(obj.children[i]) - if (result) return result - } - } - - // Check contentItem - if (obj.contentItem) { - return findStackView(obj.contentItem) - } - - return null - } - - var stackView = findStackView(item) + if (stackView) { console.log("Found StackView, pushing restore mode page") stackView.push(pagePath, { "backupFilePath": root.backupFilePath, "backupFileName": fileName, - "serverName": "", // Will be obtained from ServersModel + "serverName": "", "serverIp": serverIp, "isFromSetupWizard": true, "wizardHostname": root.restoreHostname,