<?php
/**
 * Wave Framework <http://github.com/kristovaher/Wave-Framework>
 * Tools Functions Library
 *
 * This script includes various general-use functions that are used by developer tools in Wave Framework 
 * that are stored in /tools/ folder. This includes scripts such as directory cleaner and mass-file mover 
 * through FTP.
 * 
 * - Directory cleaner function
 * - File index creation function
 * - FTP file mover
 * - System backup archive creator
 * - It is recommended to remove all files from /tools/ subfolder prior to deploying project in live
 *
 * @package    Tools
 * @author     Kristo Vaher < [email protected]>
 * @copyright  Copyright (c) 2012, Kristo Vaher
 * @license    GNU Lesser General Public License Version 3
 * @tutorial   /doc/pages/guide_tools.htm
 * @since      1.0.0
 * @version    3.6.4
 */
/**
 * This function clears a folder and all of its subfolders and is run recursively
 *
 * @param string $directory location of directory in filesystem
 * @param integer $cutoff UNIX timestamp after which files will not be removed
 * @return array as log
 */
function dirCleaner($directory,$cutoff=0){
	// Log will be stored in this array
	$log=array();
	// Scanning the directory for files
	$files=fileIndex($directory,'files');
	// Scanning the directory for files
	$folders=fileIndex($directory,'folders');
	
	// This will loop over all the files if files were found in this directory
	if($files && !empty($files)){
		foreach($files as $file){
			// Testing if file modification date is older than the $cutoff timestamp 
			if($cutoff==0 || filemtime($file)<=$cutoff){
				// Attempting to remove the file
				if(unlink($file)){
					$log[]='DELETED '.$file;
				} else {
					$log[]='FAILED '.$file;
				}
			} else {
				$log[]='KEPT '.$file;
			}
		}
	}
	
	// This will loop over all the folders if folders were found in this directory
	if($folders && !empty($folders)){
		foreach($folders as $folder){
			// Testing if the directory is empty
			$contents=scandir($folder);
			if(count($contents)<=2){
				// Attempting to remove the folder
				if(rmdir($folder)){
					$log[]='DELETED '.$folder;
				} else {
					$log[]='FAILED '.$folder;
				}
			} else {
				$log[]='NOT EMPTY '.$folder;
			}
		}
	}
	
	// Log is returned
	return $log;
	
}
/**
 * This function lists all files and directories in a specific directory
 *
 * @param string $directory location of directory in filesystem
 * @param string $type can be 'all', 'folders', 'files' or 'filenames'
 * @param bool $recursive whether folders are parsed recursively
 * @param bool $files whether files are included
 * @return array as file index
 */
function fileIndex($directory,$type='all',$recursive=true,$files=false){
	// File names are stored in this array
	$index=array();
	
	// Scanning the current directory
	if(!$files){
		$files=scandir($directory);
	}
	
	// This will loop over all the files if files were found in this directory
	if(!empty($files)){
		foreach($files as $f){
			// As long as the current file is not the current or parent directory
			if($f!='.' && $f!='..'){
				// If file is another directory then this is parsed recursively
				if(is_dir($directory.$f)){
					// Whether the parsing is recursive
					if($recursive){
						// File data from recursive parsing is merged with current files list
						$index=array_merge($index,fileIndex($directory.$f.DIRECTORY_SEPARATOR,$type,true));
					}
					// Adding directory to index, if supported
					if($type=='all' || $type=='folders'){
						$index[]=$directory.$f.DIRECTORY_SEPARATOR;
					}
				} else {
					// Adding file to index, if supported
					if($type=='all' || $type=='files' || $type=='filenames'){
						if($type=='filenames'){
							$index[]=$f;
						} else {
							$index[]=$directory.$f;
						}
					}
				}
			}
		}
	}
	
	// Index is returned
	return $index;
	
}
/**
 * This function counts the amount of files in a folder
 *
 * @param string $directory location of directory in filesystem
 * @return int
 */
function fileCount($directory){
	// Counter for file total
	$count=0;
	
	// Scanning the current directory
	$files=scandir($directory);
	
	// This will loop over all the files if files were found in this directory
	if(!empty($files)){
		foreach($files as $f){
			// As long as the current file is not the current or parent directory
			if($f!='.' && $f!='..'){
				// If this is not a folder
				if(!is_dir($directory.$f)){
					$count++;
				}
			}
		}
	}
	
	// Total count is returned
	return $count;
	
}
/**
 * This function moves files from source folder to target folder.
 *
 * @param resource $ftp FTP connection resource
 * @param string $from source folder
 * @param string $to target folder
 * @return array as log
 */
