PageRenderTime 89ms CodeModel.GetById 29ms app.highlight 44ms RepoModel.GetById 2ms app.codeStats 0ms

/wp-admin/includes/class-wp-filesystem-ftpext.php

https://gitlab.com/em645jn/brochure
PHP | 610 lines | 329 code | 73 blank | 208 comment | 81 complexity | bfb31034e27e41277316dcdb2edadfa7 MD5 | raw file
  1<?php
  2/**
  3 * WordPress FTP Filesystem.
  4 *
  5 * @package WordPress
  6 * @subpackage Filesystem
  7 */
  8
  9/**
 10 * WordPress Filesystem Class for implementing FTP.
 11 *
 12 * @since 2.5.0
 13 * @package WordPress
 14 * @subpackage Filesystem
 15 * @uses WP_Filesystem_Base Extends class
 16 */
 17class WP_Filesystem_FTPext extends WP_Filesystem_Base {
 18	public $link;
 19
 20	/**
 21	 * @access public
 22	 *
 23	 * @param array $opt
 24	 */
 25	public function __construct( $opt = '' ) {
 26		$this->method = 'ftpext';
 27		$this->errors = new WP_Error();
 28
 29		// Check if possible to use ftp functions.
 30		if ( ! extension_loaded('ftp') ) {
 31			$this->errors->add('no_ftp_ext', __('The ftp PHP extension is not available'));
 32			return;
 33		}
 34
 35		// This Class uses the timeout on a per-connection basis, Others use it on a per-action basis.
 36
 37		if ( ! defined('FS_TIMEOUT') )
 38			define('FS_TIMEOUT', 240);
 39
 40		if ( empty($opt['port']) )
 41			$this->options['port'] = 21;
 42		else
 43			$this->options['port'] = $opt['port'];
 44
 45		if ( empty($opt['hostname']) )
 46			$this->errors->add('empty_hostname', __('FTP hostname is required'));
 47		else
 48			$this->options['hostname'] = $opt['hostname'];
 49
 50		// Check if the options provided are OK.
 51		if ( empty($opt['username']) )
 52			$this->errors->add('empty_username', __('FTP username is required'));
 53		else
 54			$this->options['username'] = $opt['username'];
 55
 56		if ( empty($opt['password']) )
 57			$this->errors->add('empty_password', __('FTP password is required'));
 58		else
 59			$this->options['password'] = $opt['password'];
 60
 61		$this->options['ssl'] = false;
 62		if ( isset($opt['connection_type']) && 'ftps' == $opt['connection_type'] )
 63			$this->options['ssl'] = true;
 64	}
 65
 66	/**
 67	 * @access public
 68	 *
 69	 * @return bool
 70	 */
 71	public function connect() {
 72		if ( isset($this->options['ssl']) && $this->options['ssl'] && function_exists('ftp_ssl_connect') )
 73			$this->link = @ftp_ssl_connect($this->options['hostname'], $this->options['port'], FS_CONNECT_TIMEOUT);
 74		else
 75			$this->link = @ftp_connect($this->options['hostname'], $this->options['port'], FS_CONNECT_TIMEOUT);
 76
 77		if ( ! $this->link ) {
 78			$this->errors->add( 'connect',
 79				/* translators: %s: hostname:port */
 80				sprintf( __( 'Failed to connect to FTP Server %s' ),
 81					$this->options['hostname'] . ':' . $this->options['port']
 82				)
 83			);
 84			return false;
 85		}
 86
 87		if ( ! @ftp_login( $this->link,$this->options['username'], $this->options['password'] ) ) {
 88			$this->errors->add( 'auth',
 89				/* translators: %s: username */
 90				sprintf( __( 'Username/Password incorrect for %s' ),
 91					$this->options['username']
 92				)
 93			);
 94			return false;
 95		}
 96
 97		// Set the Connection to use Passive FTP
 98		@ftp_pasv( $this->link, true );
 99		if ( @ftp_get_option($this->link, FTP_TIMEOUT_SEC) < FS_TIMEOUT )
100			@ftp_set_option($this->link, FTP_TIMEOUT_SEC, FS_TIMEOUT);
101
102		return true;
103	}
104
105	/**
106	 * Retrieves the file contents.
107	 *
108	 * @since 2.5.0
109	 * @access public
110	 *
111	 * @param string $file Filename.
112	 * @return string|false File contents on success, false if no temp file could be opened,
113	 *                      or if the file couldn't be retrieved.
114	 */
115	public function get_contents( $file ) {
116		$tempfile = wp_tempnam($file);
117		$temp = fopen($tempfile, 'w+');
118
119		if ( ! $temp ) {
120			unlink( $tempfile );
121			return false;
122		}
123
124		if ( ! @ftp_fget( $this->link, $temp, $file, FTP_BINARY ) ) {
125			fclose( $temp );
126			unlink( $tempfile );
127			return false;
128		}
129
130		fseek( $temp, 0 ); // Skip back to the start of the file being written to
131		$contents = '';
132
133		while ( ! feof($temp) )
134			$contents .= fread($temp, 8192);
135
136		fclose($temp);
137		unlink($tempfile);
138		return $contents;
139	}
140
141	/**
142	 * @access public
143	 *
144	 * @param string $file
145	 * @return array
146	 */
147	public function get_contents_array($file) {
148		return explode("\n", $this->get_contents($file));
149	}
150
151	/**
152	 * @access public
153	 *
154	 * @param string $file
155	 * @param string $contents
156	 * @param bool|int $mode
157	 * @return bool
158	 */
159	public function put_contents($file, $contents, $mode = false ) {
160		$tempfile = wp_tempnam($file);
161		$temp = fopen( $tempfile, 'wb+' );
162
163		if ( ! $temp ) {
164			unlink( $tempfile );
165			return false;
166		}
167
168		mbstring_binary_safe_encoding();
169
170		$data_length = strlen( $contents );
171		$bytes_written = fwrite( $temp, $contents );
172
173		reset_mbstring_encoding();
174
175		if ( $data_length !== $bytes_written ) {
176			fclose( $temp );
177			unlink( $tempfile );
178			return false;
179		}
180
181		fseek( $temp, 0 ); // Skip back to the start of the file being written to
182
183		$ret = @ftp_fput( $this->link, $file, $temp, FTP_BINARY );
184
185		fclose($temp);
186		unlink($tempfile);
187
188		$this->chmod($file, $mode);
189
190		return $ret;
191	}
192
193	/**
194	 * @access public
195	 *
196	 * @return string
197	 */
198	public function cwd() {
199		$cwd = @ftp_pwd($this->link);
200		if ( $cwd )
201			$cwd = trailingslashit($cwd);
202		return $cwd;
203	}
204
205	/**
206	 * @access public
207	 *
208	 * @param string $dir
209	 * @return bool
210	 */
211	public function chdir($dir) {
212		return @ftp_chdir($this->link, $dir);
213	}
214
215	/**
216	 * @access public
217	 *
218	 * @param string $file
219	 * @param int $mode
220	 * @param bool $recursive
221	 * @return bool
222	 */
223	public function chmod($file, $mode = false, $recursive = false) {
224		if ( ! $mode ) {
225			if ( $this->is_file($file) )
226				$mode = FS_CHMOD_FILE;
227			elseif ( $this->is_dir($file) )
228				$mode = FS_CHMOD_DIR;
229			else
230				return false;
231		}
232
233		// chmod any sub-objects if recursive.
234		if ( $recursive && $this->is_dir($file) ) {
235			$filelist = $this->dirlist($file);
236			foreach ( (array)$filelist as $filename => $filemeta )
237				$this->chmod($file . '/' . $filename, $mode, $recursive);
238		}
239
240		// chmod the file or directory
241		if ( ! function_exists('ftp_chmod') )
242			return (bool)@ftp_site($this->link, sprintf('CHMOD %o %s', $mode, $file));
243		return (bool)@ftp_chmod($this->link, $mode, $file);
244	}
245
246	/**
247	 * @access public
248	 *
249	 * @param string $file
250	 * @return string
251	 */
252	public function owner($file) {
253		$dir = $this->dirlist($file);
254		return $dir[$file]['owner'];
255	}
256	/**
257	 * @access public
258	 *
259	 * @param string $file
260	 * @return string
261	 */
262	public function getchmod($file) {
263		$dir = $this->dirlist($file);
264		return $dir[$file]['permsn'];
265	}
266
267	/**
268	 * @access public
269	 *
270	 * @param string $file
271	 * @return string
272	 */
273	public function group($file) {
274		$dir = $this->dirlist($file);
275		return $dir[$file]['group'];
276	}
277
278	/**
279	 * @access public
280	 *
281	 * @param string $source
282	 * @param string $destination
283	 * @param bool   $overwrite
284	 * @param string|bool $mode
285	 * @return bool
286	 */
287	public function copy($source, $destination, $overwrite = false, $mode = false) {
288		if ( ! $overwrite && $this->exists($destination) )
289			return false;
290		$content = $this->get_contents($source);
291		if ( false === $content )
292			return false;
293		return $this->put_contents($destination, $content, $mode);
294	}
295
296	/**
297	 * @access public
298	 *
299	 * @param string $source
300	 * @param string $destination
301	 * @param bool $overwrite
302	 * @return bool
303	 */
304	public function move($source, $destination, $overwrite = false) {
305		return ftp_rename($this->link, $source, $destination);
306	}
307
308	/**
309	 * @access public
310	 *
311	 * @param string $file
312	 * @param bool $recursive
313	 * @param string $type
314	 * @return bool
315	 */
316	public function delete($file, $recursive = false, $type = false) {
317		if ( empty($file) )
318			return false;
319		if ( 'f' == $type || $this->is_file($file) )
320			return @ftp_delete($this->link, $file);
321		if ( !$recursive )
322			return @ftp_rmdir($this->link, $file);
323
324		$filelist = $this->dirlist( trailingslashit($file) );
325		if ( !empty($filelist) )
326			foreach ( $filelist as $delete_file )
327				$this->delete( trailingslashit($file) . $delete_file['name'], $recursive, $delete_file['type'] );
328		return @ftp_rmdir($this->link, $file);
329	}
330
331	/**
332	 * @access public
333	 *
334	 * @param string $file
335	 * @return bool
336	 */
337	public function exists($file) {
338		$list = @ftp_nlist($this->link, $file);
339
340		if ( empty( $list ) && $this->is_dir( $file ) ) {
341			return true; // File is an empty directory.
342		}
343
344		return !empty($list); //empty list = no file, so invert.
345	}
346
347	/**
348	 * @access public
349	 *
350	 * @param string $file
351	 * @return bool
352	 */
353	public function is_file($file) {
354		return $this->exists($file) && !$this->is_dir($file);
355	}
356
357	/**
358	 * @access public
359	 *
360	 * @param string $path
361	 * @return bool
362	 */
363	public function is_dir($path) {
364		$cwd = $this->cwd();
365		$result = @ftp_chdir($this->link, trailingslashit($path) );
366		if ( $result && $path == $this->cwd() || $this->cwd() != $cwd ) {
367			@ftp_chdir($this->link, $cwd);
368			return true;
369		}
370		return false;
371	}
372
373	/**
374	 * @access public
375	 *
376	 * @param string $file
377	 * @return bool
378	 */
379	public function is_readable($file) {
380		return true;
381	}
382
383	/**
384	 * @access public
385	 *
386	 * @param string $file
387	 * @return bool
388	 */
389	public function is_writable($file) {
390		return true;
391	}
392
393	/**
394	 * @access public
395	 *
396	 * @param string $file
397	 * @return bool
398	 */
399	public function atime($file) {
400		return false;
401	}
402
403	/**
404	 * @access public
405	 *
406	 * @param string $file
407	 * @return int
408	 */
409	public function mtime($file) {
410		return ftp_mdtm($this->link, $file);
411	}
412
413	/**
414	 * @access public
415	 *
416	 * @param string $file
417	 * @return int
418	 */
419	public function size($file) {
420		return ftp_size($this->link, $file);
421	}
422
423	/**
424	 * @access public
425	 *
426	 * @param string $file
427	 * @return bool
428	 */
429	public function touch($file, $time = 0, $atime = 0) {
430		return false;
431	}
432
433	/**
434	 * @access public
435	 *
436	 * @param string $path
437	 * @param mixed $chmod
438	 * @param mixed $chown
439	 * @param mixed $chgrp
440	 * @return bool
441	 */
442	public function mkdir($path, $chmod = false, $chown = false, $chgrp = false) {
443		$path = untrailingslashit($path);
444		if ( empty($path) )
445			return false;
446
447		if ( !@ftp_mkdir($this->link, $path) )
448			return false;
449		$this->chmod($path, $chmod);
450		return true;
451	}
452
453	/**
454	 * @access public
455	 *
456	 * @param string $path
457	 * @param bool $recursive
458	 * @return bool
459	 */
460	public function rmdir($path, $recursive = false) {
461		return $this->delete($path, $recursive);
462	}
463
464	/**
465	 * @access public
466	 *
467	 * @staticvar bool $is_windows
468	 * @param string $line
469	 * @return array
470	 */
471	public function parselisting($line) {
472		static $is_windows = null;
473		if ( is_null($is_windows) )
474			$is_windows = stripos( ftp_systype($this->link), 'win') !== false;
475
476		if ( $is_windows && preg_match('/([0-9]{2})-([0-9]{2})-([0-9]{2}) +([0-9]{2}):([0-9]{2})(AM|PM) +([0-9]+|<DIR>) +(.+)/', $line, $lucifer) ) {
477			$b = array();
478			if ( $lucifer[3] < 70 )
479				$lucifer[3] +=2000;
480			else
481				$lucifer[3] += 1900; // 4digit year fix
482			$b['isdir'] = ( $lucifer[7] == '<DIR>');
483			if ( $b['isdir'] )
484				$b['type'] = 'd';
485			else
486				$b['type'] = 'f';
487			$b['size'] = $lucifer[7];
488			$b['month'] = $lucifer[1];
489			$b['day'] = $lucifer[2];
490			$b['year'] = $lucifer[3];
491			$b['hour'] = $lucifer[4];
492			$b['minute'] = $lucifer[5];
493			$b['time'] = @mktime($lucifer[4] + (strcasecmp($lucifer[6], "PM") == 0 ? 12 : 0), $lucifer[5], 0, $lucifer[1], $lucifer[2], $lucifer[3]);
494			$b['am/pm'] = $lucifer[6];
495			$b['name'] = $lucifer[8];
496		} elseif ( !$is_windows && $lucifer = preg_split('/[ ]/', $line, 9, PREG_SPLIT_NO_EMPTY)) {
497			//echo $line."\n";
498			$lcount = count($lucifer);
499			if ( $lcount < 8 )
500				return '';
501			$b = array();
502			$b['isdir'] = $lucifer[0]{0} === 'd';
503			$b['islink'] = $lucifer[0]{0} === 'l';
504			if ( $b['isdir'] )
505				$b['type'] = 'd';
506			elseif ( $b['islink'] )
507				$b['type'] = 'l';
508			else
509				$b['type'] = 'f';
510			$b['perms'] = $lucifer[0];
511			$b['permsn'] = $this->getnumchmodfromh( $b['perms'] );
512			$b['number'] = $lucifer[1];
513			$b['owner'] = $lucifer[2];
514			$b['group'] = $lucifer[3];
515			$b['size'] = $lucifer[4];
516			if ( $lcount == 8 ) {
517				sscanf($lucifer[5], '%d-%d-%d', $b['year'], $b['month'], $b['day']);
518				sscanf($lucifer[6], '%d:%d', $b['hour'], $b['minute']);
519				$b['time'] = @mktime($b['hour'], $b['minute'], 0, $b['month'], $b['day'], $b['year']);
520				$b['name'] = $lucifer[7];
521			} else {
522				$b['month'] = $lucifer[5];
523				$b['day'] = $lucifer[6];
524				if ( preg_match('/([0-9]{2}):([0-9]{2})/', $lucifer[7], $l2) ) {
525					$b['year'] = date("Y");
526					$b['hour'] = $l2[1];
527					$b['minute'] = $l2[2];
528				} else {
529					$b['year'] = $lucifer[7];
530					$b['hour'] = 0;
531					$b['minute'] = 0;
532				}
533				$b['time'] = strtotime( sprintf('%d %s %d %02d:%02d', $b['day'], $b['month'], $b['year'], $b['hour'], $b['minute']) );
534				$b['name'] = $lucifer[8];
535			}
536		}
537
538		// Replace symlinks formatted as "source -> target" with just the source name
539		if ( isset( $b['islink'] ) && $b['islink'] ) {
540			$b['name'] = preg_replace( '/(\s*->\s*.*)$/', '', $b['name'] );
541		}
542
543		return $b;
544	}
545
546	/**
547	 * @access public
548	 *
549	 * @param string $path
550	 * @param bool $include_hidden
551	 * @param bool $recursive
552	 * @return bool|array
553	 */
554	public function dirlist($path = '.', $include_hidden = true, $recursive = false) {
555		if ( $this->is_file($path) ) {
556			$limit_file = basename($path);
557			$path = dirname($path) . '/';
558		} else {
559			$limit_file = false;
560		}
561
562		$pwd = @ftp_pwd($this->link);
563		if ( ! @ftp_chdir($this->link, $path) ) // Cant change to folder = folder doesn't exist
564			return false;
565		$list = @ftp_rawlist($this->link, '-a', false);
566		@ftp_chdir($this->link, $pwd);
567
568		if ( empty($list) ) // Empty array = non-existent folder (real folder will show . at least)
569			return false;
570
571		$dirlist = array();
572		foreach ( $list as $k => $v ) {
573			$entry = $this->parselisting($v);
574			if ( empty($entry) )
575				continue;
576
577			if ( '.' == $entry['name'] || '..' == $entry['name'] )
578				continue;
579
580			if ( ! $include_hidden && '.' == $entry['name'][0] )
581				continue;
582
583			if ( $limit_file && $entry['name'] != $limit_file)
584				continue;
585
586			$dirlist[ $entry['name'] ] = $entry;
587		}
588
589		$ret = array();
590		foreach ( (array)$dirlist as $struc ) {
591			if ( 'd' == $struc['type'] ) {
592				if ( $recursive )
593					$struc['files'] = $this->dirlist($path . '/' . $struc['name'], $include_hidden, $recursive);
594				else
595					$struc['files'] = array();
596			}
597
598			$ret[ $struc['name'] ] = $struc;
599		}
600		return $ret;
601	}
602
603	/**
604	 * @access public
605	 */
606	public function __destruct() {
607		if ( $this->link )
608			ftp_close($this->link);
609	}
610}