• File: elFinderVolumeSFTPphpseclib.class.php
  • Full Path: /home/blwgracecity/jesusexp.org/wp-content/plugins/wordpress-seo/src/introductions/elFinderVolumeSFTPphpseclib.class.php
  • File size: 16 KB
  • MIME-type: text/x-php
  • Charset: utf-8
<?php

/**
 * Simple elFinder driver for SFTP using phpseclib 1
 *
 * @author Dmitry (dio) Levashov
 * @author Cem (discofever), sitecode
 * @reference http://phpseclib.sourceforge.net/sftp/2.0/examples.html
 **/
class elFinderVolumeSFTPphpseclib extends elFinderVolumeFTP {

    /**
     * Constructor
     * Extend options with required fields
     *
     * @author Dmitry (dio) Levashov
     * @author Cem (DiscoFever)
     */
    public function __construct()
    {
        $opts = array(
            'host' => 'localhost',
            'user' => '',
            'pass' => '',
            'port' => 22,
            'path' => '/',
            'timeout' => 20,
            'owner' => true,
            'tmbPath' => '',
            'tmpPath' => '',
            'separator' => '/',
            'phpseclibDir' => '../phpseclib/',
            'connectCallback' => null, //provide your own already instantiated phpseclib $Sftp object returned by this callback
                                       //'connectCallback'=> function($options) {
                                       //     //load and instantiate phpseclib $sftp
                                       //     return $sftp;
                                       // },
            'checkSubfolders' => -1,
            'dirMode' => 0755,
            'fileMode' => 0644,
            'rootCssClass' => 'elfinder-navbar-root-ftp',
        );
        $this->options = array_merge($this->options, $opts);
        $this->options['mimeDetect'] = 'internal';
    }

    /**
     * Prepare
     * Call from elFinder::netmout() before volume->mount()
     *
     * @param $options
     *
     * @return array volume root options
     * @author Naoki Sawada
     */
    public function netmountPrepare($options)
    {
        $options['statOwner'] = true;
        $options['allowChmodReadOnly'] = true;
        $options['acceptedName'] = '#^[^/\\?*:|"<>]*[^./\\?*:|"<>]$#';
        return $options;
    }

    /*********************************************************************/
    /*                        INIT AND CONFIGURE                         */
    /*********************************************************************/

    /**
     * Prepare SFTP connection
     * Connect to remote server and check if credentials are correct, if so, store the connection
     *
     * @return bool
     * @author Dmitry (dio) Levashov
     * @author Cem (DiscoFever)
     **/
    protected function init()
    {
        if (!$this->options['connectCallback']) {
            if (!$this->options['host']
                || !$this->options['port']) {
                return $this->setError('Required options undefined.');
            }

            if (!$this->options['path']) {
                $this->options['path'] = '/';
            }

            // make net mount key
            $this->netMountKey = md5(join('-', array('sftpphpseclib', $this->options['host'], $this->options['port'], $this->options['path'], $this->options['user'])));

            set_include_path(get_include_path() . PATH_SEPARATOR . getcwd().'/'.$this->options['phpseclibDir']);
            include_once('Net/SFTP.php');

            if (!class_exists('Net_SFTP')) {
                return $this->setError('SFTP extension not loaded. Install phpseclib version 1: http://phpseclib.sourceforge.net/ Set option "phpseclibDir" accordingly.');
            }

            // remove protocol from host
            $scheme = parse_url($this->options['host'], PHP_URL_SCHEME);

            if ($scheme) {
                $this->options['host'] = substr($this->options['host'], strlen($scheme) + 3);
            }
        } else {
            // make net mount key
            $this->netMountKey = md5(join('-', array('sftpphpseclib', $this->options['path'])));
        }

        // normalize root path
        $this->root = $this->options['path'] = $this->_normpath($this->options['path']);

        if (empty($this->options['alias'])) {
            $this->options['alias'] = $this->options['user'] . '@' . $this->options['host'];
            if (!empty($this->options['netkey'])) {
                elFinder::$instance->updateNetVolumeOption($this->options['netkey'], 'alias', $this->options['alias']);
            }
        }

        $this->rootName = $this->options['alias'];
        $this->options['separator'] = '/';

        if (is_null($this->options['syncChkAsTs'])) {
            $this->options['syncChkAsTs'] = true;
        }

        return $this->needOnline? $this->connect() : true;

    }


    /**
     * Configure after successfull mount.
     *
     * @return void
     * @throws elFinderAbortException
     * @author Dmitry (dio) Levashov
     */
    protected function configure()
    {
        parent::configure();

        if (!$this->tmp) {
            $this->disabled[] = 'mkfile';
            $this->disabled[] = 'paste';
            $this->disabled[] = 'upload';
            $this->disabled[] = 'edit';
            //$this->disabled[] = 'archive';
            //$this->disabled[] = 'extract';
        }

        $this->disabled[] = 'archive';
        $this->disabled[] = 'extract';
    }

