<?php

class ftps{

	// Public
	var $ftp_conn;
	var $error = array();
	var $log_on = 1;
	
	// Private
	private $host = '';
	private $port = '';
	private $user = '';
	private $pass = '';
	
	function __construct(){
	}
	
	function __destruct(){
		if(!empty($this->ftp_conn)){
			@ftp_close($this->ftp_conn);
		}
	}
	
	function connect($host, $port, $user, $pass){
		
		//we shall add this restriction in next version.
		/* // There is a bug in PHP versions less than 5.6.0 causing the ftp_put/ftp_fput to fail with error Accepted Data Connection
		// Tested with 5.5.38 and 5.4.45
		if(version_compare(phpversion(), '5.6.0', '<')){
			$this->$error['no_support_php56'] = 'FTPS for remote import is not supported for PHP version less than 5.6 your source server current PHP version is '.phpversion();
			return false;
		}
		 */
		 
		if(!function_exists('ftp_ssl_connect')){
			$this->error[] = 'ftp_ssl_connect is disabled on your server. Please install OpenSSL in order to enable the function.';
			return false;
		}
		 
		$this->ftp_conn = @ftp_ssl_connect($host, $port, 90);
		if(!$this->ftp_conn){
			$this->error[] = 'SSL connection not found';
			return false;
		}
		$login_result = @ftp_login($this->ftp_conn, $user, $pass);
		if(!$login_result){
			$this->error[] = 'Please enter correct login details';
			return false;
		}
		ftp_pasv($this->ftp_conn,true);
		
		// Used for fetching and inserting data on remote server
		$this->host = $host;
		$this->port = $port;
		$this->user = $user;
		$this->pass = $pass;
		
		return true;
	}
	
	function delete($filename){
		if(!@ftp_delete($this->ftp_conn, $filename)){
			$this->error[] = "Could not delete $filename";
			return false;
		}
		return true;
	}
	
	function is_dir($dir){
		$old = $this->pwd();
		if(!$this->chdir($dir)){
			$this->error[] = "Could not find directory $dir";
			return false;
		}
		$this->chdir($old);
		return true;
	}
	
	function chdir($dir){
		if(!ftp_chdir($this->ftp_conn, $dir)){
			$this->error[] = "Could not change directory to $dir";
			return false;
		}
		return true;
	}
	
	function pwd(){
		return @ftp_pwd($this->ftp_conn);
	}
	
	function chmod($pathname, $mode){
		return @ftp_site($this->ftp_conn, sprintf('CHMOD %o %s', $mode, $pathname));
	}
	
	function utime($pathname, $mtime) {
		return @ftp_site($this->ftp_conn, sprintf("UTIME ".$mtime." ".$pathname)); // Try to set the mtime if UTIME command is supported by FTP
	}
	
	function file_exists($file){
		$dir = dirname($file);
		$filename = basename($file);
		if(!$list = $this->filelist($dir)){
			$this->error[] = "File $file could not be found";
			return false;
		}
		if(!in_array($filename, $list) && !in_array($file, $list)){
			$this->error[] = "File $file could not be found";
			return false;
		}
		return true;
	}
	
	function rename($from, $to){
		if(!@ftp_rename($this->ftp_conn, $from, $to)){
			$this->error[] = "Could not move $from to $to";
			return false;
		}
		return true;
	}
	
	function rmdir($dir){
		if(!@ftp_rmdir($this->ftp_conn, $dir)){
			$this->error[] = "Could not remove directory $dir";
			return false;
		}
		return true;
	}
	
	function mkdir($dirname, $mode = 0755){
		if(!@ftp_mkdir($this->ftp_conn, $dirname)){
			$this->error[] = "Could not make directory $dirname";
			return false;
		}
		$this->chmod($dirname, $mode);
		return true;
	}

	function mmkdir($dir, $mode = 0755) {
		if(empty($dir)) return FALSE;
		if($this->is_dir($dir) or $dir == "/" ) return TRUE;
		if(!$this->mmkdir(dirname($dir), $mode)) return false;
		$r=$this->mkdir($dir, $mode);
		$this->chmod($dir, $mode);
		return $r;
	}
	
