<?php

/**
 * Plugin IO Functions
 *
 * This file contains functions related to reading and writing to the file
 * system.
 *
 * @link /lib/wfu_io.php
 *
 * @package Iptanus File Upload Plugin
 * @subpackage Core Components
 * @since 2.1.2
 */
 
if ( ! defined( 'ABSPATH' ) ) exit;

/**
 * Create FTP Directory Recursively.
 *
 * This function creates an FTP directory recursively (including
 * subdirectories).
 *
 * @since 3.10.0
 *
 * @redeclarable
 *
 * @param stream $conn_id The FTP connection ID.
 * @param string $basepath The parent path of the directory to be created.
 * @param string $path The directory to be created.
 */
function wfu_mk_dir_deep($conn_id, $basepath, $path) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	@ftp_chdir($conn_id, $basepath);
	$parts = explode('/', $path);
	foreach ( $parts as $part ) {
		if( !@ftp_chdir($conn_id, $part) ) {
			ftp_mkdir($conn_id, $part);
			ftp_chdir($conn_id, $part);
			ftp_chmod($conn_id, 493, $part);
		}
	}
}

/**
 * Check If Path Is Directory.
 *
 * This function checks whether a path is a valid directory.
 *
 * @since 3.9.1
 *
 * @redeclarable
 *
 * @param string $path The path to check.
 * @param string $ftpdata FTP credentials in case of FTP method.
 *
 * @return bool True if the path is directory, false otherwise.
 */
function wfu_is_dir($path, $ftpdata) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$result = false;
	//check whether this is an sftp dir
	if ( substr($path, 0, 7) == "sftp://" ) {
		$ftpinfo = wfu_decode_ftpinfo($ftpdata);
		if ( !$ftpinfo["error"] ) {
			$data = $ftpinfo["data"];
			//extract relative FTP path
			$ftp_port = $data["port"];
			if ( $ftp_port == "" ) $ftp_port = "22";
			$flat_host = preg_replace("/^(.*\.)?([^.]*\..*)$/", "$2", $data["ftpdomain"].":".$ftp_port);
			$pos1 = strpos($path, $flat_host);
			if ( $pos1 ) {
				$path = substr($path, $pos1 + strlen($flat_host));
				{
					$conn = ssh2_connect($data["ftpdomain"], $ftp_port);
					if ( $conn && @ssh2_auth_password($conn, $data["username"], $data["password"]) ) {
						$sftp = @ssh2_sftp($conn);
						if ( $sftp ) {
							$result = is_dir('ssh2.sftp://'.intval($sftp).$path);
						}
					}
				}
			}
		}
	}
	elseif ( substr($path, 0, 7) == "ftps://" ) {
		$ftpinfo = wfu_decode_ftpinfo($ftpdata);
		if ( !$ftpinfo["error"] ) {
			$data = $ftpinfo["data"];
			//extract relative FTP path
			$ftp_port = $data["port"];
			$flat_host = preg_replace("/^(.*\.)?([^.]*\..*)$/", "$2", $data["ftpdomain"].":".$ftp_port);
			$pos1 = strpos($path, $flat_host);
			if ( $pos1 ) {
				$path = substr($path, $pos1 + strlen($flat_host));
				$conn = @ftp_ssl_connect($data["ftpdomain"], $ftp_port);
				if ( $conn && ftp_login($conn, $data["username"], $data["password"]) ) {
					$original_dir = ftp_pwd($conn);
					if ( @ftp_chdir($conn, $path) ) {
						ftp_chdir($conn, $original_dir);
						$result = true;
					}
					else $result = false;
				}
			}
		}
	}
	else $result = is_dir($path);
	
	return $result;
}

/**
 * Create Directory.
 *
 * This function creates a directory.
 *
 * @since 2.1.2
 *
 * @redeclarable
 *
 * @param string $path The path of the directory to create.
 * @param string $method File upload method, 'normal' or 'ftp'.
 * @param string $ftpdata FTP credentials in case of FTP method.
 * @param bool $passive Optional. True if passive mode is used, false otherwise.
 *
 * @return string Empty string if the directory was created successfully, or an
 *         error message if it failed.
 */