    /**
     * Connect to sftp server
     *
     * @return bool
     * @author sitecode
     **/
    protected function connect()
    {
        //use ca
        if ($this->options['connectCallback']) {
            $this->connect = $this->options['connectCallback']($this->options);
            if (!$this->connect || !$this->connect->isConnected()) {
                return $this->setError('Unable to connect successfully');
            }

            return true;
        }

        try{
            $host = $this->options['host'] . ($this->options['port'] != 22 ? ':' . $this->options['port'] : '');
            $this->connect = new Net_SFTP($host);
            //TODO check fingerprint before login, fail if no match to last time
            if (!$this->connect->login($this->options['user'], $this->options['pass'])) {
                return $this->setError('Unable to connect to SFTP server ' . $host);
            }
        } catch (Exception $e) {
            return $this->setError('Error while connecting to SFTP server '  . $host . ': ' . $e->getMessage());
        }

        if (!$this->connect->chdir($this->root)
            /*|| $this->root != $this->connect->pwd()*/) {
            //$this->umount();
            return $this->setError('Unable to open root folder.');
        }

        return true;
    }

    /**
     * Call rawlist
     *
     * @param string $path
     *
     * @return array
     */
    protected function ftpRawList($path)
    {
        return $this->connect->rawlist($path ?: '.') ?: [];
/*
        $raw = $this->connect->rawlist($path ?: '.') ?: [];
        $raw = array_map(function($key, $value) {
            $value['name'] = $key;
            return $value;
        }, array_keys($raw), $raw);
        return $raw;
*/
    }

    /*********************************************************************/
    /*                               FS API                              */
    /*********************************************************************/

    /**
     * Close opened connection
     *
     * @return void
     * @author Dmitry (dio) Levashov
     **/
    public function umount()
    {
        $this->connect && $this->connect->disconnect();
    }


    /**
     * Parse line from rawlist() output and return file stat (array)
     *
     * @param  string $raw line from rawlist() output
     * @param         $base
     * @param bool    $nameOnly
     *
     * @return array
     * @author Dmitry Levashov
     */
    protected function parseRaw($raw, $base, $nameOnly = false)
    {
        $info = $raw;
        $stat = array();

        if ($info['filename'] == '.' || $info['filename'] == '..') {
            return false;
        }

        $name = $info['filename'];

        if (preg_match('|(.+)\-\>(.+)|', $name, $m)) {
            $name = trim($m[1]);
            // check recursive processing
            if ($this->cacheDirTarget && $this->_joinPath($base, $name) !== $this->cacheDirTarget) {
                return array();
            }
            if (!$nameOnly) {
                $target = trim($m[2]);
                if (substr($target, 0, 1) !== $this->separator) {
                    $target = $this->getFullPath($target, $base);
                }
                $target = $this->_normpath($target);
                $stat['name'] = $name;
                $stat['target'] = $target;
                return $stat;
            }
        }

        if ($nameOnly) {
            return array('name' => $name);
        }

        $stat['ts'] = $info['mtime'];

        if ($this->options['statOwner']) {
            $stat['owner'] = $info['uid'];
            $stat['group'] = $info['gid'];
            $stat['perm'] = $info['permissions'];
            $stat['isowner'] = isset($stat['owner']) ? ($this->options['owner'] ? true : ($stat['owner'] == $this->options['user'])) : true;
        }

        $owner_computed = isset($stat['isowner']) ? $stat['isowner'] : $this->options['owner'];
        $perm = $this->parsePermissions($info['permissions'], $owner_computed);
        $stat['name'] = $name;
        $stat['mime'] = $info['type'] == NET_SFTP_TYPE_DIRECTORY ? 'directory' : $this->mimetype($stat['name'], true);
        $stat['size'] = $stat['mime'] == 'directory' ? 0 : $info['size'];
        $stat['read'] = $perm['read'];
        $stat['write'] = $perm['write'];

        return $stat;
    }

    /**
     * Parse permissions string. Return array(read => true/false, write => true/false)
     *
     * @param  int $perm
     *                                             The isowner parameter is computed by the caller.
     *                                             If the owner parameter in the options is true, the user is the actual owner of all objects even if the user used in the ftp Login
     *                                             is different from the file owner id.
     *                                             If the owner parameter is false to understand if the user is the file owner we compare the ftp user with the file owner id.
     * @param Boolean $isowner                     . Tell if the current user is the owner of the object.
     *
     * @return array
     * @author Dmitry (dio) Levashov
     * @author sitecode
     */
    protected function parsePermissions($permissions, $isowner = true)
    {
        $permissions = decoct($permissions);
        $perm = $isowner ? decbin($permissions[-3]) : decbin($permissions[-1]);

        return array(
            'read' => $perm[-3],
            'write' => $perm[-2]
        );
    }

