You are here

securepages.module in Secure Pages 6

Provide method of creating allowing certain pages to only viewable from https pages

File

securepages.module
View source
<?php

/**
 * @file
 * Provide method of creating allowing certain pages to only viewable from
 * https pages
 */

/**
 * Implementation of hook_boot().
 */
function securepages_boot() {
  $path = isset($_GET['q']) ? $_GET['q'] : '';
  if ($path == 'admin/build/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' || php_sapi_name() == 'cli') {
    if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest') {
      return;
    }
  }

  /**
   * If this is a cached page we need to do the check here even though we
   * have no access to the menu.
   */
  $cache_mode = variable_get('cache', CACHE_DISABLED);

  // Get the page from the cache.
  $cache = $cache_mode == CACHE_DISABLED ? '' : page_get_cache();
  if ($cache) {
    return;
  }
  if (!isset($_SESSION['securepages_redirect'])) {
    securepages_redirect();
  }
}

/**
 * Implementation of hook_init().
 */
function securepages_init() {
  if (!variable_get('securepages_enable', 0) || basename($_SERVER['PHP_SELF']) != 'index.php' || php_sapi_name() == 'cli') {
    return;
  }

  /**
   * If we have redirected in the hook_boot(). Then don't try to redirect
   * again. This will prevent a loop
   */
  if (!isset($_SESSION['securepages_redirect'])) {
    securepages_redirect();
  }
  else {
    unset($_SESSION['securepages_redirect']);
  }
}

/**
 * Implementation of hook_menu().
 */
function securepages_menu() {
  $items = array();
  $items['admin/build/securepages'] = array(
    'title' => 'Secure Pages',
    'description' => 'Configure which pages are and are not to be viewed in SSL',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'securepages_settings',
    ),
    'access arguments' => array(
      'administer site configuration',
    ),
    'type' => MENU_NORMAL_ITEM,
    'secure' => 1,
    'file' => 'securepages.admin.inc',
  );
  return $items;
}

/**
 * Implementation of hook_form_alter().
 */
function securepages_form_alter(&$form, &$form_state, $form_id) {
  if (!variable_get('securepages_enable', 0)) {
    return;
  }
  if (isset($form['#action']) && securepages_can_alter_url($form['#action'])) {
    @extract(@parse_url($form['#action']));
    if (isset($query)) {
      parse_str($query, $query);
    }
    else {
      $query = array();
    }
    if (isset($query['q'])) {
      $path = $query['q'];
    }
    else {
      $base_path = base_path();
      $path = !strncmp($path, $base_path, drupal_strlen($base_path)) ? drupal_substr($path, drupal_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, array(
        'query' => $query,
        'secure' => TRUE,
      ));
    }
    elseif ($page_match === 0 && securepages_is_secure() && variable_get('securepages_switch', FALSE)) {
      $form['#action'] = securepages_url($path, array(
        'query' => $query,
        'secure' => FALSE,
      ));
    }
  }
}

/**
 * Implementation of hook_link_alter().
 */
function securepages_link_alter(&$links, $node, $comment = NULL) {
  if (!variable_get('securepages_enable', 0)) {
    return;
  }
  foreach ($links as $module => $link) {
    if (isset($link['href']) && $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'], array(
          'secure' => TRUE,
        ));
      }
      elseif ($page_match === 0 && securepages_is_secure() && variable_get('securepages_switch', FALSE)) {
        $links[$module]['href'] = securepages_url($link['href'], array(
          'secure' => FALSE,
        ));
      }
    }
  }
}

/**
 * Check the current page and see if we need to redirect to the secure or
 * insecure version of the page.
 */
function securepages_redirect() {
  global $base_url;
  $path = isset($_GET['q']) ? $_GET['q'] : '';
  $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();
  }
}

/**
 * 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) {
  global $base_root;
  $_SESSION['securepages_redirect'] = TRUE;
  $path = !empty($_REQUEST['q']) ? $_REQUEST['q'] : '';
  $query = count($_GET) > 1 ? securepages_get_query($_GET) : NULL;
  $url = securepages_url($path, array(
    'query' => $query,
    'secure' => $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);

  // Make sure the cache is clear so that the next page will not pick up a cached version.
  cache_clear_all($base_root . request_uri(), 'cache_page');
  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 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', '');
  $path = securepages_strtolower(trim($path, '/'));
  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, '/')) . ')$/';
    $result = preg_match($regexp, $path);
    if (function_exists('drupal_get_path_alias')) {
      $path_alias = drupal_get_path_alias($path);
      $result |= preg_match($regexp, $path_alias);
    }
    return !($secure xor $result) ? 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/build/securepages/test', array(
    'absolute' => TRUE,
  )));
  $response = drupal_http_request($url);
  return $response->code == 200 ? TRUE : FALSE;
}

/**
 * Check if the current page is SSL
 */
