<?php
defined("ABSPATH") or die("");
/**
 * Utility class used for various task
 *
 * Standard: PSR-2
 * @link http://www.php-fig.org/psr/psr-2
 *
 * @package DUP_PRO
 * @subpackage classes/utilities
 * @copyright (c) 2017, Snapcreek LLC
 * @license	https://opensource.org/licenses/GPL-3.0 GNU Public License
 *
 */

require_once(DUPLICATOR_PRO_PLUGIN_PATH . '/lib/pcrypt/class.pcrypt.php');
require_once(DUPLICATOR_PRO_PLUGIN_PATH . '/classes/class.io.php');
require_once(DUPLICATOR_PRO_PLUGIN_PATH . '/classes/class.constants.php');
require_once(DUPLICATOR_PRO_PLUGIN_PATH . '/classes/entities/class.global.entity.php');
require_once(DUPLICATOR_PRO_PLUGIN_PATH . '/classes/utilities/class.u.multisite.php');

DUP_PRO_Constants::init();

class DUP_PRO_U
{
	/**
     * Is PHP 5.2.9 or better running
     */
    public static $on_php_529_plus;

    /**
     * Is PHP 5.3 or better running
     */
    public static $on_php_53_plus;

    /**
     * Is PHP 5.4 or better running
     */
    public static $on_php_54_plus;

	/**
     * Is PHP 7 or better running
     */
    public static $PHP7_plus;

    /**
     * array of ini disable functions
     *
     * @var array
     */
    private static $iniDisableFuncs = null;

	// Pseudo-constants
    private static $type_format_array;


    public static function init()
    {
        self::$type_format_array = array('boolean' => '%s', 'integer' => '%d', 'double' => '%g', 'string' => '%s');
	    self::$on_php_529_plus = version_compare(PHP_VERSION, '5.2.9') >= 0;
        self::$on_php_53_plus  = version_compare(PHP_VERSION, '5.3.0') >= 0;
        self::$on_php_54_plus  = version_compare(PHP_VERSION, '5.4.0') >= 0;
		self::$PHP7_plus = version_compare(PHP_VERSION, '7.0.0', '>=');
    }

    public static function getArchitectureString()
    {
        $php_int_size = PHP_INT_SIZE;
        
        switch($php_int_size) {
            case 4:
                return DUP_PRO_U::esc_html__('32-bit');
                break;
            case 8:
                return DUP_PRO_U::esc_html__('64-bit');
                break;
            default:
                return DUP_PRO_U::esc_html__('Unknown');
        }
    }
    
    /**
     * return absolute path for the directories that are core directories
     * @return string array
     */
    public static function getWPCoreDirs()
    {
        $wp_core_dirs = array(get_home_path().'wp-admin',get_home_path().'wp-includes');

        //if wp_content is overrided
        $wp_path = get_home_path()."wp-content";
        if(get_home_path().'wp-content' != WP_CONTENT_DIR){
            $wp_path = WP_CONTENT_DIR;
        }
        $wp_path = str_replace("\\", "/", $wp_path);
        
        $wp_core_dirs[] = $wp_path;
        $wp_core_dirs[] = $wp_path.'/plugins';
        $wp_core_dirs[] = $wp_path.'/themes';
          
        return $wp_core_dirs;
    }
    /**
     * return absolute path for the files that are core directories
     * @return string array
     */
    public static function getWPCoreFiles()
    {
        $wp_cored_dirs = array(get_home_path().'wp-config.php');
        return $wp_cored_dirs;
    }

    /**
      * Converts an absolute path to a relative path
      *
      * @param string $from The the path relative to $to
      * @param string $to   The full path of the directory to transform
      *
      * @return string  A string of the result
      */
    public static function getRelativePath($from, $to)
    {
        // some compatibility fixes for Windows paths
        $from = is_dir($from) ? rtrim($from, '\/').'/' : $from;
        $to   = is_dir($to) ? rtrim($to, '\/').'/' : $to;
        $from = str_replace('\\', '/', $from);
        $to   = str_replace('\\', '/', $to);

        $from    = explode('/', $from);
        $to      = explode('/', $to);
        $relPath = $to;

        foreach ($from as $depth => $dir) {
            // find first non-matching dir
            if ($dir === $to[$depth]) {
                // ignore this directory
                array_shift($relPath);
            } else {
                // get number of remaining dirs to $from
                $remaining = count($from) - $depth;
                if ($remaining > 1) {
                    // add traversals up to first matching dir
                    $padLength = (count($relPath) + $remaining - 1) * -1;
                    $relPath   = array_pad($relPath, $padLength, '..');
                    break;
                } else {
                    //$relPath[0] = './' . $relPath[0];
                }
            }
        }
        return implode('/', $relPath);
    }

