Internal
Public Access
Adds two kinds of coverage, continuing #1's remaining scope: 1. Pure-function tests for mapSshError() and escapeForShellSingleQuotes(), promoted from private members to public statics purely so tests can call them without spinning up a process. escapeForShellSingleQuotes() is the actual security boundary for password auth (it's what stops a password containing a single quote from breaking out of the askpass script's quoting), so it gets a real adversarial test, not just a happy-path one. 2. State-machine tests (connect -> Connected, auth failure -> Failed with the right mapped message, connection refused -> Failed, input round-tripping, reconnect) driven against tests/fixtures/fake_ssh.sh, a small controllable stand-in for the real ssh binary, instead of a real network/SSH server. This needed one small testability seam: a new constructor overload that overrides the launched program ("ssh" in production, the fixture script in tests). POSIX-only for now: the fixture is a shell script, so the state-machine tests QSKIP on Windows until an equivalent fixture exists there; the pure-function tests run everywhere. RdpSessionBackend coverage is still open -- it's a bigger lift again (FreeRDP's own event loop, not just a QProcess), left for a follow-up. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
70 lines
2.4 KiB
C++
70 lines
2.4 KiB
C++
#ifndef ORBITHUB_SSH_SESSION_BACKEND_H
|
|
#define ORBITHUB_SSH_SESSION_BACKEND_H
|
|
|
|
#include "session_backend.h"
|
|
|
|
#include <QProcess>
|
|
#include <QString>
|
|
#include <QTimer>
|
|
|
|
class SshSessionBackend : public SessionBackend
|
|
{
|
|
Q_OBJECT
|
|
|
|
public:
|
|
explicit SshSessionBackend(const Profile& profile, QObject* parent = nullptr);
|
|
// Test-only: overrides the executable launched instead of "ssh", so
|
|
// tests can point it at a controllable fixture script.
|
|
SshSessionBackend(const Profile& profile, const QString& sshProgramOverride, QObject* parent);
|
|
~SshSessionBackend() override;
|
|
|
|
// Pure, state-free helpers exposed as public statics purely so tests
|
|
// can exercise them directly without spinning up a real ssh process.
|
|
static QString mapSshError(const QString& rawError);
|
|
static QString escapeForShellSingleQuotes(const QString& value);
|
|
|
|
public slots:
|
|
void connectSession(const SessionConnectOptions& options) override;
|
|
void disconnectSession() override;
|
|
void reconnectSession(const SessionConnectOptions& options) override;
|
|
void sendInput(const QString& input) override;
|
|
void confirmHostKey(bool trustHost) override;
|
|
void updateTerminalSize(int columns, int rows) override;
|
|
|
|
private slots:
|
|
void onProcessStarted();
|
|
void onProcessErrorOccurred(QProcess::ProcessError error);
|
|
void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
|
|
void onReadyReadStandardOutput();
|
|
void onReadyReadStandardError();
|
|
void onConnectedProbeTimeout();
|
|
|
|
private:
|
|
QProcess* m_process;
|
|
QTimer* m_connectedProbeTimer;
|
|
SessionState m_state;
|
|
bool m_userInitiatedDisconnect;
|
|
bool m_reconnectPending;
|
|
SessionConnectOptions m_reconnectOptions;
|
|
SessionConnectOptions m_activeOptions;
|
|
QString m_lastRawError;
|
|
QString m_askPassScriptPath;
|
|
bool m_waitingForPasswordPrompt;
|
|
bool m_waitingForHostKeyConfirmation;
|
|
bool m_passwordSubmitted;
|
|
int m_terminalColumns;
|
|
int m_terminalRows;
|
|
QString m_sshProgram;
|
|
|
|
void setState(SessionState state, const QString& message);
|
|
bool startSshProcess(const SessionConnectOptions& options);
|
|
bool configureAskPass(const SessionConnectOptions& options,
|
|
QProcessEnvironment& environment,
|
|
QString& error);
|
|
void cleanupAskPassScript();
|
|
QString knownHostsFileForNullDevice() const;
|
|
void applyTerminalSizeIfAvailable();
|
|
};
|
|
|
|
#endif
|