You are here

drd_server.module in Drupal Remote Dashboard Server 6.2

Same filename and directory in other branches
  1. 6 drd_server.module
  2. 7.2 drd_server.module
  3. 7 drd_server.module

File

drd_server.module
View source
<?php

define('DRUPAL_ROOT', getcwd());
define('DRD_SERVER_API_VERSION', '2.4.0');
define('DRD_SERVER_ERROR_WRONG_API', 1001);
define('DRD_SERVER_ERROR_WRONG_REFERER', 1002);
define('DRD_SERVER_ERROR_NO_REFERER', 1003);
define('DRD_SERVER_ERROR_NO_OP', 1004);
define('DRD_SERVER_ERROR_NO_FUNC', 1005);
define('DRD_SERVER_ERROR_NO_LOG_PHP', 1006);
define('DRD_SERVER_ERROR_MISSING_AES', 1007);
define('DRD_SERVER_ERROR_WRONG_KEYS', 1999);
include 'drd_server.aes.inc';

/**
 * Define the REQUEST_TIME if not yet available
 */
if (!defined('REQUEST_TIME')) {
  if (isset($_SERVER['REQUEST_TIME'])) {
    define('REQUEST_TIME', $_SERVER['REQUEST_TIME']);
  }
  else {
    define('REQUEST_TIME', time());
  }
}

/** ======================================================================
 *
 * Hooks
 *
 */

/**
 * Implementation of hook_xmlrpc().
 *
 * This function provides Drupal with an array to map XML-RPC callbacks to the
 * functions implemented by this module.
 */
function drd_server_xmlrpc() {
  include_once 'drd_server.domain.inc';
  include_once 'drd_server.server.inc';
  return array(
    'drd.key' => 'drd_server_key',
    'drd.execute' => 'drd_server_execute',
    'drd.status' => 'drd_server_status',
    'drd.heartbeat' => 'drd_server_heartbeat',
    'drd.config.server.read' => 'drd_server_config_server_read',
    'drd.config.server.save' => 'drd_server_config_server_save',
    'drd.config.domain.read' => 'drd_server_config_domain_read',
    'drd.config.domain.save' => 'drd_server_config_domain_save',
    'drd.retrieve.blocks' => 'drd_server_render_blocks',
  );
}

/**
 * Implements hook_drd_server_actions().
 */
