/administrator/components/com_admintools/models/update.php
PHP | 476 lines | 316 code | 56 blank | 104 comment | 49 complexity | 65f42c502b48e2340c2e54f8ab5b39f0 MD5 | raw file
- <?php
- /**
- * @package AdminTools
- * @copyright Copyright (c)2010 Nicholas K. Dionysopoulos
- * @license GNU General Public License version 3, or later
- * @version $Id: update.php 82 2010-10-23 19:13:50Z nikosdion $
- */
- // Protect from unauthorized access
- defined('_JEXEC') or die('Restricted Access');
- jimport('joomla.application.component.model');
- /**
- * The Live Update model
- *
- */
- class AdmintoolsModelUpdate extends JModel
- {
- private $update_url = '';
- private $isPro = false;
- /**
- * Public constructor
- * @param unknown_type $config
- */
- public function __construct( $config = array() )
- {
- parent::__construct($config);
- // Determine the appropriate update URL based on whether we're on Core or Professional edition
- jimport('joomla.filesystem.file');
- $this->isPro = JFile::exists(JPATH_COMPONENT_ADMINISTRATOR.DS.'tables'.DS.'redirs.php');
- if($this->isPro)
- {
- $this->update_url = 'https://www.akeebabackup.com/updates/atpro.ini';
- }
- else
- {
- $this->update_url = 'https://www.akeebabackup.com/updates/atcore.ini';
- }
- }
- /**
- * Does the server support URL fopen() wrappers?
- * @return bool
- */
- private function hasURLfopen()
- {
- // If we are not allowed to use ini_get, we assume that URL fopen is
- // disabled.
- if(!function_exists('ini_get'))
- return false;
- if( !ini_get('allow_url_fopen') )
- return false;
- return true;
- }
- /**
- * Does the server support the cURL extension?
- * @return bool
- */
- private function hascURL()
- {
- if(!function_exists('curl_exec'))
- {
- return false;
- }
- return true;
- }
- /**
- * Returns the date and time when the last update check was made.
- * @return JDate
- */
- private function lastUpdateCheck()
- {
- // Get a reference to component's parameters
- $component =& JComponentHelper::getComponent( 'com_admintools' );
- $params = new JParameter($component->params);
- $lastdate = $params->get('lastupdatecheck', '2009-04-02');
- jimport('joomla.utilities.date');
- $date = new JDate($lastdate);
- return $date;
- }
- /**
- * Gets an object with the latest version information, taken from the update.ini data
- * @return JObject|bool An object holding the data, or false on failure
- */
- private function getLatestVersion($force = false)
- {
- $inidata = false;
- jimport('joomla.utilities.date');
- $curdate = new JDate();
- $lastdate = $this->lastUpdateCheck();
- $difference = ($curdate->toUnix(false) - $lastdate->toUnix(false)) / 3600;
- $inidata = $this->getUpdateINIcached();
- $cached = false;
- // Make sure we ask the server at most every 24 hrs (unless $force is true)
- if( ($difference < 24) && (!empty($inidata)) && (!$force) )
- {
- $cached = true;
- // Cached INI data is valid
- }
- // Prefer to use cURL if it exists and we don't have cached data
- elseif( $this->hascURL() )
- {
- $inidata = $this->getUpdateINIcURL();
- }
- // If cURL doesn't exist, or if it returned an error, try URL fopen() wrappers
- elseif( $this->hasURLfopen() )
- {
- $inidata = $this->getUpdateINIfopen();
- }
- // Make sure we do have INI data and not junk...
- if($inidata != false)
- {
- if( strpos($inidata, '; Live Update provision file') !== 0 )
- {
- $inidata = false;
- }
- }
- // If we have a valid update.ini, update the cache and read the version information
- if($inidata != false)
- {
- if(!$cached) $this->setUpdateINIcached($inidata);
- require_once JPATH_COMPONENT_ADMINISTRATOR.DS.'helpers'.DS.'ini.php';
- $parsed=AdmintoolsHelperINI::parse_ini_file($inidata, false, true);
- // Determine status by parsing the version
- $version = $parsed['version'];
- if( preg_match('#^[0-9\.]*a[0-9\.]*#', $version) == 1 )
- {
- $status = 'alpha';
- } elseif( preg_match('#^[0-9\.]*b[0-9\.]*#', $version) == 1 )
- {
- $status = 'beta';
- } elseif( preg_match('#^[0-9\.]*$#', $version) == 1 )
- {
- $status = 'stable';
- } else {
- $status = 'svn';
- }
- // Special processing for the link in Admin Tools Professional
- $suffix = '';
- if($this->isPro)
- {
- $component =& JComponentHelper::getComponent( 'com_admintools' );
- $params = new JParameter($component->params);
- $username = $params->get('update_username', '');
- $password = $params->get('update_password', '');
- if( !empty($username) && !empty($password) )
- {
- $suffix = '?username='.urlencode($username).'&password='.urlencode($password).'&format=raw';
- }
- }
- $ret = new JObject;
- $ret->version = $parsed['version'];
- $ret->status = $status;
- $ret->reldate = $parsed['date'];
- $ret->url = $parsed['link'];
- $ret->urlsuffix = $suffix;
- return $ret;
- }
- return false;
- }
- /**
- * Retrieves the update.ini data using URL fopen() wrappers
- * @return string|bool The update.ini contents, or FALSE on failure
- */
- private function getUpdateINIfopen()
- {
- return @file_get_contents($this->update_url);
- }
- /**
- * Retrieves the update.ini data using cURL extention calls
- * @return string|bool The update.ini contents, or FALSE on failure
- */
- private function getUpdateINIcURL()
- {
- $process = curl_init($this->update_url);
- curl_setopt($process, CURLOPT_HEADER, 0);
- // Pretend we are IE7, so that webservers play nice with us
- curl_setopt($process, 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)');
- curl_setopt($process,CURLOPT_ENCODING , 'gzip');
- curl_setopt($process, CURLOPT_TIMEOUT, 5);
- curl_setopt($process, CURLOPT_RETURNTRANSFER, 1);
- curl_setopt($process, CURLOPT_SSL_VERIFYPEER, false);
- // The @ sign allows the next line to fail if open_basedir is set or if safe mode is enabled
- @curl_setopt($process, CURLOPT_FOLLOWLOCATION, 1);
- @curl_setopt($process, CURLOPT_MAXREDIRS, 20);
- $inidata = curl_exec($process);
- curl_close($process);
- return $inidata;
- }
- private function getUpdateINIcached()
- {
- $component =& JComponentHelper::getComponent( 'com_admintools' );
- $params = new JParameter($component->params);
- $inidata = $params->get('updateini', "");
- return json_decode($inidata);
- }
- /**
- * Caches the update.ini contents to database
- * @param $inidata string The update.ini data
- */
- private function setUpdateINIcached($inidata)
- {
- $component =& JComponentHelper::getComponent( 'com_admintools' );
- $params = new JParameter($component->params);
- jimport('joomla.utilities.date');
- $date = new JDate();
- $params->set('updateini', json_encode($inidata) );
- $params->set('lastupdatecheck', $date->toUnix(false));
- $db =& JFactory::getDBO();
- $data = $params->toString();
- global $mainframe;
- if( !is_object($mainframe) )
- {
- // Joomla! 1.6
- $sql = 'UPDATE `#__extensions` SET `params` = '.$db->Quote($data).' WHERE '.
- "`element` = 'com_admintools' AND `type` = 'component'";
- }
- else
- {
- // Joomla! 1.5
- $sql = 'UPDATE `#__components` SET `params` = '.$db->Quote($data).' WHERE '.
- "`option` = 'com_admintools' AND `parent` = 0 AND `menuid` = 0";
- }
- $db->setQuery($sql);
- $db->query();
- }
- /**
- * Is the Live Update supported on this server?
- * @return bool
- */
- public function isLiveUpdateSupported()
- {
- return $this->hasURLfopen() || $this->hascURL();
- }
- /**
- * Searches for updates and returns an object containing update information
- * @return JObject An object with members: supported, update_available,
- * current_version, current_date, latest_version, latest_date,
- * package_url
- */
- public function &getUpdates($force = false)
- {
- jimport('joomla.utilities.date');
- $ret = new JObject();
- if(!$this->isLiveUpdateSupported())
- {
- $ret->supported = false;
- $ret->update_available = false;
- return $ret;
- }
- else
- {
- $ret->supported = true;
- $update = $this->getLatestVersion($force);
- // FIX 2.3: Fail gracefully if the update data couldn't be retrieved
- if(!is_object($update) || ($update === false))
- {
- $ret->supported = false;
- $ret->update_available = false;
- return $ret;
- }
- // Check if we need to upgrade, by release date
- jimport('joomla.utilities.date');
- require_once JPATH_COMPONENT_ADMINISTRATOR.DS.'version.php';
- $curdate = new JDate(ADMINTOOLS_DATE);
- $curdate = $curdate->toUnix(false);
- $relobject = new JDate($update->reldate);
- $reldate = $relobject->toUnix(false);
- $ret->latest_date = $relobject->toFormat('%Y-%m-%d');
- $version = ADMINTOOLS_VERSION;
- if( preg_match('#^[0-9\.]*a[0-9\.]*#', $version) == 1 )
- {
- $status = 'alpha';
- } elseif( preg_match('#^[0-9\.]*b[0-9\.]*#', $version) == 1 )
- {
- $status = 'beta';
- } elseif( preg_match('#^[0-9\.]*$#', $version) == 1 )
- {
- $status = 'stable';
- } else {
- $status = 'svn';
- }
- $ret->update_available = ($reldate > $curdate);
- $ret->current_version = ADMINTOOLS_VERSION;
- $ret->current_date = ADMINTOOLS_DATE;
- $ret->current_status = $status;
- $ret->latest_version = $update->version;
- $ret->status = $update->status;
- $ret->package_url = $update->url;
- $ret->package_url_suffix = $update->urlsuffix;
- return $ret;
- }
- }
- function downloadPackage($url, $target)
- {
- jimport('joomla.filesystem.file');
- // PART I. CREATE AN OUTPUT FILE
- // I'm hating myself for this... It is ugly, UGLY and OUTRIGHT UGLY, but my users
- // seem to not be able to handle permissions of the tmp directory correctly. *sigh*
- // In order to better be safe than sorry, we create the file with JFile::write(),
- // try to write to it, or chmod it to all writable and reopen if we couldn't.
- // Brian, if you get to read this: I also dispose of the file later on, so don't
- // red-flag this component, okay?
- // i. Try to create the file with JFile
- $data = '';
- $result = JFile::write($target, $data);
- if($result === false) {
- // Uh-oh! JFile couldn't write to the file.
- return false;
- }
- // ii. Moment of truth: try to open write-only
- $fp = @fopen($target, 'wb');
- if( $fp === false )
- {
- // iii. The reason I hate myself right now: mess around w/ permissions
- // Try to chmod to all writable
- $fixpermsModel = JModel::getInstance('Fixperms','AdmintoolsModel');
- $fixpermsModel->chmod($target, 511);
- // Try to open for writing
- $fp = fopen($target, 'wb');
- // If we still can't write to it, let's fail
- if( $fp === false ) return false;
- }
- $use_fopen = false;
- if(function_exists('curl_exec'))
- {
- // By default, try using cURL
- $process = curl_init($url);
- curl_setopt($process, CURLOPT_AUTOREFERER, true);
- curl_setopt($process, CURLOPT_FAILONERROR, true);
- @curl_setopt($process, CURLOPT_FOLLOWLOCATION, true);
- curl_setopt($process, CURLOPT_HEADER, false);
- curl_setopt($process, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($process, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($process, CURLOPT_CONNECTTIMEOUT, 10);
- curl_setopt($process, CURLOPT_TIMEOUT, 30);
- @curl_setopt($process, CURLOPT_MAXREDIRS, 20);
- // Pretend we are IE7, so that webservers play nice with us
- curl_setopt($process, 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)');
- curl_setopt($process, CURLOPT_FILE, $fp);
- $result = curl_exec($process);
- curl_close($process);
- fclose($fp);
- clearstatcache();
- if( filesize($target) == 0 ) {
- // Sometimes cURL silently fails. Bad boy. Bad, bad boy!
- $use_fopen = true;
- $fp = @fopen($target, 'wb');
- }
- }
- else
- {
- $use_fopen = true;
- }
- if($use_fopen) {
- // Track errors
- $track_errors = ini_set('track_errors',true);
- // Open the URL for reading
- if(function_exists('stream_context_create')) {
- // PHP 5+ way (best)
- $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)');
- $context = stream_context_create( array( 'http' => $httpopts ) );
- $ih = @fopen($url, 'r', false, $context);
- } else {
- // PHP 4 way (actually, it's just a fallback as we can't run Admin Tools in PHP4)
- 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)');
- $ih = @fopen($url, 'r');
- }
- // If the fopen() fails, we fail.
- return false;
- // Download
- $bytes = 0;
- $result = true;
- while (!feof($ih) && $result)
- {
- $contents = fread($ih, 4096);
- if ($contents == false) {
- @fclose($ih);
- JError::raiseError('500',"Downloading $url failed after $bytes bytes");
- $result = false;
- } else {
- $bytes += strlen($contents);
- fwrite($fp, $contents);
- }
- }
- // Close the handlers
- @fclose($ih);
- @fclose($fp);
- }
- // In case something went foul, let's try to make things right
- if(function_exists('curl_exec') && ($result === false))
- {
- // I will try to download to memory and write to disk using JFile::write().
- // Note: when doing a full reinstall this will most likely cause a memory outage :p
- // By default, try using cURL
- $process = curl_init($url);
- curl_setopt($process, CURLOPT_AUTOREFERER, true);
- curl_setopt($process, CURLOPT_FAILONERROR, true);
- @curl_setopt($process, CURLOPT_FOLLOWLOCATION, true);
- curl_setopt($process, CURLOPT_HEADER, false);
- curl_setopt($process, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($process, CURLOPT_SSL_VERIFYPEER, false);
- curl_setopt($process, CURLOPT_CONNECTTIMEOUT, 10);
- curl_setopt($process, CURLOPT_TIMEOUT, 30);
- @curl_setopt($process, CURLOPT_MAXREDIRS, 20);
- // Pretend we are IE7, so that webservers play nice with us
- curl_setopt($process, 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)');
- $result = curl_exec($process);
- curl_close($process);
- if($result !== false) {
- $result = JFile::write($target, $result);
- }
- }
- // If the process failed, we fail. Simple, huh?
- if($result === false) return false;
- // If the process succeedeed:
- // i. Fix the permissions to 0644
- $fixpermsModel = JModel::getInstance('Fixperms','AdmintoolsModel');
- $fixpermsModel->chmod($target, 0644);
- // ii. Return the base name
- return basename($target);
- }
- }