diff --git a/phpseclib/Net/SFTP.php b/phpseclib/Net/SFTP.php index c10fb2be..dac72a65 100644 --- a/phpseclib/Net/SFTP.php +++ b/phpseclib/Net/SFTP.php @@ -41,6 +41,7 @@ use phpseclib3\Net\SFTP\OpenFlag; use phpseclib3\Net\SFTP\OpenFlag5; use phpseclib3\Net\SFTP\PacketType as SFTPPacketType; use phpseclib3\Net\SFTP\StatusCode; +use phpseclib3\Net\SSH2\DisconnectReason; use phpseclib3\Net\SSH2\MessageType as SSH2MessageType; /** @@ -509,7 +510,15 @@ class SFTP extends SSH2 } $this->pwd = true; - $this->pwd = $this->realpath('.'); + try { + $this->pwd = $this->realpath('.'); + } catch (\UnexpectedValueException $e) { + if (!$this->canonicalize_paths) { + throw $e; + } + $this->$this->canonicalize_paths = false; + $this->reset_connection(DisconnectReason::CONNECTION_LOST); + } $this->update_stat_cache($this->pwd, []); @@ -553,7 +562,9 @@ class SFTP extends SSH2 } /** - * Enable path canonicalization + * Disable path canonicalization + * + * If this is enabled then $sftp->pwd() will not return the canonicalized absolute path * */ public function disablePathCanonicalization() @@ -636,7 +647,32 @@ class SFTP extends SSH2 } if (!$this->canonicalize_paths) { - return $path; + if ($this->pwd === true) { + return '.'; + } + if (!strlen($path) || $path[0] != '/') { + $path = $this->pwd . '/' . $path; + } + $parts = explode('/', $path); + $afterPWD = $beforePWD = []; + foreach ($parts as $part) { + switch ($part) { + //case '': // some SFTP servers /require/ double /'s. see https://github.com/phpseclib/phpseclib/pull/1137 + case '.': + break; + case '..': + if (!empty($afterPWD)) { + array_pop($afterPWD); + } else { + $beforePWD[] = '..'; + } + break; + default: + $afterPWD[] = $part; + } + } + $beforePWD = count($beforePWD) ? implode('/', $beforePWD) : '.'; + return $beforePWD . '/' . implode('/', $afterPWD); } if ($this->pwd === true) {