function drd_server_drd_server_actions() {
  return array(
    'drd.server.install' => array(
      'category' => t('Domain'),
      'label' => t('DRD: remotely install/uninstall'),
      'callback' => 'drd_install_drd_server',
      'mode' => 'domain',
      'remote' => FALSE,
      'queue' => TRUE,
      'fields' => array(
        'attention' => array(
          '#markup' => t('ATTENTION: This will flush all caches on your remote site, so please be aware and use this with care, especially if you have a lot of traffic on your remote site.'),
        ),
        'reset' => array(
          '#type' => 'checkbox',
          '#title' => t('Install'),
          '#default_value' => TRUE,
        ),
      ),
    ),
    'drd.server.domains' => array(
      'category' => t('Server'),
      'label' => t('Update server domains'),
      'callback' => 'drd_server_server_domains',
      'mode' => 'server',
      'remote' => TRUE,
      'queue' => TRUE,
      'follower' => array(
        'a' => 'drd.actions',
      ),
    ),
    'drd.info' => array(
      'label' => t('Update info'),
      'callback' => 'drd_server_domain_info',
      'mode' => 'domain',
      'remote' => TRUE,
      'queue' => TRUE,
      'refresh' => TRUE,
    ),
    'drd.cache.flush' => array(
      'label' => t('Flush Cache'),
      'callback' => 'drd_server_domain_flush_cache',
      'mode' => 'domain',
      'remote' => TRUE,
      'queue' => TRUE,
    ),
    'drd.run.cron' => array(
      'label' => t('Run Cron'),
      'callback' => 'drd_server_domain_run_cron',
      'mode' => 'domain',
      'remote' => TRUE,
      'queue' => TRUE,
      'follower' => array(
        'a' => 'drd.info',
      ),
    ),
    'drd.switch.maintenance' => array(
      'label' => t('Maintenance Mode'),
      'callback' => 'drd_server_domain_switch_maintenance',
      'mode' => 'domain',
      'remote' => TRUE,
      'queue' => TRUE,
      'fields' => array(
        'maintenancemode' => array(
          '#type' => 'checkbox',
          '#title' => t('On'),
          '#default_value' => TRUE,
        ),
      ),
    ),
    'drd.list.updates' => array(
      'label' => t('List new modules and themes'),
      'callback' => 'drd_server_domain_list_updates',
      'mode' => 'domain',
      'remote' => TRUE,
      'queue' => TRUE,
      'follower' => array(
        'a' => 'drd.info',
      ),
    ),
    'drd.run.update' => array(
      'label' => t('Run Database Updates'),
      'callback' => 'drd_server_domain_run_update',
      'mode' => 'domain',
      'remote' => TRUE,
      'queue' => TRUE,
      'follower' => array(
        'a' => 'drd.info',
      ),
    ),
    'drd.update.translation' => array(
      'label' => t('Update Translations'),
      'callback' => 'drd_server_domain_update_translation',
      'mode' => 'domain',
      'remote' => TRUE,
      'queue' => TRUE,
      'follower' => array(
        'a' => 'drd.info',
      ),
    ),
    'drd.server.php.error.log' => array(
      'category' => t('Server'),
      'label' => t('PHP Error Log'),
      'callback' => 'drd_server_server_php_error_log',
      'mode' => 'server',
      'remote' => TRUE,
      'queue' => TRUE,
    ),
    'drd.set.user1' => array(
      'label' => t('Reset User 1 Credentials'),
      'callback' => 'drd_server_domain_reset_user1',
      'mode' => 'domain',
      'remote' => TRUE,
      'queue' => FALSE,
      'fields' => array(
        'username' => array(
          '#type' => 'textfield',
          '#title' => t('Username'),
          '#default_value' => '',
        ),
        'userpass' => array(
          '#type' => 'password',
          '#title' => t('Password'),
          '#default_value' => '',
        ),
        'userpassrepeat' => array(
          '#type' => 'password',
          '#title' => t('Repeat password'),
          '#default_value' => '',
        ),
      ),
    ),
    'drd.php' => array(
      'label' => t('Execute PHP code'),
      'callback' => 'drd_server_domain_php',
      'mode' => 'domain',
      'remote' => TRUE,
      'queue' => TRUE,
      'fields' => array(
        'php' => array(
          '#type' => 'textarea',
          '#title' => t('PHP-Code'),
          '#default_value' => '',
          '#description' => t('The PHP code to be executed wrapper in php tags.'),
        ),
      ),
    ),
  );
}

/**
 * Implements hook_menu().
 */
function drd_server_menu() {
  $items['admin/drd_server/%'] = array(
    'title' => 'Check if DRD Server is installed',
    'page callback' => 'drd_server_check',
    'page arguments' => array(
      2,
    ),
    'access callback' => TRUE,
    'type' => MENU_CALLBACK,
  );
  $items['admin/settings/drd_settings'] = array(
    'title' => 'DRD Remote Settings',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'drd_server_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'file' => 'drd_server.admin.inc',
  );
  $items['admin/settings/drd_settings/%'] = array(
    'title' => 'DRD Remote Settings: Keys',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'drd_server_settings_keys',
      3,
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'file' => 'drd_server.admin.inc',
  );

  // For compatibility-Reason with D7:
  $items['admin/config/system/drd_settings'] = array(
    'title' => 'DRD Remote Settings',
    'page callback' => '_drd_server_goto_settings',
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_CALLBACK,
  );
  return $items;
}

/**
 * Implements hook_drd_config_domain().
 *
 * @return array
 *   Form definition for domain settings.
 */
function drd_server_drd_config_domain() {
  $form = array();
  $block_list = array();
  foreach (module_implements('block_info') as $module) {
    foreach (module_invoke($module, 'block_info') as $key => $block) {
      $block_list[$module . ':' . $key] = $block['info'];
    }
  }
  $form['drd_server_blocks'] = array(
    '#type' => 'checkboxes',
    '#title' => 'Blocks to display',
    '#options' => $block_list,
    '#default_value' => variable_get('drd_server_blocks', array()),
  );
  return $form;
}

/** ======================================================================
 *
 * Remote client functions
 *
 */

/**
 * This is called to update the excryption keys for this server and all it's
 * domains hosted in the same Drupal installation.
 *
 * @param string $api
 * @param int $timestamp
 * @param string $langcode
 * @param bool $debug
 * @param string $domainurl
 * @param string $aes_key
 * @param string $aes_cipher
 * @param string $aes_iv
 * @param string $aes_impl
 * @param string $cluster_token
 * @return string
 */
