You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

667 lines
13 KiB

7 years ago
  1. <?php
  2. /**
  3. * CodeIgniter
  4. *
  5. * An open source application development framework for PHP
  6. *
  7. * This content is released under the MIT License (MIT)
  8. *
  9. * Copyright (c) 2014 - 2017, British Columbia Institute of Technology
  10. *
  11. * Permission is hereby granted, free of charge, to any person obtaining a copy
  12. * of this software and associated documentation files (the "Software"), to deal
  13. * in the Software without restriction, including without limitation the rights
  14. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15. * copies of the Software, and to permit persons to whom the Software is
  16. * furnished to do so, subject to the following conditions:
  17. *
  18. * The above copyright notice and this permission notice shall be included in
  19. * all copies or substantial portions of the Software.
  20. *
  21. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  27. * THE SOFTWARE.
  28. *
  29. * @package CodeIgniter
  30. * @author EllisLab Dev Team
  31. * @copyright Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
  32. * @copyright Copyright (c) 2014 - 2017, British Columbia Institute of Technology (http://bcit.ca/)
  33. * @license http://opensource.org/licenses/MIT MIT License
  34. * @link https://codeigniter.com
  35. * @since Version 1.0.0
  36. * @filesource
  37. */
  38. defined('BASEPATH') OR exit('No direct script access allowed');
  39. /**
  40. * FTP Class
  41. *
  42. * @package CodeIgniter
  43. * @subpackage Libraries
  44. * @category Libraries
  45. * @author EllisLab Dev Team
  46. * @link https://codeigniter.com/user_guide/libraries/ftp.html
  47. */
  48. class CI_FTP {
  49. /**
  50. * FTP Server hostname
  51. *
  52. * @var string
  53. */
  54. public $hostname = '';
  55. /**
  56. * FTP Username
  57. *
  58. * @var string
  59. */
  60. public $username = '';
  61. /**
  62. * FTP Password
  63. *
  64. * @var string
  65. */
  66. public $password = '';
  67. /**
  68. * FTP Server port
  69. *
  70. * @var int
  71. */
  72. public $port = 21;
  73. /**
  74. * Passive mode flag
  75. *
  76. * @var bool
  77. */
  78. public $passive = TRUE;
  79. /**
  80. * Debug flag
  81. *
  82. * Specifies whether to display error messages.
  83. *
  84. * @var bool
  85. */
  86. public $debug = FALSE;
  87. // --------------------------------------------------------------------
  88. /**
  89. * Connection ID
  90. *
  91. * @var resource
  92. */
  93. protected $conn_id;
  94. // --------------------------------------------------------------------
  95. /**
  96. * Constructor
  97. *
  98. * @param array $config
  99. * @return void
  100. */
  101. public function __construct($config = array())
  102. {
  103. empty($config) OR $this->initialize($config);
  104. log_message('info', 'FTP Class Initialized');
  105. }
  106. // --------------------------------------------------------------------
  107. /**
  108. * Initialize preferences
  109. *
  110. * @param array $config
  111. * @return void
  112. */
  113. public function initialize($config = array())
  114. {
  115. foreach ($config as $key => $val)
  116. {
  117. if (isset($this->$key))
  118. {
  119. $this->$key = $val;
  120. }
  121. }
  122. // Prep the hostname
  123. $this->hostname = preg_replace('|.+?://|', '', $this->hostname);
  124. }
  125. // --------------------------------------------------------------------
  126. /**
  127. * FTP Connect
  128. *
  129. * @param array $config Connection values
  130. * @return bool
  131. */
  132. public function connect($config = array())
  133. {
  134. if (count($config) > 0)
  135. {
  136. $this->initialize($config);
  137. }
  138. if (FALSE === ($this->conn_id = @ftp_connect($this->hostname, $this->port)))
  139. {
  140. if ($this->debug === TRUE)
  141. {
  142. $this->_error('ftp_unable_to_connect');
  143. }
  144. return FALSE;
  145. }
  146. if ( ! $this->_login())
  147. {
  148. if ($this->debug === TRUE)
  149. {
  150. $this->_error('ftp_unable_to_login');
  151. }
  152. return FALSE;
  153. }
  154. // Set passive mode if needed
  155. if ($this->passive === TRUE)
  156. {
  157. ftp_pasv($this->conn_id, TRUE);
  158. }
  159. return TRUE;
  160. }
  161. // --------------------------------------------------------------------
  162. /**
  163. * FTP Login
  164. *
  165. * @return bool
  166. */
  167. protected function _login()
  168. {
  169. return @ftp_login($this->conn_id, $this->username, $this->password);
  170. }
  171. // --------------------------------------------------------------------
  172. /**
  173. * Validates the connection ID
  174. *
  175. * @return bool
  176. */
  177. protected function _is_conn()
  178. {
  179. if ( ! is_resource($this->conn_id))
  180. {
  181. if ($this->debug === TRUE)
  182. {
  183. $this->_error('ftp_no_connection');
  184. }
  185. return FALSE;
  186. }
  187. return TRUE;
  188. }
  189. // --------------------------------------------------------------------
  190. /**
  191. * Change directory
  192. *
  193. * The second parameter lets us momentarily turn off debugging so that
  194. * this function can be used to test for the existence of a folder
  195. * without throwing an error. There's no FTP equivalent to is_dir()
  196. * so we do it by trying to change to a particular directory.
  197. * Internally, this parameter is only used by the "mirror" function below.
  198. *
  199. * @param string $path
  200. * @param bool $suppress_debug
  201. * @return bool
  202. */
  203. public function changedir($path, $suppress_debug = FALSE)
  204. {
  205. if ( ! $this->_is_conn())
  206. {
  207. return FALSE;
  208. }
  209. $result = @ftp_chdir($this->conn_id, $path);
  210. if ($result === FALSE)
  211. {
  212. if ($this->debug === TRUE && $suppress_debug === FALSE)
  213. {
  214. $this->_error('ftp_unable_to_changedir');
  215. }
  216. return FALSE;
  217. }
  218. return TRUE;
  219. }
  220. // --------------------------------------------------------------------
  221. /**
  222. * Create a directory
  223. *
  224. * @param string $path
  225. * @param int $permissions
  226. * @return bool
  227. */
  228. public function mkdir($path, $permissions = NULL)
  229. {
  230. if ($path === '' OR ! $this->_is_conn())
  231. {
  232. return FALSE;
  233. }
  234. $result = @ftp_mkdir($this->conn_id, $path);
  235. if ($result === FALSE)
  236. {
  237. if ($this->debug === TRUE)
  238. {
  239. $this->_error('ftp_unable_to_mkdir');
  240. }
  241. return FALSE;
  242. }
  243. // Set file permissions if needed
  244. if ($permissions !== NULL)
  245. {
  246. $this->chmod($path, (int) $permissions);
  247. }
  248. return TRUE;
  249. }
  250. // --------------------------------------------------------------------
  251. /**
  252. * Upload a file to the server
  253. *
  254. * @param string $locpath
  255. * @param string $rempath
  256. * @param string $mode
  257. * @param int $permissions
  258. * @return bool
  259. */
  260. public function upload($locpath, $rempath, $mode = 'auto', $permissions = NULL)
  261. {
  262. if ( ! $this->_is_conn())
  263. {
  264. return FALSE;
  265. }
  266. if ( ! file_exists($locpath))
  267. {
  268. $this->_error('ftp_no_source_file');
  269. return FALSE;
  270. }
  271. // Set the mode if not specified
  272. if ($mode === 'auto')
  273. {
  274. // Get the file extension so we can set the upload type
  275. $ext = $this->_getext($locpath);
  276. $mode = $this->_settype($ext);
  277. }
  278. $mode = ($mode === 'ascii') ? FTP_ASCII : FTP_BINARY;
  279. $result = @ftp_put($this->conn_id, $rempath, $locpath, $mode);
  280. if ($result === FALSE)
  281. {
  282. if ($this->debug === TRUE)
  283. {
  284. $this->_error('ftp_unable_to_upload');
  285. }
  286. return FALSE;
  287. }
  288. // Set file permissions if needed
  289. if ($permissions !== NULL)
  290. {
  291. $this->chmod($rempath, (int) $permissions);
  292. }
  293. return TRUE;
  294. }
  295. // --------------------------------------------------------------------
  296. /**
  297. * Download a file from a remote server to the local server
  298. *
  299. * @param string $rempath
  300. * @param string $locpath
  301. * @param string $mode
  302. * @return bool
  303. */
  304. public function download($rempath, $locpath, $mode = 'auto')
  305. {
  306. if ( ! $this->_is_conn())
  307. {
  308. return FALSE;
  309. }
  310. // Set the mode if not specified
  311. if ($mode === 'auto')
  312. {
  313. // Get the file extension so we can set the upload type
  314. $ext = $this->_getext($rempath);
  315. $mode = $this->_settype($ext);
  316. }
  317. $mode = ($mode === 'ascii') ? FTP_ASCII : FTP_BINARY;
  318. $result = @ftp_get($this->conn_id, $locpath, $rempath, $mode);
  319. if ($result === FALSE)
  320. {
  321. if ($this->debug === TRUE)
  322. {
  323. $this->_error('ftp_unable_to_download');
  324. }
  325. return FALSE;
  326. }
  327. return TRUE;
  328. }
  329. // --------------------------------------------------------------------
  330. /**
  331. * Rename (or move) a file
  332. *
  333. * @param string $old_file
  334. * @param string $new_file
  335. * @param bool $move
  336. * @return bool
  337. */
  338. public function rename($old_file, $new_file, $move = FALSE)
  339. {
  340. if ( ! $this->_is_conn())
  341. {
  342. return FALSE;
  343. }
  344. $result = @ftp_rename($this->conn_id, $old_file, $new_file);
  345. if ($result === FALSE)
  346. {
  347. if ($this->debug === TRUE)
  348. {
  349. $this->_error('ftp_unable_to_'.($move === FALSE ? 'rename' : 'move'));
  350. }
  351. return FALSE;
  352. }
  353. return TRUE;
  354. }
  355. // --------------------------------------------------------------------
  356. /**
  357. * Move a file
  358. *
  359. * @param string $old_file
  360. * @param string $new_file
  361. * @return bool
  362. */
  363. public function move($old_file, $new_file)
  364. {
  365. return $this->rename($old_file, $new_file, TRUE);
  366. }
  367. // --------------------------------------------------------------------
  368. /**
  369. * Rename (or move) a file
  370. *
  371. * @param string $filepath
  372. * @return bool
  373. */
  374. public function delete_file($filepath)
  375. {
  376. if ( ! $this->_is_conn())
  377. {
  378. return FALSE;
  379. }
  380. $result = @ftp_delete($this->conn_id, $filepath);
  381. if ($result === FALSE)
  382. {
  383. if ($this->debug === TRUE)
  384. {
  385. $this->_error('ftp_unable_to_delete');
  386. }
  387. return FALSE;
  388. }
  389. return TRUE;
  390. }
  391. // --------------------------------------------------------------------
  392. /**
  393. * Delete a folder and recursively delete everything (including sub-folders)
  394. * contained within it.
  395. *
  396. * @param string $filepath
  397. * @return bool
  398. */
  399. public function delete_dir($filepath)
  400. {
  401. if ( ! $this->_is_conn())
  402. {
  403. return FALSE;
  404. }
  405. // Add a trailing slash to the file path if needed
  406. $filepath = preg_replace('/(.+?)\/*$/', '\\1/', $filepath);
  407. $list = $this->list_files($filepath);
  408. if ( ! empty($list))
  409. {
  410. for ($i = 0, $c = count($list); $i < $c; $i++)
  411. {
  412. // If we can't delete the item it's probably a directory,
  413. // so we'll recursively call delete_dir()
  414. if ( ! preg_match('#/\.\.?$#', $list[$i]) && ! @ftp_delete($this->conn_id, $list[$i]))
  415. {
  416. $this->delete_dir($filepath.$list[$i]);
  417. }
  418. }
  419. }
  420. if (@ftp_rmdir($this->conn_id, $filepath) === FALSE)
  421. {
  422. if ($this->debug === TRUE)
  423. {
  424. $this->_error('ftp_unable_to_delete');
  425. }
  426. return FALSE;
  427. }
  428. return TRUE;
  429. }
  430. // --------------------------------------------------------------------
  431. /**
  432. * Set file permissions
  433. *
  434. * @param string $path File path
  435. * @param int $perm Permissions
  436. * @return bool
  437. */
  438. public function chmod($path, $perm)
  439. {
  440. if ( ! $this->_is_conn())
  441. {
  442. return FALSE;
  443. }
  444. if (@ftp_chmod($this->conn_id, $perm, $path) === FALSE)
  445. {
  446. if ($this->debug === TRUE)
  447. {
  448. $this->_error('ftp_unable_to_chmod');
  449. }
  450. return FALSE;
  451. }
  452. return TRUE;
  453. }
  454. // --------------------------------------------------------------------
  455. /**
  456. * FTP List files in the specified directory
  457. *
  458. * @param string $path
  459. * @return array
  460. */
  461. public function list_files($path = '.')
  462. {
  463. return $this->_is_conn()
  464. ? ftp_nlist($this->conn_id, $path)
  465. : FALSE;
  466. }
  467. // ------------------------------------------------------------------------
  468. /**
  469. * Read a directory and recreate it remotely
  470. *
  471. * This function recursively reads a folder and everything it contains
  472. * (including sub-folders) and creates a mirror via FTP based on it.
  473. * Whatever the directory structure of the original file path will be
  474. * recreated on the server.
  475. *
  476. * @param string $locpath Path to source with trailing slash
  477. * @param string $rempath Path to destination - include the base folder with trailing slash
  478. * @return bool
  479. */
  480. public function mirror($locpath, $rempath)
  481. {
  482. if ( ! $this->_is_conn())
  483. {
  484. return FALSE;
  485. }
  486. // Open the local file path
  487. if ($fp = @opendir($locpath))
  488. {
  489. // Attempt to open the remote file path and try to create it, if it doesn't exist
  490. if ( ! $this->changedir($rempath, TRUE) && ( ! $this->mkdir($rempath) OR ! $this->changedir($rempath)))
  491. {
  492. return FALSE;
  493. }
  494. // Recursively read the local directory
  495. while (FALSE !== ($file = readdir($fp)))
  496. {
  497. if (is_dir($locpath.$file) && $file[0] !== '.')
  498. {
  499. $this->mirror($locpath.$file.'/', $rempath.$file.'/');
  500. }
  501. elseif ($file[0] !== '.')
  502. {
  503. // Get the file extension so we can se the upload type
  504. $ext = $this->_getext($file);
  505. $mode = $this->_settype($ext);
  506. $this->upload($locpath.$file, $rempath.$file, $mode);
  507. }
  508. }
  509. return TRUE;
  510. }
  511. return FALSE;
  512. }
  513. // --------------------------------------------------------------------
  514. /**
  515. * Extract the file extension
  516. *
  517. * @param string $filename
  518. * @return string
  519. */
  520. protected function _getext($filename)
  521. {
  522. return (($dot = strrpos($filename, '.')) === FALSE)
  523. ? 'txt'
  524. : substr($filename, $dot + 1);
  525. }
  526. // --------------------------------------------------------------------
  527. /**
  528. * Set the upload type
  529. *
  530. * @param string $ext Filename extension
  531. * @return string
  532. */
  533. protected function _settype($ext)
  534. {
  535. return in_array($ext, array('txt', 'text', 'php', 'phps', 'php4', 'js', 'css', 'htm', 'html', 'phtml', 'shtml', 'log', 'xml'), TRUE)
  536. ? 'ascii'
  537. : 'binary';
  538. }
  539. // ------------------------------------------------------------------------
  540. /**
  541. * Close the connection
  542. *
  543. * @return bool
  544. */
  545. public function close()
  546. {
  547. return $this->_is_conn()
  548. ? @ftp_close($this->conn_id)
  549. : FALSE;
  550. }
  551. // ------------------------------------------------------------------------
  552. /**
  553. * Display error message
  554. *
  555. * @param string $line
  556. * @return void
  557. */
  558. protected function _error($line)
  559. {
  560. $CI =& get_instance();
  561. $CI->lang->load('ftp');
  562. show_error($CI->lang->line($line));
  563. }
  564. }