function wfu_create_directory($path, $method, $ftpdata, $passive = false) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret_message = "";
	if ( $method == "" || $method == "normal" ) {
		mkdir($path, 0777, true);
	}
	else if ( $method == "ftp" && $ftpdata != "" ) {
		$ftpinfo = wfu_decode_ftpinfo($ftpdata);
		if ( !$ftpinfo["error"] ) {
			$data = $ftpinfo["data"];
			//extract relative FTP path
			$ftp_port = $data["port"];
			if ( $data["sftp"] && $ftp_port == "" ) $ftp_port = "22";
			$flat_host = preg_replace("/^(.*\.)?([^.]*\..*)$/", "$2", $data["ftpdomain"].( $ftp_port != "" ? ":".$ftp_port : "" ));
			$pos1 = strpos($path, $flat_host);
			if ( $pos1 ) {
				$path = substr($path, $pos1 + strlen($flat_host));
				if ( $data["sftp"] ) {
					wfu_create_dir_deep_sftp($data["ftpdomain"], $ftp_port, $data["username"], $data["password"], $path, $passive);
				}
				elseif ( $data["ftps"] ) {
					wfu_create_dir_deep_ftps($data["ftpdomain"], $ftp_port, $data["username"], $data["password"], $path, $passive);
				}
				else {
					if ( $ftp_port != "" ) $conn_id = ftp_connect($data["ftpdomain"], $ftp_port);
					else $conn_id = ftp_connect($data["ftpdomain"]);
					$login_result = ftp_login($conn_id, $data["username"], $data["password"]);
					if ( $conn_id && $login_result ) {
						wfu_mk_dir_deep($conn_id, '/', $path);
					}
					else {
						$ret_message = WFU_ERROR_ADMIN_FTPINFO_INVALID;
					}
					ftp_quit($conn_id);
				}
			}
			else {
				$ret_message = WFU_ERROR_ADMIN_FTPFILE_RESOLVE;
			}
		}
		else {
			$ret_message = WFU_ERROR_ADMIN_FTPINFO_EXTRACT;
		}
	}
	else {
		$ret_message = WFU_ERROR_ADMIN_FTPINFO_INVALID;
	}
	return $ret_message;
}

/**
 * Store the Uploaded File.
 *
 * This function stores the uploaded file that was saved in a temporary location
 * to its final destination. In case of a chunked upload, then the source does
 * not contain the whole file, but only a part of it. The chunk is stored in the
 * partial file in the correct position.
 *
 * @since 2.1.2
 *
 * @redeclarable
 *
 * @param string $source The temporary source path of the uploaded file.
 * @param string $target The final path of the uploaded file.
 * @param string $method File upload method, 'normal', 'ftp' or 'chunked'. In
 *        case of 'chunked' method it contains information about the chunks.
 * @param string $ftpdata FTP credentials in case of FTP method.
 * @param string $passive 'true' if FTP passive mode will be used.
 * @param string $fileperms File permissions of the stored file (FTP method).
 *
 * @return array {
 *         Store result info.
 *
 *         @type bool $uploaded True if the file was stored successfully.
 *         @type string $admin_message An admin error message on failure.
 * }
 */