function securepages_is_secure() {
  foreach (variable_get('securepages_ssl_checks', array(
    'HTTPS' => array(
      'on',
      '1',
    ),
    'HTTP_X_FORWARDED_PROTO' => 'https',
  )) as $key => $value) {
    if (isset($_SERVER[$key]) && is_array($value) && in_array($_SERVER[$key], $value)) {
      return TRUE;
    }
    elseif (isset($_SERVER[$key]) && $_SERVER[$key] == $value) {
      return TRUE;
    }
  }
  return FALSE;
}

/**
 * Generate a URL from a Drupal menu path. Will also pass-through existing URLs.
 *
 * @param $path
 *   The Drupal path being linked to, such as "admin/content/node", or an
 *   existing URL like "http://drupal.org/".  The special path
 *   '<front>' may also be given and will generate the site's base URL.
 * @param $options
 *   An associative array of additional options, with the following keys:
 *   - 'query'
 *       A query string to append to the link, or an array of query key/value
 *       properties.
 *   - 'fragment'
 *       A fragment identifier (or named anchor) to append to the link.
 *       Do not include the '#' character.
 *   - 'alias' (default FALSE)
 *       Whether the given path is an alias already.
 *   - 'external'
 *       Whether the given path is an external URL.
 *   - 'language'
 *       An optional language object. Used to build the URL to link to and
 *       look up the proper alias for the link.
 *   - 'base_url'
 *       Only used internally, to modify the base URL when a language dependent
 *       URL requires so.
 *   - 'prefix'
 *       Only used internally, to modify the path when a language dependent URL
 *       requires so.
 *   - 'secure'
 *       Specifies if the secure or insecure url should be returned.
 * @return
 *   A string containing a URL to the given path.
 *
 * When creating links in modules, consider whether l() could be a better
 * alternative than url().
 */
function securepages_url($path = NULL, $options = array()) {

  // Merge in defaults.
  $options += array(
    'fragment' => '',
    'query' => '',
    'alias' => FALSE,
    'prefix' => '',
    'secure' => TRUE,
  );
  if ($options['fragment']) {
    $options['fragment'] = '#' . $options['fragment'];
  }
  if (is_array($options['query'])) {
    $options['query'] = drupal_query_string_encode($options['query']);
  }
  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');
  }
  if (!isset($options['base_url'])) {

    // The base_url might be rewritten from the language rewrite in domain mode.
    $options['base_url'] = securepages_baseurl($options['secure']);
  }

  // Preserve the original path before aliasing.
  $original_path = $path;

  // The special path '<front>' links to the default front page.
  if ($path == '<front>') {
    $path = '';
  }
  elseif (!empty($path) && !$options['alias'] && function_exists('drupal_get_path_alias')) {
    $path = drupal_get_path_alias($path, isset($options['language']) ? $options['language']->language : '');
  }
  $base = $options['base_url'] . '/';
  $prefix = empty($path) ? rtrim($options['prefix'], '/') : $options['prefix'];
  $path = securepages_urlencode($prefix . $path);
  if ($clean_url) {

    // With Clean URLs.
    if ($options['query']) {
      return $base . $path . '?' . $options['query'] . $options['fragment'];
    }
    else {
      return $base . $path . $options['fragment'];
    }
  }
  else {

    // Without Clean URLs.
    $variables = array();
    if (!empty($path)) {
      $variables[] = 'q=' . $path;
    }
    if (!empty($options['query'])) {
      $variables[] = $options['query'];
    }
    if ($query = join('&', $variables)) {
      return $base . $script . '?' . $query . $options['fragment'];
    }
    else {
      return $base . $options['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) {
  return trim(str_replace('q=' . $query['q'], '', $_SERVER['QUERY_STRING']), '&');
}

/**
 * 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',
    ), rawurlencode($text));
  }
  else {
    return str_replace('%2F', '/', rawurlencode($text));
  }
}

/**
 * Lowercase a UTF-8 string.
 *
 * @param $text
 * The string to run the operation on.
 *
 * @return string
 * The string in lowercase.
 *
 * @ingroup php_wrappers
 */
function securepages_strtolower($text) {
  global $multibyte;
  if ($multibyte == 1) {
    return mb_strtolower($text);
  }
  else {

    // Use C-locale for ASCII-only lowercase
    $text = strtolower($text);

    // Case flip Latin-1 accented letters
    $text = preg_replace_callback('/\\xC3[\\x80-\\x96\\x98-\\x9E]/', '_securepages_unicode_caseflip', $text);
    return $text;
  }
}

/**
 * Helper function for case conversion of Latin-1.
 * Used for flipping U+C0-U+DE to U+E0-U+FD and back.
 */
function _securepages_unicode_caseflip($matches) {
  return $matches[0][0] . chr(ord($matches[0][1]) ^ 32);
}

/**
 * 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, $base_url;
  $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 (isset($host) && 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_boot Implementation of hook_boot().
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_redirect Check the current page and see if we need to redirect to the secure or insecure version of the page.
securepages_strtolower Lowercase a UTF-8 string.
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
_securepages_unicode_caseflip Helper function for case conversion of Latin-1. Used for flipping U+C0-U+DE to U+E0-U+FD and back.