function drd_server_key($api, $timestamp, $langcode, $debug, $domainurl, $aes_key = '', $aes_cipher = '', $aes_iv = '', $aes_impl = '', $cluster_token = '') {
  global $language;
  $language->language = $langcode;
  _drd_server_debug_mode($debug);
  _drd_server_watchdog('AES key change request.');
  $allowed = variable_get('drd_allowed_referer', '');
  $referer = empty($cluster_token) ? ip_address() : $cluster_token;
  if (empty($allowed)) {
    _drd_server_watchdog('AES key change request not allowed from anywhere.', array(), WATCHDOG_ALERT);
    return drd_server_error(t('Referer (' . $referer . ') not allowed, nothing configured yet.'), DRD_SERVER_ERROR_NO_REFERER);
  }
  if (strpos($allowed, $referer) === FALSE) {
    _drd_server_watchdog('AES key change request unauthorized.', array(), WATCHDOG_ALERT);
    return drd_server_error(t('Referer (' . $referer . ') not allowed.'), DRD_SERVER_ERROR_WRONG_REFERER);
  }
  $aes_keys = variable_get('drd_aes_keys', array());
  if (!empty($cluster_token)) {
    if (empty($aes_keys[$cluster_token]['cluster_mode']) || empty($aes_keys[$cluster_token]['cluster_ips']) || strpos($aes_keys[$cluster_token]['cluster_ips'], ip_address()) === FALSE) {
      _drd_server_watchdog('AES key change request unauthorized due to ip address mismatch.', array(), WATCHDOG_ALERT);
      return drd_server_error(t('Referer (' . $referer . ') not allowed from ' . ip_address() . '.'), DRD_SERVER_ERROR_WRONG_REFERER);
    }
  }
  if ($api !== DRD_SERVER_API_VERSION) {
    _drd_server_watchdog('Wrong API: %api.', array(
      '%api' => $api,
    ), WATCHDOG_ALERT);
    return drd_server_error(t('Wrong API.'), DRD_SERVER_ERROR_WRONG_API);
  }
  $sites = drd_server_read_sites();
  _drd_server_watchdog('AES key change, available domains:<pre>@domains</pre>', array(
    '@domains' => print_r($sites, TRUE),
  ), WATCHDOG_INFO);
  if (empty($domainurl)) {
    $aes_keys[$referer] = array(
      'key' => $aes_key,
      'cipher' => $aes_cipher,
      'iv' => $aes_iv,
      'impl' => $aes_impl,
      'cluster_mode' => !empty($cluster_token),
      'cluster_ips' => empty($aes_keys[$cluster_token]['cluster_ips']) ? ip_address() : $aes_keys[$cluster_token]['cluster_ips'],
    );
    variable_set('drd_aes_keys', $aes_keys);
    $domainurls = $sites;
  }
  else {
    $domainurls = array(
      $domainurl => $sites[$domainurl],
    );
  }
  _drd_server_watchdog('AES key change for the following domains:<pre>@domains</pre>', array(
    '@domains' => print_r($domainurls, TRUE),
  ), WATCHDOG_INFO);
  drd_server_key_remote($domainurls, $aes_keys);
  return drd_server_result('drd.key', TRUE);
}

/**
 * This is called to execute one of the actions that are defined on one of
 * the hook_drd_server_actions()
 *
 * @return string
 */
function drd_server_execute() {
  $args = func_get_args();
  $action_name = array_shift($args);
  $valid = _drd_server_validate_request($args, $action_name);
  if ($valid !== TRUE) {
    return $valid;
  }
  $action_name = array_pop($args);
  $actions = module_invoke_all('drd_server_actions');
  if ($action_name == 'drd.actions') {
    return drd_server_result('drd.actions', $actions);
  }
  if (!isset($actions[$action_name])) {
    return drd_server_error(t('Action %action_name does not exist.', array(
      '%action_name' => $action_name,
    )), DRD_SERVER_ERROR_NO_OP);
  }
  $action = $actions[$action_name];
  if (!function_exists($action['callback'])) {
    return drd_server_error(t('Action %action_name does not have any callback.', array(
      '%action_name' => $action_name,
    )), DRD_SERVER_ERROR_NO_FUNC);
  }
  return call_user_func_array($action['callback'], $args);
}

/**
 * @return string
 */
function drd_server_status() {
  $args = func_get_args();
  $valid = _drd_server_validate_request($args);
  if ($valid !== TRUE) {
    return $valid;
  }
  $projects_enabled = array();
  $result = db_query("SELECT name, schema_version FROM {system} WHERE status=1");
  while ($record = db_fetch_object($result)) {
    $projects_enabled[$record->name] = $record->schema_version;
  }
  $status = array(
    'maintenance_mode' => variable_get('maintenance_mode', FALSE),
    'projects_enabled' => $projects_enabled,
    'drupal_root' => DRUPAL_ROOT,
  );
  drupal_alter('drd_server_status', $status);
  return drd_server_result('drd.status', $status);
}

