JezK
Edit File: ExtensionUpdate.php
<?php defined('AUTOUPDATER_LIB') or die; class AutoUpdater_Task_ExtensionUpdate extends AutoUpdater_Task_Base { protected $admin_privileges = true; protected $high_priority = false; /** @var AutoUpdater_Helper_SiteTransient */ protected $site_transient; public $upgrader; public $old_version = ''; public $new_version = ''; public $expected_version = ''; public function __construct($payload) { parent::__construct($payload); AutoUpdater_Loader::loadClass('Helper_SiteTransient'); // Restore updates data if another plugin deleted it just before update $this->site_transient = new AutoUpdater_Helper_SiteTransient(); } /** * @return array * @throws */ public function doTask() { $type = strtolower($this->input('type')); $slug = strtolower($this->input('slug')); $path = $this->input('path'); if (!$type || (!$path && !$slug && $type !== 'translation')) { throw new AutoUpdater_Exception_Response('Nothing to update', 400); } AutoUpdater_Loader::loadClass('Helper_Extension'); if ($slug && AutoUpdater_Helper_Extension::isSlugTraversal($slug)) { throw new AutoUpdater_Exception_Response('Invalid slug', 400); } if ($path && !preg_match('!^(http|https|ftp)://!i', $path)) { throw new AutoUpdater_Exception_Response('Only URL paths are allowed', 400); } $this->expected_version = $this->input('version'); require_once AUTOUPDATER_LIB_PATH . 'Upgrader/Dependencies.php'; AutoUpdater_Loader::loadClass('Helper_License'); AutoUpdater_Loader::loadClass('Helper_Version'); $allowed_types = array('plugin', 'theme', 'translation'); if (!in_array($type, $allowed_types)) { throw new AutoUpdater_Exception_Response('Bad type', 400); } AutoUpdater_Loader::loadClass('Upgrader_Task_Base'); $class_name = AutoUpdater_Loader::loadClass('Upgrader_Task_' . ucfirst($type)); if ($class_name === false) { throw new AutoUpdater_Exception_Response('Bad type', 400); } /** @var AutoUpdater_Upgrader_Task_Base $upgrader_task */ $upgrader_task = new $class_name($this); $result = $upgrader_task->update($slug, $path); AutoUpdater_Filemanager::getInstance()->clearPhpCache(); return $this->getResponse($result, $slug, $type); } /** * @param mixed $result * @param string $slug * @param string $type * * @return array */ protected function getResponse($result, $slug, $type) { $response = array( 'success' => false, 'message' => 'Failed to update ' . $type . ': ' . $slug, 'extension' => array( 'current_version' => $this->new_version, 'expected_version' => $this->expected_version, ), ); list($errors, $feedback) = $this->collectUpgraderErrors($response); $is_result_wp_error = false; if (is_wp_error($result)) { /** @var WP_Error $result */ $error_data = $result->get_error_data(); $errors[$result->get_error_code()] = $result->get_error_message() . (is_scalar($error_data) ? ' ' . $error_data : ''); $result = false; $is_result_wp_error = true; } if ($this->site_transient->wasSiteTransientDeleted()) { $errors['update_deleted'] = 'Update information was deleted by one of plugins: ' . implode(', ', $this->site_transient->getAllPluginsDeletingSiteTransient()); } if ($this->isAlreadyUpToDate($errors, $type)) { $response['success'] = true; $response['message'] = $errors['up_to_date'] !== 'up_to_date' ? $errors['up_to_date'] : 'Up-to-date'; unset($errors['up_to_date']); if (count($errors)) { $response['warnings'] = $errors; } return $response; } $package_error = $this->detectPackageError($errors); if ($package_error) { $result = false; $response['error'] = $package_error['error']; unset($errors[$package_error['key']]); } elseif ($this->isVersionUnchanged($is_result_wp_error, $type)) { $result = false; $response['error'] = array( 'code' => 'no_update_warning', 'message' => 'No update was performed, current version: ' . $this->new_version . ', expected version: ' . $this->expected_version, ); } // Update succeeded if ($result === true || (is_null($result) && !isset($response['error']) && !count($errors))) { $response['success'] = true; unset($response['message']); if (count($errors)) { $response['warnings'] = $errors; } return $response; } if (!is_null($result) && !is_bool($result)) { $errors['unknown_error'] = 'Unexpected result type: ' . gettype($result); } return $this->buildFailureResponse($response, $errors, $feedback, $slug, $type); } /** * @param array &$response * @return array [errors, feedback] */ private function collectUpgraderErrors(array &$response) { $errors = array(); $feedback = array(); if (!$this->upgrader) { return array($errors, $feedback); } /** @var AutoUpdater_Upgrader_Skin_Plugin|AutoUpdater_Upgrader_Skin_Theme|AutoUpdater_Upgrader_Skin_Languagepack $skin */ $skin = $this->upgrader->skin; $errors = $skin->get_errors(); $feedback = $skin->get_feedback(); if ($skin instanceof AutoUpdater_Upgrader_Skin_Languagepack) { $translations = $skin->get_translations(); if (!empty($translations)) { $response['translations'] = $translations; } } return array($errors, $feedback); } /** * @param array $errors * @param string $type * @return bool */ private function isAlreadyUpToDate(array $errors, $type) { if (!array_key_exists('up_to_date', $errors)) { return false; } // WordPress Core or Translations are always considered up-to-date if (in_array($type, array('core', 'translation'))) { return true; } // Plugin/Theme version is newer than or equal to expected return $this->expected_version && $this->new_version && version_compare( AutoUpdater_Helper_Version::fixAndFormat($this->expected_version), AutoUpdater_Helper_Version::fixAndFormat($this->new_version), '<=' ); } /** * @param array $errors * @return array|null ['key' => string, 'error' => array] or null */ private function detectPackageError(array $errors) { if (array_key_exists('no_package', $errors)) { return array( 'key' => 'no_package', 'error' => array( 'code' => 'no_package_warning', 'message' => $errors['no_package'], ), ); } if (array_key_exists('download_failed', $errors)) { return array( 'key' => 'download_failed', 'error' => array( 'code' => 'no_package_warning', 'message' => $errors['download_failed'], ), ); } return null; } /** * @param bool $is_result_wp_error * @param string $type * @return bool */ private function isVersionUnchanged($is_result_wp_error, $type) { if ($is_result_wp_error || !in_array($type, array('plugin', 'theme')) || !$this->new_version) { return false; } // New version is lower than expected if ($this->expected_version) { return version_compare( AutoUpdater_Helper_Version::fixAndFormat($this->expected_version), AutoUpdater_Helper_Version::fixAndFormat($this->new_version), '>' ); } // Version did not change return version_compare( AutoUpdater_Helper_Version::fixAndFormat($this->old_version), AutoUpdater_Helper_Version::fixAndFormat($this->new_version), '=' ); } /** * @param array $response * @param array $errors * @param array $feedback * @param string $slug * @param string $type * @return array */ private function buildFailureResponse(array $response, array $errors, array $feedback, $slug, $type) { if ($response['success'] === false && in_array($type, array('plugin', 'theme')) && !AutoUpdater_Helper_License::hasValidLicense($slug)) { $errors['invalid_license'] = 'Missing a valid license key'; } if (count($errors)) { if (!isset($response['error'])) { end($errors); $response['error'] = array( 'code' => key($errors), 'message' => current($errors), ); unset($errors[$response['error']['code']]); } if (count($errors)) { $response['errors'] = $errors; } } if (count($feedback)) { $response['run_sequence'] = $feedback; } global $wp_filesystem; $response['filesystem_method'] = $wp_filesystem->method; return $response; } }