You are here

securepages.module in Secure Pages 5

File

securepages.module
View source
<?php

/**
 * Implementation of hook_init()
 */
function securepages_init() {
  global $base_url;
  $path = isset($_GET['q']) ? $_GET['q'] : '';
  if ($path == 'admin/settings/securepages/test') {
    if (securepages_is_secure()) {
      header('HTTP/1.1 200 OK');
    }
    else {
      header('HTTP/1.1 404 Not Found');
    }
    exit;
  }
  if (!variable_get('securepages_enable', 0) || basename($_SERVER['PHP_SELF']) != 'index.php') {
    return;
  }
  $page_match = securepages_match($path);
  if ($_POST) {

    // If something has been posted to here then ignore the rules.
  }
  elseif ($page_match && !securepages_is_secure()) {
    securepages_goto(TRUE);
  }
  elseif ($page_match === 0 && securepages_is_secure() && variable_get('securepages_switch', FALSE)) {
    securepages_goto(FALSE);
  }

  // Correct the base_url so that everything comes from https.
  if (securepages_is_secure()) {
    $base_url = securepages_baseurl();
  }
}

/**
 * Implementation of hook_menu()
 */
function securepages_menu($may_cache) {
  $items = array();
  if ($may_cache) {
    $items[] = array(
      'path' => 'admin/settings/securepages',
      'title' => t('Secure Pages'),
      'description' => t('Configure which pages are and are not to be viewed in SSL'),
      'callback' => 'drupal_get_form',
      'callback arguments' => 'securepages_settings',
      'access' => user_access('administer site configuration'),
      'type' => MENU_NORMAL_ITEM,
    );
  }
  return $items;
}

/**
 * Implementation of hook_settings()
 */
function securepages_settings() {
  $form = array();
  $form['securepages_enable'] = array(
    '#type' => 'radios',
    '#title' => t('Enable Secure Pages'),
    '#default_value' => variable_get('securepages_enable', 0),
    '#options' => array(
      t('Disabled'),
      t('Enabled'),
    ),
    '#disabled' => !securepages_test(),
    '#description' => t('To start using secure pages this setting must be enabled. This setting will only be able to changed when the web server has been configured for SSL.<br />If this test has failed then go <a href="!url">here</a>', array(
      '!url' => preg_replace(';^http://;i', 'https://', url($_GET['q'], NULL, NULL, TRUE)),
    )),
  );
  $form['securepages_switch'] = array(
    '#type' => 'checkbox',
    '#title' => t('Switch back to http pages when there are no matches'),
    '#return_value' => TRUE,
    '#default_value' => variable_get('securepages_switch', FALSE),
  );
  $form['securepages_basepath'] = array(
    '#type' => 'textfield',
    '#title' => t('Non-secure Base URL'),
    '#default_value' => variable_get('securepages_basepath', ''),
  );
  $form['securepages_basepath_ssl'] = array(
    '#type' => 'textfield',
    '#title' => t('Secure Base URL'),
    '#default_value' => variable_get('securepages_basepath_ssl', ''),
  );
  $form['securepages_secure'] = array(
    '#type' => 'radios',
    '#title' => t('Pages which will be be secure'),
    '#default_value' => variable_get('securepages_secure', 1),
    '#options' => array(
      t('Make secure every page except the listed pages.'),
      t('Make secure only the listed pages.'),
    ),
  );
  $form['securepages_pages'] = array(
    '#type' => 'textarea',
    '#title' => t('Pages'),
    '#default_value' => variable_get('securepages_pages', "node/add*\nnode/*/edit\nuser/*\nadmin*"),
    '#cols' => 40,
    '#rows' => 5,
    '#description' => t("Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are '<em>blog</em>' for the blog page and '<em>blog/*</em>' for every personal blog. '<em>&lt;front&gt;</em>' is the front page."),
  );
  $form['securepages_ignore'] = array(
    '#type' => 'textarea',
    '#title' => t('Ignore pages'),
    '#default_value' => variable_get('securepages_ignore', "*/autocomplete/*"),
    '#cols' => 40,
    '#rows' => 5,
    '#description' => t("The pages listed here will be ignored and be either returned in http or https. Enter one page per line as Drupal paths. The '*' character is a wildcard. Example paths are '<em>blog</em>' for the blog page and '<em>blog/*</em>' for every personal blog. '<em>&lt;front&gt;</em>' is the front page."),
  );
  return system_settings_form($form);
}

/**
 * Implementation of hook_form_alter()
 */
