PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/administrator/components/com_akeeba/liveupdate/classes/download.php

https://gitlab.com/endomorphosis/OLAAaction
PHP | 337 lines | 215 code | 44 blank | 78 comment | 50 complexity | b95b46e7bb6f552f111d81fa9349a4b9 MD5 | raw file
  1. <?php
  2. /**
  3. * @package LiveUpdate
  4. * @copyright Copyright ©2011 Nicholas K. Dionysopoulos / AkeebaBackup.com
  5. * @license GNU LGPLv3 or later <http://www.gnu.org/copyleft/lesser.html>
  6. */
  7. defined('_JEXEC') or die();
  8. /**
  9. * Allows downloading packages over the web to your server
  10. */
  11. class LiveUpdateDownloadHelper
  12. {
  13. /**
  14. * Downloads from a URL and saves the result as a local file
  15. * @param <type> $url
  16. * @param <type> $target
  17. * @return bool True on success
  18. */
  19. public static function download($url, $target)
  20. {
  21. // Import Joomla! libraries
  22. jimport('joomla.filesystem.file');
  23. /** @var bool Did we try to force permissions? */
  24. $hackPermissions = false;
  25. // Make sure the target does not exist
  26. if(JFile::exists($target)) {
  27. if(!@unlink($target)) {
  28. JFile::delete($target);
  29. }
  30. }
  31. // Try to open the output file for writing
  32. $fp = @fopen($target, 'wb');
  33. if($fp === false) {
  34. // The file can not be opened for writing. Let's try a hack.
  35. if( JFile::write($target, '') ) {
  36. if( self::chmod($target, 511) ) {
  37. $fp = @fopen($target, 'wb');
  38. $hackPermissions = true;
  39. }
  40. }
  41. }
  42. $result = false;
  43. if($fp !== false)
  44. {
  45. // First try to download directly to file if $fp !== false
  46. $adapters = self::getAdapters();
  47. $result = false;
  48. while(!empty($adapters) && ($result === false)) {
  49. // Run the current download method
  50. $method = 'get' . strtoupper( array_shift($adapters) );
  51. $result = self::$method($url, $fp);
  52. // Check if we have a download
  53. if($result === true) {
  54. // The download is complete, close the file pointer
  55. @fclose($fp);
  56. // If the filesize is not at least 1 byte, we consider it failed.
  57. clearstatcache();
  58. $filesize = @filesize($target);
  59. if($filesize <= 0) {
  60. $result = false;
  61. $fp = @fopen($target, 'wb');
  62. }
  63. }
  64. }
  65. // If we have no download, close the file pointer
  66. if($result === false) {
  67. @fclose($fp);
  68. }
  69. }
  70. if($result === false)
  71. {
  72. // Delete the target file if it exists
  73. if(file_exists($target)) {
  74. if( !@unlink($target) ) {
  75. JFile::delete($target);
  76. }
  77. }
  78. // Download and write using JFile::write();
  79. $result = JFile::write($target, self::downloadAndReturn($url) );
  80. }
  81. return $result;
  82. }
  83. /**
  84. * Downloads from a URL and returns the result as a string
  85. * @param <type> $url
  86. * @return mixed Result string on success, false on failure
  87. */
  88. public static function downloadAndReturn($url)
  89. {
  90. $adapters = self::getAdapters();
  91. $result = false;
  92. while(!empty($adapters) && ($result === false)) {
  93. // Run the current download method
  94. $method = 'get' . strtoupper( array_shift($adapters) );
  95. $result = self::$method($url, null);
  96. }
  97. return $result;
  98. }
  99. /**
  100. * Does the server support PHP's cURL extension?
  101. * @return bool True if it is supported
  102. */
  103. private static function hasCURL()
  104. {
  105. static $result = null;
  106. if(is_null($result))
  107. {
  108. $result = function_exists('curl_init');
  109. }
  110. return $result;
  111. }
  112. /**
  113. * Downloads the contents of a URL and writes them to disk (if $fp is not null)
  114. * or returns them as a string (if $fp is null)
  115. * @param string $url The URL to download from
  116. * @param resource $fp The file pointer to download to. Omit to return the contents.
  117. * @return bool|string False on failure, true on success ($fp not null) or the URL contents (if $fp is null)
  118. */
  119. private static function &getCURL($url, $fp = null, $nofollow = false)
  120. {
  121. $result = false;
  122. $ch = curl_init($url);
  123. if( !@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1) && !$nofollow ) {
  124. // Safe Mode is enabled. We have to fetch the headers and
  125. // parse any redirections present in there.
  126. curl_setopt($ch, CURLOPT_AUTOREFERER, true);
  127. curl_setopt($ch, CURLOPT_FAILONERROR, true);
  128. curl_setopt($ch, CURLOPT_HEADER, true);
  129. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  130. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  131. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
  132. curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  133. // Get the headers
  134. $data = curl_exec($ch);
  135. curl_close($ch);
  136. // Init
  137. $newURL = $url;
  138. // Parse the headers
  139. $lines = explode("\n", $data);
  140. foreach($lines as $line) {
  141. if(substr($line, 0, 9) == "Location:") {
  142. $newURL = trim(substr($line,9));
  143. }
  144. }
  145. // Download from the new URL
  146. if($url != $newURL) {
  147. return self::getCURL($newURL, $fp);
  148. } else {
  149. return self::getCURL($newURL, $fp, true);
  150. }
  151. } else {
  152. @curl_setopt($ch, CURLOPT_MAXREDIRS, 20);
  153. }
  154. curl_setopt($ch, CURLOPT_AUTOREFERER, true);
  155. curl_setopt($ch, CURLOPT_FAILONERROR, true);
  156. curl_setopt($ch, CURLOPT_HEADER, false);
  157. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  158. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
  159. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
  160. curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  161. // Pretend we are IE7, so that webservers play nice with us
  162. curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0)');
  163. if(is_resource($fp)) {
  164. curl_setopt($ch, CURLOPT_FILE, $fp);
  165. }
  166. $result = curl_exec($ch);
  167. curl_close($ch);
  168. return $result;
  169. }
  170. /**
  171. * Does the server support URL fopen() wrappers?
  172. * @return bool
  173. */
  174. private static function hasFOPEN()
  175. {
  176. static $result = null;
  177. if(is_null($result))
  178. {
  179. // If we are not allowed to use ini_get, we assume that URL fopen is
  180. // disabled.
  181. if(!function_exists('ini_get')) {
  182. $result = false;
  183. } else {
  184. $result = ini_get('allow_url_fopen');
  185. }
  186. }
  187. return $result;
  188. }
  189. private static function &getFOPEN($url, $fp = null)
  190. {
  191. $result = false;
  192. // Track errors
  193. if( function_exists('ini_set') ) {
  194. $track_errors = ini_set('track_errors',true);
  195. }
  196. // Open the URL for reading
  197. if(function_exists('stream_context_create')) {
  198. // PHP 5+ way (best)
  199. $httpopts = Array('user_agent'=>'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0)');
  200. $context = stream_context_create( array( 'http' => $httpopts ) );
  201. $ih = @fopen($url, 'r', false, $context);
  202. } else {
  203. // PHP 4 way (actually, it's just a fallback as we can't run Admin Tools in PHP4)
  204. if( function_exists('ini_set') ) {
  205. ini_set('user_agent', 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; .NET CLR 1.0.3705; .NET CLR 1.1.4322; Media Center PC 4.0)');
  206. }
  207. $ih = @fopen($url, 'r');
  208. }
  209. // If fopen() fails, abort
  210. if( !is_resource($ih) ) {
  211. return $result;
  212. }
  213. // Try to download
  214. $bytes = 0;
  215. $result = true;
  216. $return = '';
  217. while (!feof($ih) && $result)
  218. {
  219. $contents = fread($ih, 4096);
  220. if ($contents === false) {
  221. @fclose($ih);
  222. $result = false;
  223. return $result;
  224. } else {
  225. $bytes += strlen($contents);
  226. if(is_resource($fp)) {
  227. $result = @fwrite($fp, $contents);
  228. } else {
  229. $return .= $contents;
  230. unset($contents);
  231. }
  232. }
  233. }
  234. @fclose($ih);
  235. if(is_resource($fp)) {
  236. return $result;
  237. } elseif( $result === true ) {
  238. return $return;
  239. } else {
  240. return $result;
  241. }
  242. }
  243. /**
  244. * Detect and return available download adapters
  245. * @return array
  246. */
  247. private static function getAdapters()
  248. {
  249. // Detect available adapters
  250. $adapters = array();
  251. if(self::hasCURL()) $adapters[] = 'curl';
  252. if(self::hasFOPEN()) $adapters[] = 'fopen';
  253. return $adapters;
  254. }
  255. /**
  256. * Change the permissions of a file, optionally using FTP
  257. * @param string $file Absolute path to file
  258. * @param int $mode Permissions, e.g. 0755
  259. */
  260. private static function chmod($path, $mode)
  261. {
  262. if(is_string($mode))
  263. {
  264. $mode = octdec($mode);
  265. if( ($mode < 0600) || ($mode > 0777) ) $mode = 0755;
  266. }
  267. // Initialize variables
  268. jimport('joomla.client.helper');
  269. $ftpOptions = JClientHelper::getCredentials('ftp');
  270. // Check to make sure the path valid and clean
  271. $path = JPath::clean($path);
  272. if ($ftpOptions['enabled'] == 1) {
  273. // Connect the FTP client
  274. jimport('joomla.client.ftp');
  275. $ftp = &JFTP::getInstance(
  276. $ftpOptions['host'], $ftpOptions['port'], null,
  277. $ftpOptions['user'], $ftpOptions['pass']
  278. );
  279. }
  280. if(@chmod($path, $mode))
  281. {
  282. $ret = true;
  283. } elseif ($ftpOptions['enabled'] == 1) {
  284. // Translate path and delete
  285. jimport('joomla.client.ftp');
  286. $path = JPath::clean(str_replace(JPATH_ROOT, $ftpOptions['root'], $path), '/');
  287. // FTP connector throws an error
  288. $ret = $ftp->chmod($path, $mode);
  289. } else {
  290. return false;
  291. }
  292. }
  293. }