Source for file download.php

Documentation is available at download.php

  1. <?php
  2. /**
  3.  * @package     Joomla.Administrator
  4.  * @subpackage  com_joomlaupdate
  5.  *
  6.  * @copyright   Copyright (C) 2005 - 2013 Open Source Matters, Inc. All rights reserved.
  7.  * @license     GNU General Public License version 2 or later; see LICENSE.txt
  8.  */
  9.  
  10. defined('_JEXEC'or die;
  11.  
  12. /**
  13.  * Smart download helper. Automatically uses cURL or URL fopen() wrappers to
  14.  * fetch the package.
  15.  *
  16.  * @package  Joomla.Administrator
  17.  * @since    2.5.4
  18.  */
  19. {
  20.     /**
  21.      * Downloads from a URL and saves the result as a local file
  22.      *
  23.      * @param   string  $url     The URL to download from
  24.      * @param   string  $target  The file path to download to
  25.      *
  26.      * @return  bool    True on success
  27.      *
  28.      * @since   2.5.4
  29.      */
  30.     public static function download($url$target)
  31.     {
  32.         jimport('joomla.filesystem.file');
  33.  
  34.         // Make sure the target does not exist
  35.         if (JFile::exists($target))
  36.         {
  37.             if (!@unlink($target))
  38.             {
  39.                 JFile::delete($target);
  40.             }
  41.         }
  42.  
  43.         // Try to open the output file for writing
  44.         $fp @fopen($target'wb');
  45.         if ($fp === false)
  46.         {
  47.             // The file can not be opened for writing. Let's try a hack.
  48.             $empty '';
  49.             if JFile::write($target$empty) )
  50.             {
  51.                 if self::chmod($target511) )
  52.                 {
  53.                     $fp @fopen($target'wb');
  54.                 }
  55.             }
  56.         }
  57.  
  58.         $result false;
  59.         if ($fp !== false)
  60.         {
  61.             // First try to download directly to file if $fp !== false
  62.             $adapters self::getAdapters();
  63.             $result false;
  64.             while (!empty($adapters&& ($result === false))
  65.             {
  66.                 // Run the current download method
  67.                 $method 'get' strtoupper(array_shift($adapters));
  68.                 $result self::$method($url$fp);
  69.  
  70.                 // Check if we have a download
  71.                 if ($result === true)
  72.                 {
  73.  
  74.                     // The download is complete, close the file pointer
  75.                     @fclose($fp);
  76.  
  77.                     // If the filesize is not at least 1 byte, we consider it failed.
  78.                     clearstatcache();
  79.                     $filesize @filesize($target);
  80.                     if ($filesize <= 0)
  81.                     {
  82.                         $result false;
  83.                         $fp @fopen($target'wb');
  84.                     }
  85.                 }
  86.             }
  87.  
  88.             // If we have no download, close the file pointer
  89.             if ($result === false)
  90.             {
  91.                 @fclose($fp);
  92.             }
  93.         }
  94.  
  95.         if ($result === false)
  96.         {
  97.  
  98.             // Delete the target file if it exists
  99.             if (file_exists($target))
  100.             {
  101.                 if !@unlink($target) )
  102.                 {
  103.                     JFile::delete($target);
  104.                 }
  105.             }
  106.  
  107.             // Download and write using JFile::write();
  108.             $result JFile::write($targetself::downloadAndReturn($url));
  109.         }
  110.  
  111.         return $result;
  112.     }
  113.  
  114.     /**
  115.      * Downloads from a URL and returns the result as a string
  116.      *
  117.      * @param   string  $url  The URL to download from
  118.      *
  119.      * @return  mixed Result string on success, false on failure
  120.      *
  121.      * @since   2.5.4
  122.      */
  123.     public static function downloadAndReturn($url)
  124.     {
  125.         $adapters self::getAdapters();
  126.         $result false;
  127.  
  128.         while (!empty($adapters&& ($result === false))
  129.         {
  130.  
  131.             // Run the current download method
  132.             $method 'get' strtoupper(array_shift($adapters));
  133.             $result self::$method($urlnull);
  134.         }
  135.  
  136.         return $result;
  137.     }
  138.  
  139.     /**
  140.      * Does the server support PHP's cURL extension?
  141.      *
  142.      * @return  bool True if it is supported
  143.      *
  144.      * @since   2.5.4
  145.      */
  146.     private static function hasCURL()
  147.     {
  148.         static $result null;
  149.  
  150.         if (is_null($result))
  151.         {
  152.             $result function_exists('curl_init');
  153.         }
  154.  
  155.         return $result;
  156.     }
  157.  
  158.     /**
  159.      * Downloads the contents of a URL and writes them to disk (if $fp is not null)
  160.      * or returns them as a string (if $fp is null)
  161.      *
  162.      * @param   string    $url       The URL to download from
  163.      * @param   resource  $fp        The file pointer to download to. Omit to return the contents.
  164.      * @param   boolean   $nofollow  Should we follow 301/302/307 redirection HTTP headers?
  165.      *
  166.      * @return   bool|stringFalse on failure, true on success ($fp not null) or the URL contents (if $fp is null)
  167.      *
  168.      * @since   2.5.4
  169.      */
  170.     private static function &getCURL($url$fp null$nofollow false)
  171.     {
  172.         $ch curl_init($url);
  173.  
  174.         if !@curl_setopt($chCURLOPT_FOLLOWLOCATION1&& !$nofollow )
  175.         {
  176.  
  177.             // Safe Mode is enabled. We have to fetch the headers and
  178.             // parse any redirections present in there.
  179.             curl_setopt($chCURLOPT_AUTOREFERERtrue);
  180.             curl_setopt($chCURLOPT_FAILONERRORtrue);
  181.             curl_setopt($chCURLOPT_HEADERtrue);
  182.             curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
  183.             curl_setopt($chCURLOPT_SSL_VERIFYPEERfalse);
  184.             curl_setopt($chCURLOPT_CONNECTTIMEOUT10);
  185.             curl_setopt($chCURLOPT_TIMEOUT30);
  186.  
  187.             // Get the headers
  188.             $data curl_exec($ch);
  189.             curl_close($ch);
  190.  
  191.             // Init
  192.             $newURL $url;
  193.  
  194.             // Parse the headers
  195.             $lines explode("\n"$data);
  196.             foreach ($lines as $line)
  197.             {
  198.                 if (substr($line09== "Location:")
  199.                 {
  200.                     $newURL trim(substr($line9));
  201.                 }
  202.             }
  203.  
  204.             if ($url != $newURL)
  205.             {
  206.                 return self::getCURL($newURL$fp);
  207.             }
  208.             else
  209.             {
  210.                 return self::getCURL($newURL$fptrue);
  211.             }
  212.         }
  213.         else
  214.         {
  215.             @curl_setopt($chCURLOPT_MAXREDIRS20);
  216.             if (function_exists('set_time_limit'))
  217.             {
  218.                 set_time_limit(0);
  219.             }
  220.         }
  221.  
  222.         curl_setopt($chCURLOPT_HEADER0);
  223.         curl_setopt($chCURLOPT_SSL_VERIFYPEER0);
  224.         curl_setopt($chCURLOPT_USERAGENT'Joomla/' JVERSION);
  225.  
  226.         if (is_resource($fp))
  227.         {
  228.             curl_setopt($chCURLOPT_FILE$fp);
  229.         }
  230.         else
  231.         {
  232.             curl_setopt($chCURLOPT_RETURNTRANSFER1);
  233.         }
  234.  
  235.         $result curl_exec($ch);
  236.         curl_close($ch);
  237.  
  238.         return $result;
  239.     }
  240.  
  241.     /**
  242.      * Does the server support URL fopen() wrappers?
  243.      *
  244.      * @return  bool 
  245.      *
  246.      * @since   2.5.4
  247.      */
  248.     private static function hasFOPEN()
  249.     {
  250.         static $result null;
  251.  
  252.         if (is_null($result))
  253.         {
  254.  
  255.             // If we are not allowed to use ini_get, we assume that URL fopen is
  256.             // disabled.
  257.             if (!function_exists('ini_get'))
  258.             {
  259.                 $result false;
  260.             }
  261.             else
  262.             {
  263.                 $result ini_get('allow_url_fopen');
  264.             }
  265.         }
  266.  
  267.         return $result;
  268.     }
  269.  
  270.     /**
  271.      * Download from a URL using URL fopen() wrappers
  272.      *
  273.      * @param   string    $url  The URL to download from
  274.      * @param   resource  $fp   The file pointer to download to; leave null to return the d/l file as a string
  275.      *
  276.      * @return  bool|stringFalse on failure, true on success ($fp not null) or the URL contents (if $fp is null)
  277.      *
  278.      * @since   2.5.4
  279.      */
  280.     private static function &getFOPEN($url$fp null)
  281.     {
  282.         $result false;
  283.  
  284.         // Open the URL for reading
  285.         if (function_exists('stream_context_create'))
  286.         {
  287.             $opts stream_context_get_options(stream_context_get_default());
  288.             $opts['http']['user_agent''Joomla/' JVERSION;
  289.             $context stream_context_create($opts);
  290.             $ih @fopen($url'r'false$context);
  291.         }
  292.         else
  293.         {
  294.  
  295.             // PHP 4 way (actually, it's just a fallback)
  296.             if function_exists('ini_set') )
  297.             {
  298.                 ini_set('user_agent''Joomla/' JVERSION);
  299.             }
  300.             $ih @fopen($url'r');
  301.         }
  302.  
  303.         // If fopen() fails, abort
  304.         if !is_resource($ih) )
  305.         {
  306.             return $result;
  307.         }
  308.  
  309.         // Try to download
  310.         $bytes 0;
  311.         $result true;
  312.         $return '';
  313.         while (!feof($ih&& $result)
  314.         {
  315.             $contents fread($ih4096);
  316.             if ($contents === false)
  317.             {
  318.                 @fclose($ih);
  319.                 $result false;
  320.                 return $result;
  321.             }
  322.             else
  323.             {
  324.                 $bytes += strlen($contents);
  325.                 if (is_resource($fp))
  326.                 {
  327.                     $result @fwrite($fp$contents);
  328.                 }
  329.                 else
  330.                 {
  331.                     $return .= $contents;
  332.                     unset($contents);
  333.                 }
  334.             }
  335.         }
  336.  
  337.         @fclose($ih);
  338.  
  339.         if (is_resource($fp))
  340.         {
  341.             return $result;
  342.         }
  343.         elseif $result === true )
  344.         {
  345.             return $return;
  346.         }
  347.         else
  348.         {
  349.             return $result;
  350.         }
  351.     }
  352.  
  353.     /**
  354.      * Detect and return available download "adapters" (not really adapters, as
  355.      * we don't follow the Adapter pattern, yet)
  356.      *
  357.      * @return  array 
  358.      *
  359.      * @since   2.5.4
  360.      */
  361.     private static function getAdapters()
  362.     {
  363.         // Detect available adapters
  364.         $adapters array();
  365.         if (self::hasCURL())
  366.         {
  367.             $adapters['curl';
  368.         }
  369.         if (self::hasFOPEN())
  370.         {
  371.             $adapters['fopen';
  372.         }
  373.         return $adapters;
  374.     }
  375.  
  376.     /**
  377.      * Change the permissions of a file, optionally using FTP
  378.      *
  379.      * @param   string  $path  Absolute path to file
  380.      * @param   int     $mode  Permissions, e.g. 0755
  381.      *
  382.      * @return  boolean True on success
  383.      *
  384.      * @since   2.5.4
  385.      */
  386.     private static function chmod($path$mode)
  387.     {
  388.         if (is_string($mode))
  389.         {
  390.             $mode octdec($mode);
  391.             if ( ($mode 0600|| ($mode 0777) )
  392.             {
  393.                 $mode 0755;
  394.             }
  395.         }
  396.  
  397.         $ftpOptions JClientHelper::getCredentials('ftp');
  398.  
  399.         // Check to make sure the path valid and clean
  400.         $path JPath::clean($path);
  401.  
  402.         if ($ftpOptions['enabled'== 1)
  403.         {
  404.  
  405.             // Connect the FTP client
  406.             $ftp JClientFtp::getInstance(
  407.                 $ftpOptions['host']$ftpOptions['port']null,
  408.                 $ftpOptions['user']$ftpOptions['pass']
  409.             );
  410.         }
  411.  
  412.         if (@chmod($path$mode))
  413.         {
  414.             $ret true;
  415.         }
  416.         elseif ($ftpOptions['enabled'== 1)
  417.         {
  418.             // Translate path and delete
  419.             $path JPath::clean(str_replace(JPATH_ROOT$ftpOptions['root']$path)'/');
  420.  
  421.             // FTP connector throws an error
  422.             $ret $ftp->chmod($path$mode);
  423.         else
  424.         {
  425.             return false;
  426.         }
  427.         return $ret;
  428.     }
  429.  
  430. }

Documentation generated on Tue, 19 Nov 2013 15:01:40 +0100 by phpDocumentor 1.4.3