     /**
      * Gets the percentage of one value to another
      *
      * @param int $val1
      * @param int $val2
      *
      * example:  
      *     $val1 = 100
      *     $val2 = 400
      *     $res  = 25
      *
      * @return int  Returns the results
      */
    public static function percentage($val1, $val2, $precision = 0)
    {
        $division = $val1 / (float) $val2;
        $res = $division * 100;
        $res = round($res, $precision);
        return $res;
    }

    /**
     * Localize and echo the current text with escaping html
     *
     * @param string $text The text to localize
     *
     * @return string Returns the text in its desired language
     */
    public static function esc_html_e($text)
    {
        esc_html_e($text, DUP_PRO_Constants::PLUGIN_SLUG);
    }

    /**
     * Localize and echo the current text with escaping attr
     *
     * @param string $text The text to localize
     *
     * @return string Returns the text in its desired language
     */
    public static function esc_attr_e($text)
    {
        esc_attr_e($text, DUP_PRO_Constants::PLUGIN_SLUG);
    }

    /**
     * Localize and return the current text as a variable
     *
     * @param string $text The text to localize
     *
     * @return variable Returns the text as a localized variable
     */
    public static function __($text)
    {
        return __($text, DUP_PRO_Constants::PLUGIN_SLUG);
    }

    /**
     * Localize and echo the current text as a variable
     *
     * @param string $text The text to localize
     *
     * @return variable Returns the text as a localized variable
     */
    public static function _e($text)
    {
        return _e($text, DUP_PRO_Constants::PLUGIN_SLUG);
    }

    /**
     * Localize and return the current text as a variable with escaping
     *
     * @param string $text The text to localize
     *
     * @return variable Returns the text as a localized variable
     */
    public static function esc_html__($text)
    {
        return esc_html__($text, DUP_PRO_Constants::PLUGIN_SLUG);
    }

    /**
     * Localize and return the current text as a variable with escaping attribute
     *
     * @param string $text The text to localize
     *
     * @return variable Returns the text as a localized variable
     */
    public static function esc_attr__($text)
    {
        return esc_html__($text, DUP_PRO_Constants::PLUGIN_SLUG);
    }

    /**
     * Display human readable byte sizes
     *
     * @param int $size    The size in bytes
     *
     * @return string The size of bytes readable such as 100KB, 20MB, 1GB etc.
     */
    public static function byteSize($size)
    {
        try {
            $units = array('B', 'KB', 'MB', 'GB', 'TB');
            for ($i = 0; $size >= 1024 && $i < 4; $i++) {
                $size /= 1024;
            }
            return round($size, 2).$units[$i];
        } catch (Exception $e) {
            return "n/a";
        }
    }

    /**
     * Return a string with the elapsed time in seconds
     *
     * @see getMicrotime()
     *
     * @param mixed number $end     The final time in the sequence to measure
     * @param mixed number $start   The start time in the sequence to measure
     *
     * @return  string   The time elapsed from $start to $end as 5.89 sec.
     */
    public static function elapsedTime($end, $start)
    {

        return sprintf('%.3f sec.', abs($end - $start));
    }

    /**
     * Return a float with the elapsed time in seconds
     *
     * @see getMicrotime(), elapsedTime()
     *
     * @param mixed number $end     The final time in the sequence to measure
     * @param mixed number $start   The start time in the sequence to measure
     *
     * @return  string   The time elapsed from $start to $end as 5.89
     */
    public static function elapsedTimeU($end, $start)
    {
        return sprintf('%.3f', abs($end - $start));
    }

