| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Pure-PHP implementation of SFTP. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * PHP versions 4 and 5 | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |  * Currently only supports SFTPv2 and v3, which, according to wikipedia.org, "is the most widely used version,
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |  * implemented by the popular OpenSSH SFTP server".  If you want SFTPv4/5/6 support, provide me with access
 | 
					
						
							|  |  |  |  * to an SFTPv4/5/6 server. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Here's a short example of how to use this library: | 
					
						
							|  |  |  |  * <code> | 
					
						
							|  |  |  |  * <?php | 
					
						
							|  |  |  |  *    include('Net/SFTP.php'); | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *    $sftp = new Net_SFTP('www.domain.tld'); | 
					
						
							|  |  |  |  *    if (!$sftp->login('username', 'password')) { | 
					
						
							|  |  |  |  *        exit('Login Failed'); | 
					
						
							|  |  |  |  *    } | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *    echo $sftp->pwd() . "\r\n"; | 
					
						
							|  |  |  |  *    $sftp->put('filename.ext', 'hello, world!'); | 
					
						
							|  |  |  |  *    print_r($sftp->nlist()); | 
					
						
							|  |  |  |  * ?>
 | 
					
						
							|  |  |  |  * </code> | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |  * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy | 
					
						
							|  |  |  |  * of this software and associated documentation files (the "Software"), to deal | 
					
						
							|  |  |  |  * in the Software without restriction, including without limitation the rights | 
					
						
							|  |  |  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | 
					
						
							|  |  |  |  * copies of the Software, and to permit persons to whom the Software is | 
					
						
							|  |  |  |  * furnished to do so, subject to the following conditions: | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |  * The above copyright notice and this permission notice shall be included in | 
					
						
							|  |  |  |  * all copies or substantial portions of the Software. | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
					
						
							|  |  |  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
					
						
							|  |  |  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
					
						
							|  |  |  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
					
						
							|  |  |  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
					
						
							|  |  |  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
					
						
							|  |  |  |  * THE SOFTWARE. | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |  * | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |  * @category  Net | 
					
						
							|  |  |  |  * @package   Net_SFTP | 
					
						
							|  |  |  |  * @author    Jim Wigginton <terrafrost@php.net> | 
					
						
							|  |  |  |  * @copyright MMIX Jim Wigginton | 
					
						
							|  |  |  |  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License | 
					
						
							|  |  |  |  * @link      http://phpseclib.sourceforge.net | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Include Net_SSH2 | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  | if (!class_exists('Net_SSH2')) { | 
					
						
							|  |  |  |     include_once 'SSH2.php'; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /**#@+
 | 
					
						
							|  |  |  |  * @access public | 
					
						
							|  |  |  |  * @see Net_SFTP::getLog() | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Returns the message numbers | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | define('NET_SFTP_LOG_SIMPLE',  NET_SSH2_LOG_SIMPLE); | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Returns the message content | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | define('NET_SFTP_LOG_COMPLEX', NET_SSH2_LOG_COMPLEX); | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Outputs the message content in real-time. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | define('NET_SFTP_LOG_REALTIME', 3); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  | /**#@-*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * SFTP channel constant | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |  * Net_SSH2::exec() uses 0 and Net_SSH2::read() / Net_SSH2::write() use 1. | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |  * | 
					
						
							|  |  |  |  * @see Net_SSH2::_send_channel_packet() | 
					
						
							|  |  |  |  * @see Net_SSH2::_get_channel_packet() | 
					
						
							|  |  |  |  * @access private | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  | define('NET_SFTP_CHANNEL', 0x100); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | /**#@+
 | 
					
						
							|  |  |  |  * @access public | 
					
						
							|  |  |  |  * @see Net_SFTP::put() | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Reads data from a local file. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  | define('NET_SFTP_LOCAL_FILE',    1); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Reads data from a string. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  | // this value isn't really used anymore but i'm keeping it reserved for historical reasons
 | 
					
						
							|  |  |  | define('NET_SFTP_STRING',        2); | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Resumes an upload | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | define('NET_SFTP_RESUME',        4); | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Append a local file to an already existing remote file | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | define('NET_SFTP_RESUME_START',  8); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  | /**#@-*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /** | 
					
						
							|  |  |  |  * Pure-PHP implementations of SFTP. | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |  * @package Net_SFTP | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |  * @author  Jim Wigginton <terrafrost@php.net> | 
					
						
							|  |  |  |  * @version 0.1.0 | 
					
						
							|  |  |  |  * @access  public | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  | class Net_SFTP extends Net_SSH2 | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Packet Types | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @see Net_SFTP::Net_SFTP() | 
					
						
							|  |  |  |      * @var Array | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     var $packet_types = array(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Status Codes | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @see Net_SFTP::Net_SFTP() | 
					
						
							|  |  |  |      * @var Array | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     var $status_codes = array(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * The Request ID | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * The request ID exists in the off chance that a packet is sent out-of-order.  Of course, this library doesn't support | 
					
						
							|  |  |  |      * concurrent actions, so it's somewhat academic, here. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @var Integer | 
					
						
							|  |  |  |      * @see Net_SFTP::_send_sftp_packet() | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     var $request_id = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * The Packet Type | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * The request ID exists in the off chance that a packet is sent out-of-order.  Of course, this library doesn't support | 
					
						
							|  |  |  |      * concurrent actions, so it's somewhat academic, here. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @var Integer | 
					
						
							|  |  |  |      * @see Net_SFTP::_get_sftp_packet() | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     var $packet_type = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Packet Buffer | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @var String | 
					
						
							|  |  |  |      * @see Net_SFTP::_get_sftp_packet() | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     var $packet_buffer = ''; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Extensions supported by the server | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @var Array | 
					
						
							|  |  |  |      * @see Net_SFTP::_initChannel() | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     var $extensions = array(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Server SFTP version | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @var Integer | 
					
						
							|  |  |  |      * @see Net_SFTP::_initChannel() | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     var $version; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Current working directory | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @var String | 
					
						
							|  |  |  |      * @see Net_SFTP::_realpath() | 
					
						
							|  |  |  |      * @see Net_SFTP::chdir() | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     var $pwd = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Packet Type Log | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @see Net_SFTP::getLog() | 
					
						
							|  |  |  |      * @var Array | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     var $packet_type_log = array(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Packet Log | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @see Net_SFTP::getLog() | 
					
						
							|  |  |  |      * @var Array | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     var $packet_log = array(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Error information | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @see Net_SFTP::getSFTPErrors() | 
					
						
							|  |  |  |      * @see Net_SFTP::getLastSFTPError() | 
					
						
							|  |  |  |      * @var String | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |     var $sftp_errors = array(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |      * Directory Cache | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |      * Rather than always having to open a directory and close it immediately there after to see if a file is a directory or | 
					
						
							|  |  |  |      * rather than always | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @see Net_SFTP::_save_dir() | 
					
						
							|  |  |  |      * @see Net_SFTP::_remove_dir() | 
					
						
							|  |  |  |      * @see Net_SFTP::_is_dir() | 
					
						
							|  |  |  |      * @var Array | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     var $dirs = array(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Max SFTP Packet Size | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @see Net_SFTP::Net_SFTP() | 
					
						
							|  |  |  |      * @see Net_SFTP::get() | 
					
						
							|  |  |  |      * @var Array | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |     var $max_sftp_packet; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Default Constructor. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Connects to an SFTP server | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $host | 
					
						
							|  |  |  |      * @param optional Integer $port | 
					
						
							|  |  |  |      * @param optional Integer $timeout | 
					
						
							|  |  |  |      * @return Net_SFTP | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function Net_SFTP($host, $port = 22, $timeout = 10) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         parent::Net_SSH2($host, $port, $timeout); | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         $this->max_sftp_packet = 1 << 15; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         $this->packet_types = array( | 
					
						
							|  |  |  |             1  => 'NET_SFTP_INIT', | 
					
						
							|  |  |  |             2  => 'NET_SFTP_VERSION', | 
					
						
							|  |  |  |             /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+: | 
					
						
							|  |  |  |                    SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1
 | 
					
						
							|  |  |  |                pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */
 | 
					
						
							|  |  |  |             3  => 'NET_SFTP_OPEN', | 
					
						
							|  |  |  |             4  => 'NET_SFTP_CLOSE', | 
					
						
							|  |  |  |             5  => 'NET_SFTP_READ', | 
					
						
							|  |  |  |             6  => 'NET_SFTP_WRITE', | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |             7  => 'NET_SFTP_LSTAT', | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             9  => 'NET_SFTP_SETSTAT', | 
					
						
							|  |  |  |             11 => 'NET_SFTP_OPENDIR', | 
					
						
							|  |  |  |             12 => 'NET_SFTP_READDIR', | 
					
						
							|  |  |  |             13 => 'NET_SFTP_REMOVE', | 
					
						
							|  |  |  |             14 => 'NET_SFTP_MKDIR', | 
					
						
							|  |  |  |             15 => 'NET_SFTP_RMDIR', | 
					
						
							|  |  |  |             16 => 'NET_SFTP_REALPATH', | 
					
						
							|  |  |  |             17 => 'NET_SFTP_STAT', | 
					
						
							|  |  |  |             /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+: | 
					
						
							|  |  |  |                    SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
 | 
					
						
							|  |  |  |                pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */
 | 
					
						
							|  |  |  |             18 => 'NET_SFTP_RENAME', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             101=> 'NET_SFTP_STATUS', | 
					
						
							|  |  |  |             102=> 'NET_SFTP_HANDLE', | 
					
						
							|  |  |  |             /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+: | 
					
						
							|  |  |  |                    SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4
 | 
					
						
							|  |  |  |                pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */
 | 
					
						
							|  |  |  |             103=> 'NET_SFTP_DATA', | 
					
						
							|  |  |  |             104=> 'NET_SFTP_NAME', | 
					
						
							|  |  |  |             105=> 'NET_SFTP_ATTRS', | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             200=> 'NET_SFTP_EXTENDED' | 
					
						
							|  |  |  |         ); | 
					
						
							|  |  |  |         $this->status_codes = array( | 
					
						
							|  |  |  |             0 => 'NET_SFTP_STATUS_OK', | 
					
						
							|  |  |  |             1 => 'NET_SFTP_STATUS_EOF', | 
					
						
							|  |  |  |             2 => 'NET_SFTP_STATUS_NO_SUCH_FILE', | 
					
						
							|  |  |  |             3 => 'NET_SFTP_STATUS_PERMISSION_DENIED', | 
					
						
							|  |  |  |             4 => 'NET_SFTP_STATUS_FAILURE', | 
					
						
							|  |  |  |             5 => 'NET_SFTP_STATUS_BAD_MESSAGE', | 
					
						
							|  |  |  |             6 => 'NET_SFTP_STATUS_NO_CONNECTION', | 
					
						
							|  |  |  |             7 => 'NET_SFTP_STATUS_CONNECTION_LOST', | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED', | 
					
						
							|  |  |  |             9 => 'NET_SFTP_STATUS_INVALID_HANDLE', | 
					
						
							|  |  |  |             10 => 'NET_SFTP_STATUS_NO_SUCH_PATH', | 
					
						
							|  |  |  |             11 => 'NET_SFTP_STATUS_FILE_ALREADY_EXISTS', | 
					
						
							|  |  |  |             12 => 'NET_SFTP_STATUS_WRITE_PROTECT', | 
					
						
							|  |  |  |             13 => 'NET_SFTP_STATUS_NO_MEDIA', | 
					
						
							|  |  |  |             14 => 'NET_SFTP_STATUS_NO_SPACE_ON_FILESYSTEM', | 
					
						
							|  |  |  |             15 => 'NET_SFTP_STATUS_QUOTA_EXCEEDED', | 
					
						
							|  |  |  |             16 => 'NET_SFTP_STATUS_UNKNOWN_PRINCIPAL', | 
					
						
							|  |  |  |             17 => 'NET_SFTP_STATUS_LOCK_CONFLICT', | 
					
						
							|  |  |  |             18 => 'NET_SFTP_STATUS_DIR_NOT_EMPTY', | 
					
						
							|  |  |  |             19 => 'NET_SFTP_STATUS_NOT_A_DIRECTORY', | 
					
						
							|  |  |  |             20 => 'NET_SFTP_STATUS_INVALID_FILENAME', | 
					
						
							|  |  |  |             21 => 'NET_SFTP_STATUS_LINK_LOOP', | 
					
						
							|  |  |  |             22 => 'NET_SFTP_STATUS_CANNOT_DELETE', | 
					
						
							|  |  |  |             23 => 'NET_SFTP_STATUS_INVALID_PARAMETER', | 
					
						
							|  |  |  |             24 => 'NET_SFTP_STATUS_FILE_IS_A_DIRECTORY', | 
					
						
							|  |  |  |             25 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_CONFLICT', | 
					
						
							|  |  |  |             26 => 'NET_SFTP_STATUS_BYTE_RANGE_LOCK_REFUSED', | 
					
						
							|  |  |  |             27 => 'NET_SFTP_STATUS_DELETE_PENDING', | 
					
						
							|  |  |  |             28 => 'NET_SFTP_STATUS_FILE_CORRUPT', | 
					
						
							|  |  |  |             29 => 'NET_SFTP_STATUS_OWNER_INVALID', | 
					
						
							|  |  |  |             30 => 'NET_SFTP_STATUS_GROUP_INVALID', | 
					
						
							|  |  |  |             31 => 'NET_SFTP_STATUS_NO_MATCHING_BYTE_RANGE_LOCK' | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         ); | 
					
						
							|  |  |  |         // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
 | 
					
						
							|  |  |  |         // the order, in this case, matters quite a lot - see Net_SFTP::_parseAttributes() to understand why
 | 
					
						
							|  |  |  |         $this->attributes = array( | 
					
						
							|  |  |  |             0x00000001 => 'NET_SFTP_ATTR_SIZE', | 
					
						
							|  |  |  |             0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
 | 
					
						
							|  |  |  |             0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS', | 
					
						
							|  |  |  |             0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME', | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |             // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             // yields inconsistent behavior depending on how php is compiled.  so we left shift -1 (which, in
 | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |             // two's compliment, consists of all 1 bits) by 31.  on 64-bit systems this'll yield 0xFFFFFFFF80000000.
 | 
					
						
							|  |  |  |             // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
 | 
					
						
							|  |  |  |               -1 << 31 => 'NET_SFTP_ATTR_EXTENDED' | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         ); | 
					
						
							|  |  |  |         // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
 | 
					
						
							|  |  |  |         // the flag definitions change somewhat in SFTPv5+.  if SFTPv5+ support is added to this library, maybe name
 | 
					
						
							|  |  |  |         // the array for that $this->open5_flags and similarily alter the constant names.
 | 
					
						
							|  |  |  |         $this->open_flags = array( | 
					
						
							|  |  |  |             0x00000001 => 'NET_SFTP_OPEN_READ', | 
					
						
							|  |  |  |             0x00000002 => 'NET_SFTP_OPEN_WRITE', | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             0x00000004 => 'NET_SFTP_OPEN_APPEND', | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             0x00000008 => 'NET_SFTP_OPEN_CREATE', | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             0x00000010 => 'NET_SFTP_OPEN_TRUNCATE', | 
					
						
							|  |  |  |             0x00000020 => 'NET_SFTP_OPEN_EXCL' | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |         // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
 | 
					
						
							|  |  |  |         // see Net_SFTP::_parseLongname() for an explanation
 | 
					
						
							|  |  |  |         $this->file_types = array( | 
					
						
							|  |  |  |             1 => 'NET_SFTP_TYPE_REGULAR', | 
					
						
							|  |  |  |             2 => 'NET_SFTP_TYPE_DIRECTORY', | 
					
						
							|  |  |  |             3 => 'NET_SFTP_TYPE_SYMLINK', | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             4 => 'NET_SFTP_TYPE_SPECIAL', | 
					
						
							|  |  |  |             5 => 'NET_SFTP_TYPE_UNKNOWN', | 
					
						
							|  |  |  |             // the followin types were first defined for use in SFTPv5+
 | 
					
						
							|  |  |  |             // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
 | 
					
						
							|  |  |  |             6 => 'NET_SFTP_TYPE_SOCKET', | 
					
						
							|  |  |  |             7 => 'NET_SFTP_TYPE_CHAR_DEVICE', | 
					
						
							|  |  |  |             8 => 'NET_SFTP_TYPE_BLOCK_DEVICE', | 
					
						
							|  |  |  |             9 => 'NET_SFTP_TYPE_FIFO' | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         $this->_define_array( | 
					
						
							|  |  |  |             $this->packet_types, | 
					
						
							|  |  |  |             $this->status_codes, | 
					
						
							|  |  |  |             $this->attributes, | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |             $this->open_flags, | 
					
						
							|  |  |  |             $this->file_types | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         ); | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (!defined('NET_SFTP_QUEUE_SIZE')) { | 
					
						
							|  |  |  |             define('NET_SFTP_QUEUE_SIZE', 50); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Login | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $username | 
					
						
							|  |  |  |      * @param optional String $password | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |     function login($username) | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $args = func_get_args(); | 
					
						
							|  |  |  |         if (!call_user_func_array(array(&$this, '_login'), $args)) { | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $this->window_size_server_to_client[NET_SFTP_CHANNEL] = $this->window_size; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         $packet = pack('CNa*N3', | 
					
						
							|  |  |  |             NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SFTP_CHANNEL, $this->window_size, 0x4000); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!$this->_send_binary_packet($packet)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $this->_get_channel_packet(NET_SFTP_CHANNEL); | 
					
						
							|  |  |  |         if ($response === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $packet = pack('CNNa*CNa*', | 
					
						
							|  |  |  |             NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SFTP_CHANNEL], strlen('subsystem'), 'subsystem', 1, strlen('sftp'), 'sftp'); | 
					
						
							|  |  |  |         if (!$this->_send_binary_packet($packet)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $this->_get_channel_packet(NET_SFTP_CHANNEL); | 
					
						
							|  |  |  |         if ($response === false) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             // from PuTTY's psftp.exe
 | 
					
						
							|  |  |  |             $command = "test -x /usr/lib/sftp-server && exec /usr/lib/sftp-server\n" . | 
					
						
							|  |  |  |                        "test -x /usr/local/lib/sftp-server && exec /usr/local/lib/sftp-server\n" . | 
					
						
							|  |  |  |                        "exec sftp-server"; | 
					
						
							|  |  |  |             // we don't do $this->exec($command, false) because exec() operates on a different channel and plus the SSH_MSG_CHANNEL_OPEN that exec() does
 | 
					
						
							|  |  |  |             // is redundant
 | 
					
						
							|  |  |  |             $packet = pack('CNNa*CNa*', | 
					
						
							|  |  |  |                 NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SFTP_CHANNEL], strlen('exec'), 'exec', 1, strlen($command), $command); | 
					
						
							|  |  |  |             if (!$this->_send_binary_packet($packet)) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $response = $this->_get_channel_packet(NET_SFTP_CHANNEL); | 
					
						
							|  |  |  |             if ($response === false) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |         if ($this->packet_type != NET_SFTP_VERSION) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             user_error('Expected SSH_FXP_VERSION'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         extract(unpack('Nversion', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |         $this->version = $version; | 
					
						
							|  |  |  |         while (!empty($response)) { | 
					
						
							|  |  |  |             extract(unpack('Nlength', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |             $key = $this->_string_shift($response, $length); | 
					
						
							|  |  |  |             extract(unpack('Nlength', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |             $value = $this->_string_shift($response, $length); | 
					
						
							|  |  |  |             $this->extensions[$key] = $value; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* | 
					
						
							|  |  |  |          SFTPv4+ defines a 'newline' extension.  SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com', | 
					
						
							|  |  |  |          however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's | 
					
						
							|  |  |  |          not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for | 
					
						
							|  |  |  |          one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that | 
					
						
							|  |  |  |          'newline@vandyke.com' would. | 
					
						
							|  |  |  |         */ | 
					
						
							|  |  |  |         /* | 
					
						
							|  |  |  |         if (isset($this->extensions['newline@vandyke.com'])) { | 
					
						
							|  |  |  |             $this->extensions['newline'] = $this->extensions['newline@vandyke.com']; | 
					
						
							|  |  |  |             unset($this->extensions['newline@vandyke.com']); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->request_id = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* | 
					
						
							|  |  |  |          A Note on SFTPv4/5/6 support: | 
					
						
							|  |  |  |          <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.1> states the following:
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |          "If the client wishes to interoperate with servers that support noncontiguous version
 | 
					
						
							|  |  |  |           numbers it SHOULD send '3'"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |          Given that the server only sends its version number after the client has already done so, the above | 
					
						
							|  |  |  |          seems to be suggesting that v3 should be the default version.  This makes sense given that v3 is the | 
					
						
							|  |  |  |          most popular. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |          <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.5> states the following;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |          "If the server did not send the "versions" extension, or the version-from-list was not included, the
 | 
					
						
							|  |  |  |           server MAY send a status response describing the failure, but MUST then close the channel without | 
					
						
							|  |  |  |           processing any further requests."
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |          So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and | 
					
						
							|  |  |  |          a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4?  If it only implements | 
					
						
							|  |  |  |          v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed | 
					
						
							|  |  |  |          in draft-ietf-secsh-filexfer-13 would be quite impossible.  As such, what Net_SFTP would do is close the | 
					
						
							|  |  |  |          channel and reopen it with a new and updated SSH_FXP_INIT packet. | 
					
						
							|  |  |  |         */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         switch ($this->version) { | 
					
						
							|  |  |  |             case 2: | 
					
						
							|  |  |  |             case 3: | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |                 return false; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->pwd = $this->_realpath('.'); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $this->_save_dir($this->pwd); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Returns the current directory name | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return Mixed | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function pwd() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return $this->pwd; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Logs errors | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $response | 
					
						
							|  |  |  |      * @param optional Integer $status | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _logError($response, $status = -1) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if ($status == -1) { | 
					
						
							|  |  |  |             extract(unpack('Nstatus', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $error = $this->status_codes[$status]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($this->version > 2) { | 
					
						
							|  |  |  |             extract(unpack('Nlength', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |             $this->sftp_errors[] = $error . ': ' . $this->_string_shift($response, $length); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             $this->sftp_errors[] = $error; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Canonicalize the Server-Side Path Name | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it.  Returns | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |      * the absolute (canonicalized) path. | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |      * | 
					
						
							|  |  |  |      * @see Net_SFTP::chdir() | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |      * @param String $path | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |      * @return Mixed | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |     function _realpath($path) | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         if ($this->pwd === false) { | 
					
						
							|  |  |  |             // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
 | 
					
						
							|  |  |  |             if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($path), $path))) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |             switch ($this->packet_type) { | 
					
						
							|  |  |  |                 case NET_SFTP_NAME: | 
					
						
							|  |  |  |                     // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following
 | 
					
						
							|  |  |  |                     // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks
 | 
					
						
							|  |  |  |                     // at is the first part and that part is defined the same in SFTP versions 3 through 6.
 | 
					
						
							|  |  |  |                     $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway
 | 
					
						
							|  |  |  |                     extract(unpack('Nlength', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |                     return $this->_string_shift($response, $length); | 
					
						
							|  |  |  |                 case NET_SFTP_STATUS: | 
					
						
							|  |  |  |                     $this->_logError($response); | 
					
						
							|  |  |  |                     return false; | 
					
						
							|  |  |  |                 default: | 
					
						
							|  |  |  |                     user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); | 
					
						
							|  |  |  |                     return false; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         if ($path[0] != '/') { | 
					
						
							|  |  |  |             $path = $this->pwd . '/' . $path; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $path = explode('/', $path); | 
					
						
							|  |  |  |         $new = array(); | 
					
						
							|  |  |  |         foreach ($path as $dir) { | 
					
						
							|  |  |  |             if (!strlen($dir)) { | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             switch ($dir) { | 
					
						
							|  |  |  |                 case '..': | 
					
						
							|  |  |  |                     array_pop($new); | 
					
						
							|  |  |  |                 case '.': | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 default: | 
					
						
							|  |  |  |                     $new[] = $dir; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         return '/' . implode('/', $new); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Changes the current directory | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $dir | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function chdir($dir) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         // assume current dir if $dir is empty
 | 
					
						
							|  |  |  |         if ($dir === '') { | 
					
						
							|  |  |  |             $dir = './'; | 
					
						
							|  |  |  |         // suffix a slash if needed
 | 
					
						
							|  |  |  |         } elseif ($dir[strlen($dir) - 1] != '/') { | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             $dir.= '/'; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         $dir = $this->_realpath($dir); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // confirm that $dir is, in fact, a valid directory
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         if ($this->_is_dir($dir)) { | 
					
						
							|  |  |  |             $this->pwd = $dir; | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // we could do a stat on the alleged $dir to see if it's a directory but that doesn't tell us
 | 
					
						
							|  |  |  |         // the currently logged in user has the appropriate permissions or not. maybe you could see if
 | 
					
						
							|  |  |  |         // the file's uid / gid match the currently logged in user's uid / gid but how there's no easy
 | 
					
						
							|  |  |  |         // way to get those with SFTP
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // see Net_SFTP::nlist() for a more thorough explanation of the following
 | 
					
						
							|  |  |  |         $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |         switch ($this->packet_type) { | 
					
						
							|  |  |  |             case NET_SFTP_HANDLE: | 
					
						
							|  |  |  |                 $handle = substr($response, 4); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             case NET_SFTP_STATUS: | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                 $this->_logError($response); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 return false; | 
					
						
							|  |  |  |             default: | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                 user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         if (!$this->_close_handle($handle)) { | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $this->_save_dir($dir); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |         $this->pwd = $dir; | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Returns a list of files in the given directory | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param optional String $dir | 
					
						
							|  |  |  |      * @return Mixed | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function nlist($dir = '.') | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return $this->_list($dir, false); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |      * Returns a detailed list of files in the given directory | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |      * | 
					
						
							|  |  |  |      * @param optional String $dir | 
					
						
							|  |  |  |      * @return Mixed | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function rawlist($dir = '.') | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return $this->_list($dir, true); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Reads a list, be it detailed or not, of files in the given directory | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |      * $realpath exists because, in the case of the recursive deletes and recursive chmod's $realpath has already | 
					
						
							|  |  |  |      * been calculated. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $dir | 
					
						
							|  |  |  |      * @param optional Boolean $raw | 
					
						
							|  |  |  |      * @param optional Boolean $realpath | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |      * @return Mixed | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |     function _list($dir, $raw = true, $realpath = true) | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     { | 
					
						
							|  |  |  |         if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $dir = $this->_realpath($dir . '/'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         if ($dir === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2
 | 
					
						
							|  |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |         switch ($this->packet_type) { | 
					
						
							|  |  |  |             case NET_SFTP_HANDLE: | 
					
						
							|  |  |  |                 // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2
 | 
					
						
							|  |  |  |                 // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that
 | 
					
						
							|  |  |  |                 // represent the length of the string and leave it at that
 | 
					
						
							|  |  |  |                 $handle = substr($response, 4); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             case NET_SFTP_STATUS: | 
					
						
							|  |  |  |                 // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                 $this->_logError($response); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 return false; | 
					
						
							|  |  |  |             default: | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                 user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $this->_save_dir($dir); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         $contents = array(); | 
					
						
							|  |  |  |         while (true) { | 
					
						
							|  |  |  |             // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2
 | 
					
						
							|  |  |  |             // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many
 | 
					
						
							|  |  |  |             // SSH_MSG_CHANNEL_DATA messages is not known to me.
 | 
					
						
							|  |  |  |             if (!$this->_send_sftp_packet(NET_SFTP_READDIR, pack('Na*', strlen($handle), $handle))) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |             switch ($this->packet_type) { | 
					
						
							|  |  |  |                 case NET_SFTP_NAME: | 
					
						
							|  |  |  |                     extract(unpack('Ncount', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |                     for ($i = 0; $i < $count; $i++) { | 
					
						
							|  |  |  |                         extract(unpack('Nlength', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |                         $shortname = $this->_string_shift($response, $length); | 
					
						
							|  |  |  |                         extract(unpack('Nlength', $this->_string_shift($response, 4))); | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |                         $longname = $this->_string_shift($response, $length); | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                         $attributes = $this->_parseAttributes($response); | 
					
						
							|  |  |  |                         if (!isset($attributes['type'])) { | 
					
						
							|  |  |  |                             $fileType = $this->_parseLongname($longname); | 
					
						
							|  |  |  |                             if ($fileType) { | 
					
						
							|  |  |  |                                 $attributes['type'] = $fileType; | 
					
						
							|  |  |  |                             } | 
					
						
							|  |  |  |                         } | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                         if (!$raw) { | 
					
						
							|  |  |  |                             $contents[] = $shortname; | 
					
						
							|  |  |  |                         } else { | 
					
						
							|  |  |  |                             $contents[$shortname] = $attributes; | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                         if (isset($attributes['type']) && $attributes['type'] == NET_SFTP_TYPE_DIRECTORY && ($shortname != '.' && $shortname != '..')) { | 
					
						
							|  |  |  |                             $this->_save_dir($dir . '/' . $shortname); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                         } | 
					
						
							|  |  |  |                         // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the
 | 
					
						
							|  |  |  |                         // final SSH_FXP_STATUS packet should tell us that, already.
 | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 case NET_SFTP_STATUS: | 
					
						
							|  |  |  |                     extract(unpack('Nstatus', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |                     if ($status != NET_SFTP_STATUS_EOF) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                         $this->_logError($response, $status); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                         return false; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     break 2; | 
					
						
							|  |  |  |                 default: | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                     user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                     return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         if (!$this->_close_handle($handle)) { | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $contents; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Returns the file size, in bytes, or false, on failure | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Files larger than 4GB will show up as being exactly 4GB. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |      * @param String $filename | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |      * @return Mixed | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function size($filename) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $filename = $this->_realpath($filename); | 
					
						
							|  |  |  |         if ($filename === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |         return $this->_size($filename); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Save directories to cache | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $dir | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _save_dir($dir) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // preg_replace('#^/|/(?=/)|/$#', '', $dir) == str_replace('//', '/', trim($dir, '/'))
 | 
					
						
							|  |  |  |         $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $temp = &$this->dirs; | 
					
						
							|  |  |  |         foreach ($dirs as $dir) { | 
					
						
							|  |  |  |             if (!isset($temp[$dir])) { | 
					
						
							|  |  |  |                 $temp[$dir] = array(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             $temp = &$temp[$dir]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Remove directories from cache | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $dir | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _remove_dir($dir) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $temp = &$this->dirs; | 
					
						
							|  |  |  |         foreach ($dirs as $dir) { | 
					
						
							|  |  |  |             if ($dir == end($dirs)) { | 
					
						
							|  |  |  |                 unset($temp[$dir]); | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (!isset($temp[$dir])) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             $temp = &$temp[$dir]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Checks cache for directory | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Mainly used by chdir, which is, in turn, also used for determining whether or not an individual | 
					
						
							|  |  |  |      * file is a directory or not by stat() and lstat() | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $dir | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _is_dir($dir) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $dirs = explode('/', preg_replace('#^/|/(?=/)|/$#', '', $dir)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $temp = &$this->dirs; | 
					
						
							|  |  |  |         foreach ($dirs as $dir) { | 
					
						
							|  |  |  |             if (!isset($temp[$dir])) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             $temp = &$temp[$dir]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Returns general information about a file. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Returns an array on success and false otherwise. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $filename | 
					
						
							|  |  |  |      * @return Mixed | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function stat($filename) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $filename = $this->_realpath($filename); | 
					
						
							|  |  |  |         if ($filename === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $stat = $this->_stat($filename, NET_SFTP_STAT); | 
					
						
							|  |  |  |         if ($stat === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (isset($stat['type'])) { | 
					
						
							|  |  |  |             return $stat; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $pwd = $this->pwd; | 
					
						
							|  |  |  |         $stat['type'] = $this->chdir($filename) ? | 
					
						
							|  |  |  |             NET_SFTP_TYPE_DIRECTORY : | 
					
						
							|  |  |  |             NET_SFTP_TYPE_REGULAR; | 
					
						
							|  |  |  |         $this->pwd = $pwd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $stat; | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Returns general information about a file or symbolic link. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Returns an array on success and false otherwise. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $filename | 
					
						
							|  |  |  |      * @return Mixed | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function lstat($filename) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $filename = $this->_realpath($filename); | 
					
						
							|  |  |  |         if ($filename === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $lstat = $this->_stat($filename, NET_SFTP_LSTAT); | 
					
						
							|  |  |  |         if ($lstat === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (isset($lstat['type'])) { | 
					
						
							|  |  |  |             return $lstat; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $stat = $this->_stat($filename, NET_SFTP_STAT); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($lstat != $stat) { | 
					
						
							|  |  |  |             return array_merge($lstat, array('type' => NET_SFTP_TYPE_SYMLINK)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $pwd = $this->pwd; | 
					
						
							|  |  |  |         $lstat['type'] = $this->chdir($filename) ? | 
					
						
							|  |  |  |             NET_SFTP_TYPE_DIRECTORY : | 
					
						
							|  |  |  |             NET_SFTP_TYPE_REGULAR; | 
					
						
							|  |  |  |         $this->pwd = $pwd; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $lstat; | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Returns general information about a file or symbolic link | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Determines information without calling Net_SFTP::_realpath(). | 
					
						
							|  |  |  |      * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $filename | 
					
						
							|  |  |  |      * @param Integer $type | 
					
						
							|  |  |  |      * @return Mixed | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _stat($filename, $type) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         // SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
 | 
					
						
							|  |  |  |         $packet = pack('Na*', strlen($filename), $filename); | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |         if (!$this->_send_sftp_packet($type, $packet)) { | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |         switch ($this->packet_type) { | 
					
						
							|  |  |  |             case NET_SFTP_ATTRS: | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                 return $this->_parseAttributes($response); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             case NET_SFTP_STATUS: | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                 $this->_logError($response); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Returns the file size, in bytes, or false, on failure | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Determines the size without calling Net_SFTP::_realpath() | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $filename | 
					
						
							|  |  |  |      * @return Mixed | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _size($filename) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $result = $this->_stat($filename, NET_SFTP_STAT); | 
					
						
							|  |  |  |         if ($result === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return isset($result['size']) ? $result['size'] : -1; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Truncates a file to a given length | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $filename | 
					
						
							|  |  |  |      * @param Integer $new_size | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function truncate($filename, $new_size) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $attr = pack('N3', NET_SFTP_ATTR_SIZE, $new_size / 4294967296, $new_size); // 4294967296 == 0x100000000 == 1<<32
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $this->_setstat($filename, $attr, false); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Sets access and modification time of file. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * If the file does not exist, it will be created. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $filename | 
					
						
							|  |  |  |      * @param optional Integer $time | 
					
						
							|  |  |  |      * @param optional Integer $atime | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function touch($filename, $time = null, $atime = null) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $filename = $this->_realpath($filename); | 
					
						
							|  |  |  |         if ($filename === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!isset($time)) { | 
					
						
							|  |  |  |             $time = time(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (!isset($atime)) { | 
					
						
							|  |  |  |             $atime = $time; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_EXCL; | 
					
						
							|  |  |  |         $attr = pack('N3', NET_SFTP_ATTR_ACCESSTIME, $time, $atime); | 
					
						
							|  |  |  |         $packet = pack('Na*Na*', strlen($filename), $filename, $flags, $attr); | 
					
						
							|  |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |         switch ($this->packet_type) { | 
					
						
							|  |  |  |             case NET_SFTP_HANDLE: | 
					
						
							|  |  |  |                 return $this->_close_handle(substr($response, 4)); | 
					
						
							|  |  |  |             case NET_SFTP_STATUS: | 
					
						
							|  |  |  |                 $this->_logError($response); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |                 user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $this->_setstat($filename, $attr, false); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Changes file or directory owner | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Returns true on success or false on error. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $filename | 
					
						
							|  |  |  |      * @param Integer $uid | 
					
						
							|  |  |  |      * @param optional Boolean $recursive | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function chown($filename, $uid, $recursive = false) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // quoting from <http://www.kernel.org/doc/man-pages/online/pages/man2/chown.2.html>,
 | 
					
						
							|  |  |  |         // "if the owner or group is specified as -1, then that ID is not changed"
 | 
					
						
							|  |  |  |         $attr = pack('N3', NET_SFTP_ATTR_UIDGID, $uid, -1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $this->_setstat($filename, $attr, $recursive); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Changes file or directory group | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Returns true on success or false on error. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $filename | 
					
						
							|  |  |  |      * @param Integer $gid | 
					
						
							|  |  |  |      * @param optional Boolean $recursive | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function chgrp($filename, $gid, $recursive = false) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $attr = pack('N3', NET_SFTP_ATTR_UIDGID, -1, $gid); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $this->_setstat($filename, $attr, $recursive); | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Set permissions on a file. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |      * Returns the new file permissions on success or false on error. | 
					
						
							|  |  |  |      * If $recursive is true than this just returns true or false. | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |      * | 
					
						
							|  |  |  |      * @param Integer $mode | 
					
						
							|  |  |  |      * @param String $filename | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |      * @param optional Boolean $recursive | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |      * @return Mixed | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |     function chmod($mode, $filename, $recursive = false) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (is_string($mode) && is_int($filename)) { | 
					
						
							|  |  |  |             $temp = $mode; | 
					
						
							|  |  |  |             $mode = $filename; | 
					
						
							|  |  |  |             $filename = $temp; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); | 
					
						
							|  |  |  |         if (!$this->_setstat($filename, $attr, $recursive)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if ($recursive) { | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // rather than return what the permissions *should* be, we'll return what they actually are.  this will also
 | 
					
						
							|  |  |  |         // tell us if the file actually exists.
 | 
					
						
							|  |  |  |         // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
 | 
					
						
							|  |  |  |         $packet = pack('Na*', strlen($filename), $filename); | 
					
						
							|  |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |         switch ($this->packet_type) { | 
					
						
							|  |  |  |             case NET_SFTP_ATTRS: | 
					
						
							|  |  |  |                 $attrs = $this->_parseAttributes($response); | 
					
						
							|  |  |  |                 return $attrs['permissions']; | 
					
						
							|  |  |  |             case NET_SFTP_STATUS: | 
					
						
							|  |  |  |                 $this->_logError($response); | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS'); | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Sets information about a file | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $filename | 
					
						
							|  |  |  |      * @param String $attr | 
					
						
							|  |  |  |      * @param Boolean $recursive | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _setstat($filename, $attr, $recursive) | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     { | 
					
						
							|  |  |  |         if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $filename = $this->_realpath($filename); | 
					
						
							|  |  |  |         if ($filename === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         if ($recursive) { | 
					
						
							|  |  |  |             $i = 0; | 
					
						
							|  |  |  |             $result = $this->_setstat_recursive($filename, $attr, $i); | 
					
						
							|  |  |  |             $this->_read_put_responses($i); | 
					
						
							|  |  |  |             return $result; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to
 | 
					
						
							|  |  |  |         // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT.
 | 
					
						
							|  |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         /* | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |          "Because some systems must use separate system calls to set various attributes, it is possible that a failure
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |           response will be returned, but yet some of the attributes may be have been successfully modified.  If possible, | 
					
						
							|  |  |  |           servers SHOULD avoid this situation; however, clients MUST be aware that this is possible."
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |           -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
 | 
					
						
							|  |  |  |         */ | 
					
						
							|  |  |  |         $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |         if ($this->packet_type != NET_SFTP_STATUS) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             user_error('Expected SSH_FXP_STATUS'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         extract(unpack('Nstatus', $this->_string_shift($response, 4))); | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         if ($status != NET_SFTP_STATUS_OK) { | 
					
						
							|  |  |  |             $this->_logError($response, $status); | 
					
						
							|  |  |  |             return false; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Recursively sets information on directories on the SFTP server | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $path | 
					
						
							|  |  |  |      * @param String $attr | 
					
						
							|  |  |  |      * @param Integer $i | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _setstat_recursive($path, $attr, &$i) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!$this->_read_put_responses($i)) { | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $i = 0; | 
					
						
							|  |  |  |         $entries = $this->_list($path, true, false); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         if ($entries === false) { | 
					
						
							|  |  |  |             return $this->_setstat($path, $attr, false); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // normally $entries would have at least . and .. but it might not if the directories
 | 
					
						
							|  |  |  |         // permissions didn't allow reading
 | 
					
						
							|  |  |  |         if (empty($entries)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         foreach ($entries as $filename=>$props) { | 
					
						
							|  |  |  |             if ($filename == '.' || $filename == '..') { | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (!isset($props['type'])) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $temp = $path . '/' . $filename; | 
					
						
							|  |  |  |             if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { | 
					
						
							|  |  |  |                 if (!$this->_setstat_recursive($temp, $attr, $i)) { | 
					
						
							|  |  |  |                     return false; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($temp), $temp, $attr))) { | 
					
						
							|  |  |  |                     return false; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 $i++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if ($i >= NET_SFTP_QUEUE_SIZE) { | 
					
						
							|  |  |  |                     if (!$this->_read_put_responses($i)) { | 
					
						
							|  |  |  |                         return false; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     $i = 0; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($path), $path, $attr))) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $i++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($i >= NET_SFTP_QUEUE_SIZE) { | 
					
						
							|  |  |  |             if (!$this->_read_put_responses($i)) { | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 return false; | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |             $i = 0; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         return true; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Creates a directory. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $dir | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |     function mkdir($dir, $mode = -1, $recursive = false) | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     { | 
					
						
							|  |  |  |         if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $dir = $this->_realpath($dir); | 
					
						
							|  |  |  |         // by not providing any permissions, hopefully the server will use the logged in users umask - their
 | 
					
						
							|  |  |  |         // default permissions.
 | 
					
						
							|  |  |  |         $attr = $mode == -1 ? "\0\0\0\0" : pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($recursive) { | 
					
						
							|  |  |  |             $dirs = explode('/', preg_replace('#/(?=/)|/$#', '', $dir)); | 
					
						
							|  |  |  |             if (empty($dirs[0])) { | 
					
						
							|  |  |  |                 array_shift($dirs); | 
					
						
							|  |  |  |                 $dirs[0] = '/' . $dirs[0]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             for ($i = 0; $i < count($dirs); $i++) { | 
					
						
							|  |  |  |                 $temp = array_slice($dirs, 0, $i + 1); | 
					
						
							|  |  |  |                 $temp = implode('/', $temp); | 
					
						
							|  |  |  |                 $result = $this->_mkdir_helper($temp, $attr); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return $result; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         return $this->_mkdir_helper($dir, $attr); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Helper function for directory creation | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $dir | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _mkdir_helper($dir, $attr) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*a*', strlen($dir), $dir, $attr))) { | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |         if ($this->packet_type != NET_SFTP_STATUS) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             user_error('Expected SSH_FXP_STATUS'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         extract(unpack('Nstatus', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |         if ($status != NET_SFTP_STATUS_OK) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             $this->_logError($response, $status); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $this->_save_dir($dir); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Removes a directory. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $dir | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function rmdir($dir) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $dir = $this->_realpath($dir); | 
					
						
							|  |  |  |         if ($dir === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($dir), $dir))) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |         if ($this->packet_type != NET_SFTP_STATUS) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             user_error('Expected SSH_FXP_STATUS'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         extract(unpack('Nstatus', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |         if ($status != NET_SFTP_STATUS_OK) { | 
					
						
							|  |  |  |             // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             $this->_logError($response, $status); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $this->_remove_dir($dir); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Uploads a file to the SFTP server. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * By default, Net_SFTP::put() does not read from the local filesystem.  $data is dumped directly into $remote_file. | 
					
						
							|  |  |  |      * So, for example, if you set $data to 'filename.ext' and then do Net_SFTP::get(), you will get a file, twelve bytes | 
					
						
							|  |  |  |      * long, containing 'filename.ext' as its contents. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |      * Setting $mode to NET_SFTP_LOCAL_FILE will change the above behavior.  With NET_SFTP_LOCAL_FILE, $remote_file will | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |      * contain as many bytes as filename.ext does on your local filesystem.  If your filename.ext is 1MB then that is how | 
					
						
							|  |  |  |      * large $remote_file will be, as well. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Currently, only binary mode is supported.  As such, if the line endings need to be adjusted, you will need to take | 
					
						
							|  |  |  |      * care of that, yourself. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |      * $mode can take an additional two parameters - NET_SFTP_RESUME and NET_SFTP_RESUME_START. These are bitwise AND'd with | 
					
						
							|  |  |  |      * $mode. So if you want to resume upload of a 300mb file on the local file system you'd set $mode to the following: | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * NET_SFTP_LOCAL_FILE | NET_SFTP_RESUME | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * If you wanted to simply append the full contents of a local file to the full contents of a remote file you'd replace | 
					
						
							|  |  |  |      * NET_SFTP_RESUME with NET_SFTP_RESUME_START. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * If $mode & (NET_SFTP_RESUME | NET_SFTP_RESUME_START) then NET_SFTP_RESUME_START will be assumed. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * $start and $local_start give you more fine grained control over this process and take precident over NET_SFTP_RESUME | 
					
						
							|  |  |  |      * when they're non-negative. ie. $start could let you write at the end of a file (like NET_SFTP_RESUME) or in the middle | 
					
						
							|  |  |  |      * of one. $local_start could let you start your reading from the end of a file (like NET_SFTP_RESUME_START) or in the | 
					
						
							|  |  |  |      * middle of one. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Setting $local_start to > 0 or $mode | NET_SFTP_RESUME_START doesn't do anything unless $mode | NET_SFTP_LOCAL_FILE. | 
					
						
							|  |  |  |      * | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |      * @param String $remote_file | 
					
						
							|  |  |  |      * @param String $data | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |      * @param optional Integer $mode | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |      * @param optional Integer $start | 
					
						
							|  |  |  |      * @param optional Integer $local_start | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - Net_SFTP::setMode(). | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |     function put($remote_file, $data, $mode = NET_SFTP_STRING, $start = -1, $local_start = -1) | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     { | 
					
						
							|  |  |  |         if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $remote_file = $this->_realpath($remote_file); | 
					
						
							|  |  |  |         if ($remote_file === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $flags = NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE; | 
					
						
							|  |  |  |         // according to the SFTP specs, NET_SFTP_OPEN_APPEND should "force all writes to append data at the end of the file."
 | 
					
						
							|  |  |  |         // in practice, it doesn't seem to do that.
 | 
					
						
							|  |  |  |         //$flags|= ($mode & NET_SFTP_RESUME) ? NET_SFTP_OPEN_APPEND : NET_SFTP_OPEN_TRUNCATE;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($start >= 0) { | 
					
						
							|  |  |  |             $offset = $start; | 
					
						
							|  |  |  |         } elseif ($mode & NET_SFTP_RESUME) { | 
					
						
							|  |  |  |             // if NET_SFTP_OPEN_APPEND worked as it should _size() wouldn't need to be called
 | 
					
						
							|  |  |  |             $size = $this->_size($remote_file); | 
					
						
							|  |  |  |             $offset = $size !== false ? $size : 0; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             $offset = 0; | 
					
						
							|  |  |  |             $flags|= NET_SFTP_OPEN_TRUNCATE; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $packet = pack('Na*N2', strlen($remote_file), $remote_file, $flags, 0); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |         switch ($this->packet_type) { | 
					
						
							|  |  |  |             case NET_SFTP_HANDLE: | 
					
						
							|  |  |  |                 $handle = substr($response, 4); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             case NET_SFTP_STATUS: | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                 $this->_logError($response); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 return false; | 
					
						
							|  |  |  |             default: | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                 user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         if ($mode & NET_SFTP_LOCAL_FILE) { | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             if (!is_file($data)) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                 user_error("$data is not a valid file"); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |             $fp = @fopen($data, 'rb'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             if (!$fp) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             $size = filesize($data); | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if ($local_start >= 0) { | 
					
						
							|  |  |  |                 fseek($fp, $local_start); | 
					
						
							|  |  |  |             } elseif ($mode & NET_SFTP_RESUME_START) { | 
					
						
							|  |  |  |                 // do nothing
 | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 fseek($fp, $offset); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         } else { | 
					
						
							|  |  |  |             $size = strlen($data); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $sent = 0; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |         $sftp_packet_size = 4096; // PuTTY uses 4096
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         // make the SFTP packet be exactly 4096 bytes by including the bytes in the NET_SFTP_WRITE packets "header"
 | 
					
						
							|  |  |  |         $sftp_packet_size-= strlen($handle) + 25; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         $i = 0; | 
					
						
							|  |  |  |         while ($sent < $size) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             $temp = $mode & NET_SFTP_LOCAL_FILE ? fread($fp, $sftp_packet_size) : substr($data, $sent, $sftp_packet_size); | 
					
						
							|  |  |  |             $subtemp = $offset + $sent; | 
					
						
							|  |  |  |             $packet = pack('Na*N3a*', strlen($handle), $handle, $subtemp / 4294967296, $subtemp, strlen($temp), $temp); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) { | 
					
						
							|  |  |  |                 fclose($fp); | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             $sent+= strlen($temp); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $i++; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             if ($i == NET_SFTP_QUEUE_SIZE) { | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 if (!$this->_read_put_responses($i)) { | 
					
						
							|  |  |  |                     $i = 0; | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 $i = 0; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         if (!$this->_read_put_responses($i)) { | 
					
						
							|  |  |  |             if ($mode & NET_SFTP_LOCAL_FILE) { | 
					
						
							|  |  |  |                 fclose($fp); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             $this->_close_handle($handle); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         if ($mode & NET_SFTP_LOCAL_FILE) { | 
					
						
							|  |  |  |             fclose($fp); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         return $this->_close_handle($handle); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Reads multiple successive SSH_FXP_WRITE responses | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Sending an SSH_FXP_WRITE packet and immediately reading its response isn't as efficient as blindly sending out $i | 
					
						
							|  |  |  |      * SSH_FXP_WRITEs, in succession, and then reading $i responses. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param Integer $i | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _read_put_responses($i) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         while ($i--) { | 
					
						
							|  |  |  |             $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |             if ($this->packet_type != NET_SFTP_STATUS) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                 user_error('Expected SSH_FXP_STATUS'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             extract(unpack('Nstatus', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |             if ($status != NET_SFTP_STATUS_OK) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                 $this->_logError($response, $status); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $i < 0; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Close handle | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $handle | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _close_handle($handle) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // "The client MUST release all resources associated with the handle regardless of the status."
 | 
					
						
							|  |  |  |         //  -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
 | 
					
						
							|  |  |  |         $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |         if ($this->packet_type != NET_SFTP_STATUS) { | 
					
						
							|  |  |  |             user_error('Expected SSH_FXP_STATUS'); | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         extract(unpack('Nstatus', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |         if ($status != NET_SFTP_STATUS_OK) { | 
					
						
							|  |  |  |             $this->_logError($response, $status); | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Downloads a file from the SFTP server. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if | 
					
						
							|  |  |  |      * the operation was unsuccessful.  If $local_file is defined, returns true or false depending on the success of the | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |      * operation. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * $offset and $length can be used to download files in chunks. | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |      * | 
					
						
							|  |  |  |      * @param String $remote_file | 
					
						
							|  |  |  |      * @param optional String $local_file | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |      * @param optional Integer $offset | 
					
						
							|  |  |  |      * @param optional Integer $length | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |      * @return Mixed | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |     function get($remote_file, $local_file = false, $offset = 0, $length = -1) | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     { | 
					
						
							|  |  |  |         if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $remote_file = $this->_realpath($remote_file); | 
					
						
							|  |  |  |         if ($remote_file === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0); | 
					
						
							|  |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |         switch ($this->packet_type) { | 
					
						
							|  |  |  |             case NET_SFTP_HANDLE: | 
					
						
							|  |  |  |                 $handle = substr($response, 4); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                 $this->_logError($response); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 return false; | 
					
						
							|  |  |  |             default: | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                 user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($local_file !== false) { | 
					
						
							|  |  |  |             $fp = fopen($local_file, 'wb'); | 
					
						
							|  |  |  |             if (!$fp) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             $content = ''; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $start = $offset; | 
					
						
							|  |  |  |         $size = $this->max_sftp_packet < $length || $length < 0 ? $this->max_sftp_packet : $length; | 
					
						
							|  |  |  |         while (true) { | 
					
						
							|  |  |  |             $packet = pack('Na*N3', strlen($handle), $handle, $offset / 4294967296, $offset, $size); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                 if ($local_file !== false) { | 
					
						
							|  |  |  |                     fclose($fp); | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |             switch ($this->packet_type) { | 
					
						
							|  |  |  |                 case NET_SFTP_DATA: | 
					
						
							|  |  |  |                     $temp = substr($response, 4); | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                     $offset+= strlen($temp); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                     if ($local_file === false) { | 
					
						
							|  |  |  |                         $content.= $temp; | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         fputs($fp, $temp); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 case NET_SFTP_STATUS: | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                     // could, in theory, return false if !strlen($content) but we'll hold off for the time being
 | 
					
						
							|  |  |  |                     $this->_logError($response); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                     break 2; | 
					
						
							|  |  |  |                 default: | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                     user_error('Expected SSH_FXP_DATA or SSH_FXP_STATUS'); | 
					
						
							|  |  |  |                     if ($local_file !== false) { | 
					
						
							|  |  |  |                         fclose($fp); | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                     return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             if ($length > 0 && $length <= $offset - $start) { | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         if ($length > 0 && $length <= $offset - $start) { | 
					
						
							|  |  |  |             if ($local_file === false) { | 
					
						
							|  |  |  |                 $content = substr($content, 0, $length); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 ftruncate($fp, $length); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         if ($local_file !== false) { | 
					
						
							|  |  |  |             fclose($fp); | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         if (!$this->_close_handle($handle)) { | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         // if $content isn't set that means a file was written to
 | 
					
						
							|  |  |  |         return isset($content) ? $content : true; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Deletes a file on the SFTP server. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $path | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |      * @param Boolean $recursive | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |     function delete($path, $recursive = true) | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     { | 
					
						
							|  |  |  |         if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |         $path = $this->_realpath($path); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         if ($path === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
 | 
					
						
							|  |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path))) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |         if ($this->packet_type != NET_SFTP_STATUS) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             user_error('Expected SSH_FXP_STATUS'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
 | 
					
						
							|  |  |  |         extract(unpack('Nstatus', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |         if ($status != NET_SFTP_STATUS_OK) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             $this->_logError($response, $status); | 
					
						
							|  |  |  |             if (!$recursive) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             $i = 0; | 
					
						
							|  |  |  |             $result = $this->_delete_recursive($path, $i); | 
					
						
							|  |  |  |             $this->_read_put_responses($i); | 
					
						
							|  |  |  |             return $result; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Recursively deletes directories on the SFTP server | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Minimizes directory lookups and SSH_FXP_STATUS requests for speed. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $path | 
					
						
							|  |  |  |      * @param Integer $i | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _delete_recursive($path, &$i) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!$this->_read_put_responses($i)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $i = 0; | 
					
						
							|  |  |  |         $entries = $this->_list($path, true, false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // normally $entries would have at least . and .. but it might not if the directories
 | 
					
						
							|  |  |  |         // permissions didn't allow reading
 | 
					
						
							|  |  |  |         if (empty($entries)) { | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         foreach ($entries as $filename=>$props) { | 
					
						
							|  |  |  |             if ($filename == '.' || $filename == '..') { | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (!isset($props['type'])) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $temp = $path . '/' . $filename; | 
					
						
							|  |  |  |             if ($props['type'] == NET_SFTP_TYPE_DIRECTORY) { | 
					
						
							|  |  |  |                 if (!$this->_delete_recursive($temp, $i)) { | 
					
						
							|  |  |  |                     return false; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($temp), $temp))) { | 
					
						
							|  |  |  |                     return false; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 $i++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if ($i >= NET_SFTP_QUEUE_SIZE) { | 
					
						
							|  |  |  |                     if (!$this->_read_put_responses($i)) { | 
					
						
							|  |  |  |                         return false; | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                     $i = 0; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($path), $path))) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         $this->_remove_dir($path); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $i++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($i >= NET_SFTP_QUEUE_SIZE) { | 
					
						
							|  |  |  |             if (!$this->_read_put_responses($i)) { | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             $i = 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Renames a file or a directory on the SFTP server | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $oldname | 
					
						
							|  |  |  |      * @param String $newname | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function rename($oldname, $newname) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $oldname = $this->_realpath($oldname); | 
					
						
							|  |  |  |         $newname = $this->_realpath($newname); | 
					
						
							|  |  |  |         if ($oldname === false || $newname === false) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
 | 
					
						
							|  |  |  |         $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname); | 
					
						
							|  |  |  |         if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $response = $this->_get_sftp_packet(); | 
					
						
							|  |  |  |         if ($this->packet_type != NET_SFTP_STATUS) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             user_error('Expected SSH_FXP_STATUS'); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
 | 
					
						
							|  |  |  |         extract(unpack('Nstatus', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |         if ($status != NET_SFTP_STATUS_OK) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             $this->_logError($response, $status); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Parse Attributes | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * See '7.  File Attributes' of draft-ietf-secsh-filexfer-13 for more info. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $response | 
					
						
							|  |  |  |      * @return Array | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _parseAttributes(&$response) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $attr = array(); | 
					
						
							|  |  |  |         extract(unpack('Nflags', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |         // SFTPv4+ have a type field (a byte) that follows the above flag field
 | 
					
						
							|  |  |  |         foreach ($this->attributes as $key => $value) { | 
					
						
							|  |  |  |             switch ($flags & $key) { | 
					
						
							|  |  |  |                 case NET_SFTP_ATTR_SIZE: // 0x00000001
 | 
					
						
							|  |  |  |                     // size is represented by a 64-bit integer, so we perhaps ought to be doing the following:
 | 
					
						
							|  |  |  |                     // $attr['size'] = new Math_BigInteger($this->_string_shift($response, 8), 256);
 | 
					
						
							|  |  |  |                     // of course, you shouldn't be using Net_SFTP to transfer files that are in excess of 4GB
 | 
					
						
							|  |  |  |                     // (0xFFFFFFFF bytes), anyway.  as such, we'll just represent all file sizes that are bigger than
 | 
					
						
							|  |  |  |                     // 4GB as being 4GB.
 | 
					
						
							|  |  |  |                     extract(unpack('Nupper/Nsize', $this->_string_shift($response, 8))); | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                     $attr['size'] = $upper ? 4294967296 * $upper : 0; | 
					
						
							|  |  |  |                     $attr['size']+= $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                     break; | 
					
						
							|  |  |  |                 case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only)
 | 
					
						
							|  |  |  |                     $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8)); | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
 | 
					
						
							|  |  |  |                     $attr+= unpack('Npermissions', $this->_string_shift($response, 4)); | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                     // mode == permissions; permissions was the original array key and is retained for bc purposes.
 | 
					
						
							|  |  |  |                     // mode was added because that's the more industry standard terminology
 | 
					
						
							|  |  |  |                     $attr+= array('mode' => $attr['permissions']); | 
					
						
							|  |  |  |                     $fileType = $this->_parseMode($attr['permissions']); | 
					
						
							|  |  |  |                     if ($fileType !== false) { | 
					
						
							|  |  |  |                         $attr+= array('type' => $fileType); | 
					
						
							|  |  |  |                     } | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                     break; | 
					
						
							|  |  |  |                 case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
 | 
					
						
							|  |  |  |                     $attr+= unpack('Natime/Nmtime', $this->_string_shift($response, 8)); | 
					
						
							|  |  |  |                     break; | 
					
						
							|  |  |  |                 case NET_SFTP_ATTR_EXTENDED: // 0x80000000
 | 
					
						
							|  |  |  |                     extract(unpack('Ncount', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |                     for ($i = 0; $i < $count; $i++) { | 
					
						
							|  |  |  |                         extract(unpack('Nlength', $this->_string_shift($response, 4))); | 
					
						
							|  |  |  |                         $key = $this->_string_shift($response, $length); | 
					
						
							|  |  |  |                         extract(unpack('Nlength', $this->_string_shift($response, 4))); | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |                         $attr[$key] = $this->_string_shift($response, $length); | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |                     } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return $attr; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Attempt to identify the file type | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Quoting the SFTP RFC, "Implementations MUST NOT send bits that are not defined" but they seem to anyway | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param Integer $mode | 
					
						
							|  |  |  |      * @return Integer | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _parseMode($mode) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // values come from http://lxr.free-electrons.com/source/include/uapi/linux/stat.h#L12
 | 
					
						
							|  |  |  |         // see, also, http://linux.die.net/man/2/stat
 | 
					
						
							|  |  |  |         switch ($mode & 0170000) {// ie. 1111 0000 0000 0000
 | 
					
						
							|  |  |  |             case 0000000: // no file type specified - figure out the file type using alternative means
 | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             case 0040000: | 
					
						
							|  |  |  |                 return NET_SFTP_TYPE_DIRECTORY; | 
					
						
							|  |  |  |             case 0100000: | 
					
						
							|  |  |  |                 return NET_SFTP_TYPE_REGULAR; | 
					
						
							|  |  |  |             case 0120000: | 
					
						
							|  |  |  |                 return NET_SFTP_TYPE_SYMLINK; | 
					
						
							|  |  |  |             // new types introduced in SFTPv5+
 | 
					
						
							|  |  |  |             // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-05#section-5.2
 | 
					
						
							|  |  |  |             case 0010000: // named pipe (fifo)
 | 
					
						
							|  |  |  |                 return NET_SFTP_TYPE_FIFO; | 
					
						
							|  |  |  |             case 0020000: // character special
 | 
					
						
							|  |  |  |                 return NET_SFTP_TYPE_CHAR_DEVICE; | 
					
						
							|  |  |  |             case 0060000: // block special
 | 
					
						
							|  |  |  |                 return NET_SFTP_BLOCK_DEVICE; | 
					
						
							|  |  |  |             case 0140000: // socket
 | 
					
						
							|  |  |  |                 return NET_SFTP_TYPE_SOCKET; | 
					
						
							|  |  |  |             case 0160000: // whiteout
 | 
					
						
							|  |  |  |                 // "SPECIAL should be used for files that are of
 | 
					
						
							|  |  |  |                 //  a known type which cannot be expressed in the protocol"
 | 
					
						
							|  |  |  |                 return NET_SFTP_TYPE_SPECIAL; | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |                 return NET_SFTP_TYPE_UNKNOWN; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Parse Longname | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * SFTPv3 doesn't provide any easy way of identifying a file type.  You could try to open | 
					
						
							|  |  |  |      * a file as a directory and see if an error is returned or you could try to parse the | 
					
						
							|  |  |  |      * SFTPv3-specific longname field of the SSH_FXP_NAME packet.  That's what this function does. | 
					
						
							|  |  |  |      * The result is returned using the | 
					
						
							|  |  |  |      * {@link http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2 SFTPv4 type constants}.
 | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * If the longname is in an unrecognized format bool(false) is returned. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param String $longname | 
					
						
							|  |  |  |      * @return Mixed | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _parseLongname($longname) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         // http://en.wikipedia.org/wiki/Unix_file_types
 | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         // http://en.wikipedia.org/wiki/Filesystem_permissions#Notation_of_traditional_Unix_permissions
 | 
					
						
							|  |  |  |         if (preg_match('#^[^/]([r-][w-][xstST-]){3}#', $longname)) { | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |             switch ($longname[0]) { | 
					
						
							|  |  |  |                 case '-': | 
					
						
							|  |  |  |                     return NET_SFTP_TYPE_REGULAR; | 
					
						
							|  |  |  |                 case 'd': | 
					
						
							|  |  |  |                     return NET_SFTP_TYPE_DIRECTORY; | 
					
						
							|  |  |  |                 case 'l': | 
					
						
							|  |  |  |                     return NET_SFTP_TYPE_SYMLINK; | 
					
						
							|  |  |  |                 default: | 
					
						
							|  |  |  |                     return NET_SFTP_TYPE_SPECIAL; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Sends SFTP Packets | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param Integer $type | 
					
						
							|  |  |  |      * @param String $data | 
					
						
							|  |  |  |      * @see Net_SFTP::_get_sftp_packet() | 
					
						
							|  |  |  |      * @see Net_SSH2::_send_channel_packet() | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _send_sftp_packet($type, $data) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $packet = $this->request_id !== false ? | 
					
						
							|  |  |  |             pack('NCNa*', strlen($data) + 5, $type, $this->request_id, $data) : | 
					
						
							|  |  |  |             pack('NCa*',  strlen($data) + 1, $type, $data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
 | 
					
						
							|  |  |  |         $result = $this->_send_channel_packet(NET_SFTP_CHANNEL, $packet); | 
					
						
							|  |  |  |         $stop = strtok(microtime(), ' ') + strtok(''); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (defined('NET_SFTP_LOGGING')) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             $packet_type = '-> ' . $this->packet_types[$type] . | 
					
						
							|  |  |  |                            ' (' . round($stop - $start, 4) . 's)'; | 
					
						
							|  |  |  |             if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) { | 
					
						
							|  |  |  |                 echo "<pre>\r\n" . $this->_format_log(array($data), array($packet_type)) . "\r\n</pre>\r\n"; | 
					
						
							|  |  |  |                 flush(); | 
					
						
							|  |  |  |                 ob_flush(); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 $this->packet_type_log[] = $packet_type; | 
					
						
							|  |  |  |                 if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) { | 
					
						
							|  |  |  |                     $this->packet_log[] = $data; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $result; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Receives SFTP Packets | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * See '6. General Packet Format' of draft-ietf-secsh-filexfer-13 for more info. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Incidentally, the number of SSH_MSG_CHANNEL_DATA messages has no bearing on the number of SFTP packets present. | 
					
						
							|  |  |  |      * There can be one SSH_MSG_CHANNEL_DATA messages containing two SFTP packets or there can be two SSH_MSG_CHANNEL_DATA | 
					
						
							|  |  |  |      * messages containing one SFTP packet. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @see Net_SFTP::_send_sftp_packet() | 
					
						
							|  |  |  |      * @return String | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _get_sftp_packet() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |         $this->curTimeout = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |         $start = strtok(microtime(), ' ') + strtok(''); // http://php.net/microtime#61838
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // SFTP packet length
 | 
					
						
							|  |  |  |         while (strlen($this->packet_buffer) < 4) { | 
					
						
							|  |  |  |             $temp = $this->_get_channel_packet(NET_SFTP_CHANNEL); | 
					
						
							|  |  |  |             if (is_bool($temp)) { | 
					
						
							|  |  |  |                 $this->packet_type = false; | 
					
						
							|  |  |  |                 $this->packet_buffer = ''; | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             $this->packet_buffer.= $temp; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         extract(unpack('Nlength', $this->_string_shift($this->packet_buffer, 4))); | 
					
						
							|  |  |  |         $tempLength = $length; | 
					
						
							|  |  |  |         $tempLength-= strlen($this->packet_buffer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // SFTP packet type and data payload
 | 
					
						
							|  |  |  |         while ($tempLength > 0) { | 
					
						
							|  |  |  |             $temp = $this->_get_channel_packet(NET_SFTP_CHANNEL); | 
					
						
							|  |  |  |             if (is_bool($temp)) { | 
					
						
							|  |  |  |                 $this->packet_type = false; | 
					
						
							|  |  |  |                 $this->packet_buffer = ''; | 
					
						
							|  |  |  |                 return false; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             $this->packet_buffer.= $temp; | 
					
						
							|  |  |  |             $tempLength-= strlen($temp); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $stop = strtok(microtime(), ' ') + strtok(''); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $this->packet_type = ord($this->_string_shift($this->packet_buffer)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($this->request_id !== false) { | 
					
						
							|  |  |  |             $this->_string_shift($this->packet_buffer, 4); // remove the request id
 | 
					
						
							|  |  |  |             $length-= 5; // account for the request id and the packet type
 | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             $length-= 1; // account for the packet type
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $packet = $this->_string_shift($this->packet_buffer, $length); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (defined('NET_SFTP_LOGGING')) { | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  |             $packet_type = '<- ' . $this->packet_types[$this->packet_type] . | 
					
						
							|  |  |  |                            ' (' . round($stop - $start, 4) . 's)'; | 
					
						
							|  |  |  |             if (NET_SFTP_LOGGING == NET_SFTP_LOG_REALTIME) { | 
					
						
							|  |  |  |                 echo "<pre>\r\n" . $this->_format_log(array($packet), array($packet_type)) . "\r\n</pre>\r\n"; | 
					
						
							|  |  |  |                 flush(); | 
					
						
							|  |  |  |                 ob_flush(); | 
					
						
							|  |  |  |             } else { | 
					
						
							|  |  |  |                 $this->packet_type_log[] = $packet_type; | 
					
						
							|  |  |  |                 if (NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX) { | 
					
						
							|  |  |  |                     $this->packet_log[] = $packet; | 
					
						
							|  |  |  |                 } | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $packet; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Returns a log of the packets that have been sent and received. | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * Returns a string if NET_SFTP_LOGGING == NET_SFTP_LOG_COMPLEX, an array if NET_SFTP_LOGGING == NET_SFTP_LOG_SIMPLE and false if !defined('NET_SFTP_LOGGING') | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      * @return String or Array | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function getSFTPLog() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (!defined('NET_SFTP_LOGGING')) { | 
					
						
							|  |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         switch (NET_SFTP_LOGGING) { | 
					
						
							|  |  |  |             case NET_SFTP_LOG_COMPLEX: | 
					
						
							|  |  |  |                 return $this->_format_log($this->packet_log, $this->packet_type_log); | 
					
						
							|  |  |  |                 break; | 
					
						
							|  |  |  |             //case NET_SFTP_LOG_SIMPLE:
 | 
					
						
							|  |  |  |             default: | 
					
						
							|  |  |  |                 return $this->packet_type_log; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Returns all errors | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return String | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function getSFTPErrors() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         return $this->sftp_errors; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Returns the last error | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return String | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function getLastSFTPError() | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2011-05-19 18:02:11 +00:00
										 |  |  |         return count($this->sftp_errors) ? $this->sftp_errors[count($this->sftp_errors) - 1] : ''; | 
					
						
							| 
									
										
										
										
											2010-05-14 14:06:17 +00:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Get supported SFTP versions | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @return Array | 
					
						
							|  |  |  |      * @access public | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function getSupportedVersions() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $temp = array('version' => $this->version); | 
					
						
							|  |  |  |         if (isset($this->extensions['versions'])) { | 
					
						
							|  |  |  |             $temp['extensions'] = $this->extensions['versions']; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         return $temp; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Disconnect | 
					
						
							|  |  |  |      * | 
					
						
							|  |  |  |      * @param Integer $reason | 
					
						
							|  |  |  |      * @return Boolean | 
					
						
							|  |  |  |      * @access private | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     function _disconnect($reason) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $this->pwd = false; | 
					
						
							|  |  |  |         parent::_disconnect($reason); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-03-30 06:55:33 +00:00
										 |  |  | } |