PageRenderTime 53ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/helpers/updates.php

https://github.com/widgetfactory/jce-admin
PHP | 448 lines | 262 code | 71 blank | 115 comment | 66 complexity | 00980b307a790db1cb253e278124850e MD5 | raw file
  1. <?php
  2. /**
  3. * @package JCE
  4. * @copyright Copyright (c) 2010-2013 Nicholas K. Dionysopoulos / AkeebaBackup.com
  5. * @copyright Copyright (c) 2009-2014 Ryan Demmer. All rights reserved.
  6. * @license GNU LGPLv3 or later <http://www.gnu.org/copyleft/lesser.html>
  7. *
  8. * Based on the LiveUpdateDownloadHelper class from Akeeba LiveUpdate
  9. */
  10. defined('_JEXEC') or die();
  11. /**
  12. * Check for and download updates from a remote server
  13. */
  14. class UpdatesHelper {
  15. private static function applyCACert(&$ch) {
  16. $cacert = dirname(__FILE__) . '/cacert.pem';
  17. if (file_exists($cacert)) {
  18. @curl_setopt($ch, CURLOPT_CAINFO, $cacert);
  19. return true;
  20. }
  21. return false;
  22. }
  23. /**
  24. * Fetches update information from the server using cURL or fopen
  25. * @param string $url The URL to check
  26. * @param string $data Data to send via POST
  27. *
  28. * @return mixed Result string on success, false on failure
  29. */
  30. public function check($url, $data) {
  31. $result = false;
  32. if (self::hasCURL()) {
  33. $ch = curl_init($url);
  34. self::applyCACert($ch);
  35. curl_setopt($ch, CURLOPT_HEADER, 0);
  36. // Pretend we are Firefox, so that webservers play nice with us
  37. curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.2.14) Gecko/20110105 Firefox/3.6.14');
  38. curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
  39. curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  40. curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  41. // The @ sign allows the next line to fail if open_basedir is set or if safe mode is enabled
  42. @curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
  43. @curl_setopt($ch, CURLOPT_MAXREDIRS, 20);
  44. // add post data
  45. if ($data) {
  46. curl_setopt($ch, CURLOPT_POST, 1);
  47. curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
  48. }
  49. $result = curl_exec($ch);
  50. // file download
  51. if ($result === false) {
  52. return array('error' => 'CURL ERROR : ' . curl_errno($ch) . ' - ' . curl_error($ch));
  53. }
  54. curl_close($ch);
  55. } else if (self::hasFOPEN()) {
  56. $options = array('http' => array('method' => 'POST', 'timeout' => 10, 'content' => $data));
  57. $context = stream_context_create($options);
  58. $result = @file_get_contents($url, false, $context);
  59. if ($result === false) {
  60. return array('error' => WFText::_('Update check failed : Invalid response from update server'));
  61. }
  62. }
  63. return $result;
  64. }
  65. /**
  66. * Downloads from a URL and saves the result as a local file
  67. *
  68. * @param string $url The URL to fetch
  69. * @param string $target Where to save the file
  70. *
  71. * @return boolean True on success
  72. */
  73. public static function download($url, $target) {
  74. // Import Joomla! libraries
  75. JLoader::import('joomla.filesystem.file');
  76. /** @var bool Did we try to force permissions? */
  77. $hackPermissions = false;
  78. // Make sure the target does not exist
  79. if (JFile::exists($target)) {
  80. if (!@unlink($target)) {
  81. JFile::delete($target);
  82. }
  83. }
  84. // Try to open the output file for writing
  85. $fp = @fopen($target, 'wb');
  86. if ($fp === false) {
  87. // The file can not be opened for writing. Let's try a hack.
  88. $empty = '';
  89. if (JFile::write($target, $empty)) {
  90. if (self::chmod($target, 511)) {
  91. $fp = @fopen($target, 'wb');
  92. $hackPermissions = true;
  93. }
  94. }
  95. }
  96. $result = false;
  97. if ($fp !== false) {
  98. // First try to download directly to file if $fp !== false
  99. $adapters = self::getAdapters();
  100. $result = false;
  101. while (!empty($adapters) && ($result === false)) {
  102. // Run the current download method
  103. $method = 'get' . strtoupper(array_shift($adapters));
  104. $result = self::$method($url, $fp);
  105. // Check if we have a download
  106. if ($result === true) {
  107. // The download is complete, close the file pointer
  108. @fclose($fp);
  109. // If the filesize is not at least 1 byte, we consider it failed.
  110. clearstatcache();
  111. $filesize = @filesize($target);
  112. if ($filesize <= 0) {
  113. $result = false;
  114. $fp = @fopen($target, 'wb');
  115. }
  116. }
  117. }
  118. // If we have no download, close the file pointer
  119. if ($result === false) {
  120. @fclose($fp);
  121. }
  122. }
  123. if ($result === false) {
  124. // Delete the target file if it exists
  125. if (file_exists($target)) {
  126. if (!@unlink($target)) {
  127. JFile::delete($target);
  128. }
  129. }
  130. // Download and write using JFile::write();
  131. $result = JFile::write($target, self::downloadAndReturn($url));
  132. }
  133. return $result;
  134. }
  135. /**
  136. * Downloads from a URL and returns the result as a string
  137. *
  138. * @param string $url The URL to download from
  139. *
  140. * @return mixed Result string on success, false on failure
  141. */
  142. public static function downloadAndReturn($url) {
  143. $adapters = self::getAdapters();
  144. $result = false;
  145. while (!empty($adapters) && ($result === false)) {
  146. // Run the current download method
  147. $method = 'get' . strtoupper(array_shift($adapters));
  148. $result = self::$method($url, null);
  149. }
  150. return $result;
  151. }
  152. /**
  153. * Does the server support PHP's cURL extension?
  154. *
  155. * @return boolean True if it is supported
  156. */
  157. public static function hasCURL() {
  158. static $result = null;
  159. if (is_null($result)) {
  160. $result = function_exists('curl_init');
  161. if ($result) {
  162. $cacert = dirname(__FILE__) . '/cacert.pem';
  163. // check for SSL support
  164. $version = curl_version();
  165. $ssl_supported = ($version['features'] & CURL_VERSION_SSL);
  166. $result = (bool) $ssl_supported && file_exists($cacert);
  167. }
  168. }
  169. return $result;
  170. }
  171. /**
  172. * Downloads the contents of a URL and writes them to disk (if $fp is not null)
  173. * or returns them as a string (if $fp is null) using cURL
  174. *
  175. * @param string $url The URL to download from
  176. * @param resource $fp The file pointer to download to. Omit to return the contents.
  177. *
  178. * @return boolean|string False on failure, true on success ($fp not null) or the URL contents (if $fp is null)
  179. */
  180. private static function &getCURL($url, $fp = null, $nofollow = false) {
  181. $result = false;
  182. $ch = curl_init($url);
  183. self::applyCACert($ch);
  184. if (!@curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1) && !$nofollow) {
  185. // Safe Mode is enabled. We have to fetch the headers and
  186. // parse any redirections present in there.
  187. curl_setopt($ch, CURLOPT_AUTOREFERER, true);
  188. curl_setopt($ch, CURLOPT_FAILONERROR, true);
  189. curl_setopt($ch, CURLOPT_HEADER, true);
  190. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  191. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
  192. curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  193. // Get the headers
  194. $data = curl_exec($ch);
  195. curl_close($ch);
  196. // Init
  197. $newURL = $url;
  198. // Parse the headers
  199. $lines = explode("\n", $data);
  200. foreach ($lines as $line) {
  201. if (substr($line, 0, 9) == "Location:") {
  202. $newURL = trim(substr($line, 9));
  203. }
  204. }
  205. // Download from the new URL
  206. if ($url != $newURL) {
  207. return self::getCURL($newURL, $fp);
  208. } else {
  209. return self::getCURL($newURL, $fp, true);
  210. }
  211. } else {
  212. @curl_setopt($ch, CURLOPT_MAXREDIRS, 20);
  213. }
  214. curl_setopt($ch, CURLOPT_AUTOREFERER, true);
  215. curl_setopt($ch, CURLOPT_FAILONERROR, true);
  216. curl_setopt($ch, CURLOPT_HEADER, false);
  217. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  218. curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
  219. curl_setopt($ch, CURLOPT_TIMEOUT, 30);
  220. // Pretend we are IE7, so that webservers play nice with us
  221. 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)');
  222. if (is_resource($fp)) {
  223. curl_setopt($ch, CURLOPT_FILE, $fp);
  224. }
  225. $result = curl_exec($ch);
  226. curl_close($ch);
  227. return $result;
  228. }
  229. /**
  230. * Does the server support URL fopen() wrappers?
  231. *
  232. * @return boolean
  233. */
  234. public static function hasFOPEN() {
  235. static $result = null;
  236. if (is_null($result)) {
  237. // If we are not allowed to use ini_get, we assume that URL fopen is
  238. // disabled.
  239. if (!function_exists('ini_get')) {
  240. $result = false;
  241. } else {
  242. // get wrappers
  243. $wrappers = stream_get_wrappers();
  244. $result = ini_get('allow_url_fopen') && in_array('https', $wrappers);
  245. }
  246. }
  247. return $result;
  248. }
  249. /**
  250. * Downloads the contents of a URL and writes them to disk (if $fp is not null)
  251. * or returns them as a string (if $fp is null) using fopen() URL wrappers
  252. *
  253. * @param string $url The URL to download from
  254. * @param resource $fp The file pointer to download to. Omit to return the contents.
  255. *
  256. * @return boolean|string False on failure, true on success ($fp not null) or the URL contents (if $fp is null)
  257. */
  258. private static function &getFOPEN($url, $fp = null) {
  259. $result = false;
  260. // Track errors
  261. if (function_exists('ini_set')) {
  262. $track_errors = ini_set('track_errors', true);
  263. }
  264. // Open the URL for reading
  265. if (function_exists('stream_context_create')) {
  266. // PHP 5+ way (best)
  267. $httpopts = array(
  268. '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)',
  269. 'timeout' => 10.0,
  270. );
  271. $context = stream_context_create(array('http' => $httpopts));
  272. $ih = @fopen($url, 'r', false, $context);
  273. } else {
  274. // PHP 4 way (actually, it's just a fallback as we can't run this code in PHP4)
  275. if (function_exists('ini_set')) {
  276. 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)');
  277. }
  278. $ih = @fopen($url, 'r');
  279. }
  280. // If fopen() fails, abort
  281. if (!is_resource($ih)) {
  282. return $result;
  283. }
  284. // Try to download
  285. $bytes = 0;
  286. $result = true;
  287. $return = '';
  288. while (!feof($ih) && $result) {
  289. $contents = fread($ih, 4096);
  290. if ($contents === false) {
  291. @fclose($ih);
  292. $result = false;
  293. return $result;
  294. } else {
  295. $bytes += strlen($contents);
  296. if (is_resource($fp)) {
  297. $result = @fwrite($fp, $contents);
  298. } else {
  299. $return .= $contents;
  300. unset($contents);
  301. }
  302. }
  303. }
  304. @fclose($ih);
  305. if (is_resource($fp)) {
  306. return $result;
  307. } elseif ($result === true) {
  308. return $return;
  309. } else {
  310. return $result;
  311. }
  312. }
  313. /**
  314. * Detect and return available download methods
  315. *
  316. * @return array
  317. */
  318. private static function getAdapters() {
  319. // Detect available adapters
  320. $adapters = array();
  321. if (self::hasCURL())
  322. $adapters[] = 'curl';
  323. if (self::hasFOPEN())
  324. $adapters[] = 'fopen';
  325. return $adapters;
  326. }
  327. /**
  328. * Change the permissions of a file, optionally using FTP
  329. *
  330. * @param string $file Absolute path to file
  331. * @param int $mode Permissions, e.g. 0755
  332. *
  333. * @return boolean Ture if successful
  334. */
  335. private static function chmod($path, $mode) {
  336. if (is_string($mode)) {
  337. $mode = octdec($mode);
  338. if (($mode < 0600) || ($mode > 0777))
  339. $mode = 0755;
  340. }
  341. // Initialize variables
  342. JLoader::import('joomla.client.helper');
  343. $ftpOptions = JClientHelper::getCredentials('ftp');
  344. // Check to make sure the path valid and clean
  345. $path = JPath::clean($path);
  346. if ($ftpOptions['enabled'] == 1) {
  347. // Connect the FTP client
  348. JLoader::import('joomla.client.ftp');
  349. if (version_compare(JVERSION, '3.0', 'ge')) {
  350. $ftp = JClientFTP::getInstance(
  351. $ftpOptions['host'], $ftpOptions['port'], array(), $ftpOptions['user'], $ftpOptions['pass']
  352. );
  353. } else {
  354. if (version_compare(JVERSION, '3.0', 'ge')) {
  355. $ftp = JClientFTP::getInstance(
  356. $ftpOptions['host'], $ftpOptions['port'], array(), $ftpOptions['user'], $ftpOptions['pass']
  357. );
  358. } else {
  359. $ftp = JFTP::getInstance(
  360. $ftpOptions['host'], $ftpOptions['port'], array(), $ftpOptions['user'], $ftpOptions['pass']
  361. );
  362. }
  363. }
  364. }
  365. if (@chmod($path, $mode)) {
  366. $ret = true;
  367. } elseif ($ftpOptions['enabled'] == 1) {
  368. // Translate path and delete
  369. JLoader::import('joomla.client.ftp');
  370. $path = JPath::clean(str_replace(JPATH_ROOT, $ftpOptions['root'], $path), '/');
  371. // FTP connector throws an error
  372. $ret = $ftp->chmod($path, $mode);
  373. } else {
  374. return false;
  375. }
  376. }
  377. }