FTP.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899
  1. <?php
  2. namespace Codeception\Module;
  3. use Codeception\TestInterface;
  4. use Codeception\Exception\ModuleException;
  5. /**
  6. *
  7. * Works with SFTP/FTP servers.
  8. *
  9. * In order to test the contents of a specific file stored on any remote FTP/SFTP system
  10. * this module downloads a temporary file to the local system. The temporary directory is
  11. * defined by default as ```tests/_data``` to specify a different directory set the tmp config
  12. * option to your chosen path.
  13. *
  14. * Don't forget to create the folder and ensure its writable.
  15. *
  16. * Supported and tested FTP types are:
  17. *
  18. * * FTP
  19. * * SFTP
  20. *
  21. * Connection uses php build in FTP client for FTP,
  22. * connection to SFTP uses [phpseclib](http://phpseclib.sourceforge.net/) pulled in using composer.
  23. *
  24. * For SFTP, add [phpseclib](http://phpseclib.sourceforge.net/) to require list.
  25. * ```
  26. * "require": {
  27. * "phpseclib/phpseclib": "^2.0.14"
  28. * }
  29. * ```
  30. *
  31. * ## Status
  32. *
  33. * * Stability:
  34. * - FTP: **stable**
  35. * - SFTP: **stable**
  36. *
  37. * ## Config
  38. *
  39. * * type: ftp - type of connection ftp/sftp (defaults to ftp).
  40. * * host *required* - hostname/ip address of the ftp server.
  41. * * port: 21 - port number for the ftp server
  42. * * timeout: 90 - timeout settings for connecting the ftp server.
  43. * * user: anonymous - user to access ftp server, defaults to anonymous authentication.
  44. * * password - password, defaults to empty for anonymous.
  45. * * key - path to RSA key for sftp.
  46. * * tmp - path to local directory for storing tmp files.
  47. * * passive: true - Turns on or off passive mode (FTP only)
  48. * * cleanup: true - remove tmp files from local directory on completion.
  49. *
  50. * ### Example
  51. * #### Example (FTP)
  52. *
  53. * modules:
  54. * enabled: [FTP]
  55. * config:
  56. * FTP:
  57. * type: ftp
  58. * host: '127.0.0.1'
  59. * port: 21
  60. * timeout: 120
  61. * user: 'root'
  62. * password: 'root'
  63. * key: ~/.ssh/id_rsa
  64. * tmp: 'tests/_data/ftp'
  65. * passive: true
  66. * cleanup: false
  67. *
  68. * #### Example (SFTP)
  69. *
  70. * modules:
  71. * enabled: [FTP]
  72. * config:
  73. * FTP:
  74. * type: sftp
  75. * host: '127.0.0.1'
  76. * port: 22
  77. * timeout: 120
  78. * user: 'root'
  79. * password: 'root'
  80. * key: ''
  81. * tmp: 'tests/_data/ftp'
  82. * cleanup: false
  83. *
  84. *
  85. * This module extends the Filesystem module, file contents methods are inherited from this module.
  86. */
  87. class FTP extends Filesystem
  88. {
  89. /**
  90. * FTP/SFTP connection handler
  91. */
  92. protected $ftp = null;
  93. /**
  94. * Configuration options and default settings
  95. *
  96. * @var array
  97. */
  98. protected $config = [
  99. 'type' => 'ftp',
  100. 'port' => 21,
  101. 'timeout' => 90,
  102. 'user' => 'anonymous',
  103. 'password' => '',
  104. 'key' => '',
  105. 'tmp' => 'tests/_data',
  106. 'passive' => false,
  107. 'cleanup' => true
  108. ];
  109. /**
  110. * Required configuration fields
  111. *
  112. * @var array
  113. */
  114. protected $requiredFields = ['host'];
  115. // ----------- SETUP METHODS BELOW HERE -------------------------//
  116. /**
  117. * Setup connection and login with config settings
  118. *
  119. * @param \Codeception\TestInterface $test
  120. */
  121. public function _before(TestInterface $test)
  122. {
  123. // Login using config settings
  124. $this->loginAs($this->config['user'], $this->config['password']);
  125. }
  126. /**
  127. * Close the FTP connection & Clear up
  128. */
  129. public function _after(TestInterface $test)
  130. {
  131. $this->_closeConnection();
  132. // Clean up temp files
  133. if ($this->config['cleanup']) {
  134. if (file_exists($this->config['tmp'] . '/ftp_data_file.tmp')) {
  135. unlink($this->config['tmp'] . '/ftp_data_file.tmp');
  136. }
  137. }
  138. }
  139. /**
  140. * Change the logged in user mid-way through your test, this closes the
  141. * current connection to the server and initialises and new connection.
  142. *
  143. * On initiation of this modules you are automatically logged into
  144. * the server using the specified config options or defaulted
  145. * to anonymous user if not provided.
  146. *
  147. * ``` php
  148. * <?php
  149. * $I->loginAs('user','password');
  150. * ?>
  151. * ```
  152. *
  153. * @param String $user
  154. * @param String $password
  155. */
  156. public function loginAs($user = 'anonymous', $password = '')
  157. {
  158. $this->_openConnection($user, $password); // Create new connection and login.
  159. }
  160. /**
  161. * Enters a directory on the ftp system - FTP root directory is used by default
  162. *
  163. * @param $path
  164. */
  165. public function amInPath($path)
  166. {
  167. $this->_changeDirectory($this->path = $this->absolutizePath($path) . ($path == '/' ? '' : DIRECTORY_SEPARATOR));
  168. $this->debug('Moved to ' . $this->path);
  169. }
  170. /**
  171. * Resolve path
  172. *
  173. * @param $path
  174. * @return string
  175. */
  176. protected function absolutizePath($path)
  177. {
  178. if (strpos($path, '/') === 0) {
  179. return $path;
  180. }
  181. return $this->path . $path;
  182. }
  183. // ----------- SEARCH METHODS BELOW HERE ------------------------//
  184. /**
  185. * Checks if file exists in path on the remote FTP/SFTP system.
  186. * DOES NOT OPEN the file when it's exists
  187. *
  188. * ``` php
  189. * <?php
  190. * $I->seeFileFound('UserModel.php','app/models');
  191. * ?>
  192. * ```
  193. *
  194. * @param $filename
  195. * @param string $path
  196. */
  197. public function seeFileFound($filename, $path = '')
  198. {
  199. $files = $this->grabFileList($path);
  200. $this->debug("see file: {$filename}");
  201. $this->assertContains($filename, $files, "file {$filename} not found in {$path}");
  202. }
  203. /**
  204. * Checks if file exists in path on the remote FTP/SFTP system, using regular expression as filename.
  205. * DOES NOT OPEN the file when it's exists
  206. *
  207. * ``` php
  208. * <?php
  209. * $I->seeFileFoundMatches('/^UserModel_([0-9]{6}).php$/','app/models');
  210. * ?>
  211. * ```
  212. *
  213. * @param $regex
  214. * @param string $path
  215. */
  216. public function seeFileFoundMatches($regex, $path = '')
  217. {
  218. foreach ($this->grabFileList($path) as $filename) {
  219. preg_match($regex, $filename, $matches);
  220. if (!empty($matches)) {
  221. $this->debug("file '{$filename}' matches '{$regex}'");
  222. return;
  223. }
  224. }
  225. $this->fail("no file matches found for '{$regex}'");
  226. }
  227. /**
  228. * Checks if file does not exist in path on the remote FTP/SFTP system
  229. *
  230. * @param $filename
  231. * @param string $path
  232. */
  233. public function dontSeeFileFound($filename, $path = '')
  234. {
  235. $files = $this->grabFileList($path);
  236. $this->debug("don't see file: {$filename}");
  237. $this->assertNotContains($filename, $files);
  238. }
  239. /**
  240. * Checks if file does not exist in path on the remote FTP/SFTP system, using regular expression as filename.
  241. * DOES NOT OPEN the file when it's exists
  242. *
  243. * @param $regex
  244. * @param string $path
  245. */
  246. public function dontSeeFileFoundMatches($regex, $path = '')
  247. {
  248. foreach ($this->grabFileList($path) as $filename) {
  249. preg_match($regex, $filename, $matches);
  250. if (!empty($matches)) {
  251. $this->fail("file matches found for {$regex}");
  252. }
  253. }
  254. $this->assertTrue(true);
  255. $this->debug("no files match '{$regex}'");
  256. }
  257. // ----------- UTILITY METHODS BELOW HERE -------------------------//
  258. /**
  259. * Opens a file (downloads from the remote FTP/SFTP system to a tmp directory for processing)
  260. * and stores it's content.
  261. *
  262. * Usage:
  263. *
  264. * ``` php
  265. * <?php
  266. * $I->openFile('composer.json');
  267. * $I->seeInThisFile('codeception/codeception');
  268. * ?>
  269. * ```
  270. *
  271. * @param $filename
  272. */
  273. public function openFile($filename)
  274. {
  275. $this->_openFile($this->absolutizePath($filename));
  276. }
  277. /**
  278. * Saves contents to tmp file and uploads the FTP/SFTP system.
  279. * Overwrites current file on server if exists.
  280. *
  281. * ``` php
  282. * <?php
  283. * $I->writeToFile('composer.json', 'some data here');
  284. * ?>
  285. * ```
  286. *
  287. * @param $filename
  288. * @param $contents
  289. */
  290. public function writeToFile($filename, $contents)
  291. {
  292. $this->_writeToFile($this->absolutizePath($filename), $contents);
  293. }
  294. /**
  295. * Create a directory on the server
  296. *
  297. * ``` php
  298. * <?php
  299. * $I->makeDir('vendor');
  300. * ?>
  301. * ```
  302. *
  303. * @param $dirname
  304. */
  305. public function makeDir($dirname)
  306. {
  307. $this->makeDirectory($this->absolutizePath($dirname));
  308. }
  309. /**
  310. * Currently not supported in this module, overwrite inherited method
  311. *
  312. * @param $src
  313. * @param $dst
  314. */
  315. public function copyDir($src, $dst)
  316. {
  317. $this->fail('copyDir() currently unsupported by FTP module');
  318. }
  319. /**
  320. * Rename/Move file on the FTP/SFTP server
  321. *
  322. * ``` php
  323. * <?php
  324. * $I->renameFile('composer.lock', 'composer_old.lock');
  325. * ?>
  326. * ```
  327. *
  328. * @param $filename
  329. * @param $rename
  330. */
  331. public function renameFile($filename, $rename)
  332. {
  333. $this->renameDirectory($this->absolutizePath($filename), $this->absolutizePath($rename));
  334. }
  335. /**
  336. * Rename/Move directory on the FTP/SFTP server
  337. *
  338. * ``` php
  339. * <?php
  340. * $I->renameDir('vendor', 'vendor_old');
  341. * ?>
  342. * ```
  343. *
  344. * @param $dirname
  345. * @param $rename
  346. */
  347. public function renameDir($dirname, $rename)
  348. {
  349. $this->renameDirectory($this->absolutizePath($dirname), $this->absolutizePath($rename));
  350. }
  351. /**
  352. * Deletes a file on the remote FTP/SFTP system
  353. *
  354. * ``` php
  355. * <?php
  356. * $I->deleteFile('composer.lock');
  357. * ?>
  358. * ```
  359. *
  360. * @param $filename
  361. */
  362. public function deleteFile($filename)
  363. {
  364. $this->delete($this->absolutizePath($filename));
  365. }
  366. /**
  367. * Deletes directory with all subdirectories on the remote FTP/SFTP server
  368. *
  369. * ``` php
  370. * <?php
  371. * $I->deleteDir('vendor');
  372. * ?>
  373. * ```
  374. *
  375. * @param $dirname
  376. */
  377. public function deleteDir($dirname)
  378. {
  379. $this->delete($this->absolutizePath($dirname));
  380. }
  381. /**
  382. * Erases directory contents on the FTP/SFTP server
  383. *
  384. * ``` php
  385. * <?php
  386. * $I->cleanDir('logs');
  387. * ?>
  388. * ```
  389. *
  390. * @param $dirname
  391. */
  392. public function cleanDir($dirname)
  393. {
  394. $this->clearDirectory($this->absolutizePath($dirname));
  395. }
  396. // ----------- GRABBER METHODS BELOW HERE -----------------------//
  397. /**
  398. * Grabber method for returning file/folders listing in an array
  399. *
  400. * ```php
  401. * <?php
  402. * $files = $I->grabFileList();
  403. * $count = $I->grabFileList('TEST', false); // Include . .. .thumbs.db
  404. * ?>
  405. * ```
  406. *
  407. * @param string $path
  408. * @param bool $ignore - suppress '.', '..' and '.thumbs.db'
  409. * @return array
  410. */
  411. public function grabFileList($path = '', $ignore = true)
  412. {
  413. $absolutize_path = $this->absolutizePath($path)
  414. . ($path != '' && substr($path, -1) != '/' ? DIRECTORY_SEPARATOR : '');
  415. $files = $this->_listFiles($absolutize_path);
  416. $display_files = [];
  417. if (is_array($files) && !empty($files)) {
  418. $this->debug('File List:');
  419. foreach ($files as &$file) {
  420. if (strtolower($file) != '.' &&
  421. strtolower($file) != '..' &&
  422. strtolower($file) != 'thumbs.db'
  423. ) { // Ignore '.', '..' and 'thumbs.db'
  424. // Replace full path from file listings if returned in listing
  425. $file = str_replace(
  426. $absolutize_path,
  427. '',
  428. $file
  429. );
  430. $display_files[] = $file;
  431. $this->debug(' - ' . $file);
  432. }
  433. }
  434. return $ignore ? $display_files : $files;
  435. }
  436. $this->debug("File List: <empty>");
  437. return [];
  438. }
  439. /**
  440. * Grabber method for returning file/folders count in directory
  441. *
  442. * ```php
  443. * <?php
  444. * $count = $I->grabFileCount();
  445. * $count = $I->grabFileCount('TEST', false); // Include . .. .thumbs.db
  446. * ?>
  447. * ```
  448. *
  449. * @param string $path
  450. * @param bool $ignore - suppress '.', '..' and '.thumbs.db'
  451. * @return int
  452. */
  453. public function grabFileCount($path = '', $ignore = true)
  454. {
  455. $count = count($this->grabFileList($path, $ignore));
  456. $this->debug("File Count: {$count}");
  457. return $count;
  458. }
  459. /**
  460. * Grabber method to return file size
  461. *
  462. * ```php
  463. * <?php
  464. * $size = $I->grabFileSize('test.txt');
  465. * ?>
  466. * ```
  467. *
  468. * @param $filename
  469. * @return bool
  470. */
  471. public function grabFileSize($filename)
  472. {
  473. $fileSize = $this->size($filename);
  474. $this->debug("{$filename} has a file size of {$fileSize}");
  475. return $fileSize;
  476. }
  477. /**
  478. * Grabber method to return last modified timestamp
  479. *
  480. * ```php
  481. * <?php
  482. * $time = $I->grabFileModified('test.txt');
  483. * ?>
  484. * ```
  485. *
  486. * @param $filename
  487. * @return bool
  488. */
  489. public function grabFileModified($filename)
  490. {
  491. $time = $this->modified($filename);
  492. $this->debug("{$filename} was last modified at {$time}");
  493. return $time;
  494. }
  495. /**
  496. * Grabber method to return current working directory
  497. *
  498. * ```php
  499. * <?php
  500. * $pwd = $I->grabDirectory();
  501. * ?>
  502. * ```
  503. *
  504. * @return string
  505. */
  506. public function grabDirectory()
  507. {
  508. $pwd = $this->_directory();
  509. $this->debug("PWD: {$pwd}");
  510. return $pwd;
  511. }
  512. // ----------- SERVER CONNECTION METHODS BELOW HERE -------------//
  513. /**
  514. * Open a new FTP/SFTP connection and authenticate user.
  515. *
  516. * @param string $user
  517. * @param string $password
  518. */
  519. private function _openConnection($user = 'anonymous', $password = '')
  520. {
  521. $this->_closeConnection(); // Close connection if already open
  522. if ($this->isSFTP()) {
  523. $this->sftpConnect($user, $password);
  524. } else {
  525. $this->ftpConnect($user, $password);
  526. }
  527. $pwd = $this->grabDirectory();
  528. $this->path = $pwd . ($pwd == '/' ? '' : DIRECTORY_SEPARATOR);
  529. }
  530. /**
  531. * Close open FTP/SFTP connection
  532. */
  533. private function _closeConnection()
  534. {
  535. if (!$this->ftp) {
  536. return;
  537. }
  538. if (!$this->isSFTP()) {
  539. ftp_close($this->ftp);
  540. $this->ftp = null;
  541. }
  542. }
  543. /**
  544. * Get the file listing for FTP/SFTP connection
  545. *
  546. * @param String $path
  547. * @return array
  548. */
  549. private function _listFiles($path)
  550. {
  551. if ($this->isSFTP()) {
  552. $files = @$this->ftp->nlist($path);
  553. } else {
  554. $files = @ftp_nlist($this->ftp, $path);
  555. }
  556. if ($files === false) {
  557. $this->fail("couldn't list files");
  558. }
  559. return $files;
  560. }
  561. /**
  562. * Get the current directory for the FTP/SFTP connection
  563. *
  564. * @return string
  565. */
  566. private function _directory()
  567. {
  568. if ($this->isSFTP()) {
  569. // == DIRECTORY_SEPARATOR ? '' : $pwd;
  570. $pwd = @$this->ftp->pwd();
  571. } else {
  572. $pwd = @ftp_pwd($this->ftp);
  573. }
  574. if (!$pwd) {
  575. $this->fail("couldn't get current directory");
  576. }
  577. }
  578. /**
  579. * Change the working directory on the FTP/SFTP server
  580. *
  581. * @param $path
  582. */
  583. private function _changeDirectory($path)
  584. {
  585. if ($this->isSFTP()) {
  586. $changed = @$this->ftp->chdir($path);
  587. } else {
  588. $changed = @ftp_chdir($this->ftp, $path);
  589. }
  590. if (!$changed) {
  591. $this->fail("couldn't change directory {$path}");
  592. }
  593. }
  594. /**
  595. * Download remote file to local tmp directory and open contents.
  596. *
  597. * @param $filename
  598. */
  599. private function _openFile($filename)
  600. {
  601. // Check local tmp directory
  602. if (!is_dir($this->config['tmp']) || !is_writeable($this->config['tmp'])) {
  603. $this->fail('tmp directory not found or is not writable');
  604. }
  605. // Download file to local tmp directory
  606. $tmp_file = $this->config['tmp'] . "/ftp_data_file.tmp";
  607. if ($this->isSFTP()) {
  608. $downloaded = @$this->ftp->get($filename, $tmp_file);
  609. } else {
  610. $downloaded = @ftp_get($this->ftp, $tmp_file, $filename, FTP_BINARY);
  611. }
  612. if (!$downloaded) {
  613. $this->fail('failed to download file to tmp directory');
  614. }
  615. // Open file content to variable
  616. if ($this->file = file_get_contents($tmp_file)) {
  617. $this->filepath = $filename;
  618. } else {
  619. $this->fail('failed to open tmp file');
  620. }
  621. }
  622. /**
  623. * Write data to local tmp file and upload to server
  624. *
  625. * @param $filename
  626. * @param $contents
  627. */
  628. private function _writeToFile($filename, $contents)
  629. {
  630. // Check local tmp directory
  631. if (!is_dir($this->config['tmp']) || !is_writeable($this->config['tmp'])) {
  632. $this->fail('tmp directory not found or is not writable');
  633. }
  634. // Build temp file
  635. $tmp_file = $this->config['tmp'] . "/ftp_data_file.tmp";
  636. file_put_contents($tmp_file, $contents);
  637. // Update variables
  638. $this->filepath = $filename;
  639. $this->file = $contents;
  640. // Upload the file to server
  641. if ($this->isSFTP()) {
  642. $flag = defined('NET_SFTP_LOCAL_FILE') ? NET_SFTP_LOCAL_FILE : \phpseclib\Net\SFTP::SOURCE_LOCAL_FILE;
  643. $uploaded = @$this->ftp->put($filename, $tmp_file, $flag);
  644. } else {
  645. $uploaded = ftp_put($this->ftp, $filename, $tmp_file, FTP_BINARY);
  646. }
  647. if (!$uploaded) {
  648. $this->fail('failed to upload file to server');
  649. }
  650. }
  651. /**
  652. * Make new directory on server
  653. *
  654. * @param $path
  655. */
  656. private function makeDirectory($path)
  657. {
  658. if ($this->isSFTP()) {
  659. $created = @$this->ftp->mkdir($path, true);
  660. } else {
  661. $created = @ftp_mkdir($this->ftp, $path);
  662. }
  663. if (!$created) {
  664. $this->fail("couldn't make directory {$path}");
  665. }
  666. $this->debug("Make directory: {$path}");
  667. }
  668. /**
  669. * Rename/Move directory/file on server
  670. *
  671. * @param $path
  672. * @param $rename
  673. */
  674. private function renameDirectory($path, $rename)
  675. {
  676. if ($this->isSFTP()) {
  677. $renamed = @$this->ftp->rename($path, $rename);
  678. } else {
  679. $renamed = @ftp_rename($this->ftp, $path, $rename);
  680. }
  681. if (!$renamed) {
  682. $this->fail("couldn't rename directory {$path} to {$rename}");
  683. }
  684. $this->debug("Renamed directory: {$path} to {$rename}");
  685. }
  686. /**
  687. * Delete file on server
  688. *
  689. * @param $filename
  690. */
  691. private function delete($filename, $isDir = false)
  692. {
  693. if ($this->isSFTP()) {
  694. $deleted = @$this->ftp->delete($filename, $isDir);
  695. } else {
  696. $deleted = @$this->ftpDelete($filename);
  697. }
  698. if (!$deleted) {
  699. $this->fail("couldn't delete {$filename}");
  700. }
  701. $this->debug("Deleted: {$filename}");
  702. }
  703. /**
  704. * Function to recursively delete folder, used for PHP FTP build in client.
  705. *
  706. * @param $directory
  707. * @return bool
  708. */
  709. private function ftpDelete($directory)
  710. {
  711. // here we attempt to delete the file/directory
  712. if (!(@ftp_rmdir($this->ftp, $directory) || @ftp_delete($this->ftp, $directory))) {
  713. // if the attempt to delete fails, get the file listing
  714. $filelist = @ftp_nlist($this->ftp, $directory);
  715. // loop through the file list and recursively delete the FILE in the list
  716. foreach ($filelist as $file) {
  717. $this->ftpDelete($file);
  718. }
  719. // if the file list is empty, delete the DIRECTORY we passed
  720. $this->ftpDelete($directory);
  721. }
  722. return true;
  723. }
  724. /**
  725. * Clear directory on server of all content
  726. *
  727. * @param $path
  728. */
  729. private function clearDirectory($path)
  730. {
  731. $this->debug("Clear directory: {$path}");
  732. $this->delete($path);
  733. $this->makeDirectory($path);
  734. }
  735. /**
  736. * Return the size of a given file
  737. *
  738. * @param $filename
  739. * @return bool
  740. */
  741. private function size($filename)
  742. {
  743. if ($this->isSFTP()) {
  744. $size = (int)@$this->ftp->size($filename);
  745. } else {
  746. $size = @ftp_size($this->ftp, $filename);
  747. }
  748. if ($size > 0) {
  749. return $size;
  750. }
  751. $this->fail("couldn't get the file size for {$filename}");
  752. }
  753. /**
  754. * Return the last modified time of a given file
  755. *
  756. * @param $filename
  757. * @return bool
  758. */
  759. private function modified($filename)
  760. {
  761. if ($this->isSFTP()) {
  762. $info = @$this->ftp->lstat($filename);
  763. if ($info) {
  764. return $info['mtime'];
  765. }
  766. } else {
  767. if ($time = @ftp_mdtm($this->ftp, $filename)) {
  768. return $time;
  769. }
  770. }
  771. $this->fail("couldn't get the file size for {$filename}");
  772. }
  773. /**
  774. * @param $user
  775. * @param $password
  776. */
  777. protected function sftpConnect($user, $password)
  778. {
  779. if (class_exists('Net_SFTP')) {
  780. $this->ftp = new \Net_SFTP($this->config['host'], $this->config['port'], $this->config['timeout']);
  781. } elseif (class_exists('phpseclib\Net\SFTP')) {
  782. $this->ftp = new \phpseclib\Net\SFTP($this->config['host'], $this->config['port'], $this->config['timeout']);
  783. } else {
  784. throw new ModuleException('phpseclib/phpseclib library is not installed');
  785. }
  786. if ($this->ftp === false) {
  787. $this->ftp = null;
  788. $this->fail('failed to connect to ftp server');
  789. }
  790. if (isset($this->config['key'])) {
  791. $keyFile = file_get_contents($this->config['key']);
  792. if (class_exists('Crypt_RSA')) {
  793. $password = new \Crypt_RSA();
  794. } elseif (class_exists('phpseclib\Crypt\RSA')) {
  795. $password = new \phpseclib\Crypt\RSA();
  796. } else {
  797. throw new ModuleException('phpseclib/phpseclib library is not installed');
  798. }
  799. $password->loadKey($keyFile);
  800. }
  801. if (!$this->ftp->login($user, $password)) {
  802. $this->fail('failed to authenticate user');
  803. }
  804. }
  805. /**
  806. * @param $user
  807. * @param $password
  808. */
  809. protected function ftpConnect($user, $password)
  810. {
  811. $this->ftp = ftp_connect($this->config['host'], $this->config['port'], $this->config['timeout']);
  812. if ($this->ftp === false) {
  813. $this->ftp = null;
  814. $this->fail('failed to connect to ftp server');
  815. }
  816. // Login using given access details
  817. if (!@ftp_login($this->ftp, $user, $password)) {
  818. $this->fail('failed to authenticate user');
  819. }
  820. // Set passive mode option (ftp only option)
  821. if (isset($this->config['passive'])) {
  822. ftp_pasv($this->ftp, $this->config['passive']);
  823. }
  824. }
  825. protected function isSFTP()
  826. {
  827. return strtolower($this->config['type']) == 'sftp';
  828. }
  829. }