View source
<?php
define('SUBSCRIPTION_NOT_FOUND', 1000);
define('SUBSCRIPTION_KEY_MISMATCH', 1100);
define('SUBSCRIPTION_EXPIRED', 1200);
define('SUBSCRIPTION_REPLAY_ATTACK', 1300);
define('SUBSCRIPTION_KEY_NOT_FOUND', 1400);
define('SUBSCRIPTION_MESSAGE_FUTURE', 1500);
define('SUBSCRIPTION_MESSAGE_EXPIRED', 1600);
define('SUBSCRIPTION_MESSAGE_INVALID', 1700);
define('SUBSCRIPTION_VALIDATION_ERROR', 1800);
define('SUBSCRIPTION_PROVISION_ERROR', 9000);
define('SUBSCRIPTION_MESSAGE_LIFETIME', 15 * 60);
function acquia_agent_menu() {
$items['admin/settings/acquia-agent'] = array(
'title' => 'Acquia Subscription settings',
'description' => 'Connect your site to Acquia.',
'page callback' => 'acquia_agent_settings_page',
'file' => 'acquia_agent.pages.inc',
'access arguments' => array(
'administer site configuration',
),
);
$items['admin/settings/acquia-agent/setup'] = array(
'title' => 'Acquia Subscription automatic setup',
'description' => 'Connect your site to Acquia.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'acquia_agent_automatic_setup_form',
),
'file' => 'acquia_agent.pages.inc',
'access arguments' => array(
'administer site configuration',
),
'type' => MENU_CALLBACK,
);
$items['admin/settings/acquia-agent/credentials'] = array(
'title' => 'Acquia Subscription credentials',
'description' => 'Connect your site to Acquia.',
'page callback' => 'drupal_get_form',
'page arguments' => array(
'acquia_agent_settings_credentials',
),
'file' => 'acquia_agent.pages.inc',
'access arguments' => array(
'administer site configuration',
),
'type' => MENU_CALLBACK,
);
$items['admin/settings/acquia-agent/migrate'] = array(
'title' => 'Acquia Cloud Upload',
'description' => 'Migrate your site to Acquia Cloud.',
'page callback' => 'acquia_agent_migrate_page',
'file' => 'acquia_agent.pages.inc',
'access arguments' => array(
'administer site configuration',
),
'type' => MENU_CALLBACK,
);
$items['system/acquia-migrate-check'] = array(
'title' => 'Migrate capable',
'description' => 'Check for Acquia Cloud migration capabilities',
'page callback' => 'acquia_agent_migrate_check',
'file' => 'acquia_agent.migrate.inc',
'access arguments' => array(
'access content',
),
'type' => MENU_CALLBACK,
);
$items['admin/settings/acquia-agent/refresh-status'] = array(
'title' => 'Manual update check',
'page callback' => 'acquia_agent_refresh_status',
'access arguments' => array(
'administer site configuration',
),
'type' => MENU_CALLBACK,
);
$items['system/acquia-connector-status'] = array(
'title' => 'Site status',
'description' => 'Check the site status',
'page callback' => 'acquia_agent_site_status',
'access callback' => 'acquia_agent_site_status_access',
'file' => 'acquia_agent.pages.inc',
'type' => MENU_CALLBACK,
);
return $items;
}
function acquia_agent_init() {
if (!acquia_agent_has_credentials() && !variable_get('acquia_subscription_data', FALSE) && variable_get('ah_network_key', FALSE) && variable_get('ah_network_identifier', FALSE)) {
variable_set('acquia_identifier', variable_get('ah_network_identifier', FALSE));
variable_set('acquia_key', variable_get('ah_network_key', FALSE));
$activated = acquia_agent_check_subscription();
if ($activated) {
$text = t('Your site has been automatically connected to Acquia. <a href="!url">Change subscription</a>', array(
'!url' => url('admin/settings/acquia-agent/setup'),
));
drupal_set_message($text, 'status', FALSE);
}
}
$hide_signup_messages = variable_get('acquia_agent_hide_signup_messages', 0);
if (!$hide_signup_messages && arg(2) != 'acquia-agent' && empty($_POST) && user_access('administer site configuration') && !acquia_agent_has_credentials() && arg(0) !== 'filefield' && arg(1) != 'progress') {
$text = 'Sign up for Acquia Cloud Free, a free Drupal sandbox to experiment with new features, test your code quality, and apply continuous integration best practices. Check out the <a href="!acquia-free">epic set of dev features and tools</a> that come with your free subscription.<br/>If you have an Acquia subscription, <a href="!settings">connect now</a>. Otherwise, you can turn this message off by disabling the Acquia Connector modules.';
if (isset($_SERVER['AH_SITE_GROUP'])) {
$text = '<a href="!settings">Connect your site to Acquia now</a>. <a href="!more">Learn more</a>.';
}
$message = t($text, array(
'!acquia-free' => url('https://www.acquia.com/acquia-cloud-free'),
'!settings' => url('admin/settings/acquia-agent/setup'),
));
drupal_set_message($message, 'warning', FALSE);
}
if (arg(0) == 'system' && arg(1) == 'acquia-connector-status') {
$GLOBALS['conf']['acquia_cache'] = $GLOBALS['conf']['cache'];
$GLOBALS['conf']['cache'] = FALSE;
$GLOBALS['conf']['acquia_site_offline'] = $GLOBALS['conf']['site_offline'];
$GLOBALS['conf']['site_offline'] = FALSE;
}
}
function acquia_agent_theme() {
return array(
'acquia_agent_banner_form' => array(
'arguments' => array(
'form' => NULL,
),
'file' => 'acquia_agent.pages.inc',
),
);
}
function acquia_agent_site_status_access() {
if (!isset($_GET['key'], $_GET['nonce'])) {
return FALSE;
}
$sub_data = acquia_agent_settings('acquia_subscription_data');
$sub_uuid = _acquia_agent_get_id_from_sub($sub_data);
if (!empty($sub_uuid)) {
$expected_hash = hash('sha1', "{$sub_uuid}:{$_GET['nonce']}");
if ($_GET['key'] === $expected_hash) {
return TRUE;
}
}
$acquia_debug = variable_get('acquia_agent_debug', FALSE);
if ($acquia_debug) {
$info = array(
'sub_data' => $sub_data,
'sub_uuid_from_data' => $sub_uuid,
'expected_hash' => $expected_hash,
'get' => $_GET,
'server' => $_SERVER,
'request' => $_REQUEST,
);
watchdog('acquia_agent', 'Site status request: @data', array(
'@data' => var_export($info, TRUE),
));
}
return FALSE;
}
function _acquia_agent_get_id_from_sub($sub_data) {
if (!empty($sub_data['uuid'])) {
return $sub_data['uuid'];
}
$url = parse_url($sub_data['href']);
$parts = explode('/', $url['path']);
array_pop($parts);
return end($parts);
}
function acquia_agent_check_subscription($params = array()) {
$current_subscription = acquia_agent_settings('acquia_subscription_data');
$subscription = FALSE;
$active = FALSE;
if (!acquia_agent_has_credentials()) {
variable_del('acquia_subscription_data');
}
else {
$subscription = acquia_agent_get_subscription($params);
if (is_numeric($subscription)) {
switch ($subscription) {
case SUBSCRIPTION_NOT_FOUND:
case SUBSCRIPTION_EXPIRED:
break;
default:
return $current_subscription;
}
}
elseif ($subscription === FALSE) {
return $current_subscription;
}
variable_set('acquia_subscription_data', $subscription);
if ($subscription) {
$active = acquia_agent_subscription_is_active();
}
}
module_invoke_all('acquia_subscription_status', $active, $subscription);
return $subscription;
}
function acquia_agent_get_subscription($params = array(), $identifier = NULL, $key = NULL, $acquia_network_address = NULL) {
$subscription = array();
$subscription['timestamp'] = time();
acquia_agent_load_versions();
if (IS_ACQUIA_DRUPAL) {
$params['version'] = ACQUIA_DRUPAL_VERSION;
$params['series'] = ACQUIA_DRUPAL_SERIES;
$params['branch'] = ACQUIA_DRUPAL_BRANCH;
$params['revision'] = ACQUIA_DRUPAL_REVISION;
}
if (module_exists('acquia_search')) {
if (defined('ACQUIA_SEARCH_VERSION')) {
$params['search_version']['acquia_search'] = ACQUIA_SEARCH_VERSION;
}
else {
$params['search_version']['acquia_search'] = variable_get('acquia_search_version', '6.x-3.x');
}
$info = db_result(db_query("SELECT info FROM {system} WHERE name = 'apachesolr'"));
if ($info = unserialize($info)) {
$params['search_version']['apachesolr'] = isset($info['version']) ? (string) $info['version'] : $info['core'];
}
}
$data = acquia_agent_call('acquia.agent.subscription', $params, $identifier, $key, $acquia_network_address);
if ($errno = xmlrpc_errno()) {
return $errno;
}
elseif (acquia_agent_valid_response($data, $key)) {
$subscription += $data['result']['body'];
}
else {
watchdog('acquia agent', 'HMAC validation error: <pre>@data</pre>', array(
'@data' => print_r($data, TRUE),
), WATCHDOG_ERROR);
return FALSE;
}
return $subscription;
}
function acquia_agent_report_xmlrpc_error() {
drupal_set_message(t('Error: @message (@errno)', array(
'@message' => xmlrpc_error_msg(),
'@errno' => xmlrpc_errno(),
)), 'error');
}
function acquia_agent_update_status_alter(&$projects) {
if (!($subscription = acquia_agent_has_update_service())) {
return;
}
foreach ($projects as $project => $project_info) {
if ($project == 'drupal') {
if (isset($subscription['update'])) {
$projects[$project]['status'] = isset($subscription['update']['status']) ? $subscription['update']['status'] : t('Unknown');
$projects[$project]['releases'] = isset($subscription['update']['releases']) ? $subscription['update']['releases'] : array();
$projects[$project]['recommended'] = isset($subscription['update']['recommended']) ? $subscription['update']['recommended'] : '';
$projects[$project]['latest_version'] = isset($subscription['update']['latest_version']) ? $subscription['update']['latest_version'] : '';
unset($projects[$project]['security updates']);
}
else {
$projects[$project]['status'] = UPDATE_NOT_CHECKED;
$projects[$project]['reason'] = t('No information available from Acquia');
unset($projects[$project]['releases']);
unset($projects[$project]['recommended']);
}
$projects[$project]['link'] = 'http://acquia.com/products-services/acquia-drupal';
$projects[$project]['title'] = 'Acquia Drupal';
$projects[$project]['existing_version'] = ACQUIA_DRUPAL_VERSION;
$projects[$project]['install_type'] = 'official';
unset($projects[$project]['extra']);
}
elseif ($project_info['datestamp'] == 'acquia drupal') {
$projects['drupal']['includes'][$project] = $project_info['title'];
unset($projects[$project]);
}
}
}
function acquia_agent_system_info_alter(&$info) {
if (!($subscription = acquia_agent_has_update_service())) {
return;
}
if (isset($info['acquia'])) {
$info['datestamp'] = 'acquia drupal';
}
}
function acquia_agent_has_update_service() {
acquia_agent_load_versions();
$subscription = acquia_agent_settings('acquia_subscription_data');
if (!IS_ACQUIA_DRUPAL || !$subscription['active'] || isset($subscription['update_service']) && empty($subscription['update_service'])) {
return FALSE;
}
return $subscription;
}
function acquia_agent_menu_alter(&$items) {
if (isset($items['admin/reports/updates/check'])) {
$items['admin/reports/updates/check']['page callback'] = 'acquia_agent_manual_status';
}
}
function acquia_agent_help($path, $arg) {
switch ($path) {
case 'admin/help#acquia_agent':
$output = '<h2>' . t('Acquia Connector modules') . '</h2>';
$output .= '<p>' . t("The Acquia Connector suite of modules allow you to connect your site to Acquia Insight and other valuable services.") . '<p>';
$output .= '<p>' . t("<a href='!url'>Read more about the installation and use of the Acquia Connector module on the Acquia Library</a>.", array(
'!url' => url('https://docs.acquia.com/network/install/connector', array(
'external' => TRUE,
)),
)) . '</p>';
$output .= '<dl>';
$output .= '<dt>Acquia Agent</dt>';
$output .= '<dd>' . t('Enables secure communication between your Drupal sites and Acquia Insight.') . '</dt>';
$output .= '<dt>Acquia SPI</dt>';
$output .= '<dd>' . t('Automates the collection of site information. Required for use with the Acquia Insight service.') . '</dt>';
$output .= '<dt>Acquia SPI Custom Tests</dt>';
$output .= '<dd>' . t('Acquia Insight supports custom tests for your site. See <strong>acquia_spi.api.php</strong> for information on the custom test hook and validate your tests for inclusion in outgoing SPI data with the Drush command, <strong>spi-test-validate</strong>.') . '</dt>';
$output .= '<dt>Acquia Search</dt>';
$output .= '<dd>' . t('Provides authentication service to the Apache Solr Search Integration module to enable use of Acquia\'s hosted Solr search indexes.') . '</dt>';
$output .= '</dl>';
$output .= '<h3>' . t('Configuration settings') . '</h3>';
$output .= '<dl>';
$output .= '<dt>' . t('Data collection and examination') . '</dt>';
$output .= '<dd>' . t('Upon cron (or if configured to run manually) information about your site will be sent and analyzed as part of the Acquia Insight service. You can optionally exclude information about admin privileges, content and user count, and watchdog logs.');
$output .= '<dt>' . t('Source code analysis') . '</dt>';
$output .= '<dd>' . t('If your site supports external SSL connections, Acquia Insight will examine the source code of your site to detect alterations and provide code diffs and update recommentations.');
$output .= '<dt>' . t('Receive updates from Acquia Insight') . '</dt>';
$output .= '<dd>' . t('Receive dynamic updates on the Network Settings page from Acquia.com about your subscription and new features.') . '</dd>';
$output .= '<dt>' . t('Allow Insight to update list of approved variables.') . '</dt>';
$output .= '<dd>' . t('As part of the Acquia Insight service, some variables can be corrected to their recommended settings from within the Insight system. The list of variables that can be corrected can also be updated at your discretion.') . '</dd>';
$output .= '</dl>';
return $output;
}
}
function acquia_agent_refresh_status() {
acquia_agent_check_subscription();
drupal_goto('admin/settings/acquia-agent');
}
function acquia_agent_manual_status() {
acquia_agent_check_subscription();
update_manual_status();
}
function acquia_agent_cron() {
acquia_agent_check_subscription();
}
function acquia_agent_watchdog($log_entry) {
$cron_failure_messages = array(
'Cron has been running for more than an hour and is most likely stuck.',
'Attempting to re-run cron while it is already running.',
);
if (in_array($log_entry['message'], $cron_failure_messages, TRUE)) {
acquia_agent_check_subscription();
}
}
function acquia_agent_admin_menu() {
$links[] = array(
'title' => 'acquia_subscription_status',
'path' => 'http://acquia.com',
'weight' => -80,
'parent_path' => '<root>',
'options' => array(
'extra class' => 'admin-menu-action acquia-subscription-status',
'html' => TRUE,
),
);
return $links;
}
function acquia_agent_translated_menu_link_alter(&$item, $map) {
global $user;
if (empty($user->uid) || $item['module'] != 'admin_menu') {
return;
}
if ($item['title'] == 'acquia_subscription_status') {
$subscription = acquia_agent_settings('acquia_subscription_data');
if (empty($subscription['timestamp']) || time() - $subscription['timestamp'] > 60 * 60 * 24) {
$subscription = acquia_agent_check_subscription(array(
'no_heartbeat' => 1,
));
}
if ($subscription['active']) {
$icon = '<img src="' . base_path() . 'misc/watchdog-ok.png" height="10" alt="ok" />';
$item['title'] = t("!icon Subscription active (expires @date)", array(
'!icon' => $icon,
'@date' => format_date(strtotime($subscription['expiration_date']['value']), 'small'),
));
$item['localized_options']['extra class'] .= " acquia-active-subscription";
$item['href'] = $subscription['href'];
}
else {
$icon = '<img src="' . base_path() . 'misc/watchdog-error.png" height="10" alt="error" />';
$item['title'] = t("!icon Subscription not active", array(
'!icon' => $icon,
));
$item['localized_options']['extra class'] .= " acquia-inactive-subscription";
$item['href'] = 'http://acquia.com/network';
}
}
}
function acquia_agent_theme_registry_alter(&$theme_registry) {
if (isset($theme_registry['admin_menu_icon'])) {
$theme_registry['admin_menu_icon']['function'] = 'acquia_agent_menu_icon';
}
}
function acquia_agent_menu_icon() {
return '<img class="admin-menu-icon" src="' . base_path() . drupal_get_path('module', 'acquia_agent') . '/acquia.ico" height = "16" alt="" />';
}
function acquia_agent_valid_credentials($identifier, $key, $acquia_network_address = NULL) {
$data = acquia_agent_call('acquia.agent.validate', array(), $identifier, $key, $acquia_network_address);
return (bool) $data['result'];
}
function acquia_agent_call($method, $params, $identifier = NULL, $key = NULL, $acquia_network_address = NULL) {
$path = drupal_get_path('module', 'acquia_agent');
require_once $path . '/acquia_agent_streams.inc';
$acquia_network_address = acquia_agent_network_address($acquia_network_address);
$ip = isset($_SERVER["SERVER_ADDR"]) ? $_SERVER["SERVER_ADDR"] : '';
$host = isset($_SERVER["HTTP_HOST"]) ? $_SERVER["HTTP_HOST"] : '';
$ssl = isset($_SERVER["HTTPS"]) ? TRUE : FALSE;
$data = array(
'authenticator' => _acquia_agent_authenticator($params, $identifier, $key),
'ip' => $ip,
'host' => $host,
'ssl' => $ssl,
'body' => $params,
);
$data['result'] = _acquia_agent_request($acquia_network_address, $method, $data);
return $data;
}
function acquia_agent_connection_error_message() {
$errno = xmlrpc_errno();
if ($errno) {
switch ($errno) {
case SUBSCRIPTION_NOT_FOUND:
return t('The identifier you have provided does not exist in the Acquia Subscription or is expired. Please make sure you have used the correct value and try again.');
break;
case SUBSCRIPTION_EXPIRED:
return t('Your Acquia subscription has expired. Please renew your subscription so that you can resume using Acquia subscription services.');
break;
case SUBSCRIPTION_MESSAGE_FUTURE:
return t('Your server is unable to communicate with Acquia Insight due to a problem with your clock settings. For security reasons, we reject messages that are more than @time ahead of the actual time recorded by our servers. Please fix the clock on your server and try again.', array(
'@time' => format_interval(SUBSCRIPTION_MESSAGE_LIFETIME),
));
break;
case SUBSCRIPTION_MESSAGE_EXPIRED:
return t('Your server is unable to communicate with Acquia Insight due to a problem with your clock settings. For security reasons, we reject messages that are more than @time older than the actual time recorded by our servers. Please fix the clock on your server and try again.', array(
'@time' => format_interval(SUBSCRIPTION_MESSAGE_LIFETIME),
));
break;
case SUBSCRIPTION_VALIDATION_ERROR:
return t('The identifier and key you have provided for the Acquia Subscription do not match. Please make sure you have used the correct values and try again.');
break;
default:
return t('There is an error communicating with Acquia Insight at this time. Please check your identifier and key and try again.');
break;
}
}
return FALSE;
}
function acquia_agent_network_address($acquia_network_address = NULL) {
if (empty($acquia_network_address)) {
$acquia_network_address = acquia_agent_settings('acquia_network_address');
}
$uri = parse_url($acquia_network_address);
if (isset($uri['host'])) {
$acquia_network_address = $uri['host'];
}
$acquia_network_address .= isset($uri['port']) ? ':' . $uri['port'] : '';
$acquia_network_address .= isset($uri['path']) && isset($uri['host']) ? $uri['path'] : '';
if (in_array('ssl', stream_get_transports(), TRUE) && !defined('ACQUIA_DEVELOPMENT_NOSSL')) {
$acquia_network_address = 'https://' . $acquia_network_address;
}
else {
$acquia_network_address = 'http://' . $acquia_network_address;
}
$acquia_network_address .= '/xmlrpc.php';
return $acquia_network_address;
}
function acquia_agent_has_credentials() {
return (bool) (variable_get('acquia_identifier', '') && variable_get('acquia_key', ''));
}
function acquia_agent_subscription_is_active() {
$active = FALSE;
if (acquia_agent_has_credentials()) {
$subscription = acquia_agent_settings('acquia_subscription_data');
$active = !empty($subscription['active']);
}
return $active;
}
function acquia_agent_settings($variable_name) {
switch ($variable_name) {
case 'acquia_identifier':
return variable_get('acquia_identifier', '');
case 'acquia_key':
return variable_get('acquia_key', '');
case 'acquia_network_address':
return variable_get('acquia_network_address', 'https://rpc.acquia.com');
case 'acquia_subscription_data':
return variable_get('acquia_subscription_data', array(
'active' => FALSE,
));
case 'acquia_subscription_name':
return variable_get('acquia_subscription_name', '');
}
}
function acquia_agent_random_bytes($count) {
static $random_state;
if (empty($random_state)) {
$random_state = getmypid();
}
$output = '';
if ($fh = @fopen('/dev/urandom', 'rb')) {
$output = fread($fh, $count);
fclose($fh);
}
while (strlen($output) < $count) {
$random_state = md5(microtime() . mt_rand() . $random_state);
$output .= pack('H*', md5(mt_rand() . $random_state));
}
return substr($output, 0, $count);
}
function acquia_agent_load_versions() {
include_once 'acquia_agent_drupal_version.inc';
}
function acquia_agent_form_system_modules_alter(&$form, &$form_state) {
if (isset($form['description']['acquia_search'])) {
$subscription = acquia_agent_settings('acquia_subscription_data');
if (!module_exists('acquia_search') && empty($subscription['active'])) {
$form['status']['#disabled_modules'][] = 'acquia_search';
$text = 'Acquia Search requires an <a href="@network-url">Acquia subscription</a>';
$message = t($text, array(
'@network-url' => 'http://acquia.com/products-services/acquia-search',
));
$form['description']['acquia_search']['#value'] = '<div style="padding-left:5px; margin:8px 0px" class="messages warning" id="acquia-agent-no-search">' . $message . '</div>' . $form['description']['acquia_search']['#value'];
}
}
}