/**
 * @return string
 */
function drd_server_heartbeat() {
  $args = func_get_args();
  $valid = _drd_server_validate_request($args);
  if ($valid !== TRUE) {
    return $valid;
  }
  $period = array_shift($args);

  /**
   * Session count is "borrowed" from the admin_menu module, credit to their maintainers.
   */
  $interval = REQUEST_TIME - variable_get('user_block_seconds_online', 900);
  $heartbeat = array(
    'count' => array(
      'user' => array(
        'all' => _drd_server_count('users', 'uid'),
        'sessions' => _drd_server_count_session($interval, TRUE),
        'auth' => _drd_server_count_session($interval, FALSE),
      ),
      'content' => array(
        'node' => _drd_server_count('node', 'nid'),
        'comment' => _drd_server_count('comments', 'cid'),
      ),
    ),
    'file' => array(
      'temp' => _drd_server_count_file(0),
      'perm' => _drd_server_count_file(1),
    ),
    'watchdog' => array(
      'emergency' => 0,
      'alert' => _drd_server_count_watchdog(WATCHDOG_ALERT, $period),
      'critical' => _drd_server_count_watchdog(WATCHDOG_CRITICAL, $period),
      'error' => _drd_server_count_watchdog(WATCHDOG_ERROR, $period),
      'warning' => _drd_server_count_watchdog(WATCHDOG_WARNING, $period),
      'notice' => _drd_server_count_watchdog(WATCHDOG_NOTICE, $period),
      'info' => _drd_server_count_watchdog(WATCHDOG_INFO, $period),
      'debug' => _drd_server_count_watchdog(WATCHDOG_DEBUG, $period),
    ),
  );
  $heartbeat['detail'] = array(
    'user' => array(
      'title' => t('User'),
      'content' => array(
        format_plural($heartbeat['count']['user']['auth'], '@count authenticated user', '@count authenticated users'),
        format_plural($heartbeat['count']['user']['sessions'], '@count user session', '@count user sessions'),
        format_plural($heartbeat['count']['user']['all'], '@count registered user', '@count registered users'),
      ),
    ),
    'content' => array(
      'title' => t('Content'),
      'content' => array(
        format_plural($heartbeat['count']['content']['node'], '@count node', '@count nodes'),
        format_plural($heartbeat['count']['content']['comment'], '@count comment', '@count comments'),
      ),
    ),
    'file' => array(
      'title' => t('Files'),
      'content' => array(
        format_plural($heartbeat['file']['temp']['count'], '@count temporary file with %size', '@count temporary files with %size', array(
          '%size' => format_size($heartbeat['file']['temp']['size']),
        )),
        format_plural($heartbeat['file']['perm']['count'], '@count permanent file with %size', '@count permanent files with %size', array(
          '%size' => format_size($heartbeat['file']['perm']['size']),
        )),
      ),
    ),
    'watchdog' => array(
      'title' => t('Watchdog'),
      'content' => array(),
    ),
  );
  foreach ($heartbeat['watchdog'] as $key => $value) {
    $heartbeat['detail']['watchdog']['content'][$key] = format_plural($value, '@count %severity entry', '@count %severity entries', array(
      '%severity' => $key,
    ));
  }
  drupal_alter('drd_server_heartbeat', $heartbeat);
  return drd_server_result('drd.heartbeat', $heartbeat);
}

/**
 * @return string
 */
function drd_server_render_blocks() {
  $args = func_get_args();
  $valid = _drd_server_validate_request($args);
  if ($valid !== TRUE) {
    return $valid;
  }
  $result = '';
  foreach (variable_get('drd_server_blocks', array()) as $def) {
    if ($def) {
      list($module, $delta) = explode(':', $def);
      $block = (object) module_invoke($module, 'block', 'view', $delta);
      $result .= theme('block', $block);
    }
  }
  return drd_server_result('drd.render.blocks', $result);
}

/**
 * @return string
 */
function drd_server_config_server_read() {
  $args = func_get_args();
  $valid = _drd_server_validate_request($args);
  if ($valid !== TRUE) {
    return $valid;
  }
  $form = module_invoke_all('drd_config_server');
  return drd_server_result('drd.config.read.server', $form);
}

/**
 * @return string
 */
