PageRenderTime 60ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/fuel/core/vendor/phpseclib/Net/SFTP.php

https://bitbucket.org/sriedel/iccrm-wip
PHP | 1606 lines | 843 code | 166 blank | 597 comment | 166 complexity | 31cddb2fd09d01611fafe82a663ae92b MD5 | raw file
Possible License(s): MIT

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. namespace PHPSecLib;
  4. /**
  5. * Pure-PHP implementation of SFTP.
  6. *
  7. * PHP versions 4 and 5
  8. *
  9. * Currently only supports SFTPv3, which, according to wikipedia.org, "is the most widely used version,
  10. * implemented by the popular OpenSSH SFTP server". If you want SFTPv4/5/6 support, provide me with access
  11. * to an SFTPv4/5/6 server.
  12. *
  13. * The API for this library is modeled after the API from PHP's {@link http://php.net/book.ftp FTP extension}.
  14. *
  15. * Here's a short example of how to use this library:
  16. * <code>
  17. * <?php
  18. * include('Net/SFTP.php');
  19. *
  20. * $sftp = new Net_SFTP('www.domain.tld');
  21. * if (!$sftp->login('username', 'password')) {
  22. * exit('Login Failed');
  23. * }
  24. *
  25. * echo $sftp->pwd() . "\r\n";
  26. * $sftp->put('filename.ext', 'hello, world!');
  27. * print_r($sftp->nlist());
  28. * ?>
  29. * </code>
  30. *
  31. * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
  32. * of this software and associated documentation files (the "Software"), to deal
  33. * in the Software without restriction, including without limitation the rights
  34. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  35. * copies of the Software, and to permit persons to whom the Software is
  36. * furnished to do so, subject to the following conditions:
  37. *
  38. * The above copyright notice and this permission notice shall be included in
  39. * all copies or substantial portions of the Software.
  40. *
  41. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  42. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  43. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  44. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  45. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  46. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  47. * THE SOFTWARE.
  48. *
  49. * @category Net
  50. * @package Net_SFTP
  51. * @author Jim Wigginton <terrafrost@php.net>
  52. * @copyright MMIX Jim Wigginton
  53. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  54. * @link http://phpseclib.sourceforge.net
  55. */
  56. /**#@+
  57. * @access public
  58. * @see Net_SFTP::getLog()
  59. */
  60. /**
  61. * Returns the message numbers
  62. */
  63. define('NET_SFTP_LOG_SIMPLE', NET_SSH2_LOG_SIMPLE);
  64. /**
  65. * Returns the message content
  66. */
  67. define('NET_SFTP_LOG_COMPLEX', NET_SSH2_LOG_COMPLEX);
  68. /**#@-*/
  69. /**
  70. * SFTP channel constant
  71. *
  72. * Net_SSH2::exec() uses 0 and Net_SSH2::read() / Net_SSH2::write() use 1.
  73. *
  74. * @see Net_SSH2::_send_channel_packet()
  75. * @see Net_SSH2::_get_channel_packet()
  76. * @access private
  77. */
  78. define('NET_SFTP_CHANNEL', 2);
  79. /**#@+
  80. * @access public
  81. * @see Net_SFTP::put()
  82. */
  83. /**
  84. * Reads data from a local file.
  85. */
  86. define('NET_SFTP_LOCAL_FILE', 1);
  87. /**
  88. * Reads data from a string.
  89. */
  90. define('NET_SFTP_STRING', 2);
  91. /**#@-*/
  92. /**
  93. * Pure-PHP implementations of SFTP.
  94. *
  95. * @author Jim Wigginton <terrafrost@php.net>
  96. * @version 0.1.0
  97. * @access public
  98. * @package Net_SFTP
  99. */
  100. class Net_SFTP extends Net_SSH2 {
  101. /**
  102. * Packet Types
  103. *
  104. * @see Net_SFTP::Net_SFTP()
  105. * @var Array
  106. * @access private
  107. */
  108. var $packet_types = array();
  109. /**
  110. * Status Codes
  111. *
  112. * @see Net_SFTP::Net_SFTP()
  113. * @var Array
  114. * @access private
  115. */
  116. var $status_codes = array();
  117. /**
  118. * The Request ID
  119. *
  120. * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support
  121. * concurrent actions, so it's somewhat academic, here.
  122. *
  123. * @var Integer
  124. * @see Net_SFTP::_send_sftp_packet()
  125. * @access private
  126. */
  127. var $request_id = false;
  128. /**
  129. * The Packet Type
  130. *
  131. * The request ID exists in the off chance that a packet is sent out-of-order. Of course, this library doesn't support
  132. * concurrent actions, so it's somewhat academic, here.
  133. *
  134. * @var Integer
  135. * @see Net_SFTP::_get_sftp_packet()
  136. * @access private
  137. */
  138. var $packet_type = -1;
  139. /**
  140. * Packet Buffer
  141. *
  142. * @var String
  143. * @see Net_SFTP::_get_sftp_packet()
  144. * @access private
  145. */
  146. var $packet_buffer = '';
  147. /**
  148. * Extensions supported by the server
  149. *
  150. * @var Array
  151. * @see Net_SFTP::_initChannel()
  152. * @access private
  153. */
  154. var $extensions = array();
  155. /**
  156. * Server SFTP version
  157. *
  158. * @var Integer
  159. * @see Net_SFTP::_initChannel()
  160. * @access private
  161. */
  162. var $version;
  163. /**
  164. * Current working directory
  165. *
  166. * @var String
  167. * @see Net_SFTP::_realpath()
  168. * @see Net_SFTP::chdir()
  169. * @access private
  170. */
  171. var $pwd = false;
  172. /**
  173. * Packet Type Log
  174. *
  175. * @see Net_SFTP::getLog()
  176. * @var Array
  177. * @access private
  178. */
  179. var $packet_type_log = array();
  180. /**
  181. * Packet Log
  182. *
  183. * @see Net_SFTP::getLog()
  184. * @var Array
  185. * @access private
  186. */
  187. var $packet_log = array();
  188. /**
  189. * Error information
  190. *
  191. * @see Net_SFTP::getSFTPErrors()
  192. * @see Net_SFTP::getLastSFTPError()
  193. * @var String
  194. * @access private
  195. */
  196. var $sftp_errors = array();
  197. /**
  198. * File Type
  199. *
  200. * @see Net_SFTP::_parseLongname()
  201. * @var Integer
  202. * @access private
  203. */
  204. var $fileType = 0;
  205. /**
  206. * Default Constructor.
  207. *
  208. * Connects to an SFTP server
  209. *
  210. * @param String $host
  211. * @param optional Integer $port
  212. * @param optional Integer $timeout
  213. * @return Net_SFTP
  214. * @access public
  215. */
  216. function __construct($host, $port = 22, $timeout = 10)
  217. {
  218. parent::__construct($host, $port, $timeout);
  219. $this->packet_types = array(
  220. 1 => 'NET_SFTP_INIT',
  221. 2 => 'NET_SFTP_VERSION',
  222. /* the format of SSH_FXP_OPEN changed between SFTPv4 and SFTPv5+:
  223. SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.1
  224. pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3 */
  225. 3 => 'NET_SFTP_OPEN',
  226. 4 => 'NET_SFTP_CLOSE',
  227. 5 => 'NET_SFTP_READ',
  228. 6 => 'NET_SFTP_WRITE',
  229. 7 => 'NET_SFTP_LSTAT',
  230. 9 => 'NET_SFTP_SETSTAT',
  231. 11 => 'NET_SFTP_OPENDIR',
  232. 12 => 'NET_SFTP_READDIR',
  233. 13 => 'NET_SFTP_REMOVE',
  234. 14 => 'NET_SFTP_MKDIR',
  235. 15 => 'NET_SFTP_RMDIR',
  236. 16 => 'NET_SFTP_REALPATH',
  237. 17 => 'NET_SFTP_STAT',
  238. /* the format of SSH_FXP_RENAME changed between SFTPv4 and SFTPv5+:
  239. SFTPv5+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
  240. pre-SFTPv5 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.5 */
  241. 18 => 'NET_SFTP_RENAME',
  242. 101=> 'NET_SFTP_STATUS',
  243. 102=> 'NET_SFTP_HANDLE',
  244. /* the format of SSH_FXP_NAME changed between SFTPv3 and SFTPv4+:
  245. SFTPv4+: http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.4
  246. pre-SFTPv4 : http://tools.ietf.org/html/draft-ietf-secsh-filexfer-02#section-7 */
  247. 103=> 'NET_SFTP_DATA',
  248. 104=> 'NET_SFTP_NAME',
  249. 105=> 'NET_SFTP_ATTRS',
  250. 200=> 'NET_SFTP_EXTENDED'
  251. );
  252. $this->status_codes = array(
  253. 0 => 'NET_SFTP_STATUS_OK',
  254. 1 => 'NET_SFTP_STATUS_EOF',
  255. 2 => 'NET_SFTP_STATUS_NO_SUCH_FILE',
  256. 3 => 'NET_SFTP_STATUS_PERMISSION_DENIED',
  257. 4 => 'NET_SFTP_STATUS_FAILURE',
  258. 5 => 'NET_SFTP_STATUS_BAD_MESSAGE',
  259. 6 => 'NET_SFTP_STATUS_NO_CONNECTION',
  260. 7 => 'NET_SFTP_STATUS_CONNECTION_LOST',
  261. 8 => 'NET_SFTP_STATUS_OP_UNSUPPORTED'
  262. );
  263. // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-7.1
  264. // the order, in this case, matters quite a lot - see Net_SFTP::_parseAttributes() to understand why
  265. $this->attributes = array(
  266. 0x00000001 => 'NET_SFTP_ATTR_SIZE',
  267. 0x00000002 => 'NET_SFTP_ATTR_UIDGID', // defined in SFTPv3, removed in SFTPv4+
  268. 0x00000004 => 'NET_SFTP_ATTR_PERMISSIONS',
  269. 0x00000008 => 'NET_SFTP_ATTR_ACCESSTIME',
  270. // 0x80000000 will yield a floating point on 32-bit systems and converting floating points to integers
  271. // yields inconsistent behavior depending on how php is compiled. so we left shift -1 (which, in
  272. // two's compliment, consists of all 1 bits) by 31. on 64-bit systems this'll yield 0xFFFFFFFF80000000.
  273. // that's not a problem, however, and 'anded' and a 32-bit number, as all the leading 1 bits are ignored.
  274. -1 << 31 => 'NET_SFTP_ATTR_EXTENDED'
  275. );
  276. // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-6.3
  277. // the flag definitions change somewhat in SFTPv5+. if SFTPv5+ support is added to this library, maybe name
  278. // the array for that $this->open5_flags and similarily alter the constant names.
  279. $this->open_flags = array(
  280. 0x00000001 => 'NET_SFTP_OPEN_READ',
  281. 0x00000002 => 'NET_SFTP_OPEN_WRITE',
  282. 0x00000008 => 'NET_SFTP_OPEN_CREATE',
  283. 0x00000010 => 'NET_SFTP_OPEN_TRUNCATE'
  284. );
  285. // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-04#section-5.2
  286. // see Net_SFTP::_parseLongname() for an explanation
  287. $this->file_types = array(
  288. 1 => 'NET_SFTP_TYPE_REGULAR',
  289. 2 => 'NET_SFTP_TYPE_DIRECTORY',
  290. 3 => 'NET_SFTP_TYPE_SYMLINK',
  291. 4 => 'NET_SFTP_TYPE_SPECIAL'
  292. );
  293. $this->_define_array(
  294. $this->packet_types,
  295. $this->status_codes,
  296. $this->attributes,
  297. $this->open_flags,
  298. $this->file_types
  299. );
  300. }
  301. /**
  302. * Login
  303. *
  304. * @param String $username
  305. * @param optional String $password
  306. * @return Boolean
  307. * @access public
  308. */
  309. function login($username, $password = '')
  310. {
  311. if (!parent::login($username, $password)) {
  312. return false;
  313. }
  314. $this->window_size_client_to_server[NET_SFTP_CHANNEL] = $this->window_size;
  315. $packet = pack('CNa*N3',
  316. NET_SSH2_MSG_CHANNEL_OPEN, strlen('session'), 'session', NET_SFTP_CHANNEL, $this->window_size, 0x4000);
  317. if (!$this->_send_binary_packet($packet)) {
  318. return false;
  319. }
  320. $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_OPEN;
  321. $response = $this->_get_channel_packet(NET_SFTP_CHANNEL);
  322. if ($response === false) {
  323. return false;
  324. }
  325. $packet = pack('CNNa*CNa*',
  326. NET_SSH2_MSG_CHANNEL_REQUEST, $this->server_channels[NET_SFTP_CHANNEL], strlen('subsystem'), 'subsystem', 1, strlen('sftp'), 'sftp');
  327. if (!$this->_send_binary_packet($packet)) {
  328. return false;
  329. }
  330. $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_REQUEST;
  331. $response = $this->_get_channel_packet(NET_SFTP_CHANNEL);
  332. if ($response === false) {
  333. return false;
  334. }
  335. $this->channel_status[NET_SFTP_CHANNEL] = NET_SSH2_MSG_CHANNEL_DATA;
  336. if (!$this->_send_sftp_packet(NET_SFTP_INIT, "\0\0\0\3")) {
  337. return false;
  338. }
  339. $response = $this->_get_sftp_packet();
  340. if ($this->packet_type != NET_SFTP_VERSION) {
  341. user_error('Expected SSH_FXP_VERSION', E_USER_NOTICE);
  342. return false;
  343. }
  344. extract(unpack('Nversion', $this->_string_shift($response, 4)));
  345. $this->version = $version;
  346. while (!empty($response)) {
  347. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  348. $key = $this->_string_shift($response, $length);
  349. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  350. $value = $this->_string_shift($response, $length);
  351. $this->extensions[$key] = $value;
  352. }
  353. /*
  354. SFTPv4+ defines a 'newline' extension. SFTPv3 seems to have unofficial support for it via 'newline@vandyke.com',
  355. however, I'm not sure what 'newline@vandyke.com' is supposed to do (the fact that it's unofficial means that it's
  356. not in the official SFTPv3 specs) and 'newline@vandyke.com' / 'newline' are likely not drop-in substitutes for
  357. one another due to the fact that 'newline' comes with a SSH_FXF_TEXT bitmask whereas it seems unlikely that
  358. 'newline@vandyke.com' would.
  359. */
  360. /*
  361. if (isset($this->extensions['newline@vandyke.com'])) {
  362. $this->extensions['newline'] = $this->extensions['newline@vandyke.com'];
  363. unset($this->extensions['newline@vandyke.com']);
  364. }
  365. */
  366. $this->request_id = 1;
  367. /*
  368. A Note on SFTPv4/5/6 support:
  369. <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.1> states the following:
  370. "If the client wishes to interoperate with servers that support noncontiguous version
  371. numbers it SHOULD send '3'"
  372. Given that the server only sends its version number after the client has already done so, the above
  373. seems to be suggesting that v3 should be the default version. This makes sense given that v3 is the
  374. most popular.
  375. <http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-5.5> states the following;
  376. "If the server did not send the "versions" extension, or the version-from-list was not included, the
  377. server MAY send a status response describing the failure, but MUST then close the channel without
  378. processing any further requests."
  379. So what do you do if you have a client whose initial SSH_FXP_INIT packet says it implements v3 and
  380. a server whose initial SSH_FXP_VERSION reply says it implements v4 and only v4? If it only implements
  381. v4, the "versions" extension is likely not going to have been sent so version re-negotiation as discussed
  382. in draft-ietf-secsh-filexfer-13 would be quite impossible. As such, what Net_SFTP would do is close the
  383. channel and reopen it with a new and updated SSH_FXP_INIT packet.
  384. */
  385. if ($this->version != 3) {
  386. return false;
  387. }
  388. $this->pwd = $this->_realpath('.');
  389. return true;
  390. }
  391. /**
  392. * Returns the current directory name
  393. *
  394. * @return Mixed
  395. * @access public
  396. */
  397. function pwd()
  398. {
  399. return $this->pwd;
  400. }
  401. /**
  402. * Canonicalize the Server-Side Path Name
  403. *
  404. * SFTP doesn't provide a mechanism by which the current working directory can be changed, so we'll emulate it. Returns
  405. * the absolute (canonicalized) path. If $mode is set to NET_SFTP_CONFIRM_DIR (as opposed to NET_SFTP_CONFIRM_NONE,
  406. * which is what it is set to by default), false is returned if $dir is not a valid directory.
  407. *
  408. * @see Net_SFTP::chdir()
  409. * @param String $dir
  410. * @param optional Integer $mode
  411. * @return Mixed
  412. * @access private
  413. */
  414. function _realpath($dir)
  415. {
  416. /*
  417. "This protocol represents file names as strings. File names are
  418. assumed to use the slash ('/') character as a directory separator.
  419. File names starting with a slash are "absolute", and are relative to
  420. the root of the file system. Names starting with any other character
  421. are relative to the user's default directory (home directory). Note
  422. that identifying the user is assumed to take place outside of this
  423. protocol."
  424. -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-6
  425. */
  426. $file = '';
  427. if ($this->pwd !== false) {
  428. // if the SFTP server returned the canonicalized path even for non-existant files this wouldn't be necessary
  429. // on OpenSSH it isn't necessary but on other SFTP servers it is. that and since the specs say nothing on
  430. // the subject, we'll go ahead and work around it with the following.
  431. if ($dir[strlen($dir) - 1] != '/') {
  432. $file = basename($dir);
  433. $dir = dirname($dir);
  434. }
  435. if ($dir == '.' || $dir == $this->pwd) {
  436. return $this->pwd . $file;
  437. }
  438. if ($dir[0] != '/') {
  439. $dir = $this->pwd . '/' . $dir;
  440. }
  441. // on the surface it seems like maybe resolving a path beginning with / is unnecessary, but such paths
  442. // can contain .'s and ..'s just like any other. we could parse those out as appropriate or we can let
  443. // the server do it. we'll do the latter.
  444. }
  445. /*
  446. that SSH_FXP_REALPATH returns SSH_FXP_NAME does not necessarily mean that anything actually exists at the
  447. specified path. generally speaking, no attributes are returned with this particular SSH_FXP_NAME packet
  448. regardless of whether or not a file actually exists. and in SFTPv3, the longname field and the filename
  449. field match for this particular SSH_FXP_NAME packet. for other SSH_FXP_NAME packets, this will likely
  450. not be the case, but for this one, it is.
  451. */
  452. // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.9
  453. if (!$this->_send_sftp_packet(NET_SFTP_REALPATH, pack('Na*', strlen($dir), $dir))) {
  454. return false;
  455. }
  456. $response = $this->_get_sftp_packet();
  457. switch ($this->packet_type) {
  458. case NET_SFTP_NAME:
  459. // although SSH_FXP_NAME is implemented differently in SFTPv3 than it is in SFTPv4+, the following
  460. // should work on all SFTP versions since the only part of the SSH_FXP_NAME packet the following looks
  461. // at is the first part and that part is defined the same in SFTP versions 3 through 6.
  462. $this->_string_shift($response, 4); // skip over the count - it should be 1, anyway
  463. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  464. $realpath = $this->_string_shift($response, $length);
  465. // the following is SFTPv3 only code. see Net_SFTP::_parseLongname() for more information.
  466. // per the above comment, this is a shot in the dark that, on most servers, won't help us in determining
  467. // the file type for Net_SFTP::stat() and Net_SFTP::lstat() but it's worth a shot.
  468. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  469. $this->fileType = $this->_parseLongname($this->_string_shift($response, $length));
  470. break;
  471. case NET_SFTP_STATUS:
  472. extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
  473. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  474. return false;
  475. default:
  476. user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS', E_USER_NOTICE);
  477. return false;
  478. }
  479. // if $this->pwd isn't set than the only thing $realpath could be is for '.', which is pretty much guaranteed to
  480. // be a bonafide directory
  481. return $realpath . '/' . $file;
  482. }
  483. /**
  484. * Changes the current directory
  485. *
  486. * @param String $dir
  487. * @return Boolean
  488. * @access public
  489. */
  490. function chdir($dir)
  491. {
  492. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  493. return false;
  494. }
  495. if ($dir[strlen($dir) - 1] != '/') {
  496. $dir.= '/';
  497. }
  498. $dir = $this->_realpath($dir);
  499. // confirm that $dir is, in fact, a valid directory
  500. if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
  501. return false;
  502. }
  503. // see Net_SFTP::nlist() for a more thorough explanation of the following
  504. $response = $this->_get_sftp_packet();
  505. switch ($this->packet_type) {
  506. case NET_SFTP_HANDLE:
  507. $handle = substr($response, 4);
  508. break;
  509. case NET_SFTP_STATUS:
  510. extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
  511. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  512. return false;
  513. default:
  514. user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
  515. return false;
  516. }
  517. if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
  518. return false;
  519. }
  520. $response = $this->_get_sftp_packet();
  521. if ($this->packet_type != NET_SFTP_STATUS) {
  522. user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
  523. return false;
  524. }
  525. extract(unpack('Nstatus', $this->_string_shift($response, 4)));
  526. if ($status != NET_SFTP_STATUS_OK) {
  527. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  528. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  529. return false;
  530. }
  531. $this->pwd = $dir;
  532. return true;
  533. }
  534. /**
  535. * Returns a list of files in the given directory
  536. *
  537. * @param optional String $dir
  538. * @return Mixed
  539. * @access public
  540. */
  541. function nlist($dir = '.')
  542. {
  543. return $this->_list($dir, false);
  544. }
  545. /**
  546. * Returns a detailed list of files in the given directory
  547. *
  548. * @param optional String $dir
  549. * @return Mixed
  550. * @access public
  551. */
  552. function rawlist($dir = '.')
  553. {
  554. return $this->_list($dir, true);
  555. }
  556. /**
  557. * Reads a list, be it detailed or not, of files in the given directory
  558. *
  559. * @param optional String $dir
  560. * @return Mixed
  561. * @access private
  562. */
  563. function _list($dir, $raw = true)
  564. {
  565. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  566. return false;
  567. }
  568. $dir = $this->_realpath($dir);
  569. if ($dir === false) {
  570. return false;
  571. }
  572. // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.2
  573. if (!$this->_send_sftp_packet(NET_SFTP_OPENDIR, pack('Na*', strlen($dir), $dir))) {
  574. return false;
  575. }
  576. $response = $this->_get_sftp_packet();
  577. switch ($this->packet_type) {
  578. case NET_SFTP_HANDLE:
  579. // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-9.2
  580. // since 'handle' is the last field in the SSH_FXP_HANDLE packet, we'll just remove the first four bytes that
  581. // represent the length of the string and leave it at that
  582. $handle = substr($response, 4);
  583. break;
  584. case NET_SFTP_STATUS:
  585. // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
  586. extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
  587. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  588. return false;
  589. default:
  590. user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
  591. return false;
  592. }
  593. $contents = array();
  594. while (true) {
  595. // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.2
  596. // why multiple SSH_FXP_READDIR packets would be sent when the response to a single one can span arbitrarily many
  597. // SSH_MSG_CHANNEL_DATA messages is not known to me.
  598. if (!$this->_send_sftp_packet(NET_SFTP_READDIR, pack('Na*', strlen($handle), $handle))) {
  599. return false;
  600. }
  601. $response = $this->_get_sftp_packet();
  602. switch ($this->packet_type) {
  603. case NET_SFTP_NAME:
  604. extract(unpack('Ncount', $this->_string_shift($response, 4)));
  605. for ($i = 0; $i < $count; $i++) {
  606. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  607. $shortname = $this->_string_shift($response, $length);
  608. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  609. $longname = $this->_string_shift($response, $length);
  610. $attributes = $this->_parseAttributes($response); // we also don't care about the attributes
  611. if (!$raw) {
  612. $contents[] = $shortname;
  613. } else {
  614. $contents[$shortname] = $attributes;
  615. $fileType = $this->_parseLongname($longname);
  616. if ($fileType) {
  617. $contents[$shortname]['type'] = $fileType;
  618. }
  619. }
  620. // SFTPv6 has an optional boolean end-of-list field, but we'll ignore that, since the
  621. // final SSH_FXP_STATUS packet should tell us that, already.
  622. }
  623. break;
  624. case NET_SFTP_STATUS:
  625. extract(unpack('Nstatus', $this->_string_shift($response, 4)));
  626. if ($status != NET_SFTP_STATUS_EOF) {
  627. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  628. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  629. return false;
  630. }
  631. break 2;
  632. default:
  633. user_error('Expected SSH_FXP_NAME or SSH_FXP_STATUS', E_USER_NOTICE);
  634. return false;
  635. }
  636. }
  637. if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
  638. return false;
  639. }
  640. // "The client MUST release all resources associated with the handle regardless of the status."
  641. // -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.1.3
  642. $response = $this->_get_sftp_packet();
  643. if ($this->packet_type != NET_SFTP_STATUS) {
  644. user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
  645. return false;
  646. }
  647. extract(unpack('Nstatus', $this->_string_shift($response, 4)));
  648. if ($status != NET_SFTP_STATUS_OK) {
  649. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  650. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  651. return false;
  652. }
  653. return $contents;
  654. }
  655. /**
  656. * Returns the file size, in bytes, or false, on failure
  657. *
  658. * Files larger than 4GB will show up as being exactly 4GB.
  659. *
  660. * @param String $filename
  661. * @return Mixed
  662. * @access public
  663. */
  664. function size($filename)
  665. {
  666. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  667. return false;
  668. }
  669. $filename = $this->_realpath($filename);
  670. if ($filename === false) {
  671. return false;
  672. }
  673. return $this->_size($filename);
  674. }
  675. /**
  676. * Returns general information about a file.
  677. *
  678. * Returns an array on success and false otherwise.
  679. *
  680. * @param String $filename
  681. * @return Mixed
  682. * @access public
  683. */
  684. function stat($filename)
  685. {
  686. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  687. return false;
  688. }
  689. $filename = $this->_realpath($filename);
  690. if ($filename === false) {
  691. return false;
  692. }
  693. return $this->_stat($filename, NET_SFTP_STAT);
  694. }
  695. /**
  696. * Returns general information about a file or symbolic link.
  697. *
  698. * Returns an array on success and false otherwise.
  699. *
  700. * @param String $filename
  701. * @return Mixed
  702. * @access public
  703. */
  704. function lstat($filename)
  705. {
  706. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  707. return false;
  708. }
  709. $filename = $this->_realpath($filename);
  710. if ($filename === false) {
  711. return false;
  712. }
  713. return $this->_stat($filename, NET_SFTP_LSTAT);
  714. }
  715. /**
  716. * Returns general information about a file or symbolic link
  717. *
  718. * Determines information without calling Net_SFTP::_realpath().
  719. * The second parameter can be either NET_SFTP_STAT or NET_SFTP_LSTAT.
  720. *
  721. * @param String $filename
  722. * @param Integer $type
  723. * @return Mixed
  724. * @access private
  725. */
  726. function _stat($filename, $type)
  727. {
  728. // SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
  729. $packet = pack('Na*', strlen($filename), $filename);
  730. if (!$this->_send_sftp_packet($type, $packet)) {
  731. return false;
  732. }
  733. $response = $this->_get_sftp_packet();
  734. switch ($this->packet_type) {
  735. case NET_SFTP_ATTRS:
  736. $attributes = $this->_parseAttributes($response);
  737. if ($this->fileType) {
  738. $attributes['type'] = $this->fileType;
  739. }
  740. return $attributes;
  741. case NET_SFTP_STATUS:
  742. extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
  743. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  744. return false;
  745. }
  746. user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS', E_USER_NOTICE);
  747. return false;
  748. }
  749. /**
  750. * Returns the file size, in bytes, or false, on failure
  751. *
  752. * Determines the size without calling Net_SFTP::_realpath()
  753. *
  754. * @param String $filename
  755. * @return Mixed
  756. * @access private
  757. */
  758. function _size($filename)
  759. {
  760. $result = $this->_stat($filename, NET_SFTP_LSTAT);
  761. return $result === false ? false : $result['size'];
  762. }
  763. /**
  764. * Set permissions on a file.
  765. *
  766. * Returns the new file permissions on success or FALSE on error.
  767. *
  768. * @param Integer $mode
  769. * @param String $filename
  770. * @return Mixed
  771. * @access public
  772. */
  773. function chmod($mode, $filename)
  774. {
  775. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  776. return false;
  777. }
  778. $filename = $this->_realpath($filename);
  779. if ($filename === false) {
  780. return false;
  781. }
  782. // SFTPv4+ has an additional byte field - type - that would need to be sent, as well. setting it to
  783. // SSH_FILEXFER_TYPE_UNKNOWN might work. if not, we'd have to do an SSH_FXP_STAT before doing an SSH_FXP_SETSTAT.
  784. $attr = pack('N2', NET_SFTP_ATTR_PERMISSIONS, $mode & 07777);
  785. if (!$this->_send_sftp_packet(NET_SFTP_SETSTAT, pack('Na*a*', strlen($filename), $filename, $attr))) {
  786. return false;
  787. }
  788. /*
  789. "Because some systems must use separate system calls to set various attributes, it is possible that a failure
  790. response will be returned, but yet some of the attributes may be have been successfully modified. If possible,
  791. servers SHOULD avoid this situation; however, clients MUST be aware that this is possible."
  792. -- http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.6
  793. */
  794. $response = $this->_get_sftp_packet();
  795. if ($this->packet_type != NET_SFTP_STATUS) {
  796. user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
  797. return false;
  798. }
  799. extract(unpack('Nstatus', $this->_string_shift($response, 4)));
  800. if ($status != NET_SFTP_STATUS_EOF) {
  801. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  802. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  803. }
  804. // rather than return what the permissions *should* be, we'll return what they actually are. this will also
  805. // tell us if the file actually exists.
  806. // incidentally, SFTPv4+ adds an additional 32-bit integer field - flags - to the following:
  807. $packet = pack('Na*', strlen($filename), $filename);
  808. if (!$this->_send_sftp_packet(NET_SFTP_STAT, $packet)) {
  809. return false;
  810. }
  811. $response = $this->_get_sftp_packet();
  812. switch ($this->packet_type) {
  813. case NET_SFTP_ATTRS:
  814. $attrs = $this->_parseAttributes($response);
  815. return $attrs['permissions'];
  816. case NET_SFTP_STATUS:
  817. extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
  818. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  819. return false;
  820. }
  821. user_error('Expected SSH_FXP_ATTRS or SSH_FXP_STATUS', E_USER_NOTICE);
  822. return false;
  823. }
  824. /**
  825. * Creates a directory.
  826. *
  827. * @param String $dir
  828. * @return Boolean
  829. * @access public
  830. */
  831. function mkdir($dir)
  832. {
  833. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  834. return false;
  835. }
  836. $dir = $this->_realpath(rtrim($dir, '/'));
  837. if ($dir === false) {
  838. return false;
  839. }
  840. // by not providing any permissions, hopefully the server will use the logged in users umask - their
  841. // default permissions.
  842. if (!$this->_send_sftp_packet(NET_SFTP_MKDIR, pack('Na*N', strlen($dir), $dir, 0))) {
  843. return false;
  844. }
  845. $response = $this->_get_sftp_packet();
  846. if ($this->packet_type != NET_SFTP_STATUS) {
  847. user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
  848. return false;
  849. }
  850. extract(unpack('Nstatus', $this->_string_shift($response, 4)));
  851. if ($status != NET_SFTP_STATUS_OK) {
  852. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  853. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  854. return false;
  855. }
  856. return true;
  857. }
  858. /**
  859. * Removes a directory.
  860. *
  861. * @param String $dir
  862. * @return Boolean
  863. * @access public
  864. */
  865. function rmdir($dir)
  866. {
  867. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  868. return false;
  869. }
  870. $dir = $this->_realpath($dir);
  871. if ($dir === false) {
  872. return false;
  873. }
  874. if (!$this->_send_sftp_packet(NET_SFTP_RMDIR, pack('Na*', strlen($dir), $dir))) {
  875. return false;
  876. }
  877. $response = $this->_get_sftp_packet();
  878. if ($this->packet_type != NET_SFTP_STATUS) {
  879. user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
  880. return false;
  881. }
  882. extract(unpack('Nstatus', $this->_string_shift($response, 4)));
  883. if ($status != NET_SFTP_STATUS_OK) {
  884. // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED?
  885. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  886. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  887. return false;
  888. }
  889. return true;
  890. }
  891. /**
  892. * Uploads a file to the SFTP server.
  893. *
  894. * By default, Net_SFTP::put() does not read from the local filesystem. $data is dumped directly into $remote_file.
  895. * So, for example, if you set $data to 'filename.ext' and then do Net_SFTP::get(), you will get a file, twelve bytes
  896. * long, containing 'filename.ext' as its contents.
  897. *
  898. * Setting $mode to NET_SFTP_LOCAL_FILE will change the above behavior. With NET_SFTP_LOCAL_FILE, $remote_file will
  899. * contain as many bytes as filename.ext does on your local filesystem. If your filename.ext is 1MB then that is how
  900. * large $remote_file will be, as well.
  901. *
  902. * Currently, only binary mode is supported. As such, if the line endings need to be adjusted, you will need to take
  903. * care of that, yourself.
  904. *
  905. * @param String $remote_file
  906. * @param String $data
  907. * @param optional Integer $mode
  908. * @return Boolean
  909. * @access public
  910. * @internal ASCII mode for SFTPv4/5/6 can be supported by adding a new function - Net_SFTP::setMode().
  911. */
  912. function put($remote_file, $data, $mode = NET_SFTP_STRING)
  913. {
  914. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  915. return false;
  916. }
  917. $remote_file = $this->_realpath($remote_file);
  918. if ($remote_file === false) {
  919. return false;
  920. }
  921. $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_WRITE | NET_SFTP_OPEN_CREATE | NET_SFTP_OPEN_TRUNCATE, 0);
  922. if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
  923. return false;
  924. }
  925. $response = $this->_get_sftp_packet();
  926. switch ($this->packet_type) {
  927. case NET_SFTP_HANDLE:
  928. $handle = substr($response, 4);
  929. break;
  930. case NET_SFTP_STATUS:
  931. extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
  932. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  933. return false;
  934. default:
  935. user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
  936. return false;
  937. }
  938. $initialize = true;
  939. // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.2.3
  940. if ($mode == NET_SFTP_LOCAL_FILE) {
  941. if (!is_file($data)) {
  942. user_error("$data is not a valid file", E_USER_NOTICE);
  943. return false;
  944. }
  945. $fp = @fopen($data, 'rb');
  946. if (!$fp) {
  947. return false;
  948. }
  949. $sent = 0;
  950. $size = filesize($data);
  951. } else {
  952. $sent = 0;
  953. $size = strlen($data);
  954. }
  955. $size = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
  956. $sftp_packet_size = 4096; // PuTTY uses 4096
  957. $i = 0;
  958. while ($sent < $size) {
  959. $temp = $mode == NET_SFTP_LOCAL_FILE ? fread($fp, $sftp_packet_size) : $this->_string_shift($data, $sftp_packet_size);
  960. $packet = pack('Na*N3a*', strlen($handle), $handle, 0, $sent, strlen($temp), $temp);
  961. if (!$this->_send_sftp_packet(NET_SFTP_WRITE, $packet)) {
  962. fclose($fp);
  963. return false;
  964. }
  965. $sent+= strlen($temp);
  966. $i++;
  967. if ($i == 50) {
  968. if (!$this->_read_put_responses($i)) {
  969. $i = 0;
  970. break;
  971. }
  972. $i = 0;
  973. }
  974. }
  975. $this->_read_put_responses($i);
  976. if ($mode == NET_SFTP_LOCAL_FILE) {
  977. fclose($fp);
  978. }
  979. if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
  980. return false;
  981. }
  982. $response = $this->_get_sftp_packet();
  983. if ($this->packet_type != NET_SFTP_STATUS) {
  984. user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
  985. return false;
  986. }
  987. extract(unpack('Nstatus', $this->_string_shift($response, 4)));
  988. if ($status != NET_SFTP_STATUS_OK) {
  989. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  990. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  991. return false;
  992. }
  993. return true;
  994. }
  995. /**
  996. * Reads multiple successive SSH_FXP_WRITE responses
  997. *
  998. * Sending an SSH_FXP_WRITE packet and immediately reading its response isn't as efficient as blindly sending out $i
  999. * SSH_FXP_WRITEs, in succession, and then reading $i responses.
  1000. *
  1001. * @param Integer $i
  1002. * @return Boolean
  1003. * @access private
  1004. */
  1005. function _read_put_responses($i)
  1006. {
  1007. while ($i--) {
  1008. $response = $this->_get_sftp_packet();
  1009. if ($this->packet_type != NET_SFTP_STATUS) {
  1010. user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
  1011. return false;
  1012. }
  1013. extract(unpack('Nstatus', $this->_string_shift($response, 4)));
  1014. if ($status != NET_SFTP_STATUS_OK) {
  1015. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1016. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  1017. break;
  1018. }
  1019. }
  1020. return $i < 0;
  1021. }
  1022. /**
  1023. * Downloads a file from the SFTP server.
  1024. *
  1025. * Returns a string containing the contents of $remote_file if $local_file is left undefined or a boolean false if
  1026. * the operation was unsuccessful. If $local_file is defined, returns true or false depending on the success of the
  1027. * operation
  1028. *
  1029. * @param String $remote_file
  1030. * @param optional String $local_file
  1031. * @return Mixed
  1032. * @access public
  1033. */
  1034. function get($remote_file, $local_file = false)
  1035. {
  1036. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  1037. return false;
  1038. }
  1039. $remote_file = $this->_realpath($remote_file);
  1040. if ($remote_file === false) {
  1041. return false;
  1042. }
  1043. $size = $this->_size($remote_file);
  1044. if ($size === false) {
  1045. return false;
  1046. }
  1047. $packet = pack('Na*N2', strlen($remote_file), $remote_file, NET_SFTP_OPEN_READ, 0);
  1048. if (!$this->_send_sftp_packet(NET_SFTP_OPEN, $packet)) {
  1049. return false;
  1050. }
  1051. $response = $this->_get_sftp_packet();
  1052. switch ($this->packet_type) {
  1053. case NET_SFTP_HANDLE:
  1054. $handle = substr($response, 4);
  1055. break;
  1056. case NET_SFTP_STATUS: // presumably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
  1057. extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
  1058. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  1059. return false;
  1060. default:
  1061. user_error('Expected SSH_FXP_HANDLE or SSH_FXP_STATUS', E_USER_NOTICE);
  1062. return false;
  1063. }
  1064. if ($local_file !== false) {
  1065. $fp = fopen($local_file, 'wb');
  1066. if (!$fp) {
  1067. return false;
  1068. }
  1069. } else {
  1070. $content = '';
  1071. }
  1072. $read = 0;
  1073. while ($read < $size) {
  1074. $packet = pack('Na*N3', strlen($handle), $handle, 0, $read, 1 << 20);
  1075. if (!$this->_send_sftp_packet(NET_SFTP_READ, $packet)) {
  1076. return false;
  1077. }
  1078. $response = $this->_get_sftp_packet();
  1079. switch ($this->packet_type) {
  1080. case NET_SFTP_DATA:
  1081. $temp = substr($response, 4);
  1082. $read+= strlen($temp);
  1083. if ($local_file === false) {
  1084. $content.= $temp;
  1085. } else {
  1086. fputs($fp, $temp);
  1087. }
  1088. break;
  1089. case NET_SFTP_STATUS:
  1090. extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
  1091. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  1092. break 2;
  1093. default:
  1094. user_error('Expected SSH_FXP_DATA or SSH_FXP_STATUS', E_USER_NOTICE);
  1095. return false;
  1096. }
  1097. }
  1098. if (!$this->_send_sftp_packet(NET_SFTP_CLOSE, pack('Na*', strlen($handle), $handle))) {
  1099. return false;
  1100. }
  1101. $response = $this->_get_sftp_packet();
  1102. if ($this->packet_type != NET_SFTP_STATUS) {
  1103. user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
  1104. return false;
  1105. }
  1106. extract(unpack('Nstatus/Nlength', $this->_string_shift($response, 8)));
  1107. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  1108. // check the status from the NET_SFTP_STATUS case in the above switch after the file has been closed
  1109. if ($status != NET_SFTP_STATUS_OK) {
  1110. return false;
  1111. }
  1112. extract(unpack('Nstatus', $this->_string_shift($response, 4)));
  1113. if ($status != NET_SFTP_STATUS_OK) {
  1114. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1115. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  1116. return false;
  1117. }
  1118. if (isset($content)) {
  1119. return $content;
  1120. }
  1121. fclose($fp);
  1122. return true;
  1123. }
  1124. /**
  1125. * Deletes a file on the SFTP server.
  1126. *
  1127. * @param String $path
  1128. * @return Boolean
  1129. * @access public
  1130. */
  1131. function delete($path)
  1132. {
  1133. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  1134. return false;
  1135. }
  1136. $path = $this->_realpath($path);
  1137. if ($path === false) {
  1138. return false;
  1139. }
  1140. // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
  1141. if (!$this->_send_sftp_packet(NET_SFTP_REMOVE, pack('Na*', strlen($path), $path))) {
  1142. return false;
  1143. }
  1144. $response = $this->_get_sftp_packet();
  1145. if ($this->packet_type != NET_SFTP_STATUS) {
  1146. user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
  1147. return false;
  1148. }
  1149. // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
  1150. extract(unpack('Nstatus', $this->_string_shift($response, 4)));
  1151. if ($status != NET_SFTP_STATUS_OK) {
  1152. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1153. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  1154. return false;
  1155. }
  1156. return true;
  1157. }
  1158. /**
  1159. * Renames a file or a directory on the SFTP server
  1160. *
  1161. * @param String $oldname
  1162. * @param String $newname
  1163. * @return Boolean
  1164. * @access public
  1165. */
  1166. function rename($oldname, $newname)
  1167. {
  1168. if (!($this->bitmap & NET_SSH2_MASK_LOGIN)) {
  1169. return false;
  1170. }
  1171. $oldname = $this->_realpath($oldname);
  1172. $newname = $this->_realpath($newname);
  1173. if ($oldname === false || $newname === false) {
  1174. return false;
  1175. }
  1176. // http://tools.ietf.org/html/draft-ietf-secsh-filexfer-13#section-8.3
  1177. $packet = pack('Na*Na*', strlen($oldname), $oldname, strlen($newname), $newname);
  1178. if (!$this->_send_sftp_packet(NET_SFTP_RENAME, $packet)) {
  1179. return false;
  1180. }
  1181. $response = $this->_get_sftp_packet();
  1182. if ($this->packet_type != NET_SFTP_STATUS) {
  1183. user_error('Expected SSH_FXP_STATUS', E_USER_NOTICE);
  1184. return false;
  1185. }
  1186. // if $status isn't SSH_FX_OK it's probably SSH_FX_NO_SUCH_FILE or SSH_FX_PERMISSION_DENIED
  1187. extract(unpack('Nstatus', $this->_string_shift($response, 4)));
  1188. if ($status != NET_SFTP_STATUS_OK) {
  1189. extract(unpack('Nlength', $this->_string_shift($response, 4)));
  1190. $this->sftp_errors[] = $this->status_codes[$status] . ': ' . $this->_string_shift($response, $length);
  1191. return false;
  1192. }
  1193. return true;
  1194. }
  1195. /**
  1196. * Parse Attributes
  1197. *
  1198. * See '7. File Attributes' of draft-ietf-secsh-filexfer-13 for more info.
  1199. *
  1200. * @param String $response
  1201. * @return Array
  1202. * @access private
  1203. */
  1204. function _parseAttributes(&$response)
  1205. {
  1206. $attr = array();
  1207. extract(unpack('Nflags', $this->_string_shift($response, 4)));
  1208. // SFTPv4+ have a type field (a byte) that follows the above flag field
  1209. foreach ($this->attributes as $key => $value) {
  1210. switch ($flags & $key) {
  1211. case NET_SFTP_ATTR_SIZE: // 0x00000001
  1212. // size is represented by a 64-bit integer, so we perhaps ought to be doing the following:
  1213. // $attr['size'] = new Math_BigInteger($this->_string_shift($response, 8), 256);
  1214. // of course, you shouldn't be using Net_SFTP to transfer files that are in excess of 4GB
  1215. // (0xFFFFFFFF bytes), anyway. as such, we'll just represent all file sizes that are bigger than
  1216. // 4GB as being 4GB.
  1217. extract(unpack('Nupper/Nsize', $this->_string_shift($response, 8)));
  1218. if ($upper) {
  1219. $attr['size'] = 0xFFFFFFFF;
  1220. } else {
  1221. $attr['size'] = $size < 0 ? ($size & 0x7FFFFFFF) + 0x80000000 : $size;
  1222. }
  1223. break;
  1224. case NET_SFTP_ATTR_UIDGID: // 0x00000002 (SFTPv3 only)
  1225. $attr+= unpack('Nuid/Ngid', $this->_string_shift($response, 8));
  1226. break;
  1227. case NET_SFTP_ATTR_PERMISSIONS: // 0x00000004
  1228. $attr+= unpack('Npermissions', $this->_string_shift($response, 4));
  1229. break;
  1230. case NET_SFTP_ATTR_ACCESSTIME: // 0x00000008
  1231. $attr+= unpack('Natime/Nmtime'

Large files files are truncated, but you can click here to view the full file