    /**
     * Gets the contents of the file as an attachment type
     *
     * @param string $filepath      The full path the file to read
     * @param string $contentType   The header content type to force when pushing the attachment
     *
     * @return  string   Returns the contents of the file as an attachment type
     */
    public static function getDownloadAttachment($filepath, $contentType)
    {
        // Clean previous or after eny notice texts
        ob_clean();
        ob_start();
        $filename = basename($filepath);

        header('Content-Type: $contentType');
        header("Content-Disposition: attachment; filename={$filename}");
        header("Pragma: public");

        if (readfile($filepath) === false) {
            throw new Exception(self::__("Couldn't read {$filepath}"));
        }
        ob_end_flush();
    }

    /**
     * Return the path of an executable program
     *
     * @param string $exeFilename  A file name or path to a file name of the executable
     *
     * @return  string | null   Returns the full path of the executable or null if not found
     */
    public static function getExeFilepath($exeFilename)
    {
        $filepath = null;

        if (DUP_PRO_Shell_U::isShellExecEnabled()) {
            if (shell_exec("hash $exeFilename 2>&1") == NULL) {
                $filepath = $exeFilename;
            } else {
                $possible_paths = array(
                    "/usr/bin/$exeFilename",
                    "/opt/local/bin/$exeFilename"
                );

                foreach ($possible_paths as $path) {
                    if (@file_exists($path)) {
                        $filepath = $path;
                        break;
                    }
                }
            }
        }
        return $filepath;
    }

    /**
     * Return the WP admin page URL from the slug
     *
     * @param string $menuSlug  The slug to search on
     *
     * @return  string   Returns the URL of the menu by the slug
     */
    public static function getMenuPageURL($menuSlug, $echo = true)
    {
		if(is_multisite()) {
            return DUP_PRO_MU::networkMenuPageUrl($menuSlug, $echo);
        } else {
            return menu_page_url($menuSlug, $echo);
        }
    }

    /**
     * Get current microtime as a float.  Method is used for simple profiling
     *
     * @see elapsedTime
     *
     * @return  string   A float in the form "msec sec", where sec is the number of seconds since the Unix epoch
     */
    public static function getMicrotime()
    {
        return microtime(true);
    }

    /**
     * Gets an SQL lock request
     *
     * @see releaseSqlLock()
     *
     * @return  bool    Returns true if an SQL lock request was successful
     */
    public static function getSqlLock($lock_name = 'duplicator_pro_lock')
    {
        global $wpdb;

        $query_string = "select GET_LOCK('{$lock_name}', 0)";

        $ret_val = $wpdb->get_var($query_string);

        if ($ret_val == 0) {
            DUP_PRO_LOG::trace("Mysql lock {$lock_name} denied");
            return false;
        } else if ($ret_val == null) {
            DUP_PRO_LOG::trace("Error retrieving mysql lock {$lock_name}");
            return false;
        } else {
            DUP_PRO_LOG::trace("Mysql lock {$lock_name} acquired");
            return true;
        }
    }

    /**
     * Gets an SQL lock request
     *
     * @see releaseSqlLock()
     *
     * @return  bool    Returns true if an SQL lock request was successful
     */
    public static function isSqlLockLocked($lock_name = 'duplicator_pro_lock')
    {
        global $wpdb;

        $query_string = "select IS_FREE_LOCK('{$lock_name}')";

        $ret_val = $wpdb->get_var($query_string);

        if ($ret_val == 0) {
            DUP_PRO_LOG::trace("MySQL lock {$lock_name} is in use");
            return true;
        } else if ($ret_val == null) {
            DUP_PRO_LOG::trace("Error retrieving mysql lock {$lock_name}");
            return false;
        } else {
            DUP_PRO_LOG::trace("MySQL lock {$lock_name} is free");
            return false;
        }
    }

    const SECURE_ISSUE_DIE   = 'die';
    const SECURE_ISSUE_THROW = 'throw';
    const SECURE_ISSUE_RETURN = 'return';