function drd_server_config_server_save() {
  $args = func_get_args();
  $valid = _drd_server_validate_request($args);
  if ($valid !== TRUE) {
    return $valid;
  }
  $values = json_decode(array_shift($args), TRUE);
  $form = module_invoke_all('drd_config_server');
  return drd_server_config_save($form, $values);
}

/**
 * @return string
 */
function drd_server_config_domain_read() {
  $args = func_get_args();
  $valid = _drd_server_validate_request($args);
  if ($valid !== TRUE) {
    return $valid;
  }
  $form = module_invoke_all('drd_config_domain');
  return drd_server_result('drd.config.read.domain', $form);
}

/**
 * @return string
 */
function drd_server_config_domain_save() {
  $args = func_get_args();
  $valid = _drd_server_validate_request($args);
  if ($valid !== TRUE) {
    return $valid;
  }
  $values = json_decode(array_shift($args), TRUE);
  $form = module_invoke_all('drd_config_domain');
  return drd_server_config_save($form, $values);
}

/**
 * @param array $form
 * @param array $values
 * @param bool $return
 * @return string
 */
function drd_server_config_save($form, $values, $return = TRUE) {
  foreach ($form as $key => $element) {
    if (is_array($element) && substr($key, 0, 1) != '#') {
      drd_server_config_save($element, $values, FALSE);
    }
    if (!empty($values[$key])) {
      variable_set($key, $values[$key]);
    }
  }
  if ($return) {
    return drd_server_result('drd.config.save', TRUE);
  }
}

/** ======================================================================
 *
 * Helper functions
 *
 */

/**
 * Safely read the settings.php file and return the relevant variables.
 *
 * @param string $shortname
 * @param string $file
 * @return array
 */
function _drd_server_read_settings($shortname, $file) {
  $drd_db_url = '';
  $drd_db_prefix = '';
  try {
    $php = php_strip_whitespace($file);
    $php = str_replace('<?php', '', $php);
    $php = str_replace('<?', '', $php);
    $php = str_replace('?>', '', $php);
    $php = str_replace('ini_set', '@ini_set', $php);
    $php = str_replace('@@ini_set', '@ini_set', $php);
    $php = str_replace('$db_url', '$drd_db_url', $php);
    $php = str_replace('$db_prefix', '$drd_db_prefix', $php);
    eval($php);
  } catch (Exception $e) {

    // Ignore it
    _drd_server_watchdog('Read Settings - Exception occured:<pre>@exception</pre>', array(
      '@exception' => print_r($e, TRUE),
    ), WATCHDOG_ERROR);
    return array(
      '',
      '',
    );
  }
  $db_url = is_array($drd_db_url) ? $drd_db_url['default'] : $drd_db_url;
  $db_prefix = is_array($drd_db_prefix) ? $drd_db_prefix['default'] : $drd_db_prefix;
  if (empty($base_url)) {
    if ($shortname == 'default') {
      $base_url = $GLOBALS['base_url'];
    }
    else {
      $base_url = $shortname;
    }
  }
  return array(
    $base_url,
    $db_url,
    $db_prefix,
  );
}

/**
 * This is called to update encryption keys on all domains.
 *
 * @param array $domainurls
 * @param array $aes_keys
 */
function drd_server_key_remote($domainurls, $aes_keys) {
  global $db_url, $db_prefix;
  if (is_string($db_url)) {
    $db_url = array(
      'default' => $db_url,
    );
  }
  $orig_db_prefix = $db_prefix;
  $used_extension = '';
  $i = 0;
  foreach ($domainurls as $url => $shortname) {
    $file = DRUPAL_ROOT . '/sites/' . $shortname . '/settings.php';
    if (file_exists($file)) {
      $i++;
      list($base_url, $url, $prefix) = _drd_server_read_settings($shortname, $file);
      if (empty($url)) {
        _drd_server_watchdog('Set AES key - Failed as url is empty: @shortname', array(
          '@shortname' => $shortname,
        ), WATCHDOG_ERROR);
        continue;
      }
      $extension = substr($url, 0, strpos($url, '://'));
      if (empty($used_extension)) {
        $used_extension = $extension;
      }
      elseif ($used_extension == $extension) {

        // Everything is fine, we can continue as is.
      }
      else {

        // We are having a problem here: the current domain uses a different db extension than the previous one.
        if (in_array(strtolower($used_extension), array(
          'mysql',
          'mysqli',
        )) && in_array(strtolower($extension), array(
          'mysql',
          'mysqli',
        ))) {

          // This is a case we can handle: use the same extension for the current domain as for the previous one.
          $url = str_replace($extension . '://', $used_extension . '://', $url);
        }
        else {

          // This is a case we can't handle and we produce a message and skip.
          _drd_server_watchdog('Set AES key - Failed because of mixed usage of @used and @current', array(
            '@used' => $used_extension,
            '@current' => $extension,
          ), WATCHDOG_ERROR);
          continue;
        }
      }
      $db_url['drd_' . $i] = $url;
      $db_prefix = $prefix;
      db_set_active('drd_' . $i);
      variable_set('drd_aes_keys', $aes_keys);
    }
  }

  // Reset to the default database
  $db_prefix = $orig_db_prefix;
  db_set_active();
}

