1<?php 2/** 3 * EGroupware API - Check for updates 4 * 5 * @link http://www.egroupware.org 6 * @author Ralf Becker <RalfBecker-AT-outdoor-training.de> 7 * @license http://opensource.org/licenses/gpl-license.php GPL - GNU General Public License 8 * @package api 9 * @subpackage framework 10 * @access public 11 */ 12 13namespace EGroupware\Api\Framework; 14 15use EGroupware\Api\Html; 16use EGroupware\Api\Cache; 17use EGroupware\Api; 18 19/** 20 * Check for updates 21 * 22 * https://www.egroupware.org/currentversion 23 * 24 * Contains multiple lines with version numbers: 25 * 1. current stable version eg. 17.1.20180118 26 * 2. last stable security update eg. 17.1.20180118 27 * 3. last old-stable security up.eg. 16.1.20171106 (only if that is still secure!) 28 * 4. further old secure versions, if available 29 */ 30class Updates 31{ 32 /** 33 * URL to check for security or maintenance updates 34 */ 35 const CURRENT_VERSION_URL = 'https://www.egroupware.org/currentversion'; 36 /** 37 * How long to cache (in secs) / often to check for updates 38 */ 39 const VERSIONS_CACHE_TIMEOUT = 7200; 40 /** 41 * After how many days of not applied security updates, start warning non-admins too 42 */ 43 const WARN_USERS_DAYS = 5; 44 45 /** 46 * Get versions of available updates 47 * 48 * @param string $api =null major api version to return security for, default latest 49 * @return array verions for keys "current" and "security" 50 */ 51 public static function available($api=null) 52 { 53 $versions = Cache::getTree(__CLASS__, 'versions', function() use ($api) 54 { 55 $versions = array(); 56 $security = null; 57 if (($remote = file_get_contents(self::CURRENT_VERSION_URL, false, Api\Framework::proxy_context()))) 58 { 59 $all_versions = explode("\n", $remote); 60 $current = array_shift($all_versions); 61 if (empty($all_versions)) $all_versions = array($current); 62 // find latest security release for optional API version 63 foreach(array_reverse($all_versions) as $security) 64 { 65 if (isset($api) && $api === substr($security, 0, strlen($api))) break; 66 } 67 $versions = array( 68 'current' => $current, // last maintenance update 69 'security' => $security, // last security update 70 ); 71 } 72 return $versions; 73 }, array(), self::VERSIONS_CACHE_TIMEOUT); 74 75 //error_log(__METHOD__."($api) returning ".array2string($versions)); 76 return $versions; 77 } 78 79 /** 80 * Check update status 81 * 82 * @return string 83 * @todo Check from client-side, if server-side check fails 84 */ 85 public static function notification() 86 { 87 $api = preg_replace('/ ?EPL$/', '', self::api_version()); 88 $api_major = $matches = null; 89 if (preg_match('/^(\d+\.\d+)\./', $api, $matches)) 90 { 91 $api_major = $matches[1]; 92 } 93 94 $versions = self::available($api_major); 95 96 if ($versions) 97 { 98 if (version_compare($api, $versions['security'], '<')) 99 { 100 if (!$GLOBALS['egw_info']['user']['apps']['admin'] && !self::update_older($versions['security'], self::WARN_USERS_DAYS)) 101 { 102 return null; 103 } 104 return Html::a_href(Html::image('api', 'security-update', lang('EGroupware security update %1 needs to be installed!', $versions['security'])), 105 'http://www.egroupware.org/changelog', null, ' target="_blank"'); 106 } 107 if ($GLOBALS['egw_info']['user']['apps']['admin'] && version_compare($api, $versions['current'], '<')) 108 { 109 $msg = substr($versions['current'], 0, strlen($api_major)) == $api_major ? 110 lang('EGroupware maintenance update %1 available', $versions['current']) : 111 lang('New EGroupware release %1 available', $versions['current']); 112 return Html::a_href(Html::image('api', 'update', $msg), 113 'http://www.egroupware.org/changelog', null, ' target="_blank"'); 114 } 115 } 116 elseif ($GLOBALS['egw_info']['user']['apps']['admin']) 117 { 118 $error = lang('Automatic update check failed, you need to check manually!'); 119 if (!ini_get('allow_url_fopen')) 120 { 121 $error .= "\n".lang('%1 setting "%2" = %3 disallows access via http!', 122 'php.ini', 'allow_url_fopen', array2string(ini_get('allow_url_fopen'))); 123 } 124 return Html::a_href(Html::image('api', 'update', $error), 125 'http://www.egroupware.org/changelog', null, ' target="_blank" data-api-version="'.$api.'"'); 126 } 127 return null; 128 } 129 130 /** 131 * Check if version is older then $days days 132 * 133 * @param string $version eg. "14.1.20140715" last part is checked (only if > 20140000!) 134 * @param int $days 135 * @return boolean 136 */ 137 protected static function update_older($version, $days) 138 { 139 list(,,$date) = explode('.', $version); 140 if ($date < 20140000) return false; 141 $version_timestamp = mktime(0, 0, 0, (int)substr($date, 4, 2), (int)substr($date, -2), (int)substr($date, 0, 4)); 142 143 return (time() - $version_timestamp) / 86400 > $days; 144 } 145 146 /** 147 * Get current API version from api/setup/setup.inc.php "maintenance_release" or database, whichever is bigger 148 * 149 * @param string &$changelog on return path to changelog 150 * @return string 151 */ 152 public static function api_version(&$changelog=null) 153 { 154 $changelog = EGW_SERVER_ROOT.'/doc/rpm-build/debian.changes'; 155 156 return Cache::getTree(__CLASS__, 'api_version', function() 157 { 158 $version = preg_replace('/[^0-9.]/', '', $GLOBALS['egw_info']['server']['versions']['api']); 159 160 if (empty($GLOBALS['egw_info']['server']['versions']['maintenance_release'])) 161 { 162 $setup_info = null; 163 include (EGW_SERVER_ROOT.'/api/setup/setup.inc.php'); 164 $GLOBALS['egw_info']['server']['versions'] += $setup_info['api']['versions']; 165 unset($setup_info); 166 } 167 if (version_compare($version, $GLOBALS['egw_info']['server']['versions']['maintenance_release'], '<')) 168 { 169 $version = $GLOBALS['egw_info']['server']['versions']['maintenance_release']; 170 } 171 return $version; 172 }, array(), 300); 173 } 174} 175