    /**
     * Does the current user have the capability
     *
     * @param type $permission
     * @param type $exit    //  SECURE_ISSUE_DIE die script with die function
     *                          SECURE_ISSUE_THROW throw an exception if fail
     *                          SECURE_ISSUE_RETURN return false if fail
     *
     * @return boolean      // return false is fail and $exit is SECURE_ISSUE_THROW
     *                      // true if success
     *
     * @throws Exception    // thow exception if $exit is SECURE_ISSUE_THROW
     */
    public static function hasCapability($permission = 'read', $exit = self::SECURE_ISSUE_DIE)
    {
        $capability = apply_filters('wpfront_user_role_editor_duplicator_pro_translate_capability', $permission);

        if (!current_user_can($capability)) {
            $exitMsg = DUP_PRO_U::esc_html__('You do not have sufficient permissions to access this page.');
            DUP_PRO_LOG::trace('You do not have sufficient permissions to access this page. PERMISSION: '.$permission);

            switch ($exit) {
                case self::SECURE_ISSUE_THROW:
                    throw new Exception($exitMsg);
                case self::SECURE_ISSUE_RETURN:
                    return false;
                case self::SECURE_ISSUE_DIE:
                default:
                    wp_die($exitMsg);
            }
        }
        return true;
    }

    /**
     * Does the current user have the capability
     *
     * @return null Dies if user doesn't have the correct capability
     */
    public static function checkAjax()
    {
        if (!wp_doing_ajax()) {
            $errorMsg = DUP_PRO_U::esc_html__('You do not have called from AJAX to access this page.');
            DUP_PRO_LOG::trace($errorMsg);
            error_log($errorMsg);
            wp_die($errorMsg);
        }
    }

    /**
     * Creates the snapshot directory if it doesn't already exists
     *
     * @return null
     */
    public static function initStorageDirectory()
    {
        $global = DUP_PRO_Global_Entity::get_instance();

        $path_wproot = duplicator_pro_get_root_path();
        $path_ssdir = DUP_PRO_U::safePath(DUPLICATOR_PRO_SSDIR_PATH);
        $path_plugin = DUP_PRO_U::safePath(DUPLICATOR_PRO_PLUGIN_PATH);

        //--------------------------------
        //CHMOD DIRECTORY ACCESS
        //wordpress root directory
        DUP_PRO_IO::changeMode($path_wproot, 0755);

        //snapshot directory
        DUP_PRO_IO::createDir($path_ssdir);
        DUP_PRO_IO::changeMode($path_ssdir, 0755);

        //snapshot tmp directory
        $path_ssdir_tmp = $path_ssdir . '/tmp';
        DUP_PRO_IO::createDir($path_ssdir_tmp);
        DUP_PRO_IO::changeMode($path_ssdir_tmp, 0755);

        //plugins dir/files
        DUP_PRO_IO::changeMode($path_plugin . 'files', 0755);

        //--------------------------------
        //FILE CREATION
        //SSDIR: Create Index File
        $ssfile = @fopen($path_ssdir . '/index.php', 'w');
        @fwrite($ssfile, '<?php error_reporting(0);  if (stristr(php_sapi_name(), "fcgi")) { $url  =  "http://" . $_SERVER["HTTP_HOST"]; header("Location: {$url}/404.html");} else { header("HTTP/1.1 404 Not Found", true, 404);} exit();');
        @fclose($ssfile);

        //SSDIR: Create .htaccess
        // $storage_htaccess_off = DUP_PRO_Settings::Get('storage_htaccess_off');
        if ($global->storage_htaccess_off)
        {
            @unlink($path_ssdir . '/.htaccess');
        }
        else
        {
            $htfile = @fopen($path_ssdir . '/.htaccess', 'w');
            $htoutput = "Options -Indexes";
            @fwrite($htfile, $htoutput);
            @fclose($htfile);
        }

        //SSDIR: Robots.txt file
        $robotfile = @fopen($path_ssdir . '/robots.txt', 'w');
        @fwrite($robotfile, "User-agent: * \nDisallow: /" . DUPLICATOR_PRO_SSDIR_NAME . '/');
        @fclose($robotfile);
    }

    /**
     * Wrap to prevent malware scanners from reporting false/positive
     * Switched from our old method to avoid WordFence reporting a false positive
     *
     * @param string $string The string to decrypt i.e. base64_decond
     *
     * @return string Returns the string base64 decoded
     */
    public static function installerDecrypt($string)
    {
        return base64_decode($string);
    }