/**
 * Determines all available sites/domains in the current Drupal installation.
 *
 * @return array
 */
function drd_server_read_sites() {
  $sites = array();
  if (file_exists(DRUPAL_ROOT . '/sites/sites.php')) {
    try {
      include DRUPAL_ROOT . '/sites/sites.php';
    } catch (Exception $e) {

      //Ignore
    }
  }
  if (empty($sites)) {
    foreach (scandir(DRUPAL_ROOT . '/sites') as $shortname) {
      if (is_dir(DRUPAL_ROOT . '/sites/' . $shortname) && !in_array($shortname, array(
        '.',
        '..',
        'all',
      ))) {
        $file = DRUPAL_ROOT . '/sites/' . $shortname . '/settings.php';
        if (file_exists($file)) {
          list($base_url, $db_url, $db_prefix) = _drd_server_read_settings($shortname, $file);
          if (empty($base_url)) {
            _drd_server_watchdog('Reading Sites - Failed as url is empty: @shortname', array(
              '@shortname' => $shortname,
            ), WATCHDOG_ERROR);
            continue;
          }
          $pos = strpos($base_url, '://');
          if ($pos > 0) {
            $base_url = substr($base_url, $pos + 3);
          }
          $sites[$base_url] = $shortname;
        }
      }
    }
  }
  return $sites;
}

/**
 * @return array
 */
function drd_get_allowed_referers($refs = NULL) {
  if (!isset($refs)) {
    $refs = variable_get('drd_allowed_referer', '');
  }
  $refs = str_replace(',', ' ', $refs);
  $refs = str_replace(';', ' ', $refs);
  return explode(' ', $refs);
}

/**
 * Get the AES encryption values for the remote DRD dashboard, identified
 * by their IP address.
 *
 * @return array
 */
function drd_server_aes() {
  $cluster_mode = !empty($_SERVER['HTTP_X_DRD_AUTH_TOKEN']);
  $referer = $cluster_mode ? $_SERVER['HTTP_X_DRD_AUTH_TOKEN'] : ip_address();
  $aes_keys = variable_get('drd_aes_keys', array());
  if (empty($aes_keys[$referer])) {
    return FALSE;
  }
  if (!empty($aes_keys[$referer]['cluster_mode']) && (empty($aes_keys[$referer]['cluster_ips']) || strpos($aes_keys[$referer]['cluster_ips'], ip_address()) === FALSE)) {
    return FALSE;
  }
  return $aes_keys[$referer];
}

/**
 * Prepare the result for returning it to the XMLRPC caller.
 *
 * @param $mode
 *   The function that was called by the XMLRPC caller.
 * @param $drd_result
 *   The result from the drd_server functions to be returned after hooks and alterers have been called.
 * @param $args
 *   More arguments if available and/or required.
 * @return string
 *   Returns the final result to be sent to the XMLRPC caller.
 */
function drd_server_result() {
  $args = func_get_args();
  $mode = array_shift($args);
  $drd_result = array_shift($args);
  foreach (module_implements('drd_server_result') as $module) {
    $drd_result = module_invoke($module, 'drd_server_result', $mode, $drd_result, $args);
  }
  drupal_alter('drd_server_result', $drd_result);
  $result = new stdClass();
  $result->message = $drd_result;
  $result->messages = drupal_get_messages();
  return drd_server_output($result);
}

/**
 * Prepare an error message for returning to the XMLRPC caller.
 *
 * @param string $message
 *   The message to be sent to the XMLRPC caller.
 * @param int $code
 *   Type of error.
 * @return string
 *   Returns the encoded message.
 */
function drd_server_error($message, $code = 1) {
  if (is_object($message)) {
    $message = (array) $message;
  }
  if (is_array($message)) {
    $message = implode(' ', $message);
  }
  $encrypt = !in_array($code, array(
    DRD_SERVER_ERROR_WRONG_REFERER,
    DRD_SERVER_ERROR_NO_REFERER,
    DRD_SERVER_ERROR_MISSING_AES,
    DRD_SERVER_ERROR_WRONG_KEYS,
  ));
  return drd_server_output(xmlrpc_error($code, strip_tags($message)), $encrypt);
}

