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));
}