    /**
     * Copies an array to an objects array
     *
     * @param array &$sourceArray   The source array
     * @param array &$destArray     The destination array in the class
     * @param object $className     The class name where the $destArray exists
     *
     * @return null
     */
    public static function objectArrayCopy(&$sourceArray, &$destArray, $className)
    {
        foreach ($sourceArray as $source_object) {
            $dest_object = new $className();
            self::objectCopy($source_object, $dest_object);
            array_push($destArray, $dest_object);
        }
    }

    /**
     * Copies simple values from one object to another
     *
     * @param array &$srcObject  The source object
     * @param array &$destObject          The destination object to copy to
     * @param array &$skipMemberArray     List of members to skip when copying
     *
     * @return null
     */
    public static function objectCopy($srcObject, $destObject, $skipMemberArray = null)
    {
        foreach ($srcObject as $member_name => $member_value) {
            if (!is_object($member_value) && (($skipMemberArray == null) || !in_array($member_name, $skipMemberArray))) {
                // Skipping all object members
                $destObject->$member_name = $member_value;
            }
        }
    }

    /**
     * Is the server PHP 5.3 or better
     *
     * @return  bool    Returns true if the server PHP 5.3 or better
     */
    public static function isCurlExists()
    {
        return function_exists('curl_version');
    }

    /**
     * Is the server PHP 5.3 or better
     *
     * @return  bool    Returns true if the server PHP 5.3 or better
     */
    public static function PHP53()
    {
        return version_compare(PHP_VERSION, '5.3.2', '>=');
    }

    /**
     * Is the server PHP 5.5 or better
     *
     * @return  bool    Returns true if the server PHP 5.3 or better
     */
    public static function PHP55()
    {
        return version_compare(PHP_VERSION, '5.5.0', '>=');
    }

	/**
     * Is the server PHP 5.6 or better
     *
     * @return  bool    Returns true if the server PHP 5.3 or better
     */
    public static function PHP56()
    {
        return version_compare(PHP_VERSION, '5.6.0', '>=');
    }

        /**
     * Is the server PHP 5.5 or better
     *
     * @return  bool    Returns true if the server PHP 5.3 or better
     */
    public static function PHP70()
    {
        return version_compare(PHP_VERSION, '7.0.0', '>=');
    }

    /**
     * Releases the SQL lock request
     *
     * @see getSqlLock()
     *
     * @return  bool    Returns true if an SQL lock request was released
     */
    public static function releaseSqlLock($lock_name = 'duplicator_pro_lock')
    {
        global $wpdb;

        $query_string = "select RELEASE_LOCK('{$lock_name}')";
        $ret_val = $wpdb->get_var($query_string);

        if ($ret_val == 0) {
            DUP_PRO_LOG::trace("Failed releasing sql lock {$lock_name} because it wasn't established by this thread");
        } else if ($ret_val == null) {
            DUP_PRO_LOG::trace("Tried to release sql lock {$lock_name} but it didn't exist");
        } else {
            // Lock was released
            DUP_PRO_LOG::trace("SQL lock {$lock_name} released");
        }
    }

    /**
     * Makes path safe for any OS
     *      Paths should ALWAYS READ be "/"
     *          uni: /home/path/file.txt
     *          win:  D:/home/path/file.txt
     *
     * @param string $path		The path to make safe
     *
     * @return string A path with all slashes facing "/"
     */
    public static function safePath($path)
    {
        return str_replace("\\", '/', $path);
    }
    
    public static function safePathUntrailingslashit($path)
    {
        return rtrim(str_replace("\\", '/', $path), '/');
    }

    public static function safePathTrailingslashit($path)
    {
        return self::safePathUntrailingslashit($path).'/';
    }

    /**
     * Sets a value or returns a default
     *
     * @param mixed $val		The value to set
	 * @param mixed $default	The value to default to if the val is not set
     *
     * @return mixed  A value or a default
     */
    public static function setVal($val, $default = null)
    {
        return isset($val) ? $val : $default;
    }

    /**
     * Check is set and not empty, sets a value or returns a default
     *
     * @param mixed $val		The value to set
	 * @param mixed $default	The value to default to if the val is not set
     *
     * @return mixed  A value or a default
     */
    public static function isEmpty($val, $default = null)
    {
        return isset($val) && !empty($val) ? $val : $default;
    }