function ftpFileMover($ftp,$from,$to){
	// Log will be stored in this array
	$log=array();
	// Getting list of files to move
	$files=ftp_nlist($ftp,$from);
	
	// If files exist that have to be moved
	if(!empty($files)){
		// Getting information about the target folder
		$target=ftp_nlist($ftp,$to);
		// Processing each file individually
		foreach($files as $f){
			// As long as the file is not a current or parent directory
			if($f!='.' && $f!='..'){
				// FTP does not have an option to check for directory, so system assumes it based on file size
				if(ftp_size($ftp,$from.$f)>=0){
					// FTP moves the file through rename function
					if(ftp_rename($ftp,$from.$f,$to.$f)){
						// General permissions assigned to file
						ftp_chmod($ftp,0644,$to.$f);
						$log[]='SUCCESS '.$to.$f;
					} else {
						$log[]='FAILED '.$to.$f;
					}
				} else {
					// Checking if the file does not already exist in the folder
					if(!in_array($f,$target)){
						// Creating a new folder
						if(ftp_mkdir($ftp,$to.$f.'/')){
							// General permissions assigned to folder
							ftp_chmod($ftp,0755,$to.$f.'/');
							$log[]='SUCCESS '.$to.$f.'/';
						} else {
							$log[]='FAILED '.$to.$f.'/';
						}
					} else {
						$log[]='UNCHANGED '.$to.$f.'/';
					}
					// Subfolder will be parsed separately
					$log=array_merge($log,ftpFileMover($ftp,$from.$f.'/',$to.$f.'/'));
				}
			}
		}
	}
	
	// Log is returned
	return $log;
}
/**
 * This function creates a *.zip archive of all the core files (everything except /filesystem/)
 *
 * @param string $source root folder to backup
 * @param string $archive archive location and filename for backup
 * @param boolean $filesystemBackup whether filesystem folder will also be backed up
 * @return boolean
 */
function systemBackup($source,$archive,$filesystemBackup=false){
	// This is to find absolute path of source directory
	$source=realpath($source).DIRECTORY_SEPARATOR;
	
	// Default framework file system
	$files=array(
		'controllers',
		'engine',
		'filesystem',
		'models',
		'overrides',
		'resources',
		'tools',
		'views',
		'.htaccess',
		'.version',
		'config.ini',
		'favicon.ico',
		'index.php',
		'license.txt',
		'nginx.conf',
		'readme.txt',
	);
	
	// This returns all absolute paths of all files in $source directory
	$files=fileIndex($source,'files',true,$files);
	
	// If files exist
	if(!empty($files)){
		// Creating Zip archive
		$zip=new ZipArchive;
		if($zip->open($archive,ZipArchive::CREATE)){
			// Archive comment notes the creation date and time
			$zip->setArchiveComment('Wave Framework backup created at '.date('d.m.Y H:i:s').' by script run at '.$_SERVER['REQUEST_URI']);
			// Each file is added to archive
			foreach($files as $f){
				if(is_readable($f)){
					//This is the path it will be stored as in archive
					$archivePath=str_replace($source,'',$f);
					// Checking for directory filtering
					$dir=explode(DIRECTORY_SEPARATOR,$archivePath);
					// Backup ignores filesystem scripts entirely if it is not enabled
					if($filesystemBackup || $dir[0]!='filesystem'){
						// Each file is added individually to archive
						if(!$zip->addFile($f,$archivePath)){
							// Error is thrown when one file cannot be added
							trigger_error('Failed to archive '.$f,E_USER_ERROR);
							// Archive is closed and function returns false
							$zip->close();
							// Removing incomplete archive
							unlink($archive);
							// Archive creation failed
							return false;
						}
					}
				}
			}
			// Closing the archive
			$zip->close();
			// Processing complete
			return true;
		} else {
			// Script returns false since archive was not created
			return false;
		}
	} else {
		// Script returns false since archive was not created
		return false;
	}
	
}
/**
 * This returns true IP based on user agent string
 *
 * @param boolean|array $trustedProxies array of trusted proxies
 * @return string
 */
function getTrueIP($trustedProxies=false){
	// Default for trusted proxies, * means all are trusted
	if(!$trustedProxies){
		$trustedProxies=array('*');
	}
	// IP may be forwarded (such as when website is used through a proxy), this can check for such an occasion
	if(isset($_SERVER['HTTP_CLIENT_IP']) && (in_array('*',$trustedProxies) || in_array($_SERVER['REMOTE_ADDR'],$trustedProxies))){
		$tmp=explode(',',$_SERVER['HTTP_CLIENT_IP']);
		$ip=$tmp[0];
	} elseif(isset($_SERVER['HTTP_X_FORWARDED_FOR']) && (in_array('*',$trustedProxies) || in_array($_SERVER['REMOTE_ADDR'],$trustedProxies))){
		$tmp=explode(',',$_SERVER['HTTP_X_FORWARDED_FOR']);
		$ip=$tmp[0];
	} else {
		$ip=$_SERVER['REMOTE_ADDR'];
	}
	
	// Returning the IP address
	return $ip;
	
}
/**
 * Purpose of this function is to convert an INI setting for things like 
 * memory to a value in bytes. This function originates from PHP documentation.
 *
 * @param $value
 * @return integer
 */
function iniSettingToBytes($value) {
    $value=trim($value);
    $unit=strtolower($value[strlen($value)-1]);
    switch($unit) {
        // The 'G' modifier is available since PHP 5.1.0
        case 'g':
            $value *= 1024;
        case 'm':
            $value *= 1024;
        case 'k':
            $value *= 1024;
    }
    return $value;
}
/**
 * This just prints out a warning about using default HTTP authentication variables.
 */
function passwordNotification($password,$echo=true){
	if($password=='hellowave'){
		$content='<div style="font:10px Verdana;color:#c00000;border:2px solid #c00000;border-radius:4px;margin:10px auto;padding:10px;background-color:#ffffff;width:500px;"><b>Hi, developer!</b><br/><br/>Please note that you are using default HTTP authentication password for your developer tools. It is highly recommended that you change your http-authentication-password setting in \'config.ini\' file to another password in order to protect your developer tools from potential malicious attacks. This message will display until password has been changed.</div>';
		if($echo){
			echo $content;
		} else {
			return $content;
		}
	}
}
?> 
  |