function wfu_upload_file($source, $target, $method, $ftpdata, $passive, $fileperms, $move_uploaded_file = true) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret_array = array();
	$ret_array["uploaded"] = false;
	$ret_array["admin_message"] = "";
	$ret_message = "";
	//$target_perms = substr(sprintf('%o', fileperms(dirname($target))), -4);
	//$target_perms = octdec($target_perms);
	//$target_perms = (int)$target_perms;
	if ( $method == "" || $method == "normal" ) {
		$ret_array["uploaded"] = ( $move_uploaded_file ? move_uploaded_file($source, $target) : rename($source, $target) );
		if ( !$ret_array["uploaded"] && !is_writable(dirname($target)) ) {
			$ret_message = WFU_ERROR_ADMIN_DIR_PERMISSION;
		}
	}
	elseif ( $method == "ftp" &&  $ftpdata != "" ) {
		$result = false;
		$ftpinfo = wfu_decode_ftpinfo($ftpdata);
		if ( !$ftpinfo["error"] ) {
			$data = $ftpinfo["data"];
			//extract relative FTP path
			$ftp_port = $data["port"];
			if ( $data["sftp"] && $ftp_port == "" ) $ftp_port = "22";
			$flat_host = preg_replace("/^(.*\.)?([^.]*\..*)$/", "$2", $data["ftpdomain"].( $ftp_port != "" ? ":".$ftp_port : "" ));
			$pos1 = strpos($target, $flat_host);
			if ( $pos1 ) {
				$target = substr($target, $pos1 + strlen($flat_host));
				if ( $data["sftp"] ) {
					$ret_message = wfu_upload_file_sftp($data["ftpdomain"], $ftp_port, $data["username"], $data["password"], $source, $target, $fileperms);
					$ret_array["uploaded"] = ( $ret_message == "" );
					wfu_unlink($source, "wfu_upload_file:1");
				}
				elseif ( $data["ftps"] ) {
					$ret_message = wfu_upload_file_ftps($data["ftpdomain"], $ftp_port, $data["username"], $data["password"], $source, $target, $fileperms, ( $passive === "true" ));
					$ret_array["uploaded"] = ( $ret_message == "" );
					wfu_unlink($source, "wfu_upload_file:2");
				}
				else {
					if ( $ftp_port != "" ) $conn_id = ftp_connect($data["ftpdomain"], $ftp_port);
					else $conn_id = ftp_connect($data["ftpdomain"]);
					$login_result = ftp_login($conn_id, $data["username"], $data["password"]);
					if ( $conn_id && $login_result ) {
						if ( $passive == "true" ) ftp_pasv($conn_id, true);
//						$temp_fname = tempnam(dirname($target), "tmp");
//						move_uploaded_file($source, $temp_fname);
//						ftp_chmod($conn_id, 0755, dirname($target));
						$ret_array["uploaded"] = ftp_put($conn_id, $target, $source, FTP_BINARY);
						//apply user-defined permissions to file
						$fileperms = trim($fileperms);
						if ( strlen($fileperms) == 4 && sprintf("%04o", octdec($fileperms)) == $fileperms ) {
							$fileperms = octdec($fileperms);
							$fileperms = (int)$fileperms;
							ftp_chmod($conn_id, $fileperms, $target);
						}
//						ftp_chmod($conn_id, 0755, $target);
//						ftp_chmod($conn_id, $target_perms, dirname($target));
						wfu_unlink($source, "wfu_upload_file:3");
						if ( !$ret_array["uploaded"] ) {
							$ret_message = WFU_ERROR_ADMIN_DIR_PERMISSION;
						}
					}
					else {
						$ret_message = WFU_ERROR_ADMIN_FTPINFO_INVALID;
					}
					ftp_quit($conn_id);
				}
			}
			else {
				$ret_message = WFU_ERROR_ADMIN_FTPFILE_RESOLVE;
			}
		}
		else {
			$ret_message = WFU_ERROR_ADMIN_FTPINFO_EXTRACT.$ftpdata;
		}
	}		
	else {
		$ret_message = WFU_ERROR_ADMIN_FTPINFO_INVALID;
	}

	$ret_array["admin_message"] = $ret_message;
	return $ret_array;
}

/**
 * Store the Uploaded File in sFTP.
 *
 * This function stores the uploaded file that was saved in a temporary location
 * to its final sFTP destination.
 *
 * @since 4.0.0
 *
 * @redeclarable
 *
 * @param string $ftp_host The sFTP host.
 * @param string $ftp_port The sFTP port.
 * @param string $ftp_username Username for sFTP authentication.
 * @param string $ftp_password Password for sFTP authentication.
 * @param string $source The temporary source path of the uploaded file.
 * @param string $target The final path of the uploaded file.
 * @param string $fileperms File permissions of the stored file (FTP method).
 * @param bool $passive Optional. True if passive mode is used, false otherwise.
 *
 * @return string Empty string if the file was stored successfully, or an error
 *         message if it failed.
 */
