Ferreteria/v0.6/clade/IO/Aspect/Connx/Plug/Shell/Remote/SSH

From Woozle Writes Code
< Ferreteria‎ | v0.6‎ | clade‎ | IO‎ | Aspect‎ | Connx‎ | Plug‎ | Shell‎ | Remote
Revision as of 22:56, 10 December 2025 by Woozle (talk | contribs) (Created page with "{{page/clade/v2 |fam= {{!}} align=right {{!}} <code>{{l/ver/clade|IO\Aspect\Connx\Plug|Shell}}</code> {{!}} align=center {{!}} ⇒ <code>{{l/ver/clade|IO\Aspect\Connx\Plug\Shell|SSH}}</code> {{!}} align=left {{!}} ''(none)'' |alia= {{!-!}} '''ActionIface''' {{!!}} <code>{{l/ver/clade/full|Sys\Events|ItWent}}</code> {{!-!}} '''Base'''* [ca,i] {{!!}} <code>{{l/ver/clade/full|IO\Aspect\Connx\Plug\Shell|Remote}}</code> {{!-!}} '''BufferIface''' {{!!}} <code>{{l/v...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search
clade: IO\Aspect\Connx\Plug\Shell\Remote\SSH
Clade Family
Shell SSH (none)
Clade Aliases
Alias Clade
ActionIface Sys\Events\ItWent
Base* [ca,i] IO\Aspect\Connx\Plug\Shell\Remote
BufferIface IO\Aspect\Connx\Buffer
CommOpIface Sys\Events\ItWent\CommOp
CredsIface IO\Aspect\Creds
ProcIface IO\Aspect\Connx\Process
QRes* [c,i] Data\Mem\QVar\Res
SockIface IO\Aspect\Socket
Tunnel* [c,i] IO\Aspect\Connx\aux\Tunnel
Subpages

Code

as of 2025-12-10:

interface iSSH extends BaseIface {
    // OBJECTS
    function OCred() : CredsIface;
}
class cSSH extends BaseClass implements iSSH {

    // ++ CONFIG ++ //

    public function SType()             : string { return 'ssh plug'; }

    // -- CONFIG -- //
    // ++ OBJECTS ++ //

    public function OCred() : CredsIface { return $this->OSock()->OCred(); }

    // -- OBJECTS -- //
    // ++ LIFECYCLE ++ //

    protected function ActualOpen() : ActionIface {

        $oAct = $this->HowItWent();

        // [+STANDARD CODE]: get connection specs

        $oCred = $this->OCred();
        $oSock = $this->OSock();
        $oHost = $oSock->OHost();
        $sUser = $oCred->QSUser()->GetIt();
        $sHost = $oHost->QSAddr()->GetIt();
        $onPort = $oHost->QIPort();

        // [-STANDARD CODE]

        if ($onPort->HasIt()) {
            $nPort = $onPort->GetIt();
            $ftRem = "$sUser@$sHost:$nPort";
        } else {
            $ftRem = "$sUser@$sHost";
        }

        echo "Opening SSH connection to [$ftRem]...".CRLF;
        try {
            if ($onPort->HasIt()) {
                $rSSH = @ssh2_connect($sHost, $nPort);
            } else {
                $rSSH = @ssh2_connect($sHost); // use ssh2_connect's default port
            }
        } catch (\Exception $e) {
            $rSSH = FALSE;
            echo $e;
            $this->AmHere('TODO 2025-12-07: better diagnostics');
        }
        if ($rSSH === FALSE) {
            $arErr = error_get_last();
            $oScrn = self::Screen();
            echo $oScrn->ErrorIt('Connection error').': '.$arErr['message'].CRLF;
            die();
        }
        $this->QNative()->SetIt($rSSH);

        $ftUser = self::Screen()->GreenIt($sUser);
        echo "Authenticating remote user '$ftUser'...".CRLF;
        @$rok = ssh2_auth_agent($rSSH,$sUser);
        $ok = ($rok !== FALSE);
        $oAct->SetOkay($ok);
        if ($ok) {
            $oAct->AddMsgString("User authenticated.");
        } else {
            $oAct->AddMsgString("SSH connection failed.");
        }
        return $oAct;
    }
    protected function ActualShut() : ActionIface {
        $rConn = $this->QNative()->GetIt();
        $ok = ssh2_disconnect($rConn);
        $oAct = $this->HowItWent();
        $oAct->SetOkay($ok);
        return $oAct;
    }

    // -- LIFECYCLE -- //
    // ++ UI ++ //

    public function DescribeInline() : string { return 'ssh'; }

    // -- UI -- //
    // ++ OBJECTS ++ //

    private $osNat = NULL; protected function QNative() : QResIface { return $this->osNat ?? ($this->osNat = QResClass::AsNew()); }

    public function NewTunnel(CredsIface $oICreds, ?int $nPortLocal=NULL) : TunnelIface {
        return new TunnelClass($this->Creds(),$oICreds,$nPortLocal);
    }

    // -- OBJECTS -- //
    // ++ ACTION ++ //

    public function DoCommand(string|array $saCmd, BufferIface $oBuff, ?CommOpIface $oAct=NULL) : CommOpIface {
        $sCmdFull = $this->WrapCommand($saCmd);
        $oAct = parent::DoCommand($sCmdFull,$oBuff,$oAct);

        return $oAct;
    }
    public function RunProcess(string|array $saCmd) : ProcIface {
        $sCmdFull = $this->WrapCommand($saCmd);
        return parent::RunProcess($sCmdFull);
    }

    // -- ACTION -- //
    // ++ FIGURING ++ //

    // 2025-03-29 FUTURE: maybe this should always return an array -- but that's harder to debug.
    protected function WrapCommand(string|array $saCmd) : string {
        // [+STANDARD CODE]: get connection specs

        $oCred = $this->OCred();
        $oSock = $this->OSock();
        $oHost = $oSock->OHost();
        $sUser = $oCred->QSUser()->GetIt();
        $sHost = $oHost->QSAddr()->GetIt();
        $onPort = $oHost->QIPort();

        // [-STANDARD CODE]

        if ($onPort->HasIt()) {
            $nPort = $onPort->GetIt();
            $sPort = ' -p '.$nPort;
        } else {
            $sPort = '';
        }

        $sCmdSafe = escapeshellarg($saCmd);

        $sCmdFull = "ssh$sPort $sUser@$sHost $sCmdSafe";
        return $sCmdFull;
    }
}