Skip to content

ssh2.sftp:// stream handling broken > 8.3.9 #15484

@mastacontrola

Description

@mastacontrola

Description

The following code:

<?php
class SSH
{
    protected $data = [
        'host' => '',
        'username' => '',
        'password' => '',
        'port' => 22,
        'timeout' => 90,
    ];
    private $_link;
    private $_sftp;
    private $_lastConnectionHash;
    private $_lastLoginHash;
    private $_currentConnectionHash;
    private $_currentLoginHash;
    public function __set($key, $value)
    {
        return $this->data[$key] = $value;
    }
    public function __get($key)
    {
        return $this->data[$key];
    }
    public function sftp()
    {
        if (!isset($this->_sftp) && !($this->_sftp = @ssh2_sftp($this->_link))) {
            $this->ssherror($this->data);
        }
    }
    public function disconnect()
    {
        if ($this->_sftp) {
            @ssh2_disconnect($this->_sftp);
            $this->_sftp = null;
            unset($this->_sftp);
        }
        $test = @ssh2_disconnect($this->_link);
        $this->_link = null;
        unset($this->_link);
        return $test;
    }
    public function __call($func, $args)
    {
        if (str_contains($func, 'scp')) {
            $linker = $this->_link;
        } elseif (str_contains($func, 'sftp_')) {
            if (!$this->_sftp) {
                $this->sftp();
            }
            $linker = $this->_sftp;
        } else {
            $linker = $this->_link;
        }
        if ($func != 'fetch_stream') {
            array_unshift(
                $args,
                $linker
            );
        }
        $func = 'ssh2_' . $func;
        return $func(...$args);
    }
    public function connect(
        $host = '',
        $port = 0,
        $autologin = true,
        $connectmethod = 'ssh2_connect'
    ) {
        try {
            $this->_currentConnectionHash = password_hash(
                print_r($this->data, 1),
                PASSWORD_BCRYPT,
                ['cost'=>11]
            );
            if ($this->_link
                && $this->_currentConnectionHash == $this->_lastConnectionHash
            ) {
                return $this;
            }
            if (!$host) {
                $host = $this->host;
            }
            if (!$port) {
                $port = $this->port;
            }
            $this->_link = ssh2_connect($host, $port);
            if ($this->_link === false) {
                trigger_error(_('SSH Connection Failed'), E_USER_NOTICE);
                $this->ssherror($this->data);
            }
            if ($autologin) {
                $this->login();
            }
            $this->_lastConnectionHash = $this->_currentConnectionHash;
        } catch (Exception $e) {
            error_log($e->getMessage());
            return false;
        }
        return $this;
    }
    public function ssherror($data)
    {
        $error = error_get_last();
        throw new Exception(
            sprintf(
                '%s: %s, %s: %s, %s: %s, %s: %s, %s: %s, %s: %s',
                _('Type'),
                $error['type'],
                _('File'),
                $error['file'],
                _('Line'),
                $error['line'],
                _('Message'),
                $error['message'],
                _('Host'),
                $data['host'],
                _('Username'),
                $data['username']
            )
        );
    }
    public function login(
        $username = null,
        $password = null
    ) {
        try {
            $this->_currentLoginHash = password_hash(
                is_object($this->_link) ? spl_object_id($this->_link) : spl_object_id($this),
                PASSWORD_BCRYPT,
                ['cost'=>11]
            );
            if ($this->_currentLoginHash == $this->_lastLoginHash) {
                return $this;
            }
            if (!$username) {
                $username = $this->username;
            }
            if (!$password) {
                $password = $this->password;
            }
            if ($this->auth_password($username, $password) === false) {
                $this->ssherror($this->data);
            }
        } catch (Exception $e) {
            throw new Exception($e->getMessage());
        }
        $this->_lastLoginHash = $this->_currentLoginHash;
        return $this;
    }
    public function exists($path)
    {
        $this->sftp();
        $sftp_wrap = "ssh2.sftp://{$this->_sftp}{$path}";
        return @is_dir($sftp_wrap) || @file_exists($sftp_wrap);
    }
    public function scanFilesystem($remote_file)
    {
        if (!$this->exists($remote_file)) {
            return [];
        }
        $sftp = $this->_sftp;
        $dir = "ssh2.sftp://$sftp$remote_file";
        $tempArray = [];

        if (is_dir($dir)) {
            if ($dh = opendir($dir)) {
                while (($file = readdir($dh)) !== false) {
                    if ($file == '.' || $file == '..') {
                        continue;
                    }
                    $filetype = filetype("$dir/$file");
                    if ($filetype == 'dir') {
                        $tmp = $this->scanFilesystem("$remote_file/$file/");
                        foreach ($tmp as $t) {
                            $tempArray[] = "$remote_file/$file/$t";
                        }
                    } else {
                        $tempArray[] = "$remote_file/$file";
                    }
                }
                closedir($dh);
            }
        }
        ob_start();
        var_dump($tempArray);
        $contents = ob_get_contents();
        ob_end_clean();
        error_log($contents);

        return $tempArray;
    }
}
$ssh = new SSH();
$ssh->host = '10.20.0.250';
$ssh->username = 'test';
$ssh->password = 'testpassword';
$ssh->connect();
$files = $ssh->scanFilesystem('/images/test');
?>

Resulted in this output - as I'm outputting to errorLog:

array()

But I expected this output instead:

[18-Aug-2024 13:53:04 UTC] array(2) {
  [0]=>
  string(21) "/images/test/d1p1.img"
  [1]=>
  string(21) "/images/test/d1p2.img"
}

More Information:
Directory from user->shell:

/images/test$ ls
d1p1.img  d1p2.img

Installing php:remi-8.3.9-1 all works.

Installing php:remi-8.3.10-1 same exact file fails.

PHP Version

PHP 8.3.10

Operating System

Fedora 40, Rocky 9, CentOS Stream 9

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions