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

/administrator/components/com_joomlaupdate/helpers/download.php

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