1<?php 2/** 3 * @package Joomla.Administrator 4 * @subpackage com_joomlaupdate 5 * 6 * @copyright Copyright (C) 2005 - 2020 Open Source Matters, Inc. All rights reserved. 7 * @license GNU General Public License version 2 or later; see LICENSE.txt 8 */ 9 10defined('_JEXEC') or die; 11 12/** 13 * The Joomla! update controller for the Update view 14 * 15 * @since 2.5.4 16 */ 17class JoomlaupdateControllerUpdate extends JControllerLegacy 18{ 19 /** 20 * Performs the download of the update package 21 * 22 * @return void 23 * 24 * @since 2.5.4 25 */ 26 public function download() 27 { 28 $this->checkToken(); 29 30 $options['format'] = '{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}'; 31 $options['text_file'] = 'joomla_update.php'; 32 JLog::addLogger($options, JLog::INFO, array('Update', 'databasequery', 'jerror')); 33 $user = JFactory::getUser(); 34 35 try 36 { 37 JLog::add(JText::sprintf('COM_JOOMLAUPDATE_UPDATE_LOG_START', $user->id, $user->name, JVERSION), JLog::INFO, 'Update'); 38 } 39 catch (RuntimeException $exception) 40 { 41 // Informational log only 42 } 43 44 $this->_applyCredentials(); 45 46 /** @var JoomlaupdateModelDefault $model */ 47 $model = $this->getModel('Default'); 48 $result = $model->download(); 49 $file = $result['basename']; 50 $message = null; 51 $messageType = null; 52 53 // The validation was not successful for now just a warning. 54 // TODO: In Joomla 4 this will abort the installation 55 if ($result['check'] === false) 56 { 57 $message = JText::_('COM_JOOMLAUPDATE_VIEW_UPDATE_CHECKSUM_WRONG'); 58 $messageType = 'warning'; 59 60 try 61 { 62 JLog::add($message, JLog::INFO, 'Update'); 63 } 64 catch (RuntimeException $exception) 65 { 66 // Informational log only 67 } 68 } 69 70 if ($file) 71 { 72 JFactory::getApplication()->setUserState('com_joomlaupdate.file', $file); 73 $url = 'index.php?option=com_joomlaupdate&task=update.install&' . JFactory::getSession()->getFormToken() . '=1'; 74 75 try 76 { 77 JLog::add(JText::sprintf('COM_JOOMLAUPDATE_UPDATE_LOG_FILE', $file), JLog::INFO, 'Update'); 78 } 79 catch (RuntimeException $exception) 80 { 81 // Informational log only 82 } 83 } 84 else 85 { 86 JFactory::getApplication()->setUserState('com_joomlaupdate.file', null); 87 $url = 'index.php?option=com_joomlaupdate'; 88 $message = JText::_('COM_JOOMLAUPDATE_VIEW_UPDATE_DOWNLOADFAILED'); 89 $messageType = 'error'; 90 } 91 92 $this->setRedirect($url, $message, $messageType); 93 } 94 95 /** 96 * Start the installation of the new Joomla! version 97 * 98 * @return void 99 * 100 * @since 2.5.4 101 */ 102 public function install() 103 { 104 $this->checkToken('get'); 105 JFactory::getApplication()->setUserState('com_joomlaupdate.oldversion', JVERSION); 106 107 $options['format'] = '{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}'; 108 $options['text_file'] = 'joomla_update.php'; 109 JLog::addLogger($options, JLog::INFO, array('Update', 'databasequery', 'jerror')); 110 111 try 112 { 113 JLog::add(JText::_('COM_JOOMLAUPDATE_UPDATE_LOG_INSTALL'), JLog::INFO, 'Update'); 114 } 115 catch (RuntimeException $exception) 116 { 117 // Informational log only 118 } 119 120 $this->_applyCredentials(); 121 122 /** @var JoomlaupdateModelDefault $model */ 123 $model = $this->getModel('Default'); 124 125 $file = JFactory::getApplication()->getUserState('com_joomlaupdate.file', null); 126 $model->createRestorationFile($file); 127 128 $this->display(); 129 } 130 131 /** 132 * Finalise the upgrade by running the necessary scripts 133 * 134 * @return void 135 * 136 * @since 2.5.4 137 */ 138 public function finalise() 139 { 140 /* 141 * Finalize with login page. Used for pre-token check versions 142 * to allow updates without problems but with a maximum of security. 143 */ 144 if (!JSession::checkToken('get')) 145 { 146 $this->setRedirect('index.php?option=com_joomlaupdate&view=update&layout=finaliseconfirm'); 147 148 return false; 149 } 150 151 $options['format'] = '{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}'; 152 $options['text_file'] = 'joomla_update.php'; 153 JLog::addLogger($options, JLog::INFO, array('Update', 'databasequery', 'jerror')); 154 155 try 156 { 157 JLog::add(JText::_('COM_JOOMLAUPDATE_UPDATE_LOG_FINALISE'), JLog::INFO, 'Update'); 158 } 159 catch (RuntimeException $exception) 160 { 161 // Informational log only 162 } 163 164 $this->_applyCredentials(); 165 166 /** @var JoomlaupdateModelDefault $model */ 167 $model = $this->getModel('Default'); 168 169 $model->finaliseUpgrade(); 170 171 $url = 'index.php?option=com_joomlaupdate&task=update.cleanup&' . JFactory::getSession()->getFormToken() . '=1'; 172 $this->setRedirect($url); 173 } 174 175 /** 176 * Clean up after ourselves 177 * 178 * @return void 179 * 180 * @since 2.5.4 181 */ 182 public function cleanup() 183 { 184 /* 185 * Cleanup with login page. Used for pre-token check versions to be able to update 186 * from =< 3.2.7 to allow updates without problems but with a maximum of security. 187 */ 188 if (!JSession::checkToken('get')) 189 { 190 $this->setRedirect('index.php?option=com_joomlaupdate&view=update&layout=finaliseconfirm'); 191 192 return false; 193 } 194 195 $options['format'] = '{DATE}\t{TIME}\t{LEVEL}\t{CODE}\t{MESSAGE}'; 196 $options['text_file'] = 'joomla_update.php'; 197 JLog::addLogger($options, JLog::INFO, array('Update', 'databasequery', 'jerror')); 198 199 try 200 { 201 JLog::add(JText::_('COM_JOOMLAUPDATE_UPDATE_LOG_CLEANUP'), JLog::INFO, 'Update'); 202 } 203 catch (RuntimeException $exception) 204 { 205 // Informational log only 206 } 207 208 $this->_applyCredentials(); 209 210 /** @var JoomlaupdateModelDefault $model */ 211 $model = $this->getModel('Default'); 212 213 $model->cleanUp(); 214 215 $url = 'index.php?option=com_joomlaupdate&view=default&layout=complete'; 216 $this->setRedirect($url); 217 218 try 219 { 220 JLog::add(JText::sprintf('COM_JOOMLAUPDATE_UPDATE_LOG_COMPLETE', JVERSION), JLog::INFO, 'Update'); 221 } 222 catch (RuntimeException $exception) 223 { 224 // Informational log only 225 } 226 } 227 228 /** 229 * Purges updates. 230 * 231 * @return void 232 * 233 * @since 3.0 234 */ 235 public function purge() 236 { 237 // Check for request forgeries 238 $this->checkToken(); 239 240 // Purge updates 241 /** @var JoomlaupdateModelDefault $model */ 242 $model = $this->getModel('Default'); 243 $model->purge(); 244 245 $url = 'index.php?option=com_joomlaupdate'; 246 $this->setRedirect($url, $model->_message); 247 } 248 249 /** 250 * Uploads an update package to the temporary directory, under a random name 251 * 252 * @return void 253 * 254 * @since 3.6.0 255 */ 256 public function upload() 257 { 258 // Check for request forgeries 259 $this->checkToken(); 260 261 // Did a non Super User tried to upload something (a.k.a. pathetic hacking attempt)? 262 JFactory::getUser()->authorise('core.admin') or jexit(JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN')); 263 264 $this->_applyCredentials(); 265 266 /** @var JoomlaupdateModelDefault $model */ 267 $model = $this->getModel('Default'); 268 269 try 270 { 271 $model->upload(); 272 } 273 catch (RuntimeException $e) 274 { 275 $url = 'index.php?option=com_joomlaupdate'; 276 $this->setRedirect($url, $e->getMessage(), 'error'); 277 278 return; 279 } 280 281 $token = JSession::getFormToken(); 282 $url = 'index.php?option=com_joomlaupdate&task=update.captive&' . $token . '=1'; 283 $this->setRedirect($url); 284 } 285 286 /** 287 * Checks there is a valid update package and redirects to the captive view for super admin authentication. 288 * 289 * @return array 290 * 291 * @since 3.6.0 292 */ 293 public function captive() 294 { 295 // Check for request forgeries 296 $this->checkToken('get'); 297 298 // Did a non Super User tried to upload something (a.k.a. pathetic hacking attempt)? 299 if (!JFactory::getUser()->authorise('core.admin')) 300 { 301 throw new RuntimeException(JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'), 403); 302 } 303 304 // Do I really have an update package? 305 $tempFile = JFactory::getApplication()->getUserState('com_joomlaupdate.temp_file', null); 306 307 JLoader::import('joomla.filesystem.file'); 308 309 if (empty($tempFile) || !JFile::exists($tempFile)) 310 { 311 throw new RuntimeException(JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'), 403); 312 } 313 314 $this->input->set('view', 'upload'); 315 $this->input->set('layout', 'captive'); 316 317 $this->display(); 318 } 319 320 /** 321 * Checks the admin has super administrator privileges and then proceeds with the update. 322 * 323 * @return array 324 * 325 * @since 3.6.0 326 */ 327 public function confirm() 328 { 329 // Check for request forgeries 330 $this->checkToken(); 331 332 // Did a non Super User tried to upload something (a.k.a. pathetic hacking attempt)? 333 if (!JFactory::getUser()->authorise('core.admin')) 334 { 335 throw new RuntimeException(JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'), 403); 336 } 337 338 // Get the model 339 /** @var JoomlaupdateModelDefault $model */ 340 $model = $this->getModel('default'); 341 342 // Get the captive file before the session resets 343 $tempFile = JFactory::getApplication()->getUserState('com_joomlaupdate.temp_file', null); 344 345 // Do I really have an update package? 346 if (!$model->captiveFileExists()) 347 { 348 throw new RuntimeException(JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'), 403); 349 } 350 351 // Try to log in 352 $credentials = array( 353 'username' => $this->input->post->get('username', '', 'username'), 354 'password' => $this->input->post->get('passwd', '', 'raw'), 355 'secretkey' => $this->input->post->get('secretkey', '', 'raw'), 356 ); 357 358 $result = $model->captiveLogin($credentials); 359 360 if (!$result) 361 { 362 $model->removePackageFiles(); 363 364 throw new RuntimeException(JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'), 403); 365 } 366 367 // Set the update source in the session 368 JFactory::getApplication()->setUserState('com_joomlaupdate.file', basename($tempFile)); 369 370 try 371 { 372 JLog::add(JText::sprintf('COM_JOOMLAUPDATE_UPDATE_LOG_FILE', $tempFile), JLog::INFO, 'Update'); 373 } 374 catch (RuntimeException $exception) 375 { 376 // Informational log only 377 } 378 379 // Redirect to the actual update page 380 $url = 'index.php?option=com_joomlaupdate&task=update.install&' . JFactory::getSession()->getFormToken() . '=1'; 381 $this->setRedirect($url); 382 } 383 384 /** 385 * Method to display a view. 386 * 387 * @param boolean $cachable If true, the view output will be cached 388 * @param array $urlparams An array of safe URL parameters and their variable types, for valid values see {@link JFilterInput::clean()}. 389 * 390 * @return JoomlaupdateControllerUpdate This object to support chaining. 391 * 392 * @since 2.5.4 393 */ 394 public function display($cachable = false, $urlparams = array()) 395 { 396 // Get the document object. 397 $document = JFactory::getDocument(); 398 399 // Set the default view name and format from the Request. 400 $vName = $this->input->get('view', 'update'); 401 $vFormat = $document->getType(); 402 $lName = $this->input->get('layout', 'default', 'string'); 403 404 // Get and render the view. 405 if ($view = $this->getView($vName, $vFormat)) 406 { 407 // Get the model for the view. 408 /** @var JoomlaupdateModelDefault $model */ 409 $model = $this->getModel('Default'); 410 411 // Push the model into the view (as default). 412 $view->setModel($model, true); 413 $view->setLayout($lName); 414 415 // Push document object into the view. 416 $view->document = $document; 417 $view->display(); 418 } 419 420 return $this; 421 } 422 423 /** 424 * Applies FTP credentials to Joomla! itself, when required 425 * 426 * @return void 427 * 428 * @since 2.5.4 429 */ 430 protected function _applyCredentials() 431 { 432 JFactory::getApplication()->getUserStateFromRequest('com_joomlaupdate.method', 'method', 'direct', 'cmd'); 433 434 if (!JClientHelper::hasCredentials('ftp')) 435 { 436 $user = JFactory::getApplication()->getUserStateFromRequest('com_joomlaupdate.ftp_user', 'ftp_user', null, 'raw'); 437 $pass = JFactory::getApplication()->getUserStateFromRequest('com_joomlaupdate.ftp_pass', 'ftp_pass', null, 'raw'); 438 439 if ($user != '' && $pass != '') 440 { 441 // Add credentials to the session 442 if (!JClientHelper::setCredentials('ftp', $user, $pass)) 443 { 444 JError::raiseWarning(500, JText::_('JLIB_CLIENT_ERROR_HELPER_SETCREDENTIALSFROMREQUEST_FAILED')); 445 } 446 } 447 } 448 } 449 450 /** 451 * Checks the admin has super administrator privileges and then proceeds with the final & cleanup steps. 452 * 453 * @return array 454 * 455 * @since 3.6.3 456 */ 457 public function finaliseconfirm() 458 { 459 // Check for request forgeries 460 $this->checkToken(); 461 462 // Did a non Super User try do this? 463 if (!JFactory::getUser()->authorise('core.admin')) 464 { 465 throw new RuntimeException(JText::_('JLIB_APPLICATION_ERROR_ACCESS_FORBIDDEN'), 403); 466 } 467 468 // Get the model 469 /** @var JoomlaupdateModelDefault $model */ 470 $model = $this->getModel('default'); 471 472 // Try to log in 473 $credentials = array( 474 'username' => $this->input->post->get('username', '', 'username'), 475 'password' => $this->input->post->get('passwd', '', 'raw'), 476 'secretkey' => $this->input->post->get('secretkey', '', 'raw'), 477 ); 478 479 $result = $model->captiveLogin($credentials); 480 481 // The login fails? 482 if (!$result) 483 { 484 JFactory::getApplication()->enqueueMessage(JText::_('JGLOBAL_AUTH_INVALID_PASS'), 'warning'); 485 $this->setRedirect('index.php?option=com_joomlaupdate&view=update&layout=finaliseconfirm'); 486 487 return false; 488 } 489 490 // Redirect back to the actual finalise page 491 $this->setRedirect('index.php?option=com_joomlaupdate&task=update.finalise&' . JFactory::getSession()->getFormToken() . '=1'); 492 } 493} 494