function wfu_upload_file_sftp($ftp_host, $ftp_port, $ftp_username, $ftp_password, $source, $target, $fileperms, $passive = false) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret_message = "";
	{
		$conn = @ssh2_connect($ftp_host, $ftp_port);
		if ( !$conn ) $ret_message = WFU_ERROR_ADMIN_FTPHOST_FAIL;
		else {
			if ( !@ssh2_auth_password($conn, $ftp_username, $ftp_password) ) $ret_message = WFU_ERROR_ADMIN_FTPLOGIN_FAIL;
			else {
				$sftp = @ssh2_sftp($conn);
				if ( !$sftp ) $ret_message = WFU_ERROR_ADMIN_SFTPINIT_FAIL;
				else {
					$f = @fopen("ssh2.sftp://".intval($sftp)."$target", 'w');
					if ( !$f ) $ret_message = WFU_ERROR_ADMIN_FTPFILE_RESOLVE;
					else {
						$contents = @file_get_contents($source);
						if ( $contents === false ) $ret_message = WFU_ERROR_ADMIN_FTPSOURCE_FAIL;
						else {
							if ( @fwrite($f, $contents) === false ) $ret_message = WFU_ERROR_ADMIN_FTPTRANSFER_FAIL;
							//apply user-defined permissions to file
							$fileperms = trim($fileperms);
							if ( strlen($fileperms) == 4 && sprintf("%04o", octdec($fileperms)) == $fileperms ) {
								$fileperms = octdec($fileperms);
								$fileperms = (int)$fileperms;
								ssh2_sftp_chmod($sftp, $target, $fileperms);
							}
						}
						@fclose($f);
					}
				}
			}
		}
	}
	
	return $ret_message;
}

/**
 * Store the Uploaded File in FTPS.
 *
 * This function stores the uploaded file that was saved in a temporary location
 * to its final FTPS destination.
 *
 * @since 5.1.5
 *
 * @redeclarable
 *
 * @param string $ftp_host The FTPS host.
 * @param string $ftp_port The FTPS port.
 * @param string $ftp_username Username for FTPS authentication.
 * @param string $ftp_password Password for FTPS authentication.
 * @param string $source The temporary source path of the uploaded file.
 * @param string $target The final path of the uploaded file.
 * @param string $fileperms File permissions of the stored file (FTP method).
 * @param bool $passive Optional. True if passive mode is used, false otherwise.
 *
 * @return string Empty string if the file was stored successfully, or an error
 *         message if it failed.
 */
function wfu_upload_file_ftps($ftp_host, $ftp_port, $ftp_username, $ftp_password, $source, $target, $fileperms, $passive = false) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret_message = "";
	$conn = @ftp_ssl_connect($ftp_host, $ftp_port, 120);
	if ( !$conn ) $ret_message = WFU_ERROR_ADMIN_FTPHOST_FAIL;
	else {
		if ( !@ftp_login($conn, $ftp_username, $ftp_password) ) $ret_message = WFU_ERROR_ADMIN_FTPLOGIN_FAIL;
		else {
			if ( $passive ) {
				if ( WFU_VAR("WFU_FTPS_USEPASVADDRESS") === "false" )
					ftp_set_option($conn, FTP_USEPASVADDRESS, false);
				ftp_pasv($conn, true);
			}
			if ( !ftp_put($conn, $target, $source, FTP_BINARY) ) $ret_message = WFU_ERROR_ADMIN_FTPTRANSFER_FAIL;
			else {
				$fileperms = trim($fileperms);
				if ( strlen($fileperms) == 4 && sprintf("%04o", octdec($fileperms)) == $fileperms ) {
					$fileperms = octdec($fileperms);
					$fileperms = (int)$fileperms;
					ftp_chmod($conn_id, $fileperms, $target);
				}
			}
		}
		ftp_close($conn);
	}
	
	return $ret_message;
}

/**
 * Create sFTP Directory Recursively.
 *
 * This function creates an sFTP directory recursively (including
 * subdirectories).
 *
 * @since 4.0.0
 *
 * @redeclarable
 *
 * @param string $ftp_host The sFTP host.
 * @param string $ftp_port The sFTP port.
 * @param string $ftp_username Username for sFTP authentication.
 * @param string $ftp_password Password for sFTP authentication.
 * @param string $path The path of the directory to create.
 * @param bool $passive Optional. True if passive mode is used, false otherwise.
 *
 * @return string Empty string if the directory was created successfully, or an
 *         error message if it failed.
 */
