diff --git a/module/classes/provider/drupal7.class.php b/module/classes/provider/drupal7.class.php index 0a8a025..7020a13 100644 --- a/module/classes/provider/drupal7.class.php +++ b/module/classes/provider/drupal7.class.php @@ -75,58 +75,94 @@ public function getUpdateRows(){ // Return $updates = array(); - // Pull Projects - $projects = update_get_projects(); + // Get update information from cache and refreshes it when necessary. + $available = update_get_available(); - // Pull Available Releases - $packages = - db_select('cache_update', 'cu') - ->fields('cu') - ->condition('cid', 'available_releases::%', 'LIKE') - ->execute() - ->fetchAllAssoc('cid'); + // Pull Projects + module_load_include('inc', 'update', 'update.compare'); + $project_data = update_calculate_project_data($available); // Process Rows - foreach( $packages AS $package ){ - - // Unserialize Store - $package->data = unserialize( $package->data ); - $project = isset($projects[$package->data['short_name']]) ? $projects[$package->data['short_name']] : null; - $package_update = reset($package->data['releases']); + foreach ($project_data as $name => $project) { + // Filter out projects which are up to date already. + if ($project['status'] == UPDATE_CURRENT) { + continue; + } + + // The project name to display can vary based on the info we have. + if (!empty($project['title'])) + $project_name = check_plain($project['title']); + elseif (!empty($project['info']['name'])) + $project_name = check_plain($project['info']['name']); + else + $project_name = check_plain($name); + + if ($project['project_type'] == 'theme' || $project['project_type'] == 'theme-disabled') { + $project_name .= ' ' . '(Theme)'; + } + + if (empty($project['recommended'])) { + // If we don't know what to recommend they upgrade to, we should skip + // the project entirely. + continue; + } + + $recommended_release = $project['releases'][$project['recommended']]; + + switch ($project['status']) { + case UPDATE_NOT_SECURE: + case UPDATE_REVOKED: + $project_name .= ' ' . '(Security update)'; + break; + + case UPDATE_NOT_SUPPORTED: + $project_name .= ' ' . '(Unsupported)'; + break; + + case UPDATE_UNKNOWN: + case UPDATE_NOT_FETCHED: + case UPDATE_NOT_CHECKED: + case UPDATE_NOT_CURRENT: + break; + + default: + // Jump out of the switch and onto the next project in foreach. + continue 2; + } // Push Return switch( $project['project_type'] ){ case 'core': - /* $updates[] = $this->caller->_newUpdateRow(array( - 'update_id' => $project['project_type'].':'.$package_update['version'], - 'extension_id' => 'drupal', - 'name' => 'Drupal', - 'element' => 'drupal', + 'update_id' => $project['project_type'].':'.$project['name'].':'.$recommended_release['version'], + 'extension_id' => $project['name'], + 'name' => $project_name, + 'description' => $project['info']['package'], + 'element' => $project['info']['project'], 'type' => $project['project_type'], - 'status' => 'manual', - 'version' => $project['info']['version'], - 'detailsurl' => $package_update['download_link'], - 'infourl' => $package_update['release_link'], - 'installed_version' => $project['info']['version'] + 'version' => $recommended_release['version'], + 'detailsurl' => $project['link'], + 'infourl' => $recommended_release['release_link'], + 'downloadurl' => $recommended_release['download_link'], + 'installed_version' => $project['info']['version'], + 'status' => 'manual' )); break; - */ case 'module': case 'theme': $updates[] = $this->caller->_newUpdateRow(array( - 'update_id' => $project['project_type'].':'.$project['name'].':'.$package_update['version'], + 'update_id' => $project['project_type'].':'.$project['name'].':'.$recommended_release['version'], 'extension_id' => $project['name'], - 'name' => $project['info']['name'], + 'name' => $project_name, 'description' => $project['info']['package'], 'element' => $project['info']['project'], 'type' => $project['project_type'], - 'status' => 'available', - 'version' => $package_update['version'], - 'detailsurl' => $package_update['download_link'], - 'infourl' => $package_update['release_link'], + 'version' => $recommended_release['version'], + 'detailsurl' => $project['link'], + 'infourl' => $recommended_release['release_link'], + 'downloadurl' => $recommended_release['download_link'], 'installed_version' => $project['info']['version'] - )); + )); break; default: break; @@ -141,95 +177,39 @@ public function getUpdateRows(){ public function doInstallUpdate( $update_row ){ - // Pull Projects - $projects = update_get_projects(); + module_load_include('inc', 'update', 'update.manager'); - // Pull Available Releases - $packages = - db_select('cache_update', 'cu') - ->fields('cu') - ->condition('cid', 'available_releases::%', 'LIKE') - ->execute() - ->fetchAllAssoc('cid'); - - // Find & Process Package - foreach( $packages AS $package ){ - - // Unserialize Store - $package->data = unserialize( $package->data ); - $project = isset($projects[$package->data['short_name']]) ? $projects[$package->data['short_name']] : null; - $package_update = reset($package->data['releases']); - $package_uid = $project['project_type'].':'.$project['name'].':'.$package_update['version']; - - // Match Package - if( $update_row->update_id == $package_uid ){ - - // Type Switch - switch( $update_row->type ){ - - case 'core': - $this->caller->out(' - Error: Core Updates Not Supported'); - break; - - case 'module': - case 'theme': - - // Download Package - if( !($local_cache = update_manager_file_get($package_update['download_link'])) ){ - $this->caller->out(' - Error: Failed to download '. $project['name'] .' from '. $package_update['download_link']); - return false; - } - - // Extract it. - $extract_directory = _update_manager_extract_directory(); - try { - update_manager_archive_extract($local_cache, $extract_directory); - } - catch (Exception $e) { - $this->caller->out(' - Error: ' . $e->getMessage()); - return false; - } - - // Verify it. - $archive_errors = update_manager_archive_verify($project['name'], $local_cache, $extract_directory); - if (!empty($archive_errors)) { - foreach ($archive_errors as $key => $error) { - $this->caller->out(' - Error: ' . $error); - } - return false; - } - - // Load Updater - $project_folder = $extract_directory . '/' . $project['name']; - try { - $updater = Updater::factory($project_folder); - } - catch (Exception $e) { - $this->caller->out(' - Error: ' . $e->getMessage()); - return false; - } - $context = array( - 'results' => array() - ); - - // Run Updater - update_authorize_batch_copy_project( $project['name'], get_class($updater), drupal_realpath($project_folder), new FileTransferLocal(DRUPAL_ROOT), $context ); - - // Verify - if( empty($context['finished']) ){ - $message = isset($context['results']['log'][ $project['name'] ]) ? reset($context['results']['log'][ $project['name'] ])['message'] : 'Unknown Installer Error'; - $this->caller->out(' - Error: ' . $message); - return false; - } - else { - $this->caller->out(' - ' . ucfirst($update_row->type) . ' installed successfully'); - } - - break; - - } + /* + * TODO / Validate + if (!$this->updateAccessAllowed()) { + $this->caller->out(' - Error: Permission denied to process update.'); + return false; + } + */ - } + $this->caller->out('Processing Update ID: '. $update_row->update_id); + + // Type Switch + switch( $update_row->type ){ + + case 'core': + $this->caller->out(' - Error: Core Updates Not Supported'); + break; + + case 'module': + case 'theme': + + // STEP 1 : Download & Extract New Package. + if ($this->doFileTransferUpdate($update_row)) { + // STEP 2 : Update database. + /* + if ($this->doDatabaseUpdate($project)) { + + } + */ + } + + break; } @@ -238,4 +218,124 @@ public function doInstallUpdate( $update_row ){ } + public function doFileTransferUpdate($update_row) { + + $project = $update_row->extension_id; + + // Actually try to download the file. + if (!($local_cache = update_manager_file_get($update_row->downloadurl))) { + $this->caller->out(' - Error: Failed to download ' . $project . ' from ' . $update_row->downloadurl); + return false; + } + + // Extract it. + $extract_directory = _update_manager_extract_directory(); + try { + update_manager_archive_extract($local_cache, $extract_directory); + } + catch (Exception $e) { + $this->caller->out(' - Error: ' . $e->getMessage()); + return false; + } + + $archive_errors = update_manager_archive_verify($project, $local_cache, $extract_directory); + if (!empty($archive_errors)) { + // We just need to make sure our array keys don't collide, so use the + // numeric keys from the $archive_errors array. + foreach ($archive_errors as $key => $error) { + $this->caller->out(' - Error: ' . $key . ": " . $error); + } + return false; + } + + // Store maintenance_mode setting so we can restore it when done. + $maintenance_mode = variable_get('maintenance_mode', FALSE); + if ($maintenance_mode == FALSE) { + variable_set('maintenance_mode', TRUE); + } + + // Make sure the Updater registry is loaded. + drupal_get_updaters(); + + $updates = array(); + $directory = _update_manager_extract_directory(); + + $project_location = $directory . '/' . $project; + $updater = Updater::factory($project_location); + $project_real_location = drupal_realpath($project_location); + $updater_name = get_class($updater); + $local_url = $project_real_location; + + // If the owner of the last directory we extracted is the same as the + // owner of our configuration directory (e.g. sites/default) where we're + // trying to install the code, there's no need to prompt for FTP/SSH + // credentials. Instead, we instantiate a FileTransferLocal and invoke + // update_authorize_run_update() directly. + // + // THIS PLUGIN WILL ONLY WORK IF IT IS THE SAME USER / LOCAL. + if (fileowner($project_real_location) == fileowner(conf_path())) { + module_load_include('inc', 'update', 'update.authorize'); + $filetransfer = new FileTransferLocal(DRUPAL_ROOT); + + // Modified version of update_authorize_run_update() without the batch process. + unset($filetransfer->connection); + + $updater = new $updater_name($local_url); + + try { + if ($updater->isInstalled()) { + // This is an update. + $tasks = $updater->update($filetransfer); + } + } + catch (UpdaterException $e) { + $this->caller->out(' - Error: ' . $e->getMessage()); + return false; + } + + $this->caller->out(' - ' . ucfirst($project) . ' installed successfully'); + + $offline = variable_get('maintenance_mode', FALSE); + + // Now that the update completed, we need to clear the cache of available + // update data and recompute our status, so prevent show bogus results. + _update_authorize_clear_update_status(); + + // Take the site out of maintenance mode if it was previously that way. + if ($offline && $maintenance_mode == FALSE) { + variable_set('maintenance_mode', FALSE); + $this->caller->out(' - Your site has been taken out of maintenance mode.'); + } + + // File Update Completed, return True to process Database Update. + return true; + } + else { + $this->caller->out(" - Error: User doesn't have access to file transfers. FTP credentials are required."); + return false; + } + } + + /** + * Copy of update_access_allowed() in update.php. + * @return bool + */ + private function updateAccessAllowed() { + global $update_free_access, $user; + + // Allow the global variable in settings.php to override the access check. + if (!empty($update_free_access)) { + return TRUE; + } + // Calls to user_access() might fail during the Drupal 6 to 7 update process, + // so we fall back on requiring that the user be logged in as user #1. + try { + require_once DRUPAL_ROOT . '/' . drupal_get_path('module', 'user') . '/user.module'; + return user_access('administer software updates'); + } + catch (Exception $e) { + return ($user->uid == 1); + } + } + } \ No newline at end of file diff --git a/module/connect.php b/module/connect.php index 82ef657..a3b2f3e 100644 --- a/module/connect.php +++ b/module/connect.php @@ -69,6 +69,23 @@ // User Auth Filter if( !empty($userFilter) ){ + + if (!function_exists('getallheaders')) + { + function getallheaders() + { + $headers = ''; + foreach ($_SERVER as $name => $value) + { + if (substr($name, 0, 5) == 'HTTP_') + { + $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; + } + } + return $headers; + } + } + $headers = getallheaders(); $authCredentials = null; if( !empty($headers['Authorization']) ){