function securepages_form_alter($form_id, &$form) {
  if (!variable_get('securepages_enable', 0)) {
    return;
  }
  if (isset($form['#action']) && securepages_can_alter_url($form['#action'])) {
    extract(parse_url($form['#action']));
    parse_str($query, $query);
    if (isset($query['q'])) {
      $path = $query['q'];
    }
    else {
      $base_path = base_path();
      $path = !strncmp($path, $base_path, strlen($base_path)) ? substr($path, strlen($base_path)) : $path;
    }
    $path = drupal_get_normal_path($path);
    $query = drupal_query_string_encode($query);
    $page_match = securepages_match($path);
    if ($page_match && !securepages_is_secure()) {
      $form['#action'] = securepages_url($path, $query, NULL, TRUE);
    }
    elseif ($page_match === 0 && securepages_is_secure() && variable_get('securepages_switch', FALSE)) {
      $form['#action'] = securepages_url($path, $query, NULL, FALSE);
    }
  }
}

/**
 * Implementation of hook_link_alter()
 */
function securepages_link_alter(&$node, &$links) {
  if (!variable_get('securepages_enable', 0)) {
    return;
  }
  foreach ($links as $module => $link) {
    if ($link['href'] && securepages_can_alter_url($link['href'])) {
      $page_match = securepages_match($link['href']);
      if ($page_match && !securepages_is_secure()) {
        $links[$module]['href'] = securepages_url($link['href'], NULL, NULL, TRUE);
      }
      elseif ($page_match === 0 && securepages_is_secure() && variable_get('securepages_switch', FALSE)) {
        $links[$module]['href'] = securepages_url($link['href'], NULL, NULL, FALSE);
      }
    }
  }
}

/**
 * securepage_goto()
 *
 * Redirects the current page to the secure or insecure version.
 *
 * @param $secure
 *  Determine which version of the set to move to.
 */
function securepages_goto($secure) {
  $path = !empty($_REQUEST['q']) ? $_REQUEST['q'] : '';
  $query = count($_GET) > 1 ? securepages_get_query($_GET) : NULL;
  $url = securepages_url($path, $query, NULL, $secure);
  if (function_exists('module_invoke_all')) {
    foreach (module_implements('exit') as $module) {
      if ($module != 'devel') {
        module_invoke($module, 'exit');
      }
    }
  }
  else {
    bootstrap_invoke_all('exit');
  }
  header('Location: ' . $url);
  exit;
}

/**
 * securepages_match()
 *
 * check the page past and see if it should be secure or insecure.
 *
 * @param $path
 *  the page of the page to check.
 *
 * @return
 *  0 - page should be insecure.
 *  1 - page should be secure.
 *  NULL - do not change page.
 */
function securepages_match($path) {

  /**
   * Check to see if the current menu item has a preference and ignore the
   * secure pages settings
   */
  if (function_exists('menu_get_item')) {
    $item = menu_get_item(menu_get_active_item());
    if (isset($item['secure'])) {
      return $item['secure'];
    }
  }

  /**
   * Check to see if the page matches the current settings
   */
  $secure = variable_get('securepages_secure', 1);
  $pages = variable_get('securepages_pages', "node/add*\nnode/*/edit\nuser/*\nadmin*");
  $ignore = variable_get('securepages_ignore', "*/autocomplete/*\n*/ajax/*");
  if ($ignore) {
    $regexp = '/^(' . preg_replace(array(
      '/(\\r\\n?|\\n)/',
      '/\\\\\\*/',
      '/(^|\\|)\\\\<front\\\\>($|\\|)/',
    ), array(
      '|',
      '.*',
      '\\1' . preg_quote(variable_get('site_frontpage', 'node'), '/') . '\\2',
    ), preg_quote($ignore, '/')) . ')$/';
    if (preg_match($regexp, $path)) {
      return securepages_is_secure() ? 1 : 0;
    }
  }
  if ($pages) {
    $regexp = '/^(' . preg_replace(array(
      '/(\\r\\n?|\\n)/',
      '/\\\\\\*/',
      '/(^|\\|)\\\\<front\\\\>($|\\|)/',
    ), array(
      '|',
      '.*',
      '\\1' . preg_quote(variable_get('site_frontpage', 'node'), '/') . '\\2',
    ), preg_quote($pages, '/')) . ')$/';
    return !($secure xor preg_match($regexp, $path)) ? 1 : 0;
  }
  else {
    return;
  }
}

/**
 * Secure Pages SSL Test
 */
function securepages_test() {

  // If we are in an SSL page then assume that SSL is configured correctly.
  if (securepages_is_secure()) {
    return TRUE;
  }
  $url = 'https://' . preg_replace(';^http[s]?://;s', '', url('admin/settings/securepages/test', NULL, NULL, TRUE));
  $response = drupal_http_request($url);
  return $response->code == 200 ? TRUE : FALSE;
}

/**
 * Check if the current page is SSL
 */
function securepages_is_secure() {
  return isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on' ? TRUE : FALSE;
}

/**
 * Generate a URL from a Drupal menu path. Will also pass-through existing URLs.
 * 
 * When creating links in modules, consider whether l() could be a better alternative than url().
 * 
 */