function wfu_create_dir_deep_sftp($ftp_host, $ftp_port, $ftp_username, $ftp_password, $path, $passive = false) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret_message = "";
	{
		$conn = @ssh2_connect($ftp_host, $ftp_port);
		if ( !$conn ) $ret_message = WFU_ERROR_ADMIN_FTPHOST_FAIL;
		else {
			if ( !@ssh2_auth_password($conn, $ftp_username, $ftp_password) ) $ret_message = WFU_ERROR_ADMIN_FTPLOGIN_FAIL;
			else {
				$sftp = @ssh2_sftp($conn);
				if ( !$sftp ) $ret_message = WFU_ERROR_ADMIN_SFTPINIT_FAIL;
				else {
					ssh2_sftp_mkdir($sftp, $path, 493, true );
				}
			}
		}
	}
	
	return $ret_message;
}

/**
 * Create FTPS Directory Recursively.
 *
 * This function creates an FTPS directory recursively (including
 * subdirectories).
 *
 * @since 5.1.5
 *
 * @redeclarable
 *
 * @param string $ftp_host The sFTP host.
 * @param string $ftp_port The sFTP port.
 * @param string $ftp_username Username for sFTP authentication.
 * @param string $ftp_password Password for sFTP authentication.
 * @param string $path The path of the directory to create.
 * @param bool $passive Optional. True if passive mode is used, false otherwise.
 *
 * @return string Empty string if the directory was created successfully, or an
 *         error message if it failed.
 */
function wfu_create_dir_deep_ftps($ftp_host, $ftp_port, $ftp_username, $ftp_password, $path, $passive = false) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret_message = "";
	$conn = @ftp_ssl_connect($ftp_host, $ftp_port, 120);
	if ( !$conn ) $ret_message = WFU_ERROR_ADMIN_FTPHOST_FAIL;
	else {
		if ( !@ftp_login($conn, $ftp_username, $ftp_password) ) $ret_message = WFU_ERROR_ADMIN_FTPLOGIN_FAIL;
		else {
			if ( $passive ) {
				if ( WFU_VAR("WFU_FTPS_USEPASVADDRESS") === "false" )
					ftp_set_option($conn, FTP_USEPASVADDRESS, false);
				ftp_pasv($conn, true);
			}
			$dirs = explode('/', trim($path, '/'));
			$dirpath = "";
			foreach ( $dirs as $dir ) {
				$dirpath .= '/' . $dir;
				if ( @ftp_chdir($conn, $dirpath) ) {
					ftp_chdir($conn, '/');
					continue;
				}
				if ( !ftp_mkdir($conn, $dirpath) ) {
					break;
				}
			}
		}
		ftp_close($conn);
	}
	
	
	{
		$conn = @ssh2_connect($ftp_host, $ftp_port);
		if ( !$conn ) $ret_message = WFU_ERROR_ADMIN_FTPHOST_FAIL;
		else {
			if ( !@ssh2_auth_password($conn, $ftp_username, $ftp_password) ) $ret_message = WFU_ERROR_ADMIN_FTPLOGIN_FAIL;
			else {
				$sftp = @ssh2_sftp($conn);
				if ( !$sftp ) $ret_message = WFU_ERROR_ADMIN_SFTPINIT_FAIL;
				else {
					ssh2_sftp_mkdir($sftp, $path, 493, true );
				}
			}
		}
	}
	
	return $ret_message;
}

/**
 * Check If SFTP File Exists.
 *
 * This function checks whether a file stored in an SFTP location exists.
 *
 * @since 4.15.0
 *
 * @redeclarable
 *
 * @param string $filepath The path of the file.
 *
 * @return boolean True if the file is a valid SFTP path and exists, false
 *         otherwise.
 */
function wfu_file_exists_sftp($filepath) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret = false;
	$ftpinfo = wfu_decode_ftpurl($filepath);
	if ( $ftpinfo["error"] ) return $ret;
	$data = $ftpinfo["data"];
	{
		$conn = @ssh2_connect($data["ftpdomain"], $data["port"]);
		if ( $conn && @ssh2_auth_password($conn, $data["username"], $data["password"]) ) {
			$sftp = @ssh2_sftp($conn);
			$ret = ( $sftp && @file_exists("ssh2.sftp://".intval($sftp).$data["filepath"]) );
		}
	}
	
	return $ret;
}