/**
 * Prepare the output to be sent back to the DRD dashboard. This preparation
 * json encodes the output and then encrypts the resulting string if encryption
 * is available.
 *
 * @param string $output
 * @param bool $encrypt
 * @return string
 */
function drd_server_output($output, $encrypt = TRUE) {
  if (!empty($output)) {

    // json_encode() does not escape <, > and &, so we do it with str_replace().
    $output = str_replace(array(
      '<',
      '>',
      '&',
    ), array(
      '\\u003c',
      '\\u003e',
      '\\u0026',
    ), json_encode($output));
    if ($encrypt) {
      $aes = drd_server_aes();
      if (!empty($aes)) {
        $output = drd_server_aes_encrypt($output, TRUE, $aes['key'], $aes['cipher'], _drd_server_iv(), $aes['impl']);
      }
    }
  }
  return $output;
}

/**
 * Callback for the url 'admin/drd_server/%' to find out if a proper result is
 * returned which indicates whether drd_server is installed on a certain domain
 * or not.
 *
 * @param int $var
 */
function drd_server_check($var) {
  drupal_set_header('Charset: utf-8');
  drupal_set_header('Content-Type: text/plain');
  print _drd_server_get_check_token($GLOBALS['base_url'], $var);
  exit;
}

/**
 * @param string $url
 * @param int $var
 * @return string
 */
function _drd_server_get_check_token($url, $var) {
  return md5(implode('-', array(
    $var,
    $url,
    $var / 2,
  )));
}

/**
 * @param bool $debug
 * @return bool
 */
function _drd_server_debug_mode($debug = NULL) {
  static $debug_mode = FALSE;
  if (isset($debug)) {
    $debug_mode = $debug;
  }
  return $debug_mode;
}

/**
 * Internal watchdog replacement which only reports to watchdog if debugging
 * is switched on by the remote DRD dashboard.
 *
 * @param string $message
 * @param array $variables
 * @param int $severity
 * @param string $link
 */
function _drd_server_watchdog($message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
  if (_drd_server_debug_mode()) {
    watchdog('DRD Server', $message, $variables, $severity, $link);
  }
}

/**
 * Callback to get and decode the IV from the request header and store it for
 * the response.
 *
 * @return string
 */
function _drd_server_iv() {
  static $iv;
  if (!isset($iv)) {
    if (empty($_SERVER['HTTP_X_DRD_IV'])) {
      $iv = empty($_SERVER['HTTP_X_DRD_IV']);
    }
    else {
      $iv = base64_decode($_SERVER['HTTP_X_DRD_IV']);
    }
  }
  return $iv;
}

/**
 * @param array $args
 * @param string $action_name
 * @return array
 */
function _drd_server_validate_request(&$args, $action_name = '') {
  $langcode = $args[2];
  $debug = $args[3];
  global $language;
  $language->language = $langcode;
  _drd_server_debug_mode($debug);
  $aes = drd_server_aes();
  if (empty($aes)) {
    _drd_server_watchdog('Execution request unauthorized.', array(), WATCHDOG_ALERT);
    return array(
      drd_server_error(t('Referer (' . ip_address() . ') not allowed.'), DRD_SERVER_ERROR_WRONG_REFERER),
    );
  }
  array_push($args, $action_name);
  $iv = _drd_server_iv();
  foreach ($args as $i => $value) {
    if (!empty($value) && !in_array($i, array(
      2,
      3,
    ))) {
      $args[$i] = drd_server_aes_decrypt($value, TRUE, $aes['key'], $aes['cipher'], $iv, $aes['impl']);
    }
  }
  $api = array_shift($args);
  $timestamp = array_shift($args);
  $langcode = array_shift($args);
  $debug = array_shift($args);
  if (!_drd_server_validate_timestamp($timestamp)) {
    _drd_server_watchdog('Wrong encryption keys.', array(), WATCHDOG_EMERGENCY);
    return array(
      drd_server_error(t('Wrong encryption keys.'), DRD_SERVER_ERROR_WRONG_KEYS),
    );
  }
  if ($api !== DRD_SERVER_API_VERSION) {
    _drd_server_watchdog('Wrong API: %api.', array(
      '%api' => $api,
    ), WATCHDOG_ALERT);
    return array(
      drd_server_error(t('Wrong API.'), DRD_SERVER_ERROR_WRONG_API),
    );
  }
  $action_name = empty($args[0]) ? t('simple') : $args[0];
  if (sizeof($args) < 2) {
    _drd_server_watchdog('Remote execution request: !action_name', array(
      '!action_name' => $action_name,
    ));
  }
  else {
    _drd_server_watchdog('Remote execution request: !action_name <pre>!args</pre>', array(
      '!action_name' => $action_name,
      '!args' => print_r($args, TRUE),
    ));
  }
  return TRUE;
}