    /**
     * Returns the last N lines of a file. Simular to tail command
     *
     * @param string $filepath The full path to the file to be tailed
     * @param int $lines The number of lines to return with each tail call
     *
     * @return string The last N parts of the file
     */
    public static function tailFile($filepath, $lines = 2)
    {
        // Open file
        $f = @fopen($filepath, "rb");
        if ($f === false) return false;

        // Sets buffer size
        $buffer = 256;

        // Jump to last character
        fseek($f, -1, SEEK_END);

        // Read it and adjust line number if necessary
        // (Otherwise the result would be wrong if file doesn't end with a blank line)
        if (fread($f, 1) != "\n") $lines -= 1;

        // Start reading
        $output = '';
        $chunk  = '';

        // While we would like more
        while (ftell($f) > 0 && $lines >= 0) {
            // Figure out how far back we should jump
            $seek   = min(ftell($f), $buffer);
            // Do the jump (backwards, relative to where we are)
            fseek($f, -$seek, SEEK_CUR);
            // Read a chunk and prepend it to our output
            $output = ($chunk  = fread($f, $seek)).$output;
            // Jump back to where we started reading
            fseek($f, -mb_strlen($chunk, '8bit'), SEEK_CUR);
            // Decrease our line counter
            $lines -= substr_count($chunk, "\n");
        }

        // While we have too many lines
        // (Because of buffer size we might have read too many)
        while ($lines++ < 0) {
            // Find first newline and remove all text before that
            $output = substr($output, strpos($output, "\n") + 1);
        }
        fclose($f);
        return trim($output);
    }

	/**
     * Returns an array of the WordPress core tables.
     *
     * @return array  Returns all WP core tables
     */
    public static function getWPCoreTables()
    {
        global $wpdb;
        $result = array();
        foreach (self::getWPCoreTablesEnd() as $tend) {
            $result[] = $wpdb->prefix.$tend;
        }
        return $result;
    }

    public static function getWPCoreTablesEnd()
    {
        return array(
            'commentmeta',
            'comments',
            'links',
            'options',
            'postmeta',
            'posts',
            'term_relationships',
            'term_taxonomy',
            'termmeta',
            'terms',
            'usermeta',
            'blogs',
            'blog_versions',
            'blogmeta',
            'users',
            'site',
            'sitemeta',
            'signups',
            'registration_log',
            'blog_versions');
    }

    public static function isWPCoreTable($table)
    {
        global $wpdb;

        if (strpos($table, $wpdb->prefix) !== 0) {
            return false;
        }

        $subTName = substr($table, strlen($wpdb->prefix));
        $coreEnds = self::getWPCoreTablesEnd();

        if (in_array($subTName, $coreEnds)) {
            return true;
        } else if (is_multisite()) {
            $exTable = explode('_', $subTName);
            if (count($exTable) >= 2 && is_numeric($exTable[0])) {
                $tChekc = implode('_', array_slice($exTable, 1));
                if (get_blog_details((int) $exTable[0], false) !== false && in_array($tChekc, $coreEnds)) {
                    return true;
                }
            }
        }

        return false;
    }

    public static function getWPBlogIdTable($table)
    {
        global $wpdb;

        if (!is_multisite() || strpos($table, $wpdb->prefix) !== 0) {
            return 0;
        }

        $subTName = substr($table, strlen($wpdb->prefix));
        $exTable  = explode('_', $subTName);
        if (count($exTable) >= 2 && is_numeric($exTable[0]) && get_blog_details((int) $exTable[0], false) !== false) {
            return (int) $exTable[0];
        } else {
            return 0;
        }
    }

    /**
     * Check given table is exist in real
     * 
     * @param $table string Table name
     * @return booleam
     */
    public static function isTableExists($table)
    {
		// It will clear the $GLOBALS['wpdb']->last_error var
		$GLOBALS['wpdb']->flush();
		$sql = "SELECT 1 FROM ".esc_sql($table)." LIMIT 1;";
		$ret = $GLOBALS['wpdb']->get_var($sql);
		if (empty($GLOBALS['wpdb']->last_error))   return true;
        return false;
    }

