中国专业的PHP网站内容管理系统-织梦内容管理系统
No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

680 líneas
17KB

  1. <?php if(!defined('DEDEINC')) exit('dedecms');
  2. /**
  3. * FTP 操作类
  4. * 不支持 SFTP 和 SSL FTP 协议, 仅支持标准 FTP 协议.
  5. * 需要传递一个数组配置
  6. * 示例:
  7. * $config['hostname'] = 'ftp.example.com';
  8. * $config['username'] = 'your-username';
  9. * $config['password'] = 'your-password';
  10. * $config['debug'] = TRUE;
  11. *
  12. * @version $Id: ftp.class.php 1 2010-07-05 11:43:09Z tianya $
  13. * @package DedeCMS.Libraries
  14. * @copyright Copyright (c) 2007 - 2019, DesDev, Inc.
  15. * @license http://help.dedecms.com/usersguide/license.html
  16. * @link http://www.dedecms.com
  17. */
  18. @set_time_limit(1000);
  19. class FTP {
  20. var $hostname = '';
  21. var $username = '';
  22. var $password = '';
  23. var $port = 21;
  24. var $passive = TRUE;
  25. var $debug = FALSE;
  26. var $conn_id = FALSE;
  27. function __construct($config = array())
  28. {
  29. $this->FTP($config);
  30. }
  31. /**
  32. * 析构函数 - 设置参数
  33. *
  34. * 构造函数则传递一个配置数组
  35. */
  36. function FTP($config = array())
  37. {
  38. if (count($config) > 0)
  39. {
  40. $this->initialize($config);
  41. }
  42. }
  43. /**
  44. * 初始化设置
  45. *
  46. * @access public
  47. * @param array
  48. * @return void
  49. */
  50. function initialize($config = array())
  51. {
  52. foreach ($config as $key => $val)
  53. {
  54. if (isset($this->$key))
  55. {
  56. $this->$key = $val;
  57. }
  58. }
  59. // 准备主机名
  60. $this->hostname = preg_replace('|.+?://|', '', $this->hostname);
  61. }
  62. /**
  63. * FTP 链接
  64. *
  65. * @access public
  66. * @param array 链接值
  67. * @return bool
  68. */
  69. function connect($config = array())
  70. {
  71. if (count($config) > 0)
  72. {
  73. $this->initialize($config);
  74. }
  75. if (FALSE === ($this->conn_id = @ftp_connect($this->hostname, $this->port)))
  76. {
  77. if ($this->debug == TRUE)
  78. {
  79. $this->_error('无法链接');
  80. }
  81. return FALSE;
  82. }
  83. if ( ! $this->_login())
  84. {
  85. if ($this->debug == TRUE)
  86. {
  87. $this->_error('无法登录');
  88. }
  89. return FALSE;
  90. }
  91. // 如果需要则设置传输模式
  92. if ($this->passive == TRUE)
  93. {
  94. ftp_pasv($this->conn_id, TRUE);
  95. }
  96. return TRUE;
  97. }
  98. /**
  99. * FTP 登录
  100. *
  101. * @access private
  102. * @return bool
  103. */
  104. function _login()
  105. {
  106. return @ftp_login($this->conn_id, $this->username, $this->password);
  107. }
  108. /**
  109. * 验证连接ID
  110. *
  111. * @access private
  112. * @return bool
  113. */
  114. function _is_conn()
  115. {
  116. if ( ! is_resource($this->conn_id))
  117. {
  118. if ($this->debug == TRUE)
  119. {
  120. $this->_error('无法链接');
  121. }
  122. return FALSE;
  123. }
  124. return TRUE;
  125. }
  126. /**
  127. * 更改目录
  128. * 第二个参数可以让我们暂时关闭,以便调试
  129. * 此功能可用于检测是否存在一个文件夹
  130. * 抛出一个错误。没有什么的FTP相当于is_dir()
  131. * 因此,我们试图改变某一特定目录。
  132. *
  133. * @access public
  134. * @param string
  135. * @param bool
  136. * @return bool
  137. */
  138. function changedir($path = '', $supress_debug = FALSE)
  139. {
  140. if ($path == '' OR ! $this->_is_conn())
  141. {
  142. return FALSE;
  143. }
  144. $result = @ftp_chdir($this->conn_id, $path);
  145. if ($result === FALSE)
  146. {
  147. if ($this->debug == TRUE AND $supress_debug == FALSE)
  148. {
  149. $this->_error('无法更改目录');
  150. }
  151. return FALSE;
  152. }
  153. return TRUE;
  154. }
  155. /**
  156. * 创建一个目录
  157. *
  158. * @access public
  159. * @param string
  160. * @return bool
  161. */
  162. function mkdir($path = '', $permissions = NULL)
  163. {
  164. if ($path == '' OR ! $this->_is_conn())
  165. {
  166. return FALSE;
  167. }
  168. $result = @ftp_mkdir($this->conn_id, $path);
  169. if ($result === FALSE)
  170. {
  171. if ($this->debug == TRUE)
  172. {
  173. $this->_error('无法创建文件夹');
  174. }
  175. return FALSE;
  176. }
  177. // 如果需要设置权限
  178. if ( ! is_null($permissions))
  179. {
  180. $this->chmod($path, (int)$permissions);
  181. }
  182. return TRUE;
  183. }
  184. /**
  185. * 创建深级目录
  186. *
  187. * @access public
  188. * @param string
  189. * @return bool
  190. */
  191. function rmkdir($path = '', $pathsymbol = '/')
  192. {
  193. $pathArray = explode($pathsymbol,$path);
  194. $pathstr = $pathsymbol;
  195. foreach($pathArray as $val)
  196. {
  197. if(!empty($val))
  198. {
  199. //构建文件夹路径
  200. $pathstr = $pathstr.$val.$pathsymbol;
  201. if (! $this->_is_conn())
  202. {
  203. return FALSE;
  204. }
  205. $result = @ftp_chdir($this->conn_id, $pathstr);
  206. if($result === FALSE)
  207. {
  208. //如果不存在这个目录则创建
  209. if(!$this->mkdir($pathstr))
  210. {
  211. return FALSE;
  212. }
  213. }
  214. }
  215. }
  216. return TRUE;
  217. }
  218. /**
  219. * 上传一个文件到服务器
  220. *
  221. * @access public
  222. * @param string
  223. * @param string
  224. * @param string
  225. * @return bool
  226. */
  227. function upload($locpath, $rempath, $mode = 'auto', $permissions = NULL)
  228. {
  229. if (!$this->_is_conn())
  230. {
  231. return FALSE;
  232. }
  233. if (!file_exists($locpath))
  234. {
  235. $this->_error('不存在源文件');
  236. return FALSE;
  237. }
  238. // 未指定则设置模式
  239. if ($mode == 'auto')
  240. {
  241. // 获取文件扩展名,以便本类上传类型
  242. $ext = $this->_getext($locpath);
  243. $mode = $this->_settype($ext);
  244. }
  245. $mode = ($mode == 'ascii') ? FTP_ASCII : FTP_BINARY;
  246. $result = @ftp_put($this->conn_id, $rempath, $locpath, $mode);
  247. if ($result === FALSE)
  248. {
  249. if ($this->debug == TRUE)
  250. {
  251. $this->_error('无法上传');
  252. }
  253. return FALSE;
  254. }
  255. // 如果需要设置文件权限
  256. if ( ! is_null($permissions))
  257. {
  258. $this->chmod($rempath, (int)$permissions);
  259. }
  260. return TRUE;
  261. }
  262. /**
  263. * 重命名(或者移动)一个文件
  264. *
  265. * @access public
  266. * @param string
  267. * @param string
  268. * @param bool
  269. * @return bool
  270. */
  271. function rename($old_file, $new_file, $move = FALSE)
  272. {
  273. if ( ! $this->_is_conn())
  274. {
  275. return FALSE;
  276. }
  277. $result = @ftp_rename($this->conn_id, $old_file, $new_file);
  278. if ($result === FALSE)
  279. {
  280. if ($this->debug == TRUE)
  281. {
  282. $msg = ($move == FALSE) ? '无法重命名' : '无法移动';
  283. $this->_error($msg);
  284. }
  285. return FALSE;
  286. }
  287. return TRUE;
  288. }
  289. /**
  290. * 移动一个文件
  291. *
  292. * @access public
  293. * @param string
  294. * @param string
  295. * @return bool
  296. */
  297. function move($old_file, $new_file)
  298. {
  299. return $this->rename($old_file, $new_file, TRUE);
  300. }
  301. /**
  302. * 重命名或者移动一个文件
  303. *
  304. * @access public
  305. * @param string
  306. * @return bool
  307. */
  308. function delete_file($filepath)
  309. {
  310. if ( ! $this->_is_conn())
  311. {
  312. return FALSE;
  313. }
  314. $result = @ftp_delete($this->conn_id, $filepath);
  315. if ($result === FALSE)
  316. {
  317. if ($this->debug == TRUE)
  318. {
  319. $this->_error('无法删除');
  320. }
  321. return FALSE;
  322. }
  323. return TRUE;
  324. }
  325. /**
  326. * 删除一个文件夹,递归删除一切(包括子文件夹)中内容
  327. *
  328. * @access public
  329. * @param string
  330. * @return bool
  331. */
  332. function delete_dir($filepath)
  333. {
  334. if ( ! $this->_is_conn())
  335. {
  336. return FALSE;
  337. }
  338. // 如果需要在尾部加上尾随"/"
  339. $filepath = preg_replace("/(.+?)\/*$/", "\\1/", $filepath);
  340. $list = $this->list_files($filepath);
  341. if ($list !== FALSE AND count($list) > 0)
  342. {
  343. foreach ($list as $item)
  344. {
  345. // 如果我们不能删除该项目,它则可能是一个文件夹
  346. // 将调用 delete_dir()
  347. if ( ! @ftp_delete($this->conn_id, $item))
  348. {
  349. $this->delete_dir($item);
  350. }
  351. }
  352. }
  353. $result = @ftp_rmdir($this->conn_id, $filepath);
  354. if ($result === FALSE)
  355. {
  356. if ($this->debug == TRUE)
  357. {
  358. $this->_error('无法删除');
  359. }
  360. return FALSE;
  361. }
  362. return TRUE;
  363. }
  364. /**
  365. * 设置文件权限
  366. *
  367. * @access public
  368. * @param string 文件地址
  369. * @param string 权限
  370. * @return bool
  371. */
  372. function chmod($path, $perm)
  373. {
  374. if ( ! $this->_is_conn())
  375. {
  376. return FALSE;
  377. }
  378. // 仅PHP5才能运行
  379. if ( ! function_exists('ftp_chmod'))
  380. {
  381. if ($this->debug == TRUE)
  382. {
  383. $this->_error('无法更改权限');
  384. }
  385. return FALSE;
  386. }
  387. $result = @ftp_chmod($this->conn_id, $perm, $path);
  388. if ($result === FALSE)
  389. {
  390. if ($this->debug == TRUE)
  391. {
  392. $this->_error('无法更改权限');
  393. }
  394. return FALSE;
  395. }
  396. return TRUE;
  397. }
  398. /**
  399. * 在指定的目录的FTP文件列表
  400. *
  401. * @access public
  402. * @return array
  403. */
  404. function list_files($path = '.')
  405. {
  406. if ( ! $this->_is_conn())
  407. {
  408. return FALSE;
  409. }
  410. return ftp_nlist($this->conn_id, $path);
  411. }
  412. /**
  413. * 返回指定目录下文件的详细列表
  414. *
  415. * @access public
  416. * @return array
  417. */
  418. function list_rawfiles($path = '.', $type='dir')
  419. {
  420. if ( ! $this->_is_conn())
  421. {
  422. return FALSE;
  423. }
  424. $ftp_rawlist = ftp_rawlist($this->conn_id, $path, TRUE);
  425. foreach ($ftp_rawlist as $v) {
  426. $info = array();
  427. $vinfo = preg_split("/[\s]+/", $v, 9);
  428. if ($vinfo[0] !== "total") {
  429. $info['chmod'] = $vinfo[0];
  430. $info['num'] = $vinfo[1];
  431. $info['owner'] = $vinfo[2];
  432. $info['group'] = $vinfo[3];
  433. $info['size'] = $vinfo[4];
  434. $info['month'] = $vinfo[5];
  435. $info['day'] = $vinfo[6];
  436. $info['time'] = $vinfo[7];
  437. $info['name'] = $vinfo[8];
  438. $rawlist[$info['name']] = $info;
  439. }
  440. }
  441. $dir = array();
  442. $file = array();
  443. foreach ($rawlist as $k => $v) {
  444. if ($v['chmod']{0} == "d") {
  445. $dir[$k] = $v;
  446. } elseif ($v['chmod']{0} == "-") {
  447. $file[$k] = $v;
  448. }
  449. }
  450. return ($type == 'dir')? $dir : $file;
  451. }
  452. /**
  453. * 检索一个本地目录下的所有内容(包括子目录和所有文件),并通过FTP为这个目录创建一份镜像。
  454. * 源路径下的任何结构都会被创建到服务器上。你必须给出源路径和目标路径
  455. *
  456. * @access public
  457. * @param string 含有尾随"/"的源路径
  458. * @param string 目标路径 - 含有尾随"/"的文件夹
  459. * @return bool
  460. */
  461. function mirror($locpath, $rempath)
  462. {
  463. if ( ! $this->_is_conn())
  464. {
  465. return FALSE;
  466. }
  467. // 打开本地文件路径
  468. if ($fp = @opendir($locpath))
  469. {
  470. // 尝试打开远程文件的路径.
  471. if ( ! $this->changedir($rempath, TRUE))
  472. {
  473. // 如果不能打开则创建
  474. if ( ! $this->rmkdir($rempath) OR ! $this->changedir($rempath))
  475. {
  476. return FALSE;
  477. }
  478. }
  479. // 递归读取本地目录
  480. while (FALSE !== ($file = readdir($fp)))
  481. {
  482. if (@is_dir($locpath.$file) && substr($file, 0, 1) != '.')
  483. {
  484. $this->mirror($locpath.$file."/", $rempath.$file."/");
  485. }
  486. elseif (substr($file, 0, 1) != ".")
  487. {
  488. // 获取文件扩展名,以便本类上传类型
  489. $ext = $this->_getext($file);
  490. $mode = $this->_settype($ext);
  491. $this->upload($locpath.$file, $rempath.$file, $mode);
  492. }
  493. }
  494. return TRUE;
  495. }
  496. return FALSE;
  497. }
  498. /**
  499. * 取出文件扩展名
  500. *
  501. * @access private
  502. * @param string
  503. * @return string
  504. */
  505. function _getext($filename)
  506. {
  507. if (FALSE === strpos($filename, '.'))
  508. {
  509. return 'txt';
  510. }
  511. $x = explode('.', $filename);
  512. return end($x);
  513. }
  514. /**
  515. * 设置上传类型
  516. *
  517. * @access private
  518. * @param string
  519. * @return string
  520. */
  521. function _settype($ext)
  522. {
  523. $text_types = array(
  524. 'txt',
  525. 'text',
  526. 'php',
  527. 'phps',
  528. 'php4',
  529. 'js',
  530. 'css',
  531. 'htm',
  532. 'html',
  533. 'phtml',
  534. 'shtml',
  535. 'log',
  536. 'xml'
  537. );
  538. return (in_array($ext, $text_types)) ? 'ascii' : 'binary';
  539. }
  540. /**
  541. * 关闭连接
  542. *
  543. * @access public
  544. * @param string 源路径
  545. * @param string 目的地路径
  546. * @return bool
  547. */
  548. function close()
  549. {
  550. if ( ! $this->_is_conn())
  551. {
  552. return FALSE;
  553. }
  554. @ftp_close($this->conn_id);
  555. }
  556. /**
  557. * 显示错误信息
  558. *
  559. * @access private
  560. * @param string
  561. * @return bool
  562. */
  563. function _error($msg)
  564. {
  565. $errorTrackFile = dirname(__FILE__).'/../data/ftp_error_trace.inc';
  566. $emsg = '';
  567. $emsg .= "<div><h3>DedeCMS Error Warning!</h3>\r\n";
  568. $emsg .= "<div><a href='http://bbs.dedecms.com' target='_blank' style='color:red'>Technical Support: http://bbs.dedecms.com</a></div>";
  569. $emsg .= "<div style='line-helght:160%;font-size:14px;color:green'>\r\n";
  570. $emsg .= "<div style='color:blue'><br />Error page: <font color='red'>".$this->GetCurUrl()."</font></div>\r\n";
  571. $emsg .= "<div>Error infos: {$msg}</div>\r\n";
  572. $emsg .= "<br /></div></div>\r\n";
  573. echo $emsg;
  574. $savemsg = 'Page: '.$this->GetCurUrl()."\r\nError: ".$msg;
  575. //保存错误日志
  576. $fp = @fopen($errorTrackFile, 'a');
  577. @fwrite($fp, '<'.'?php exit();'."\r\n/*\r\n{$savemsg}\r\n*/\r\n?".">\r\n");
  578. @fclose($fp);
  579. }
  580. /**
  581. * 获得当前的脚本网址
  582. *
  583. * @access public
  584. * @return string
  585. */
  586. function GetCurUrl()
  587. {
  588. if(!empty($_SERVER["REQUEST_URI"]))
  589. {
  590. $scriptName = $_SERVER["REQUEST_URI"];
  591. $nowurl = $scriptName;
  592. }
  593. else
  594. {
  595. $scriptName = $_SERVER["PHP_SELF"];
  596. if(empty($_SERVER["QUERY_STRING"])) {
  597. $nowurl = $scriptName;
  598. }
  599. else {
  600. $nowurl = $scriptName."?".$_SERVER["QUERY_STRING"];
  601. }
  602. }
  603. return $nowurl;
  604. }
  605. }//End Class