    /**
     * Cache dir contents
     *
     * @param  string $path dir path
     *
     * @return void
     * @author Dmitry Levashov, sitecode
     **/
    protected function cacheDir($path)
    {
        $this->dirsCache[$path] = array();
        $hasDir = false;

        $list = array();
        $encPath = $this->convEncIn($path);
        foreach ($this->ftpRawList($encPath) as $raw) {
            if (($stat = $this->parseRaw($raw, $encPath))) {
                $list[] = $stat;
            }
        }
        $list = $this->convEncOut($list);
        $prefix = ($path === $this->separator) ? $this->separator : $path . $this->separator;
        $targets = array();
        foreach ($list as $stat) {
            $p = $prefix . $stat['name'];
            if (isset($stat['target'])) {
                // stat later
                $targets[$stat['name']] = $stat['target'];
            } else {
                $stat = $this->updateCache($p, $stat);
                if (empty($stat['hidden'])) {
                    if (!$hasDir && $stat['mime'] === 'directory') {
                        $hasDir = true;
                    }
                    $this->dirsCache[$path][] = $p;
                }
            }
        }
        // stat link targets
        foreach ($targets as $name => $target) {
            $stat = array();
            $stat['name'] = $name;
            $p = $prefix . $name;
            $cacheDirTarget = $this->cacheDirTarget;
            $this->cacheDirTarget = $this->convEncIn($target, true);
            if ($tstat = $this->stat($target)) {
                $stat['size'] = $tstat['size'];
                $stat['alias'] = $target;
                $stat['thash'] = $tstat['hash'];
                $stat['mime'] = $tstat['mime'];
                $stat['read'] = $tstat['read'];
                $stat['write'] = $tstat['write'];

                if (isset($tstat['ts'])) {
                    $stat['ts'] = $tstat['ts'];
                }
                if (isset($tstat['owner'])) {
                    $stat['owner'] = $tstat['owner'];
                }
                if (isset($tstat['group'])) {
                    $stat['group'] = $tstat['group'];
                }
                if (isset($tstat['perm'])) {
                    $stat['perm'] = $tstat['perm'];
                }
                if (isset($tstat['isowner'])) {
                    $stat['isowner'] = $tstat['isowner'];
                }
            } else {

                $stat['mime'] = 'symlink-broken';
                $stat['read'] = false;
                $stat['write'] = false;
                $stat['size'] = 0;

            }
            $this->cacheDirTarget = $cacheDirTarget;
            $stat = $this->updateCache($p, $stat);
            if (empty($stat['hidden'])) {
                if (!$hasDir && $stat['mime'] === 'directory') {
                    $hasDir = true;
                }
                $this->dirsCache[$path][] = $p;
            }
        }

        if (isset($this->sessionCache['subdirs'])) {
            $this->sessionCache['subdirs'][$path] = $hasDir;
        }
    }


    /***************** file stat ********************/

    /**
     * Return stat for given path.
     * Stat contains following fields:
     * - (int)    size    file size in b. required
     * - (int)    ts      file modification time in unix time. required
     * - (string) mime    mimetype. required for folders, others - optionally
     * - (bool)   read    read permissions. required
     * - (bool)   write   write permissions. required
     * - (bool)   locked  is object locked. optionally
     * - (bool)   hidden  is object hidden. optionally
     * - (string) alias   for symlinks - link target path relative to root path. optionally
     * - (string) target  for symlinks - link target path. optionally
     * If file does not exists - returns empty array or false.
     *
     * @param  string $path file path
     *
     * @return array|false
     * @author Dmitry (dio) Levashov
     **/
    protected function _stat($path)
    {
        $outPath = $this->convEncOut($path);
        if (isset($this->cache[$outPath])) {
            return $this->convEncIn($this->cache[$outPath]);
        } else {
            $this->convEncIn();
        }
        if ($path === $this->root) {
            $res = array(
                'name' => $this->root,
                'mime' => 'directory',
                'dirs' => -1
            );
            if ($this->needOnline && (($this->ARGS['cmd'] === 'open' && $this->ARGS['target'] === $this->encode($this->root)) || $this->isMyReload())) {
                $check = array(
                    'ts' => true,
                    'dirs' => true,
                );
                $ts = 0;
                foreach ($this->ftpRawList($path) as $str) {
                    $info = preg_split('/\s+/', $str, 9);
                    if ($info[8] === '.') {
                        $info[8] = 'root';
                        if ($stat = $this->parseRaw(join(' ', $info), $path)) {
                            unset($stat['name']);
                            $res = array_merge($res, $stat);
                            if ($res['ts']) {
                                $ts = 0;
                                unset($check['ts']);
                            }
                        }
                    }
                    if ($check && ($stat = $this->parseRaw($str, $path))) {
                        if (isset($stat['ts']) && !empty($stat['ts'])) {
                            $ts = max($ts, $stat['ts']);
                        }
                        if (isset($stat['dirs']) && $stat['mime'] === 'directory'