    /**
    * Finds if its a valid executable or not
    * @param type $exe A non zero length executable path to find if that is executable or not.
    * @param type $expectedValue expected value for the result
    * @return boolean
    */
    public static function isExecutable($cmd)
    {
        if (strlen($cmd) < 1) return false;

        if (@is_executable($cmd)){
            return true;
        }
        
        $output = shell_exec($cmd);
        if (!is_null($output)) {
            return true;
        }
        
        $output = shell_exec($cmd . ' -?');
        if (!is_null($output)) {
            return true;
        }

        return false;
    }

    /**
     * Look into string and try to fix its natural expected value type
     * @param mixed $string Simple string
     * @return mixed value with it's natural string type
     */
    public static function valType($string)
    {
        if (is_array($string)) {
            foreach ($string as $key => $str) {
                $string[$key] = DUP_PRO_U::valType($str);
            }
        } else if (!is_string($string)) {
            return $string;
        } else {
            if (!is_bool($string)) {
                if (is_numeric($string)) {
                    if ((int) $string == $string) {
                        return (int) $string;
                    } else if ((float) $string == $string) {
                        return (float) $string;
                    }
                }

                if (is_string($string)) {
                    if (in_array(strtolower($string), array('true', 'false'), true) !== false) {
                        return ($string == 'true' ? true : false);
                    }
                }
            }
        }
        return $string;
    }

    /**
	* TODO: Migrate method over to class.snaplib.u.url.php
    * Validate is SSL active
    * @return boolean true/false
    */
    public static function is_ssl()
    {
        if(isset($_SERVER['HTTPS']) && !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off')
            return true;

        if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower($_SERVER['HTTP_X_FORWARDED_PROTO']) == 'https')
            return true;

        return false;
    }

    /**
     * Get default chunk size in byte
     * 
     * @param int $min_chunk_size Min minimum chunk size in bytes
     * @return int An integer chunk size  byte value.
     */
    public static function get_default_chunk_size_in_byte($min_chunk_size = '') {
        
        if (empty($min_chunk_size))  $min_chunk_size = 2 * MB_IN_BYTES; // 2 MB;
        $post_max_size_in_bytes = self::get_bytes_from_shorthand(ini_get('post_max_size'));
        $considered_post_max_size_in_bytes = $post_max_size_in_bytes - KB_IN_BYTES;

        $upload_max_filesize_in_bytes = self::get_bytes_from_shorthand(ini_get('upload_max_filesize'));
        $considered_upload_max_filesize_in_bytes = $upload_max_filesize_in_bytes - KB_IN_BYTES; 

        $memory_limit_in_bytes = self::get_bytes_from_shorthand(ini_get('memory_limit'));
        $considered_memory_limit_in_bytes = $memory_limit_in_bytes - KB_IN_BYTES;

        $chunk_size_in_byte = min(
            $considered_post_max_size_in_bytes,
            $considered_upload_max_filesize_in_bytes,
            $considered_memory_limit_in_bytes,
            // In extraction process, 2 MB is improving speed, so we are using 5MB instead of 10 MB
            $min_chunk_size
        );
        
        return $chunk_size_in_byte;
    }

    /**
     * Converts a shorthand byte value to an integer byte value.
     *
     * @param string $value A (PHP ini) byte value, either shorthand or ordinary.
     * @return int An integer byte value.
     */
    private static function get_bytes_from_shorthand($value) {
        $value = strtolower(trim($value));
        $bytes = (int) $value;
     
        if (false !== strpos($value, 'g')) {
            $bytes *= GB_IN_BYTES;
        } elseif (false !== strpos($value, 'm')) {
            $bytes *= MB_IN_BYTES;            
        } elseif (false !== strpos($value, 'k')) {
            $bytes *= KB_IN_BYTES;
        }
     
        // For windows 32 bit int max limit
        if ($bytes < 0) {
            return PHP_INT_MAX;
        }

        return min($bytes, PHP_INT_MAX);
        // Deal with large (float) values which run into the maximum integer size.
    }

