#ifndef ORBITHUB_VNC_SESSION_BACKEND_H #define ORBITHUB_VNC_SESSION_BACKEND_H #include "session_backend.h" #include #include #include class QTcpSocket; // Implements RFB (RFC 6143) directly against QTcpSocket -- there is no // permissively licensed VNC client library to vendor the way FreeRDP was // for RDP (LibVNCClient is GPLv2, gtk-vnc is LGPL but GTK-tied), so this is // an original implementation. Threading follows SshSessionBackend's model // (a QObject moved to its own QThread, driven by Qt's own async socket // signals) rather than RdpSessionBackend's manual worker-thread/blocking // loop, since QTcpSocket is already async -- there's no legacy synchronous // C API to wrangle here. // // Scope (see plan / issue #3 for the full rationale): standard VNC // Authentication (security type 2) and no-auth (type 1) only -- not // Apple's Screen Sharing scheme (type 30). Raw + CopyRect encodings only. // No dynamic resize, no remote cursor shape sync, no clipboard sync. class VncSessionBackend : public SessionBackend { Q_OBJECT public: explicit VncSessionBackend(const Profile& profile, QObject* parent = nullptr); ~VncSessionBackend() override; // Pure, state-free helpers exposed as public statics purely so tests // can exercise them without a live connection. static QByteArray vncAuthResponse(const QByteArray& challenge, const QString& password); static QByteArray desKeyFromPassword(const QString& password); static QString mapSocketError(QAbstractSocket::SocketError error, const QString& rawDetail); static quint32 keysymForQtKey(int key, const QString& text); 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; void sendKeyEvent(int key, quint32 nativeScanCode, const QString& text, bool pressed, int modifiers) override; void sendMouseMoveEvent(int x, int y) override; void sendMouseButtonEvent(int x, int y, int button, bool pressed) override; void sendMouseWheelEvent(int x, int y, int deltaX, int deltaY) override; private slots: void onSocketConnected(); void onSocketReadyRead(); void onSocketDisconnected(); void onSocketErrorOccurred(QAbstractSocket::SocketError error); private: enum class RfbState { Idle, WaitingProtocolVersion, WaitingSecurityTypeCount, WaitingSecurityTypeList, WaitingSecurityTypeV33, WaitingSecurityFailureReasonLength, WaitingSecurityFailureReason, WaitingVncAuthChallenge, WaitingSecurityResult, WaitingSecurityResultReasonLength, WaitingSecurityResultReason, WaitingServerInitHeader, WaitingServerName, WaitingServerMessageType, WaitingFramebufferUpdateHeader, WaitingRectangleHeader, WaitingRawPixelData, WaitingCopyRectSource, WaitingSetColourMapHeader, WaitingSetColourMapData, WaitingServerCutTextHeader, WaitingServerCutTextData, }; struct PendingRectangle { int x = 0; int y = 0; int width = 0; int height = 0; qint32 encoding = 0; }; QTcpSocket* m_socket; SessionState m_state; RfbState m_rfbState; QByteArray m_recvBuffer; bool m_userInitiatedDisconnect; bool m_reconnectPending; SessionConnectOptions m_reconnectOptions; SessionConnectOptions m_activeOptions; int m_negotiatedMinorVersion; int m_securityTypeCount; QByteArray m_offeredSecurityTypes; quint8 m_chosenSecurityType; quint32 m_pendingLength; QImage m_framebuffer; int m_pendingRectanglesRemaining; PendingRectangle m_currentRectangle; quint8 m_pointerButtonMask; int m_lastPointerX; int m_lastPointerY; void setState(SessionState state, const QString& message); void resetProtocolState(); void processReceiveBuffer(); bool haveBytes(int count) const; void sendVersionReply(); void sendClientInit(); void sendSetPixelFormatAndEncodings(); void requestFramebufferUpdate(bool incremental); void failConnection(const QString& displayMessage, const QString& rawMessage); void finishHandshakeIntoRunningState(); void onRectangleFinished(); void sendPointerEvent(); void sendWheelClick(quint8 wheelBit); }; #endif