name : download.php
<?php
/*
 * @package   bfNetwork
 * @copyright Copyright (C) 2011,2012,2013,2014,2015,2016,2017,2018,2019,2020,2021,2022 Blue Flame Digital Solutions Ltd. All rights reserved.
 * @license   GNU General Public License version 3 or later
 *
 * @see       https://mySites.guru/
 * @see       https://www.phil-taylor.com/
 *
 * @author    Phil Taylor / Blue Flame Digital Solutions Limited.
 *
 * bfNetwork is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * bfNetwork is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this package.  If not, see http://www.gnu.org/licenses/
 *
 * If you have any questions regarding this code, please contact [email protected]
 */

/**
 * @copyright  Copyright (c)2010-2014 Nicholas K. Dionysopoulos
 * @license    GNU General Public License version 3, or later
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * @note This file has been modified by mySites.gurut and no longer reflects the original work of its author.
 */
class AcuDownload
{
    /**
     * Parameters passed from the GUI when importing from URL.
     *
     * @var array
     */
    private $params = array();

    /**
     * The download adapter which will be used by this class.
     *
     * @var AcuDownloadInterface
     */
    private $adapter = null;

    public function __construct()
    {
        // Find the best fitting adapter
        $allAdapters = AcuDownload::getFiles(dirname(__FILE__) . '/adapter', array(), array('abstract.php'));
        $priority    = 0;

        foreach ($allAdapters as $adapterInfo) {
            $adapter = new $adapterInfo['classname']();

            if (! $adapter->isSupported()) {
                continue;
            }

            if ($adapter->priority > $priority) {
                $this->adapter = $adapter;
                $priority      = $adapter->priority;
            }
        }
    }

    /**
     * Forces the use of a specific adapter.
     *
     * @param  $className  The name of the class or the name of the adapter, e.g. 'AcuDownloadAdapterCurl' or 'curl'
     */
    public function setAdapter($className)
    {
        $adapter = null;

        if (class_exists($className, true)) {
            $adapter = new $className();
        } elseif (class_exists('AcuDownloadAdapter' . ucfirst($className))) {
            $className = 'AcuDownloadAdapter' . ucfirst($className);
            $adapter   = new $className();
        }

        if (is_object($adapter) && ($adapter instanceof AcuDownloadInterface)) {
            $this->adapter = $adapter;
        }
    }

    /**
     * Used to decode the $params array.
     *
     * @param string $key     The parameter key you want to retrieve the value for
     * @param mixed  $default The default value, if none is specified
     *
     * @return mixed The value for this parameter key
     */
    private function getParam($key, $default = null)
    {
        if (array_key_exists($key, $this->params)) {
            return $this->params[$key];
        } else {
            return $default;
        }
    }

    /**
     * Download data from a URL and return it.
     *
     * @param string $url The URL to download from
     *
     * @return bool|string The downloaded data or false on failure
     */
    public function getFromURL($url)
    {
        try {
            return $this->adapter->downloadAndReturn($url);
        } catch (Exception $e) {
            return false;
        }
    }

