diff --git a/phpseclib/Net/SFTP/Stream.php b/phpseclib/Net/SFTP/Stream.php index 01ebee8c..551ee38a 100644 --- a/phpseclib/Net/SFTP/Stream.php +++ b/phpseclib/Net/SFTP/Stream.php @@ -126,11 +126,22 @@ class Net_SFTP_Stream { */ var $context; + /** + * Notification callback function + * + * @var Callable + * @access public + */ + var $notification; + /** * Path Parser * * Extract a path from a URI and actually connect to an SSH server if appropriate * + * If "notification" is set as a context parameter the message code for successful login is + * NET_SSH2_MSG_USERAUTH_SUCCESS. For a failed login it's NET_SSH2_MSG_USERAUTH_FAILURE. + * * @param String $path * @return String * @access private @@ -143,6 +154,11 @@ class Net_SFTP_Stream { return false; } + $context = stream_context_get_params($this->context); + if (isset($context['notification'])) { + $this->notification = $context['notification']; + } + if ($host[0] == '$') { $host = substr($host, 1); global $$host; @@ -181,8 +197,27 @@ class Net_SFTP_Stream { $this->sftp = self::$instances[$host][$port][$user][(string) $pass]; } else { $this->sftp = new Net_SFTP($host, isset($port) ? $port : 22); - if (!$this->sftp->login($user, $pass)) { - return false; + if (isset($this->notification) && is_callable($this->notification)) { + /* if !is_callable($this->notification) we could do this: + + user_error('fopen(): failed to call user notifier', E_USER_WARNING); + + the ftp wrapper gives errors like that when the notifier isn't callable. + i've opted not to do that, however, since the ftp wrapper gives the line + on which the fopen occurred as the line number - not the line that the + user_error is on. + */ + call_user_func($this->notification, STREAM_NOTIFY_CONNECT, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); + call_user_func($this->notification, STREAM_NOTIFY_AUTH_REQUIRED, STREAM_NOTIFY_SEVERITY_INFO, '', 0, 0, 0); + if (!$this->sftp->login($user, $pass)) { + call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_ERR, 'Login Failure', NET_SSH2_MSG_USERAUTH_FAILURE, 0, 0); + return false; + } + call_user_func($this->notification, STREAM_NOTIFY_AUTH_RESULT, STREAM_NOTIFY_SEVERITY_INFO, 'Login Success', NET_SSH2_MSG_USERAUTH_SUCCESS, 0, 0); + } else { + if (!$this->sftp->login($user, $pass)) { + return false; + } } self::$instances[$host][$port][$user][(string) $pass] = $this->sftp; } @@ -201,7 +236,7 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function stream_open($path, $mode, $options, &$opened_path) + function _stream_open($path, $mode, $options, &$opened_path) { $path = $this->_parse_path($path); @@ -239,7 +274,7 @@ class Net_SFTP_Stream { * @return Mixed * @access public */ - function stream_read($count) + function _stream_read($count) { switch ($this->mode) { case 'w': @@ -256,7 +291,16 @@ class Net_SFTP_Stream { //} $result = $this->sftp->get($this->path, false, $this->pos, $count); - if (empty($result)) { + if (isset($this->notification) && is_callable($this->notification)) { + if ($result === false) { + call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); + return 0; + } + // seems that PHP calls stream_read in 8k chunks + call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($result), $size); + } + + if (empty($result)) { // ie. false or empty string $this->eof = true; return false; } @@ -272,7 +316,7 @@ class Net_SFTP_Stream { * @return Mixed * @access public */ - function stream_write($data) + function _stream_write($data) { switch ($this->mode) { case 'r': @@ -280,6 +324,15 @@ class Net_SFTP_Stream { } $result = $this->sftp->put($this->path, $data, NET_SFTP_STRING, $this->pos); + if (isset($this->notification) && is_callable($this->notification)) { + if (!$result) { + call_user_func($this->notification, STREAM_NOTIFY_FAILURE, STREAM_NOTIFY_SEVERITY_ERR, $this->sftp->getLastSFTPError(), NET_SFTP_OPEN, 0, 0); + return 0; + } + // seems that PHP splits up strings into 8k blocks before calling stream_write + call_user_func($this->notification, STREAM_NOTIFY_PROGRESS, STREAM_NOTIFY_SEVERITY_INFO, '', 0, strlen($data), strlen($data)); + } + if ($result === false) { return false; } @@ -297,7 +350,7 @@ class Net_SFTP_Stream { * @return Integer * @access public */ - function stream_tell() + function _stream_tell() { return $this->pos; } @@ -315,7 +368,7 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function stream_eof() + function _stream_eof() { return $this->eof; } @@ -328,7 +381,7 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function stream_seek($offset, $whence) + function _stream_seek($offset, $whence) { switch ($whence) { case SEEK_SET: @@ -357,7 +410,7 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function stream_metadata($path, $option, $var) + function _stream_metadata($path, $option, $var) { $path = $this->_parse_path($path); if ($path === false) { @@ -389,7 +442,7 @@ class Net_SFTP_Stream { * @return Resource * @access public */ - function stream_cast($cast_as) + function _stream_cast($cast_as) { return $this->sftp->fsock; } @@ -401,7 +454,7 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function stream_lock($operation) + function _stream_lock($operation) { return false; } @@ -418,7 +471,7 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function rename($path_from, $path_to) + function _rename($path_from, $path_to) { $path1 = parse_url($path_from); $path2 = parse_url($path_to); @@ -457,7 +510,7 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function dir_opendir($path, $options) + function _dir_opendir($path, $options) { $path = $this->_parse_path($path); if ($path === false) { @@ -474,7 +527,7 @@ class Net_SFTP_Stream { * @return Mixed * @access public */ - function dir_readdir() + function _dir_readdir() { if (isset($this->entries[$this->pos])) { return $this->entries[$this->pos++]; @@ -488,7 +541,7 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function dir_rewinddir() + function _dir_rewinddir() { $this->pos = 0; return true; @@ -500,7 +553,7 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function dir_closedir() + function _dir_closedir() { return true; } @@ -516,7 +569,7 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function mkdir($path, $mode, $options) + function _mkdir($path, $mode, $options) { $path = $this->_parse_path($path); if ($path === false) { @@ -540,7 +593,7 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function rmdir($path, $options) + function _rmdir($path, $options) { $path = $this->_parse_path($path); if ($path === false) { @@ -558,7 +611,7 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function stream_flush() + function _stream_flush() { return true; } @@ -569,7 +622,7 @@ class Net_SFTP_Stream { * @return Mixed * @access public */ - function stream_stat() + function _stream_stat() { $results = $this->sftp->stat($this->path); if ($results === false) { @@ -585,7 +638,7 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function unlink($path) + function _unlink($path) { $path = $this->_parse_path($path); if ($path === false) { @@ -607,7 +660,7 @@ class Net_SFTP_Stream { * @return Mixed * @access public */ - function url_stat($path, $flags) + function _url_stat($path, $flags) { $path = $this->_parse_path($path); if ($path === false) { @@ -629,7 +682,7 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function stream_truncate($new_size) + function _stream_truncate($new_size) { if (!$this->sftp->truncate($this->path, $new_size)) { return false; @@ -653,10 +706,54 @@ class Net_SFTP_Stream { * @return Boolean * @access public */ - function stream_set_option($option, $arg1, $arg2) + function _stream_set_option($option, $arg1, $arg2) { return false; } + + /** + * Close an resource + * + * @access public + */ + function _stream_close() + { + } + + /** + * __call Magic Method + * + * When you're utilizing an SFTP stream you're not calling the methods in this class directly - PHP is calling them for you. + * Which kinda begs the question... what methods is PHP calling and what parameters is it passing to them? This function + * lets you figure that out. + * + * If NET_SFTP_STREAM_LOGGING is defined all calls will be output on the screen and then (regardless of whether or not + * NET_SFTP_STREAM_LOGGING is enabled) the parameters will be passed through to the appropriate method. + * + * @param String + * @param Array + * @return Mixed + * @access public + */ + function __call($name, $arguments) + { + if (defined('NET_SFTP_STREAM_LOGGING')) { + echo $name . '('; + $last = count($arguments) - 1; + foreach ($arguments as $i => $argument) { + var_export($argument); + if ($i != $last) { + echo ','; + } + } + echo ")\r\n"; + } + $name = '_' . $name; + if (!method_exists($this, $name)) { + return false; + } + return call_user_func_array(array($this, $name), $arguments); + } } -stream_wrapper_register('sftp', 'Net_SFTP_Stream'); +stream_wrapper_register('sftp', 'Net_SFTP_Stream'); \ No newline at end of file