/**
 * Check If FTPS File Exists.
 *
 * This function checks whether a file stored in an FTPS location exists.
 *
 * @since 5.1.5
 *
 * @redeclarable
 *
 * @param string $filepath The path of the file.
 *
 * @return boolean True if the file is a valid FTPS path and exists, false
 *         otherwise.
 */
function wfu_file_exists_ftps($filepath) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret = false;
	$ftpinfo = wfu_decode_ftpurl($filepath);
	if ( $ftpinfo["error"] ) return $ret;
	$data = $ftpinfo["data"];
	$conn = @ftp_ssl_connect($data["ftpdomain"], $data["port"]);
	if ( $conn && ftp_login($conn, $data["username"], $data["password"]) ) {
		wfu_check_set_ftps_passive($conn);
		$filepath = $data["filepath"];
		$dirpath = wfu_basedir($filepath);
		$filename = wfu_basename($filepath);
		$filelist = @ftp_nlist($conn, $dirpath);
		$ret = ( $filelist !== false && in_array( $filepath, $filelist ) );
	}
	if ( $conn ) ftp_close($conn);
	
	return $ret;
}

/**
 * Get SFTP File Info.
 *
 * This function returns stat information for files stored in an SFTP location.
 *
 * @since 4.15.0
 *
 * @redeclarable
 *
 * @param string $filepath The path of the file.
 *
 * @return array|false The stat array or false on error.
 */
function wfu_stat_sftp($filepath) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret = false;
	$ftpinfo = wfu_decode_ftpurl($filepath);
	if ( $ftpinfo["error"] ) return $ret;
	$data = $ftpinfo["data"];
	{
		$conn = @ssh2_connect($data["ftpdomain"], $data["port"]);
		if ( $conn && @ssh2_auth_password($conn, $data["username"], $data["password"]) ) {
			$sftp = @ssh2_sftp($conn);
			if ( $sftp ) $ret = ssh2_sftp_stat($sftp, $data["filepath"]);
		}
	}
	
	return $ret;
}

/**
 * Get FTPS File Info.
 *
 * This function returns stat information for files stored in an FTPS location.
 *
 * @since 5.1.5
 *
 * @redeclarable
 *
 * @param string $filepath The path of the file.
 *
 * @return array|false The stat array or false on error.
 */
function wfu_stat_ftps($filepath) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret = false;
	$ftpinfo = wfu_decode_ftpurl($filepath);
	if ( $ftpinfo["error"] ) return $ret;
	$data = $ftpinfo["data"];
	$conn = @ftp_ssl_connect($data["ftpdomain"], $data["port"]);
	if ( $conn && ftp_login($conn, $data["username"], $data["password"]) ) {
		wfu_check_set_ftps_passive($conn);
		$size = ftp_size($conn, $data['filepath']);
		$mtime = ftp_mdtm($conn, $data['filepath']);
		$ret = array( "mtime" => $mtime, "size" => $size );
	}
	if ( $conn ) ftp_close($conn);
	
	return $ret;
}

/**
 * Get SFTP File Size.
 *
 * This function returns file size for files stored in an SFTP location.
 *
 * @since 4.15.0
 *
 * @redeclarable
 *
 * @param string $filepath The path of the file.
 *
 * @return int|false The file size or false on error.
 */
function wfu_filesize_sftp($filepath) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret = false;
	$ftpinfo = wfu_decode_ftpurl($filepath);
	if ( $ftpinfo["error"] ) return $ret;
	$data = $ftpinfo["data"];
	{
		$conn = @ssh2_connect($data["ftpdomain"], $data["port"]);
		if ( $conn && @ssh2_auth_password($conn, $data["username"], $data["password"]) ) {
			$sftp = @ssh2_sftp($conn);
			if ( $sftp ) $ret = @filesize("ssh2.sftp://".intval($sftp).$data["filepath"]);
		}
	}
	
	return $ret;
}

/**
 * Get FTPS File Size.
 *
 * This function returns file size for files stored in an FTPS location.
 *
 * @since 5.1.5
 *
 * @redeclarable
 *
 * @param string $filepath The path of the file.
 *
 * @return int|false The file size or false on error.
 */