    /**
     * Get default chunk size in KB
     *
     * @param int $min_chunk_size Min minimum chunk size in bytes
     * @return int An integer chunk size KB value.
     */
    public static function get_default_chunk_size_in_kb($min_chunk_size = '') {
        if (empty($min_chunk_size))  $min_chunk_size = 10 * MB_IN_BYTES; // 10 MB;

        $chunk_size_in_byte = self::get_default_chunk_size_in_byte($min_chunk_size);
        $chunk_size_in_kb = floor($chunk_size_in_byte / KB_IN_BYTES);
        
        return $chunk_size_in_kb;
    }

    /**
     * Get default chunk size in MB
     * Not used now, but for future use
     *
     * @param int $min_chunk_size Min minimum chunk size in bytes
     * @return int An integer chunk size MB value.
     */
    public static function get_default_chunk_size_in_mb($min_chunk_size = '') {
        if (empty($min_chunk_size))  $min_chunk_size = 10 * MB_IN_BYTES; // 10 MB;

        $chunk_size_in_byte = self::get_default_chunk_size_in_byte($min_chunk_size);
        $chunk_size_in_mb = floor($chunk_size_in_byte / MB_IN_BYTES);
        
        return $chunk_size_in_mb;
    }

    /**
     * Write contents to a file
     *
     * @param  resource $handle  File handle to write to
     * @param  string   $content Contents to write to the file
     * @return integer
     * @throws Exception
     */
    public static function fwrite($handle, $content) {
        $write_res = @fwrite( $handle, $content );
        if (false === $write_res) {
            if (($meta = stream_get_meta_data( $handle))) {
                $msg = sprintf('Unable to write to: %s.', $meta['uri']);
                DUP_PRO_Log::trace($msg);
                throw new Exception($msg);
            }
        } elseif (strlen($content) !== $write_res) {
            if (($meta = stream_get_meta_data($handle))) {
                $msg = sprintf("Error writing %s to archive. Out of disk space.", $meta['uri']);
                DUP_PRO_Log::trace($msg);
                throw new Exception($msg);
            }
        }
        return $write_res;
    }

    /**
     * Seeks on a file pointer
     *
     * @param  string  $handle File handle to seeks
     * @return integer
     */
    public static function fseek($handle, $offset, $mode = SEEK_SET) {
        $seek_result = @fseek($handle, $offset, $mode);
        if (-1 === $seek_result) {
            if (($meta = stream_get_meta_data($handle))) {
                $msg = sprintf('Unable to seek to offset %d on %s.', $offset, $meta['uri']);
                DUP_PRO_Log::trace($msg);
                throw new Exception($msg);
            }
        }

        return $seek_result;
    }

    /**
     * Tells on a file pointer
     *
     * @param  string  $handle File handle to tells
     * @return integer
     */
    public static function ftell($handle) {
        $tell_result = @ftell($handle);
        if (false === $tell_result) {
            if (($meta = stream_get_meta_data($handle))) {
                $msg = sprintf('Unable to get current pointer position of %s.', $meta['uri']);
                DUP_PRO_Log::trace($msg);
                throw new Exception($msg);
            }
        }
        return $tell_result;
    }

    /**
     * return ini disable functions array
     *
     * @return array
     */
    public static function getIniDisableFuncs() {
        if (is_null(self::$iniDisableFuncs)) {
            $tmpFuncs = ini_get ( 'disable_functions' );
            $tmpFuncs = explode(',',$tmpFuncs);
            self::$iniDisableFuncs = array();
            foreach ($tmpFuncs as $cFunc) {
                self::$iniDisableFuncs[] = trim($cFunc);
            }
        }

        return self::$iniDisableFuncs;
    }

    /**
     * Check if functione exists and isn't in ini disable_functions
     *
     * @param string $function_name
     * @return bool
     */
    public static function isIniFunctionEnalbe($function_name) {
        return function_exists($function_name) && !in_array($function_name, self::getIniDisableFuncs());
    }

    /**
     * Check to see if the URL is valid
     *
     * @param string $url - preferably a fully qualified URL
     * @return boolean - true if it is out there somewhere
     */
    public static function urlExists($url) {
        if (($url == '') || ($url == null)) { return false; }
        $response = wp_remote_head($url, array('timeout' => 5));
        $accepted_status_codes = array(200, 301, 302);
        if (!is_wp_error($response) && in_array(wp_remote_retrieve_response_code($response), $accepted_status_codes)) {
            return true;
        }
        return false;
    }
}