    /**
     * Performs the staggered download of file.
     *
     * @param array $params A parameters array, as sent by the user interface
     *
     * @return array A return status array
     */
    public function importFromURL($params)
    {
        $this->params = $params;

        // Fetch data
        $filename    = $this->getParam('file');
        $frag        = $this->getParam('frag', -1);
        $totalSize   = $this->getParam('totalSize', -1);
        $doneSize    = $this->getParam('doneSize', -1);
        $maxExecTime = $this->getParam('maxExecTime', 15);
        $runTimeBias = $this->getParam('runTimeBias', 75);
        $minExecTime = $this->getParam('minExecTime', 5);

        $localFilename = 'myjoomla-upgradefile.zip';

        // This would have been //JFactory::getConfig()->get('tmp_path', JPATH_ROOT . '/tmp');
        $tmpDir = dirname(__FILE__) . '/../../tmp';
        $tmpDir = rtrim($tmpDir, '/\\');

        /**
         * debugMsg('Importing from URL'); debugMsg('  file      : ' . $filename); debugMsg('  frag      : ' . $frag);
         * debugMsg('  totalSize : ' . $totalSize); debugMsg('  doneSize  : ' . $doneSize);. /*
         */

        // Init retArray
        $retArray = array(
            'status'    => true,
            'error'     => '',
            'frag'      => $frag,
            'totalSize' => $totalSize,
            'doneSize'  => $doneSize,
            'percent'   => 0,
        );

        try {
            $timerParameters = array(
                'min_exec_time' => $minExecTime,
                'max_exec_time' => $maxExecTime,
                'run_time_bias' => $runTimeBias,
            );
            $timer = new AcuTimer($timerParameters);
            $start = $timer->getRunningTime(); // Mark the start of this download
            $break = false; // Don't break the step

            // Figure out where on Earth to put that file
            $local_file = $tmpDir . '/' . $localFilename;

            //debugMsg("- Importing from $filename");

            while (($timer->getTimeLeft() > 0) && ! $break) {
                // Do we have to initialize the file?
                if (-1 == $frag) {
                    //debugMsg("-- First frag, killing local file");
                    // Currently downloaded size
                    $doneSize = 0;

                    if (@file_exists($local_file)) {
                        @unlink($local_file);
                    }

                    // Delete and touch the output file
                    $fp = @fopen($local_file, 'wb');

                    if (false !== $fp) {
                        @fclose($fp);
                    }

                    // Init
                    $frag = 0;

                    //debugMsg("-- First frag, getting the file size");
                    $retArray['totalSize'] = $this->adapter->getFileSize($filename);
                    $totalSize             = $retArray['totalSize'];
                }

                // Calculate from and length
                $length = 1048576;
                $from   = $frag * $length;
                $to     = $length + $from - 1;

                // Try to download the first frag
                $required_time = 1.0;
                //debugMsg("-- Importing frag $frag, byte position from/to: $from / $to");

                try {
                    $result = $this->adapter->downloadAndReturn($filename, $from, $to);

                    if (false === $result) {
                        throw new Exception(JText::sprintf('COM_CMSUPDATE_ERR_LIB_COULDNOTDOWNLOADFROMURL', $filename), 500);
                    }
                } catch (Exception $e) {
                    $result = false;
                    $error  = $e->getMessage();
                }

                if (false === $result) {
                    // Failed download
                    if (0 == $frag) {
                        // Failure to download first frag = failure to download. Period.
                        $retArray['status'] = false;
                        $retArray['error']  = $error;

                        //debugMsg("-- Download FAILED");

                        return $retArray;
                    } else {
                        // Since this is a staggered download, consider this normal and finish
                        $frag = -1;
                        //debugMsg("-- Import complete");
                        $totalSize = $doneSize;
                        $break     = true;
                    }
                }

                // Add the currently downloaded frag to the total size of downloaded files
                if ($result) {
                    $filesize = strlen($result);
                    //debugMsg("-- Successful download of $filesize bytes");
                    $doneSize += $filesize;

                    // Append the file
                    $fp = @fopen($local_file, 'ab');

                    if (false === $fp) {
                        //debugMsg("-- Can't open local file $local_file for writing");
                        // Can't open the file for writing
                        $retArray['status'] = false;
                        $retArray['error']  = JText::sprintf('COM_CMSUPDATE_ERR_LIB_COULDNOTWRITELOCALFILE', $local_file);

                        return $retArray;
                    }

                    fwrite($fp, $result);
                    fclose($fp);

                    //debugMsg("-- Appended data to local file $local_file");

                    ++$frag;

                    //debugMsg("-- Proceeding to next fragment, frag $frag");

                    if (($filesize < $length) || ($filesize > $length)) {
                        // A partial download or a download larger than the frag size means we are done
                        $frag = -1;
                        //debugMsg("-- Import complete (partial download of last frag)");
                        $totalSize = $doneSize;
                        $break     = true;
                    }
                }

                // Advance the frag pointer and mark the end
                $end = $timer->getRunningTime();

                // Do we predict that we have enough time?
                $required_time = max(1.1 * ($end - $start), $required_time);

                if ($required_time > (10 - $end + $start)) {
                    $break = true;
                }

                $start = $end;
            }

            if (-1 == $frag) {
                $percent = 100;
            } elseif ($doneSize <= 0) {
                $percent = 0;
            } else {
                if ((int) $totalSize > 0) {
                    $percent = 100 * ((int) $doneSize / (int) $totalSize);
                } else {
                    $percent = 0;
                }
            }

            // Update $retArray
            $retArray = array(
                'status'    => true,
                'error'     => '',
                'frag'      => $frag,
                'totalSize' => $totalSize,
                'doneSize'  => $doneSize,
                'percent'   => $percent,
            );
        } catch (Exception $e) {
            //debugMsg("EXCEPTION RAISED:");
            //debugMsg($e->getMessage());
            $retArray['status'] = false;
            $retArray['error']  = $e->getMessage();
        }

        return $retArray;
    }

    /**
     * This method will crawl a starting directory and get all the valid files that will be analyzed by __construct.
     * Then it organizes them into an associative array.
     *
     * @param string $path          Folder where we should start looking
     * @param array  $ignoreFolders Folder ignore list
     * @param array  $ignoreFiles   File ignore list
     *
     * @return array Associative array, where the `fullpath` key contains the path to the file,
     * and the `classname` key contains the name of the class
     */
    protected static function getFiles($path, $ignoreFolders = array(), $ignoreFiles = array())
    {
        $return = array();

        $files = self::scanDirectory($path, $ignoreFolders, $ignoreFiles);

        // Ok, I got the files, now I have to organize them
        foreach ($files as $file) {
            $clean = str_replace($path, '', $file);
            $clean = trim(str_replace('\\', '/', $clean), '/');

            $parts = explode('/', $clean);

            $return[] = array(
                'fullpath'  => $file,
                'classname' => 'AcuDownloadAdapter' . ucfirst(basename($parts[0], '.php')),
            );
        }

        return $return;
    }

    /**
     * Recursive function that will scan every directory unless it's in the ignore list. Files that aren't in the ignore
     * list are returned.
     *
     * @param string $path          Folder where we should start looking
     * @param array  $ignoreFolders Folder ignore list
     * @param array  $ignoreFiles   File ignore list
     *
     * @return array List of all the files
     */
    protected static function scanDirectory($path, $ignoreFolders = array(), $ignoreFiles = array())
    {
        $return = array();

        $handle = @opendir($path);

        if (! $handle) {
            return $return;
        }

        while (false !== ($file = readdir($handle))) {
            if ('.' == $file || '..' == $file) {
                continue;
            }

            $fullpath = $path . '/' . $file;

            if ((is_dir($fullpath) && in_array($file, $ignoreFolders)) || (is_file($fullpath) && in_array($file, $ignoreFiles))) {
                continue;
            }

            if (is_dir($fullpath)) {
                $return = array_merge(self::scanDirectory($fullpath, $ignoreFolders, $ignoreFiles), $return);
            } else {
                $return[] = $path . '/' . $file;
            }
        }

        return $return;
    }
}

© 2025 Cubjrnet7