/**
 * @param string $timestamp
 * @return bool
 */
function _drd_server_validate_timestamp($timestamp) {
  for ($i = 0; $i < strlen($timestamp); $i++) {
    if (strpos('0123456789', $timestamp[$i]) === FALSE) {
      return FALSE;
    }
  }

  // TODO: Shall we check how old the timestamp is? Might be tricky, thinking about timezones
  return TRUE;
}

/**
 * Redirect to the D6 equivalent
 */
function _drd_server_goto_settings() {
  drupal_goto('admin/settings/drd_settings');
}

/**
 * @param string $table
 * @param string $primary
 * @return int
 */
function _drd_server_count($table, $primary) {
  try {
    return db_result(db_query("SELECT COUNT(DISTINCT " . $primary . ") FROM {" . $table . "}"));
  } catch (Exception $ex) {
    return 0;
  }
}

/**
 * @param int $timestamp
 * @param bool $anonymous
 * @return int
 */
function _drd_server_count_session($timestamp = 0, $anonymous = TRUE) {
  if ($anonymous) {
    return sess_count($timestamp);
  }
  else {
    return db_result(db_query("SELECT COUNT(DISTINCT uid) FROM {sessions} WHERE uid > 0 AND timestamp >= %d", $timestamp));
  }
}

/**
 * @param int $status
 * @return array
 */
function _drd_server_count_file($status) {
  $count = db_result(db_query("SELECT COUNT(fid) FROM {files} WHERE status = %d", $status));
  $size = db_result(db_query("SELECT SUM(filesize) FROM {files} WHERE status = %d", $status));
  return array(
    'count' => $count,
    'size' => $size,
  );
}

/**
 * @param int $severity
 * @param int $period
 * @return int
 */
function _drd_server_count_watchdog($severity, $period) {
  if (!module_exists('dblog')) {
    return 0;
  }
  if ($period) {
    return db_result(db_query("SELECT COUNT(wid) FROM {watchdog} WHERE severity = %d AND timestamp >= %d", $severity, REQUEST_TIME - $period));
  }
  else {
    return db_result(db_query("SELECT COUNT(wid) FROM {watchdog} WHERE severity = %d", $severity));
  }
}

Functions

Namesort descending Description
drd_get_allowed_referers
drd_server_aes Get the AES encryption values for the remote DRD dashboard, identified by their IP address.
drd_server_check Callback for the url 'admin/drd_server/%' to find out if a proper result is returned which indicates whether drd_server is installed on a certain domain or not.
drd_server_config_domain_read
drd_server_config_domain_save
drd_server_config_save
drd_server_config_server_read
drd_server_config_server_save
drd_server_drd_config_domain Implements hook_drd_config_domain().
drd_server_drd_server_actions Implements hook_drd_server_actions().
drd_server_error Prepare an error message for returning to the XMLRPC caller.
drd_server_execute This is called to execute one of the actions that are defined on one of the hook_drd_server_actions()
drd_server_heartbeat
drd_server_key This is called to update the excryption keys for this server and all it's domains hosted in the same Drupal installation.
drd_server_key_remote This is called to update encryption keys on all domains.
drd_server_menu Implements hook_menu().
drd_server_output Prepare the output to be sent back to the DRD dashboard. This preparation json encodes the output and then encrypts the resulting string if encryption is available.
drd_server_read_sites Determines all available sites/domains in the current Drupal installation.
drd_server_render_blocks
drd_server_result Prepare the result for returning it to the XMLRPC caller.
drd_server_status
drd_server_xmlrpc Implementation of hook_xmlrpc().
_drd_server_count
_drd_server_count_file
_drd_server_count_session
_drd_server_count_watchdog
_drd_server_debug_mode
_drd_server_get_check_token
_drd_server_goto_settings Redirect to the D6 equivalent
_drd_server_iv Callback to get and decode the IV from the request header and store it for the response.
_drd_server_read_settings Safely read the settings.php file and return the relevant variables.
_drd_server_validate_request
_drd_server_validate_timestamp
_drd_server_watchdog Internal watchdog replacement which only reports to watchdog if debugging is switched on by the remote DRD dashboard.

Constants