	function exec($command){
		return @ftp_exec($this->ftp_conn, $command);
	}
	
	function is_exists($file){
		if($this->file_exists($file)){
			return true;
		}
		return false;
	}
	
	function _parselisting($line) {
		$is_windows = false;//($this->OS_remote == FTP_OS_Windows);
		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)) {
			$b = array();
			if ($lucifer[3]<70) { $lucifer[3]+=2000; } else { $lucifer[3]+=1900; } // 4digit year fix
			$b['isdir'] = ($lucifer[7]=="<DIR>");
			if ( $b['isdir'] )
				$b['type'] = 'd';
			else
				$b['type'] = 'f';
			$b['size'] = $lucifer[7];
			$b['month'] = $lucifer[1];
			$b['day'] = $lucifer[2];
			$b['year'] = $lucifer[3];
			$b['hour'] = $lucifer[4];
			$b['minute'] = $lucifer[5];
			$b['time'] = @mktime($lucifer[4]+(strcasecmp($lucifer[6],"PM")==0?12:0),$lucifer[5],0,$lucifer[1],$lucifer[2],$lucifer[3]);
			$b['am/pm'] = $lucifer[6];
			$b['name'] = $lucifer[8];
		} else if (!$is_windows && $lucifer=preg_split("/[ ]/",$line,9,PREG_SPLIT_NO_EMPTY)) {
			//echo $line."\n";
			$lcount=count($lucifer);
			if ($lcount<8) return '';
			$b = array();
			$b['isdir'] = $lucifer[0][0] === "d";
			$b['islink'] = $lucifer[0][0] === "l";
			if ( $b['isdir'] )
				$b['type'] = 'd';
			elseif ( $b['islink'] )
				$b['type'] = 'l';
			else
				$b['type'] = 'f';
			$b['perms'] = $lucifer[0];
			$b['number'] = $lucifer[1];
			$b['owner'] = $lucifer[2];
			$b['group'] = $lucifer[3];
			$b['size'] = $lucifer[4];
			if ($lcount==8) {
				sscanf($lucifer[5],"%d-%d-%d",$b['year'],$b['month'],$b['day']);
				sscanf($lucifer[6],"%d:%d",$b['hour'],$b['minute']);
				$b['time'] = @mktime($b['hour'],$b['minute'],0,$b['month'],$b['day'],$b['year']);
				$b['name'] = $lucifer[7];
			} else {
				$b['month'] = $lucifer[5];
				$b['day'] = $lucifer[6];
				if (preg_match("/([0-9]{2}):([0-9]{2})/",$lucifer[7],$l2)) {
					$b['year'] = date("Y");
					$b['hour'] = $l2[1];
					$b['minute'] = $l2[2];
					
					// The LS command gives time for the files which are modified in the last 6 months
					// Hence if the time is there it can also be created within the last year but within 6 months
					// e.g. if today is 21st Jan, 2016, LS will show time for files created from 22ND July onwards
					// So we cannot assume the year to be the current year. We simply take the last year
					if(strtotime(sprintf("%d %s %d %02d:%02d",$b['day'],$b['month'],$b['year'],$b['hour'],$b['minute'])) > time()){
						$b['year'] = $b['year'] - 1;
					}
					
				} else {
					$b['year'] = $lucifer[7];
					$b['hour'] = 0;
					$b['minute'] = 0;
				}
				$b['time'] = strtotime(sprintf("%d %s %d %02d:%02d",$b['day'],$b['month'],$b['year'],$b['hour'],$b['minute']));
				$b['name'] = $lucifer[8];
			}
		}
		
		$bad=array("(?)");
		if(in_array($b["owner"], $bad)) $b["owner"]=NULL;
		if(in_array($b["group"], $bad)) $b["group"]=NULL;
		
		$orig_perm = substr($b["perms"], 1);
		$b["perms"] = 0;
		//echo $orig_perm.'-'.(int)($orig_perm[4]=="w").' - '.$b["perms"].'<br>';
		$b["perms"]+=00400*(int)($orig_perm[0]=="r");
		$b["perms"]+=00200*(int)($orig_perm[1]=="w");
		$b["perms"]+=00100*(int)in_array($orig_perm[2], array("x","s"));
		$b["perms"]+=00040*(int)($orig_perm[3]=="r");
		$b["perms"]+=00020*(int)($orig_perm[4]=="w");
		$b["perms"]+=00010*(int)in_array($orig_perm[5], array("x","s"));
		$b["perms"]+=00004*(int)($orig_perm[6]=="r");
		$b["perms"]+=00002*(int)($orig_perm[7]=="w");
		$b["perms"]+=00001*(int)in_array($orig_perm[8], array("x","t"));
		$b["perms"]+=04000*(int)in_array($orig_perm[2], array("S","s"));
		$b["perms"]+=02000*(int)in_array($orig_perm[5], array("S","s"));
		$b["perms"]+=01000*(int)in_array($orig_perm[8], array("T","t"));

		return $b;
	}
	
	function filelist($dir){
		ftp_pasv($this->ftp_conn,true);
		if(!$list = ftp_nlist($this->ftp_conn, "$dir")){
			$this->error[] = "Could not get the list of files in directory $dir";
			return false;
		}
		return $list;
	}
	
	function rawlist($dir){
		ftp_pasv($this->ftp_conn,true);
		if(!$list = ftp_rawlist($this->ftp_conn, "$dir")){
			$this->error[] = "Could not get the raw list of files in directory $dir";
			return false;
		}
		return $list;
	}
	
	function put($local_file, $remote_file, $mode = FTP_ASCII){
		
		// Check last file and skip the files that have been already transferred...
		if(!empty($GLOBALS['last_file']) && $GLOBALS['start'] == 0){
			if(preg_match('#^'.$GLOBALS['last_file'].'$#', $local_file)){
				$GLOBALS['start'] = 1; // give a jump start once the last transferred file is found..
			}
			return true; //return true to skip files
		}
		
		if(!ftp_put($this->ftp_conn, $remote_file, $local_file, FTP_BINARY)){
			return false;
		}
		
		//We can run the scripts for the end time already set
		if(time() >= $GLOBALS['end']){
			$GLOBALS['end_file'] = $local_file; // set end file so that we know where to start from
		}
		
		return true;
	}
	
	function get($remote_file, $local_file){
		if(!ftp_get($this->ftp_conn, $local_file, $remote_file, FTP_BINARY)){
			$this->error[] = "Could not fetch the file $remote_file";
			return false;
		}
		
		if(file_exists($local_file)){
			return true;
		}
		$this->error[] = "Could not fetch the file $remote_file";
		return false;
	}
	
	function mput($local=".", $remote=NULL, $continious=false, $include_only=array()) {
		
		global $__settings, $ftp;
		
		 if(!@$ftp->is_dir($remote)) {
			if(!@$ftp->mkdir($remote)) {
				$this->error[] = "mput: can't create folder, Can't create remote file \"".$remote;
				return FALSE;
			}
		}

		$list = filelist_fn($local, 0);
		
		if($list===false) {
			$this->error[] = "mput: can't retrive directory listing, Can't retrive directory listing of local folder \"".$local;
			return FALSE;
		}
		
		if(empty($list)) return TRUE;
		
		foreach($list as $k=>$v) {
			$stat = stat($k);			
			if($v['dir'] == '0'){
				$list[$k]['type'] = 'f';
			}else{
				$list[$k]['type'] = 'd';
			}
			$list[$k]['name'] = $v['name'];
			$list[$k]['size'] = $stat['size'];
			$list[$k]['perms'] = octdec(substr(sprintf('%o', $stat['mode']), -4));
			$list[$k]['time'] = date('YmdHis', $stat['mtime']);
			
			if($list[$k]["name"]=="." or $list[$k]["name"]=="..") unset($list[$k]);
		}
		$ret=true;
		
		foreach($list as $ek => $el) {
			
			// ----- break the loop once last file is found...
			if(!empty($GLOBALS['end_file'])){
				break;
			}
			
			if($el['size'] == '0'){
				if($el['type'] == 'f'){
					//Create an empty file
					if(!$this->put($local."/".$el["name"], $remote."/".$el["name"])) {
						$this->error[] = "mput: can't copy file, Can't copy local file \"".$local."/".$el["name"]."\" to remote \"".$remote."/".$el["name"]."\"";
						$ret=false;
						if(!$continious) break;
					}
				}elseif($el['type'] == 'd'){
					$ftp->mkdir($remote.'/'.$el['name']);
				}
				@$ftp->chmod($remote.'/'.$el['name'], $el['perms']);
			
				//Set modified time 
				$t=$el["time"];
				if($t!==-1 and $t!==false){
					$ftp->utime($remote.'/'.$el['name'], $el["time"]);
				}
			
				unset($list[$ek]);
				continue;
			}
			
			$extension = get_extension($el['name']);
			if(!empty($include_only) && !in_array($el['name'], $include_only) && $extension !== 'sql'){
				unset($list[$ek]);
				continue;
			}
			
			$current_file = $el["path"].$el["name"];	
			//To exclude some files/folder
			if(!empty($__settings['exclude_files']) && in_array($current_file, $__settings['exclude_files'])){
				unset($list[$ek]);
				continue;
			}
			
			if($el["type"]=="d") {
				if(!$this->mput($local."/".$el["name"], $remote."/".$el["name"], $continious)){
					$this->error[] = "mput: can't copy folder, Can't copy local folder \"".$local."/".$el["name"]."\" to remote \"".$remote."/".$el["name"]."\"";
					$ret=false;
					if(!$continious) break;
				}
			}else{
				//To exclude some files if set in script's clone.php's __pre_unzip() function
				if(!empty($__settings['exclude_ext']) && in_array($extension, $__settings['exclude_ext'])){
					unset($list[$ek]);
					continue;
				}
				
				if(!$this->put($local."/".$el["name"], $remote."/".$el["name"])) {
					$this->error[] = "mput: can't copy file, Can't copy local file \"".$local."/".$el["name"]."\" to remote \"".$remote."/".$el["name"]."\"";
					$ret=false;
					if(!$continious) break;
				}
				
				
			}
			
			@$ftp->chmod($remote."/".$el["name"], $el["perms"]);
			
			//Set modified time 
			$t=$el["time"];
			if($t!==-1 and $t!==false){
				$ftp->utime($remote.'/'.$el['name'], $el["time"]);
			}
			
		}
		return $ret;
		
	}
	
	function mget($remote, $local=".", $continious=false, $include_only=array()) {
		
		global $__settings;
		
		if(!@file_exists($local)) {
			if(!@mkdir($local)) {
				$this->error[] = "mget: Cannot create folder \"".$local."\"";
				return FALSE;
			}
		}
		
		$list=$this->rawlist($remote, "-lA");
		
		if($list===false) {
			$this->error[] = "mget: Can't read remote folder \"".$remote."\" contents";
			return FALSE;
		}
		
		if(empty($list)) return true;
		
		foreach($list as $k=>$v) {
			$list[$k]=$this->_parselisting($v);
			if($list[$k]["name"]=="." or $list[$k]["name"]=="..") unset($list[$k]);
		}
		$ret=true;
		
		foreach($list as $ek => $el) {
		
			if($el['size'] == '0'){
				if($el['type'] == 'f'){
					$empty_file = @fopen($local.'/'.$el['name'], "w");
					@fclose($empty_file);
				}elseif($el['type'] == 'd'){
					mkdir($local.'/'.$el['name']);
				}
				@chmod($local.'/'.$el['name'], $el['perms']);
				
				$t=$el["time"];
				//echo 'Path :'.$local."/".$el["name"].' Date : '.$el["date"].' Strtotime : '.$t.'<br />';
				if($t!==-1 and $t!==false) @touch($local."/".$el["name"], $t);
				
				unset($list[$ek]);
				continue;
			}
			
			$extension = get_extension($el['name']);
			if(!empty($include_only) && !in_array($el['name'], $include_only) && $extension !== 'sql'){
				unset($list[$ek]);
				continue;
			}
			
			$current_file = $el["path"].$el["name"];	
			//To exclude some files/folder
			if(!empty($__settings['exclude_files']) && in_array($current_file, $__settings['exclude_files'])){
				unset($list[$ek]);
				continue;
			}
			
			if($el["type"]=="d") {
				if(!$this->mget($remote."/".$el["name"], $local."/".$el["name"], $continious)) {
					$this->error[] = "mget: Can't copy remote folder \"".$remote."/".$el["name"]."\" to local \"".$local."/".$el["name"]."\"";
					$ret=false;
					if(!$continious) break;
				}
			} else {
				
				//To exclude some files if set in script's clone.php's __pre_unzip() function
				if(!empty($__settings['exclude_ext']) && in_array($extension, $__settings['exclude_ext'])){
					unset($list[$ek]);
					continue;
				}
			
				if(!$this->get($remote."/".$el["name"], $local."/".$el["name"])) {
					$this->error[] = "mget: Can't copy remote file \"".$remote."/".$el["name"]."\" to local \"".$local."/".$el["name"]."\"";
					$ret=false;
					if(!$continious) break;
				}
			}
			@chmod($local."/".$el["name"], $el["perms"]);
			
			$t=$el["time"];
			//echo 'Path :'.$local."/".$el["name"].' Date : '.$el["date"].' Strtotime : '.$t.'<br />';
			if($t!==-1 and $t!==false) @touch($local."/".$el["name"], $t);
		}
		return $ret;
	}
	
	// Takes the DATA rather than the LOCAL file name
	function softput($remotefile=NULL, $softdata, $rest=0, $overwrite=true){
		
		$fp = fopen('php://temp', 'w');
		fwrite($fp, $softdata);
		rewind($fp);
		
		if(!ftp_fput($this->ftp_conn, $remotefile, $fp, FTP_BINARY)){
			$this->error[] = "Remote File $remotefile cannot be accessed";
			$this->error[] = "It looks like FTPS protocol is not supported with your PHP, Please try FTP/SFTP protocol";
			return false;
		}
		
		if($this->file_exists('/'.ltrim($remotefile, '/'))){
			return true;
		}
		
		$this->error[] = "Could not put the file $remotefile";
		return false;
		
	}
	
	// Returns the Data
	function softget($remotefile=NULL) {
		
		$fp = fopen('php://temp', 'r+');		
		
		if(!ftp_fget($this->ftp_conn, $fp, $remotefile, FTP_BINARY, 0)){
			$this->error[] = "Remote File $remotefile could not be accessed";
			$this->error[] = "It looks like FTPS protocol is not supported with your PHP, Please try FTP/SFTP protocol";
			return false;
		}
		
		$contents = '';
		rewind($fp);
		
		while (!feof($fp)) {
			$contents .= fread($fp, 8192);
		}
		
		fclose($fp);
		
		return $contents;
	}
	
	function nlist($dir){
		return $this->filelist($dir);
	}
	
	// For remote backup FTPS
	function backup_softput($remotefile=NULL, $fp, $pos = 0){
		
		ftp_set_option($this->ftp_conn, FTP_AUTOSEEK, false);
		if(!ftp_fput($this->ftp_conn, $remotefile, $fp, FTP_BINARY, $pos)){
			fclose($fp);
			$this->error[] = "Remote File $remotefile cannot be accessed";
			$this->error[] = "It looks like FTPS protocol is not supported with your PHP, Please try FTP/SFTP protocol";
			return false;
		}
		
		fclose($fp);
		
		return true;
		
	}

}
