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/config/system/acquia-agent'] = array(
'title' => 'Acquia Network settings',
'description' => 'Connect your site to the Acquia Network.',
'page callback' => 'acquia_agent_settings_page',
'file' => 'acquia_agent.pages.inc',
'access arguments' => array(
'administer site configuration',
),
);
$items['admin/config/system/acquia-agent/refresh-status'] = array(
'title' => 'Manual update check',
'page callback' => 'acquia_agent_refresh_status',
'access arguments' => array(
'administer site configuration',
),
'type' => MENU_CALLBACK,
);
return $items;
}
function acquia_agent_page_alter(&$page) {
if (isset($page['page_top']['toolbar']) && user_access('administer site configuration')) {
$page['page_top']['toolbar']['#pre_render'][] = 'acquia_agent_toolbar_add_links';
}
}
function acquia_agent_toolbar_add_links($toolbar) {
$link['html'] = TRUE;
if (acquia_agent_subscription_is_active()) {
$subscription = acquia_agent_settings('acquia_subscription_data');
$icon = '<img src="' . base_path() . 'misc/message-16-ok.png" alt="ok" style="vertical-align: middle;" />';
$link['title'] = t("!icon Subscription active (expires !date)", array(
'!icon' => $icon,
'!date' => format_date(strtotime($subscription['expiration_date']['value']), 'custom', 'Y/n/j'),
));
$link['attributes']['class'][] = "acquia-active-subscription";
$link['attributes']['title'] = $subscription['product']['view'];
$link['href'] = $subscription['href'];
}
else {
$icon = '<img src="' . base_path() . 'misc/message-16-error.png" alt="error" style="vertical-align: middle;" />';
$link['title'] = t("!icon Subscription not active", array(
'!icon' => $icon,
));
$link['attributes']['class'][] = "acquia-inactive-subscription";
$link['href'] = 'http://acquia.com/network';
}
$toolbar['toolbar_user']['#links'] = array_merge(array(
'acquia_agent' => $link,
), $toolbar['toolbar_user']['#links']);
return $toolbar;
}
function acquia_agent_init() {
if (arg(0) != 'overlay-ajax' && arg(3) != 'acquia-agent' && empty($_POST) && user_access('administer site configuration') && !acquia_agent_has_credentials()) {
$message = t('Get a <a href="@acquia-free">free 30 day trial</a> of Drupal support, enhanced content search, comment spam blocking and more. If you have an Acquia Network subscription, <a href="@settings">connect now</a>. You can turn this message off by disabling the Acquia Network <a href="@admin-modules">modules</a> too.', array(
'@acquia-free' => url('admin/config/system/acquia-agent'),
'@settings' => url('admin/config/system/acquia-agent/connection'),
'@admin-modules' => url('admin/modules', array(
'fragment' => 'edit-modules-acquia-network-connector',
)),
));
drupal_set_message($message, 'warning', FALSE);
}
}
function acquia_agent_theme() {
return array(
'acquia_agent_banner_form' => array(
'render element' => 'form',
'file' => 'acquia_agent.pages.inc',
),
);
}
function acquia_agent_check_subscription($params = array()) {
$subscription = FALSE;
if (!acquia_agent_has_credentials()) {
variable_del('acquia_subscription_data');
}
else {
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')) {
foreach (array(
'acquia_search',
'apachesolr',
) as $name) {
$info = system_get_info('module', $name);
$params['search_version'][$name] = isset($info['version']) ? (string) $info['version'] : (string) $info['core'];
}
}
$data = acquia_agent_call('acquia.agent.subscription', $params);
$subscription['timestamp'] = REQUEST_TIME;
if ($errno = xmlrpc_errno()) {
switch ($errno) {
case SUBSCRIPTION_NOT_FOUND:
case SUBSCRIPTION_EXPIRED:
variable_del('acquia_subscription_data');
break;
}
}
elseif (acquia_agent_valid_response($data)) {
$subscription += $data['result']['body'];
variable_set('acquia_subscription_data', $subscription);
}
else {
watchdog('acquia agent', 'HMAC validation error: <pre>@data</pre>', array(
'@data' => print_r($data, TRUE),
), WATCHDOG_ERROR);
}
}
module_invoke_all('acquia_subscription_status', acquia_agent_subscription_is_active());
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'] = $subscription['update']['status'];
$projects[$project]['releases'] = $subscription['update']['releases'];
$projects[$project]['recommended'] = $subscription['update']['recommended'];
$projects[$project]['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 the Acquia Network');
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 || empty($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_refresh_status() {
if (module_exists('acquia_spi')) {
acquia_spi_send_profile_info();
}
acquia_agent_check_subscription();
drupal_goto('admin/config/system/acquia-agent');
}
function acquia_agent_manual_status() {
if (module_exists('acquia_spi')) {
acquia_spi_send_profile_info();
}
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_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) {
$acquia_network_address = acquia_agent_network_address($acquia_network_address);
$host = isset($_SERVER["SERVER_ADDR"]) ? $_SERVER["SERVER_ADDR"] : '';
$data = array(
'authenticator' => _acquia_agent_authenticator($params, $identifier, $key),
'host' => $host,
'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 Network or is expired. Please make sure you have used the correct value and try again.');
break;
case SUBSCRIPTION_EXPIRED:
return t('Your Acquia Network subscription has expired. Please renew your subscription so that you can resume using Acquia Network services.');
break;
case SUBSCRIPTION_MESSAGE_FUTURE:
return t('Your server is unable to communicate with the Acquia Network 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 the Acquia Network 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 Network 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 the Acquia Network 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);
$port = isset($uri['port']) ? ':' . $uri['port'] : '';
$path = isset($uri['path']) ? $uri['path'] : '';
$acquia_network_address = $uri['host'] . $port . $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', FALSE) && variable_get('acquia_key', FALSE));
}
function acquia_agent_subscription_is_active() {
$active = FALSE;
if (acquia_agent_has_credentials()) {
$subscription = acquia_agent_settings('acquia_subscription_data');
if (isset($subscription['timestamp']) && time() - $subscription['timestamp'] > 60 * 60 * 24) {
$subscription = acquia_agent_check_subscription(array(
'no_heartbeat' => 1,
));
}
$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,
));
}
}
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 Network 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'];
}
}
}
function acquia_agent_stream_context_create($url, $module = 'acquia_agent') {
$opts = array();
$uri = parse_url($url);
if (isset($uri['scheme']) && $uri['scheme'] == 'https' && variable_get('acquia_agent_verify_peer', 0)) {
$pem_file = drupal_get_path('module', $module) . '/' . $uri['host'] . '.pem';
if (file_exists($pem_file)) {
$opts['ssl'] = array(
'verify_peer' => TRUE,
'cafile' => $pem_file,
'allow_self_signed' => FALSE,
'CN_match' => $uri['host'],
);
}
}
return stream_context_create($opts);
}
function acquia_agent_valid_response($data) {
$authenticator = $data['authenticator'];
$result = $data['result'];
$result_auth = $result['authenticator'];
$valid = $authenticator['nonce'] == $result_auth['nonce'];
$valid = $valid && $authenticator['time'] < $result_auth['time'];
$key = acquia_agent_settings('acquia_key');
$hash = _acquia_agent_hmac($key, $result_auth['time'], $result_auth['nonce'], $result['body']);
return $valid && $hash == $result_auth['hash'];
}
function _acquia_agent_request($url, $method, $data) {
$ctx = acquia_agent_stream_context_create($url);
if (!$ctx) {
xmlrpc_error(-1, t('SSL is not supported or setup failed'));
$result = FALSE;
}
else {
$result = xmlrpc($url, array(
$method => array(
$data,
),
), array(
'context' => $ctx,
));
}
if ($errno = xmlrpc_errno()) {
watchdog('acquia agent', '@message (@errno): %server - %method - <pre>@data</pre>', array(
'@message' => xmlrpc_error_msg(),
'@errno' => xmlrpc_errno(),
'%server' => $url,
'%method' => $method,
'@data' => print_r($data, TRUE),
), WATCHDOG_ERROR);
}
return $result;
}
function _acquia_agent_authenticator($params = array(), $identifier = NULL, $key = NULL) {
if (empty($identifier)) {
$identifier = acquia_agent_settings('acquia_identifier');
}
if (empty($key)) {
$key = acquia_agent_settings('acquia_key');
}
$time = REQUEST_TIME;
$nonce = base64_encode(hash('sha256', drupal_random_bytes(55), TRUE));
$authenticator['identifier'] = $identifier;
$authenticator['time'] = $time;
$authenticator['hash'] = _acquia_agent_hmac($key, $time, $nonce, $params);
$authenticator['nonce'] = $nonce;
return $authenticator;
}
function _acquia_agent_hmac($key, $time, $nonce, $params) {
$data = $time . ':' . $nonce . ':' . $key . ':' . serialize($params);
return base64_encode(hash_hmac('sha1', $data, $key, TRUE));
}