function wfu_filesize_ftps($filepath) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret = false;
	$ftpinfo = wfu_decode_ftpurl($filepath);
	if ( $ftpinfo["error"] ) return $ret;
	$data = $ftpinfo["data"];
	$conn = @ftp_ssl_connect($data["ftpdomain"], $data["port"]);
	if ( $conn && ftp_login($conn, $data["username"], $data["password"]) ) {
		wfu_check_set_ftps_passive($conn);
		$ret = ftp_size($conn, $data['filepath']);
	}
	if ( $conn ) ftp_close($conn);
	
	return $ret;
}

/**
 * Get SFTP File Stream Handle.
 *
 * This function returns a file stream handle for files stored in an SFTP
 * location.
 *
 * @since 4.15.0
 *
 * @redeclarable
 *
 * @param string $filepath The path of the file.
 * @param string $mode The file access mode.
 *
 * @return resource|false The file stream handle or false on error.
 */
function wfu_fopen_sftp($filepath, $mode) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret = false;
	$ftpinfo = wfu_decode_ftpurl($filepath);
	if ( $ftpinfo["error"] ) return $ret;
	$data = $ftpinfo["data"];
	{
		$conn = @ssh2_connect($data["ftpdomain"], $data["port"]);
		if ( $conn && @ssh2_auth_password($conn, $data["username"], $data["password"]) ) {
			$sftp = @ssh2_sftp($conn);
			if ( $sftp ) {
				//$ret = @fopen("ssh2.sftp://".intval($sftp).$data["filepath"], $mode);
				$contents = @file_get_contents("ssh2.sftp://".intval($sftp).$data["filepath"]);
				$stream = fopen('php://memory', 'r+');
				fwrite($stream, $contents);
				rewind($stream);
				$ret = $stream;
			}
		}
	}
	
	return $ret;
}

/**
 * Get FTPS File Stream Handle.
 *
 * This function returns a file stream handle for files stored in an FTPS
 * location.
 *
 * @since 5.1.5
 *
 * @redeclarable
 *
 * @param string $filepath The path of the file.
 * @param string $mode The file access mode.
 *
 * @return resource|false The file stream handle or false on error.
 */
function wfu_fopen_ftps($filepath, $mode) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret = false;
	$ftpinfo = wfu_decode_ftpurl($filepath);
	if ( $ftpinfo["error"] ) return $ret;
	$data = $ftpinfo["data"];
	$conn = @ftp_ssl_connect($data["ftpdomain"], $data["port"]);
	if ( $conn && ftp_login($conn, $data["username"], $data["password"]) ) {
		wfu_check_set_ftps_passive($conn);
		$stream = fopen('php://memory', 'r+');
		ftp_fget($conn, $stream, $data['filepath'], FTP_BINARY, 0);
		rewind($stream);
		$ret = $stream;
	}
	if ( $conn ) ftp_close($conn);

	return $ret;
}

/**
 * Get SFTP File Contents.
 *
 * This function returns the file contents for files stored in an SFTP location.
 *
 * @since 4.15.0
 *
 * @redeclarable
 *
 * @param string $filepath The path of the file.
 *
 * @return string|false The file contents as string or false on error.
 */
function wfu_file_get_contents_sftp($filepath) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret = false;
	$ftpinfo = wfu_decode_ftpurl($filepath);
	if ( $ftpinfo["error"] ) return $ret;
	$data = $ftpinfo["data"];
	{
		$conn = @ssh2_connect($data["ftpdomain"], $data["port"]);
		if ( $conn && @ssh2_auth_password($conn, $data["username"], $data["password"]) ) {
			$sftp = @ssh2_sftp($conn);
			if ( $sftp ) $ret = @file_get_contents("ssh2.sftp://".intval($sftp).$data["filepath"]);
		}
	}
	
	return $ret;
}

/**
 * Get FTPS File Contents.
 *
 * This function returns the file contents for files stored in an FTPS location.
 *
 * @since 5.1.5
 *
 * @redeclarable
 *
 * @param string $filepath The path of the file.
 *
 * @return string|false The file contents as string or false on error.
 */
function wfu_file_get_contents_ftps($filepath) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret = false;
	$stream = wfu_fopen_ftps($filepath, 'r');
	if ( $stream !== false ) {
		$ret = @stream_get_contents($stream);
		fclose($stream);
	}
	
	return $ret;
}