function securepages_url($path = NULL, $query = NULL, $fragment = NULL, $secure = FALSE) {
  if (isset($fragment)) {
    $fragment = '#' . $fragment;
  }

  // Return an external link if $path contains an allowed absolute URL.
  // Only call the slow filter_xss_bad_protocol if $path contains a ':' before any / ? or #.
  $colonpos = strpos($path, ':');
  if ($colonpos !== FALSE && !preg_match('![/?#]!', substr($path, 0, $colonpos)) && filter_xss_bad_protocol($path, FALSE) == check_plain($path)) {

    // Split off the fragment
    if (strpos($path, '#') !== FALSE) {
      list($path, $old_fragment) = explode('#', $path, 2);
      if (isset($old_fragment) && !isset($fragment)) {
        $fragment = '#' . $old_fragment;
      }
    }

    // Append the query
    if (isset($query)) {
      $path .= (strpos($path, '?') !== FALSE ? '&' : '?') . $query;
    }

    // Reassemble
    return $path . $fragment;
  }
  global $base_url;
  static $script;
  static $clean_url;
  if (!isset($script)) {

    // On some web servers, such as IIS, we can't omit "index.php". So, we
    // generate "index.php?q=foo" instead of "?q=foo" on anything that is not
    // Apache.
    $script = strpos($_SERVER['SERVER_SOFTWARE'], 'Apache') === FALSE ? 'index.php' : '';
  }

  // Cache the clean_url variable to improve performance.
  if (!isset($clean_url)) {
    $clean_url = (bool) variable_get('clean_url', '0');
  }
  $base = securepages_baseurl($secure) . '/';

  // The special path '<front>' links to the default front page.
  if (!empty($path) && $path != '<front>' && function_exists('drupal_get_path_alias')) {
    $path = drupal_get_path_alias($path);
    $path = drupal_urlencode($path);
    if (!$clean_url) {
      if (!empty($query)) {
        return $base . $script . '?q=' . $path . '&' . $query . $fragment;
      }
      else {
        return $base . $script . '?q=' . $path . $fragment;
      }
    }
    else {
      if (!empty($query)) {
        return $base . $path . '?' . $query . $fragment;
      }
      else {
        return $base . $path . $fragment;
      }
    }
  }
  else {
    if (!empty($query)) {
      return $base . $script . '?' . $query . $fragment;
    }
    else {
      return $base . $fragment;
    }
  }
}

/**
 * Return the secure base path
 */
function securepages_baseurl($secure = TRUE) {
  global $base_url;
  if ($secure) {
    $url = variable_get('securepages_basepath_ssl', NULL);
  }
  else {
    $url = variable_get('securepages_basepath', NULL);
  }
  if (!empty($url)) {
    return $url;
  }

  // No url has been set, so convert the base_url from 1 to the other
  return preg_replace('/http[s]?:\\/\\//i', $secure ? 'https://' : 'http://', $base_url, 1);
}

/**
 * Return a querystring without the q paramter
 */
function securepages_get_query($query) {
  unset($query['q']);
  $q = array();
  foreach ($query as $key => $value) {
    $q[] = drupal_urlencode($key) . '=' . drupal_urlencode($value);
  }
  return implode('&', $q);
}

/**
 * Copy of Drupals so we can redirect correctly
 */
function securepages_urlencode($text) {
  if (variable_get('clean_url', '0')) {
    return str_replace(array(
      '%2F',
      '%26',
      '%23',
      '//',
    ), array(
      '/',
      '%2526',
      '%2523',
      '/%252F',
    ), urlencode($text));
  }
  else {
    return str_replace('%2F', '/', urlencode($text));
  }
}

/**
 * Check the url and make sure that it is a url that you can alter this url.
 */
function securepages_can_alter_url($url) {
  global $base_path;
  extract(parse_url($url));

  // If there is no scheme then it is a relative url and can be altered
  if (!isset($scheme) && $base_path == '/') {
    return TRUE;
  }

  // If the host names are not the same then don't allow altering of the path.
  if (strtolower($host) != strtolower($_SERVER['HTTP_HOST'])) {
    return FALSE;
  }
  if (strlen($base_path) > 1 && substr($base_url, -1) != substr($path, 1, strlen($base_path))) {
    return FALSE;
  }
  return TRUE;
}

Functions

Namesort descending Description
securepages_baseurl Return the secure base path
securepages_can_alter_url Check the url and make sure that it is a url that you can alter this url.
securepages_form_alter Implementation of hook_form_alter()
securepages_get_query Return a querystring without the q paramter
securepages_goto securepage_goto()
securepages_init Implementation of hook_init()
securepages_is_secure Check if the current page is SSL
securepages_link_alter Implementation of hook_link_alter()
securepages_match securepages_match()
securepages_menu Implementation of hook_menu()
securepages_settings Implementation of hook_settings()
securepages_test Secure Pages SSL Test
securepages_url Generate a URL from a Drupal menu path. Will also pass-through existing URLs.
securepages_urlencode Copy of Drupals so we can redirect correctly