/**
 * Get MD5 of sFTP File.
 *
 * This function returns the md5 string of a file stored in an SFTP location.
 *
 * @since 4.15.0
 *
 * @redeclarable
 *
 * @param string $filepath The path of the file.
 *
 * @return string|false The md5 string of the file or false on error.
 */
function wfu_md5_file_sftp($filepath) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret = false;
	$ftpinfo = wfu_decode_ftpurl($filepath);
	if ( $ftpinfo["error"] ) return $ret;
	$data = $ftpinfo["data"];
	{
		$conn = @ssh2_connect($data["ftpdomain"], $data["port"]);
		if ( $conn && @ssh2_auth_password($conn, $data["username"], $data["password"]) ) {
			$sftp = @ssh2_sftp($conn);
			if ( $sftp ) {
				$contents = @file_get_contents("ssh2.sftp://".intval($sftp).$data["filepath"], $mode);
				$ret = md5($contents);
			}
		}
	}
	
	return $ret;
}

/**
 * Get MD5 of FTPS File.
 *
 * This function returns the md5 string of a file stored in an FTPS location.
 *
 * @since 5.1.5
 *
 * @redeclarable
 *
 * @param string $filepath The path of the file.
 *
 * @return string|false The md5 string of the file or false on error.
 */
function wfu_md5_file_ftps($filepath) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret = false;
	$contents = wfu_file_get_contents_ftps($filepath);
	if ( $contents !== false ) {
		$ret = md5($contents);
	}
	
	return $ret;
}

/**
 * Delete an sFTP File.
 *
 * This function deletes a file stored in an SFTP location.
 *
 * @since 4.15.0
 *
 * @redeclarable
 *
 * @param string $filepath The path of the file.
 *
 * @return boolean True on success, false on error.
 */
function wfu_unlink_sftp($filepath) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret = false;
	$ftpinfo = wfu_decode_ftpurl($filepath);
	if ( $ftpinfo["error"] ) return $ret;
	$data = $ftpinfo["data"];
	{
		$conn = @ssh2_connect($data["ftpdomain"], $data["port"]);
		if ( $conn && @ssh2_auth_password($conn, $data["username"], $data["password"]) ) {
			$sftp = @ssh2_sftp($conn);
			if ( $sftp ) $ret = @unlink("ssh2.sftp://".intval($sftp).$data["filepath"]);
		}
	}
	
	return $ret;
}

/**
 * Delete an FTPS File.
 *
 * This function deletes a file stored in an FTPS location.
 *
 * @since 5.1.5
 *
 * @redeclarable
 *
 * @param string $filepath The path of the file.
 *
 * @return boolean True on success, false on error.
 */
function wfu_unlink_ftps($filepath) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	$ret = false;
	$ftpinfo = wfu_decode_ftpurl($filepath);
	if ( $ftpinfo["error"] ) return $ret;
	$data = $ftpinfo["data"];
	$conn = @ftp_ssl_connect($data["ftpdomain"], $data["port"]);
	if ( $conn && ftp_login($conn, $data["username"], $data["password"]) ) {
		wfu_check_set_ftps_passive($conn);
		$ret = @ftp_delete($conn, $data['filepath']);
	}
	if ( $conn ) ftp_close($conn);

	return $ret;
}

/**
 * Check and Set FTPS Passive Mode.
 *
 * It checks the plugin advanced variables to determine whether passive mode
 * must be used for FTPS operations.
 *
 * @since 5.1.5
 *
 * @redeclarable
 *
 * @param resource $conn The connection resource.
 */
function wfu_check_set_ftps_passive($conn) {
	$a = func_get_args(); $a = WFU_FUNCTION_HOOK(__FUNCTION__, $a, $out); if (isset($out['vars'])) foreach($out['vars'] as $p => $v) $$p = $v; switch($a) { case 'R': return $out['output']; break; case 'D': die($out['output']); }
	if ( WFU_VAR("WFU_FTPS_USEPASSIVE") === "true" ) {
		if ( WFU_VAR("WFU_FTPS_USEPASVADDRESS") === "false" )
			ftp_set_option($conn, FTP_USEPASVADDRESS, false);
		ftp_pasv($conn, true);
	}
}