[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/includes/ -> bootstrap.inc (source)

   1  <?php
   2  // $Id: bootstrap.inc,v 1.206.2.31 2010/12/15 13:21:14 goba Exp $
   3  
   4  /**
   5   * @file
   6   * Functions that need to be loaded on every Drupal request.
   7   */
   8  
   9  /**
  10   * Indicates that the item should never be removed unless explicitly told to
  11   * using cache_clear_all() with a cache ID.
  12   */
  13  define('CACHE_PERMANENT', 0);
  14  
  15  /**
  16   * Indicates that the item should be removed at the next general cache wipe.
  17   */
  18  define('CACHE_TEMPORARY', -1);
  19  
  20  /**
  21   * Indicates that page caching is disabled.
  22   */
  23  define('CACHE_DISABLED', 0);
  24  
  25  /**
  26   * Indicates that page caching is enabled, using "normal" mode.
  27   */
  28  define('CACHE_NORMAL', 1);
  29  
  30  /**
  31   * Indicates that page caching is using "aggressive" mode. This bypasses
  32   * loading any modules for additional speed, which may break functionality in
  33   * modules that expect to be run on each page load.
  34   */
  35  define('CACHE_AGGRESSIVE', 2);
  36  
  37  /**
  38   * Log message severity -- Emergency: system is unusable.
  39   *
  40   * The WATCHDOG_* constant definitions correspond to the logging severity levels
  41   * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
  42   *
  43   * @see watchdog()
  44   * @see watchdog_severity_levels()
  45   */
  46  define('WATCHDOG_EMERG', 0);
  47  
  48  /**
  49   * Log message severity -- Alert: action must be taken immediately.
  50   *
  51   * The WATCHDOG_* constant definitions correspond to the logging severity levels
  52   * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
  53   *
  54   * @see watchdog()
  55   * @see watchdog_severity_levels()
  56   */
  57  define('WATCHDOG_ALERT', 1);
  58  
  59  /**
  60   * Log message severity -- Critical: critical conditions.
  61   *
  62   * The WATCHDOG_* constant definitions correspond to the logging severity levels
  63   * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
  64   *
  65   * @see watchdog()
  66   * @see watchdog_severity_levels()
  67   */
  68  define('WATCHDOG_CRITICAL', 2);
  69  
  70  /**
  71   * Log message severity -- Error: error conditions.
  72   *
  73   * The WATCHDOG_* constant definitions correspond to the logging severity levels
  74   * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
  75   *
  76   * @see watchdog()
  77   * @see watchdog_severity_levels()
  78   */
  79  define('WATCHDOG_ERROR', 3);
  80  
  81  /**
  82   * Log message severity -- Warning: warning conditions.
  83   *
  84   * The WATCHDOG_* constant definitions correspond to the logging severity levels
  85   * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
  86   *
  87   * @see watchdog()
  88   * @see watchdog_severity_levels()
  89   */
  90  define('WATCHDOG_WARNING', 4);
  91  
  92  /**
  93   * Log message severity -- Notice: normal but significant condition.
  94   *
  95   * The WATCHDOG_* constant definitions correspond to the logging severity levels
  96   * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
  97   *
  98   * @see watchdog()
  99   * @see watchdog_severity_levels()
 100   */
 101  define('WATCHDOG_NOTICE', 5);
 102  
 103  /**
 104   * Log message severity -- Informational: informational messages.
 105   *
 106   * The WATCHDOG_* constant definitions correspond to the logging severity levels
 107   * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
 108   *
 109   * @see watchdog()
 110   * @see watchdog_severity_levels()
 111   */
 112  define('WATCHDOG_INFO', 6);
 113  
 114  /**
 115   * Log message severity -- Debug: debug-level messages.
 116   *
 117   * The WATCHDOG_* constant definitions correspond to the logging severity levels
 118   * defined in RFC 3164, section 4.1.1: http://www.faqs.org/rfcs/rfc3164.html
 119   *
 120   * @see watchdog()
 121   * @see watchdog_severity_levels()
 122   */
 123  define('WATCHDOG_DEBUG', 7);
 124  
 125  /**
 126   * First bootstrap phase: initialize configuration.
 127   */
 128  define('DRUPAL_BOOTSTRAP_CONFIGURATION', 0);
 129  
 130  /**
 131   * Second bootstrap phase: try to call a non-database cache
 132   * fetch routine.
 133   */
 134  define('DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE', 1);
 135  
 136  /**
 137   * Third bootstrap phase: initialize database layer.
 138   */
 139  define('DRUPAL_BOOTSTRAP_DATABASE', 2);
 140  
 141  /**
 142   * Fourth bootstrap phase: identify and reject banned hosts.
 143   */
 144  define('DRUPAL_BOOTSTRAP_ACCESS', 3);
 145  
 146  /**
 147   * Fifth bootstrap phase: initialize session handling.
 148   */
 149  define('DRUPAL_BOOTSTRAP_SESSION', 4);
 150  
 151  /**
 152   * Sixth bootstrap phase: load bootstrap.inc and module.inc, start
 153   * the variable system and try to serve a page from the cache.
 154   */
 155  define('DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE', 5);
 156  
 157  /**
 158   * Seventh bootstrap phase: find out language of the page.
 159   */
 160  define('DRUPAL_BOOTSTRAP_LANGUAGE', 6);
 161  
 162  /**
 163   * Eighth bootstrap phase: set $_GET['q'] to Drupal path of request.
 164   */
 165  define('DRUPAL_BOOTSTRAP_PATH', 7);
 166  
 167  /**
 168   * Final bootstrap phase: Drupal is fully loaded; validate and fix
 169   * input data.
 170   */
 171  define('DRUPAL_BOOTSTRAP_FULL', 8);
 172  
 173  /**
 174   * Role ID for anonymous users; should match what's in the "role" table.
 175   */
 176  define('DRUPAL_ANONYMOUS_RID', 1);
 177  
 178  /**
 179   * Role ID for authenticated users; should match what's in the "role" table.
 180   */
 181  define('DRUPAL_AUTHENTICATED_RID', 2);
 182  
 183  /**
 184   * No language negotiation. The default language is used.
 185   */
 186  define('LANGUAGE_NEGOTIATION_NONE', 0);
 187  
 188  /**
 189   * Path based negotiation with fallback to default language
 190   * if no defined path prefix identified.
 191   */
 192  define('LANGUAGE_NEGOTIATION_PATH_DEFAULT', 1);
 193  
 194  /**
 195   * Path based negotiation with fallback to user preferences
 196   * and browser language detection if no defined path prefix
 197   * identified.
 198   */
 199  define('LANGUAGE_NEGOTIATION_PATH', 2);
 200  
 201  /**
 202   * Domain based negotiation with fallback to default language
 203   * if no language identified by domain.
 204   */
 205  define('LANGUAGE_NEGOTIATION_DOMAIN', 3);
 206  
 207  /**
 208   * Language written left to right. Possible value of $language->direction.
 209   */
 210  define('LANGUAGE_LTR', 0);
 211  
 212  /**
 213   * Language written right to left. Possible value of $language->direction.
 214   */
 215  define('LANGUAGE_RTL', 1);
 216  
 217  // Hide E_DEPRECATED messages.
 218  if (defined('E_DEPRECATED')) {
 219    error_reporting(error_reporting() & ~E_DEPRECATED);
 220  }
 221  
 222  /**
 223   * Start the timer with the specified name. If you start and stop
 224   * the same timer multiple times, the measured intervals will be
 225   * accumulated.
 226   *
 227   * @param name
 228   *   The name of the timer.
 229   */
 230  function timer_start($name) {
 231    global $timers;
 232  
 233    list($usec, $sec) = explode(' ', microtime());
 234    $timers[$name]['start'] = (float)$usec + (float)$sec;
 235    $timers[$name]['count'] = isset($timers[$name]['count']) ? ++$timers[$name]['count'] : 1;
 236  }
 237  
 238  /**
 239   * Read the current timer value without stopping the timer.
 240   *
 241   * @param name
 242   *   The name of the timer.
 243   * @return
 244   *   The current timer value in ms.
 245   */
 246  function timer_read($name) {
 247    global $timers;
 248  
 249    if (isset($timers[$name]['start'])) {
 250      list($usec, $sec) = explode(' ', microtime());
 251      $stop = (float)$usec + (float)$sec;
 252      $diff = round(($stop - $timers[$name]['start']) * 1000, 2);
 253  
 254      if (isset($timers[$name]['time'])) {
 255        $diff += $timers[$name]['time'];
 256      }
 257      return $diff;
 258    }
 259  }
 260  
 261  /**
 262   * Stop the timer with the specified name.
 263   *
 264   * @param name
 265   *   The name of the timer.
 266   * @return
 267   *   A timer array. The array contains the number of times the
 268   *   timer has been started and stopped (count) and the accumulated
 269   *   timer value in ms (time).
 270   */
 271  function timer_stop($name) {
 272    global $timers;
 273  
 274    $timers[$name]['time'] = timer_read($name);
 275    unset($timers[$name]['start']);
 276  
 277    return $timers[$name];
 278  }
 279  
 280  /**
 281   * Find the appropriate configuration directory.
 282   *
 283   * Try finding a matching configuration directory by stripping the website's
 284   * hostname from left to right and pathname from right to left. The first
 285   * configuration file found will be used; the remaining will ignored. If no
 286   * configuration file is found, return a default value '$confdir/default'.
 287   *
 288   * Example for a fictitious site installed at
 289   * http://www.drupal.org:8080/mysite/test/ the 'settings.php' is searched in
 290   * the following directories:
 291   *
 292   *  1. $confdir/8080.www.drupal.org.mysite.test
 293   *  2. $confdir/www.drupal.org.mysite.test
 294   *  3. $confdir/drupal.org.mysite.test
 295   *  4. $confdir/org.mysite.test
 296   *
 297   *  5. $confdir/8080.www.drupal.org.mysite
 298   *  6. $confdir/www.drupal.org.mysite
 299   *  7. $confdir/drupal.org.mysite
 300   *  8. $confdir/org.mysite
 301   *
 302   *  9. $confdir/8080.www.drupal.org
 303   * 10. $confdir/www.drupal.org
 304   * 11. $confdir/drupal.org
 305   * 12. $confdir/org
 306   *
 307   * 13. $confdir/default
 308   *
 309   * @param $require_settings
 310   *   Only configuration directories with an existing settings.php file
 311   *   will be recognized. Defaults to TRUE. During initial installation,
 312   *   this is set to FALSE so that Drupal can detect a matching directory,
 313   *   then create a new settings.php file in it.
 314   * @param reset
 315   *   Force a full search for matching directories even if one had been
 316   *   found previously.
 317   * @return
 318   *   The path of the matching directory.
 319   */
 320  function conf_path($require_settings = TRUE, $reset = FALSE) {
 321    static $conf = '';
 322  
 323    if ($conf && !$reset) {
 324      return $conf;
 325    }
 326  
 327    $confdir = 'sites';
 328    $uri = explode('/', $_SERVER['SCRIPT_NAME'] ? $_SERVER['SCRIPT_NAME'] : $_SERVER['SCRIPT_FILENAME']);
 329    $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
 330    for ($i = count($uri) - 1; $i > 0; $i--) {
 331      for ($j = count($server); $j > 0; $j--) {
 332        $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
 333        if (file_exists("$confdir/$dir/settings.php") || (!$require_settings && file_exists("$confdir/$dir"))) {
 334          $conf = "$confdir/$dir";
 335          return $conf;
 336        }
 337      }
 338    }
 339    $conf = "$confdir/default";
 340    return $conf;
 341  }
 342  
 343  /**
 344   * Unsets all disallowed global variables. See $allowed for what's allowed.
 345   */
 346  function drupal_unset_globals() {
 347    if (ini_get('register_globals')) {
 348      $allowed = array('_ENV' => 1, '_GET' => 1, '_POST' => 1, '_COOKIE' => 1, '_FILES' => 1, '_SERVER' => 1, '_REQUEST' => 1, 'GLOBALS' => 1);
 349      foreach ($GLOBALS as $key => $value) {
 350        if (!isset($allowed[$key])) {
 351          unset($GLOBALS[$key]);
 352        }
 353      }
 354    }
 355  }
 356  
 357  /**
 358   * Validate that a hostname (for example $_SERVER['HTTP_HOST']) is safe.
 359   *
 360   * As $_SERVER['HTTP_HOST'] is user input, ensure it only contains characters
 361   * allowed in hostnames.  See RFC 952 (and RFC 2181). $_SERVER['HTTP_HOST'] is
 362   * lowercased.
 363   *
 364   * @return
 365   *  TRUE if only containing valid characters, or FALSE otherwise.
 366   */
 367  function drupal_valid_http_host($host) {
 368    return preg_match('/^\[?(?:[a-z0-9-:\]_]+\.?)+$/', $host);
 369  }
 370  
 371  /**
 372   * Loads the configuration and sets the base URL, cookie domain, and
 373   * session name correctly.
 374   */
 375  function conf_init() {
 376    global $base_url, $base_path, $base_root;
 377  
 378    // Export the following settings.php variables to the global namespace
 379    global $db_url, $db_prefix, $db_collation, $cookie_domain, $conf, $installed_profile, $update_free_access;
 380    $conf = array();
 381  
 382    if (isset($_SERVER['HTTP_HOST'])) {
 383      // As HTTP_HOST is user input, ensure it only contains characters allowed
 384      // in hostnames. See RFC 952 (and RFC 2181).
 385      // $_SERVER['HTTP_HOST'] is lowercased here per specifications.
 386      $_SERVER['HTTP_HOST'] = strtolower($_SERVER['HTTP_HOST']);
 387      if (!drupal_valid_http_host($_SERVER['HTTP_HOST'])) {
 388        // HTTP_HOST is invalid, e.g. if containing slashes it may be an attack.
 389        header('HTTP/1.1 400 Bad Request');
 390        exit;
 391      }
 392    }
 393    else {
 394      // Some pre-HTTP/1.1 clients will not send a Host header. Ensure the key is
 395      // defined for E_ALL compliance.
 396      $_SERVER['HTTP_HOST'] = '';
 397    }
 398  
 399    if (file_exists('./'. conf_path() .'/settings.php')) {
 400      include_once './'. conf_path() .'/settings.php';
 401    }
 402  
 403    // Ignore the placeholder url from default.settings.php.
 404    if (isset($db_url) && $db_url == 'mysql://username:password@localhost/databasename') {
 405      $db_url = '';
 406    }
 407  
 408    if (isset($base_url)) {
 409      // Parse fixed base URL from settings.php.
 410      $parts = parse_url($base_url);
 411      if (!isset($parts['path'])) {
 412        $parts['path'] = '';
 413      }
 414      $base_path = $parts['path'] .'/';
 415      // Build $base_root (everything until first slash after "scheme://").
 416      $base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path']));
 417    }
 418    else {
 419      // Create base URL
 420      $base_root = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
 421  
 422      $base_url = $base_root .= '://'. $_SERVER['HTTP_HOST'];
 423  
 424      // $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not
 425      // be modified by a visitor.
 426      if ($dir = trim(dirname($_SERVER['SCRIPT_NAME']), '\,/')) {
 427        $base_path = "/$dir";
 428        $base_url .= $base_path;
 429        $base_path .= '/';
 430      }
 431      else {
 432        $base_path = '/';
 433      }
 434    }
 435  
 436    if ($cookie_domain) {
 437      // If the user specifies the cookie domain, also use it for session name.
 438      $session_name = $cookie_domain;
 439    }
 440    else {
 441      // Otherwise use $base_url as session name, without the protocol
 442      // to use the same session identifiers across http and https.
 443      list( , $session_name) = explode('://', $base_url, 2);
 444      // We escape the hostname because it can be modified by a visitor.
 445      if (!empty($_SERVER['HTTP_HOST'])) {
 446        $cookie_domain = check_plain($_SERVER['HTTP_HOST']);
 447        // Strip leading periods, www., and port numbers from cookie domain.
 448        $cookie_domain = ltrim($cookie_domain, '.');
 449        if (strpos($cookie_domain, 'www.') === 0) {
 450          $cookie_domain = substr($cookie_domain, 4);
 451        }
 452        $cookie_domain = explode(':', $cookie_domain);
 453        $cookie_domain = '.'. $cookie_domain[0];
 454      }
 455    }
 456    // To prevent session cookies from being hijacked, a user can configure the
 457    // SSL version of their website to only transfer session cookies via SSL by
 458    // using PHP's session.cookie_secure setting. The browser will then use two
 459    // separate session cookies for the HTTPS and HTTP versions of the site. So we
 460    // must use different session identifiers for HTTPS and HTTP to prevent a
 461    // cookie collision.
 462    if (ini_get('session.cookie_secure')) {
 463      $session_name .= 'SSL';
 464    }
 465    // Per RFC 2109, cookie domains must contain at least one dot other than the
 466    // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
 467    if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
 468      ini_set('session.cookie_domain', $cookie_domain);
 469    }
 470    session_name('SESS'. md5($session_name));
 471  }
 472  
 473  /**
 474   * Returns and optionally sets the filename for a system item (module,
 475   * theme, etc.). The filename, whether provided, cached, or retrieved
 476   * from the database, is only returned if the file exists.
 477   *
 478   * This function plays a key role in allowing Drupal's resources (modules
 479   * and themes) to be located in different places depending on a site's
 480   * configuration. For example, a module 'foo' may legally be be located
 481   * in any of these three places:
 482   *
 483   * modules/foo/foo.module
 484   * sites/all/modules/foo/foo.module
 485   * sites/example.com/modules/foo/foo.module
 486   *
 487   * Calling drupal_get_filename('module', 'foo') will give you one of
 488   * the above, depending on where the module is located.
 489   *
 490   * @param $type
 491   *   The type of the item (i.e. theme, theme_engine, module, profile).
 492   * @param $name
 493   *   The name of the item for which the filename is requested.
 494   * @param $filename
 495   *   The filename of the item if it is to be set explicitly rather
 496   *   than by consulting the database.
 497   *
 498   * @return
 499   *   The filename of the requested item.
 500   */
 501  function drupal_get_filename($type, $name, $filename = NULL) {
 502    static $files = array();
 503  
 504    if (!isset($files[$type])) {
 505      $files[$type] = array();
 506    }
 507  
 508    if (!empty($filename) && file_exists($filename)) {
 509      $files[$type][$name] = $filename;
 510    }
 511    elseif (isset($files[$type][$name])) {
 512      // nothing
 513    }
 514    // Verify that we have an active database connection, before querying
 515    // the database.  This is required because this function is called both
 516    // before we have a database connection (i.e. during installation) and
 517    // when a database connection fails.
 518    elseif (db_is_active() && (($file = db_result(db_query("SELECT filename FROM {system} WHERE name = '%s' AND type = '%s'", $name, $type))) && file_exists($file))) {
 519      $files[$type][$name] = $file;
 520    }
 521    else {
 522      // Fallback to searching the filesystem if the database connection is
 523      // not established or the requested file is not found.
 524      $config = conf_path();
 525      $dir = (($type == 'theme_engine') ? 'themes/engines' : "$type}s");
 526      $file = (($type == 'theme_engine') ? "$name.engine" : "$name.$type");
 527  
 528      foreach (array("$config/$dir/$file", "$config/$dir/$name/$file", "$dir/$file", "$dir/$name/$file") as $file) {
 529        if (file_exists($file)) {
 530          $files[$type][$name] = $file;
 531          break;
 532        }
 533      }
 534    }
 535  
 536    if (isset($files[$type][$name])) {
 537      return $files[$type][$name];
 538    }
 539  }
 540  
 541  /**
 542   * Load the persistent variable table.
 543   *
 544   * The variable table is composed of values that have been saved in the table
 545   * with variable_set() as well as those explicitly specified in the configuration
 546   * file.
 547   */
 548  function variable_init($conf = array()) {
 549    // NOTE: caching the variables improves performance by 20% when serving cached pages.
 550    if ($cached = cache_get('variables', 'cache')) {
 551      $variables = $cached->data;
 552    }
 553    else {
 554      $result = db_query('SELECT * FROM {variable}');
 555      while ($variable = db_fetch_object($result)) {
 556        $variables[$variable->name] = unserialize($variable->value);
 557      }
 558      cache_set('variables', $variables);
 559    }
 560  
 561    foreach ($conf as $name => $value) {
 562      $variables[$name] = $value;
 563    }
 564  
 565    return $variables;
 566  }
 567  
 568  /**
 569   * Returns a persistent variable.
 570   *
 571   * Case-sensitivity of the variable_* functions depends on the database
 572   * collation used. To avoid problems, always use lower case for persistent
 573   * variable names.
 574   *
 575   * @param $name
 576   *   The name of the variable to return.
 577   * @param $default
 578   *   The default value to use if this variable has never been set.
 579   * @return
 580   *   The value of the variable.
 581   *
 582   * @see variable_del(), variable_set()
 583   */
 584  function variable_get($name, $default) {
 585    global $conf;
 586  
 587    return isset($conf[$name]) ? $conf[$name] : $default;
 588  }
 589  
 590  /**
 591   * Sets a persistent variable.
 592   *
 593   * Case-sensitivity of the variable_* functions depends on the database
 594   * collation used. To avoid problems, always use lower case for persistent
 595   * variable names.
 596   *
 597   * @param $name
 598   *   The name of the variable to set.
 599   * @param $value
 600   *   The value to set. This can be any PHP data type; these functions take care
 601   *   of serialization as necessary.
 602   *
 603   * @see variable_del(), variable_get()
 604   */
 605  function variable_set($name, $value) {
 606    global $conf;
 607  
 608    $serialized_value = serialize($value);
 609    db_query("UPDATE {variable} SET value = '%s' WHERE name = '%s'", $serialized_value, $name);
 610    if (!db_affected_rows()) {
 611      @db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, $serialized_value);
 612    }
 613  
 614    cache_clear_all('variables', 'cache');
 615  
 616    $conf[$name] = $value;
 617  }
 618  
 619  /**
 620   * Unsets a persistent variable.
 621   *
 622   * Case-sensitivity of the variable_* functions depends on the database
 623   * collation used. To avoid problems, always use lower case for persistent
 624   * variable names.
 625   *
 626   * @param $name
 627   *   The name of the variable to undefine.
 628   *
 629   * @see variable_get(), variable_set()
 630   */
 631  function variable_del($name) {
 632    global $conf;
 633  
 634    db_query("DELETE FROM {variable} WHERE name = '%s'", $name);
 635    cache_clear_all('variables', 'cache');
 636  
 637    unset($conf[$name]);
 638  }
 639  
 640  
 641  /**
 642   * Retrieve the current page from the cache.
 643   *
 644   * Note: we do not serve cached pages when status messages are waiting (from
 645   * a redirected form submission which was completed).
 646   *
 647   * @param $status_only
 648   *   When set to TRUE, retrieve the status of the page cache only
 649   *   (whether it was started in this request or not).
 650   */
 651  function page_get_cache($status_only = FALSE) {
 652    static $status = FALSE;
 653    global $user, $base_root;
 654  
 655    if ($status_only) {
 656      return $status;
 657    }
 658    $cache = NULL;
 659  
 660    if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && count(drupal_set_message()) == 0 && $_SERVER['SERVER_SOFTWARE'] !== 'PHP CLI') {
 661      $cache = cache_get($base_root . request_uri(), 'cache_page');
 662  
 663      if (empty($cache)) {
 664        ob_start();
 665        $status = TRUE;
 666      }
 667    }
 668  
 669    return $cache;
 670  }
 671  
 672  /**
 673   * Call all init or exit hooks without including all modules.
 674   *
 675   * @param $hook
 676   *   The name of the bootstrap hook we wish to invoke.
 677   */
 678  function bootstrap_invoke_all($hook) {
 679    foreach (module_list(TRUE, TRUE) as $module) {
 680      drupal_load('module', $module);
 681      module_invoke($module, $hook);
 682    }
 683  }
 684  
 685  /**
 686   * Includes a file with the provided type and name. This prevents
 687   * including a theme, engine, module, etc., more than once.
 688   *
 689   * @param $type
 690   *   The type of item to load (i.e. theme, theme_engine, module, profile).
 691   * @param $name
 692   *   The name of the item to load.
 693   *
 694   * @return
 695   *   TRUE if the item is loaded or has already been loaded.
 696   */
 697  function drupal_load($type, $name) {
 698    static $files = array();
 699  
 700    if (isset($files[$type][$name])) {
 701      return TRUE;
 702    }
 703  
 704    $filename = drupal_get_filename($type, $name);
 705  
 706    if ($filename) {
 707      include_once "./$filename";
 708      $files[$type][$name] = TRUE;
 709  
 710      return TRUE;
 711    }
 712  
 713    return FALSE;
 714  }
 715  
 716  /**
 717   * Set HTTP headers in preparation for a page response.
 718   *
 719   * Authenticated users are always given a 'no-cache' header, and will
 720   * fetch a fresh page on every request.  This prevents authenticated
 721   * users seeing locally cached pages that show them as logged out.
 722   *
 723   * @see page_set_cache()
 724   */
 725  function drupal_page_header() {
 726    header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
 727    header("Last-Modified: ". gmdate("D, d M Y H:i:s") ." GMT");
 728    header("Cache-Control: store, no-cache, must-revalidate");
 729    header("Cache-Control: post-check=0, pre-check=0", FALSE);
 730  }
 731  
 732  /**
 733   * Set HTTP headers in preparation for a cached page response.
 734   *
 735   * The general approach here is that anonymous users can keep a local
 736   * cache of the page, but must revalidate it on every request.  Then,
 737   * they are given a '304 Not Modified' response as long as they stay
 738   * logged out and the page has not been modified.
 739   *
 740   */
 741  function drupal_page_cache_header($cache) {
 742    // Set default values:
 743    $last_modified = gmdate('D, d M Y H:i:s', $cache->created) .' GMT';
 744    $etag = '"'. md5($last_modified) .'"';
 745  
 746    // See if the client has provided the required HTTP headers:
 747    $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
 748    $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;
 749  
 750    if ($if_modified_since && $if_none_match
 751        && $if_none_match == $etag // etag must match
 752        && $if_modified_since == $last_modified) {  // if-modified-since must match
 753      header('HTTP/1.1 304 Not Modified');
 754      // All 304 responses must send an etag if the 200 response for the same object contained an etag
 755      header("Etag: $etag");
 756      return;
 757    }
 758  
 759    // Send appropriate response:
 760    header("Last-Modified: $last_modified");
 761    header("ETag: $etag");
 762  
 763    // The following headers force validation of cache:
 764    header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
 765    header("Cache-Control: must-revalidate");
 766  
 767    if (variable_get('page_compression', TRUE) && extension_loaded('zlib')) {
 768      // Determine if the browser accepts gzipped data.
 769      if (isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE) {
 770        // $cache->data is already gzip'ed, so make sure zlib.output_compression
 771        // does not compress it once more.
 772        ini_set('zlib.output_compression', '0');
 773        header('Content-Encoding: gzip');
 774      }
 775      else {
 776        // The client does not support compression, so unzip the data in the
 777        // cache. Strip the gzip header and run uncompress.
 778        $cache->data = gzinflate(substr(substr($cache->data, 10), 0, -8));
 779      }
 780    }
 781  
 782    // Send the original request's headers. We send them one after
 783    // another so PHP's header() function can deal with duplicate
 784    // headers.
 785    $headers = explode("\n", $cache->headers);
 786    foreach ($headers as $header) {
 787      header($header);
 788    }
 789  
 790    print $cache->data;
 791  }
 792  
 793  /**
 794   * Define the critical hooks that force modules to always be loaded.
 795   */
 796  function bootstrap_hooks() {
 797    return array('boot', 'exit');
 798  }
 799  
 800  /**
 801   * Unserializes and appends elements from a serialized string.
 802   *
 803   * @param $obj
 804   *   The object to which the elements are appended.
 805   * @param $field
 806   *   The attribute of $obj whose value should be unserialized.
 807   */
 808  function drupal_unpack($obj, $field = 'data') {
 809    if ($obj->$field && $data = unserialize($obj->$field)) {
 810      foreach ($data as $key => $value) {
 811        if (!empty($key) && !isset($obj->$key)) {
 812          $obj->$key = $value;
 813        }
 814      }
 815    }
 816    return $obj;
 817  }
 818  
 819  /**
 820   * Return the URI of the referring page.
 821   */
 822  function referer_uri() {
 823    if (isset($_SERVER['HTTP_REFERER'])) {
 824      return $_SERVER['HTTP_REFERER'];
 825    }
 826  }
 827  
 828  /**
 829   * Encode special characters in a plain-text string for display as HTML.
 830   *
 831   * Also validates strings as UTF-8 to prevent cross site scripting attacks on
 832   * Internet Explorer 6.
 833   *
 834   * @param $text
 835   *   The text to be checked or processed.
 836   * @return
 837   *   An HTML safe version of $text, or an empty string if $text is not
 838   *   valid UTF-8.
 839   *
 840   * @see drupal_validate_utf8().
 841   */
 842  function check_plain($text) {
 843    static $php525;
 844  
 845    if (!isset($php525)) {
 846      $php525 = version_compare(PHP_VERSION, '5.2.5', '>=');
 847    }
 848    // We duplicate the preg_match() to validate strings as UTF-8 from
 849    // drupal_validate_utf8() here. This avoids the overhead of an additional
 850    // function call, since check_plain() may be called hundreds of times during
 851    // a request. For PHP 5.2.5+, this check for valid UTF-8 should be handled
 852    // internally by PHP in htmlspecialchars().
 853    // @see http://www.php.net/releases/5_2_5.php
 854    // @todo remove this when support for either IE6 or PHP < 5.2.5 is dropped.
 855  
 856    if ($php525) {
 857      return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
 858    }
 859    return (preg_match('/^./us', $text) == 1) ? htmlspecialchars($text, ENT_QUOTES, 'UTF-8') : '';
 860  }
 861  
 862  /**
 863   * Checks whether a string is valid UTF-8.
 864   *
 865   * All functions designed to filter input should use drupal_validate_utf8
 866   * to ensure they operate on valid UTF-8 strings to prevent bypass of the
 867   * filter.
 868   *
 869   * When text containing an invalid UTF-8 lead byte (0xC0 - 0xFF) is presented
 870   * as UTF-8 to Internet Explorer 6, the program may misinterpret subsequent
 871   * bytes. When these subsequent bytes are HTML control characters such as
 872   * quotes or angle brackets, parts of the text that were deemed safe by filters
 873   * end up in locations that are potentially unsafe; An onerror attribute that
 874   * is outside of a tag, and thus deemed safe by a filter, can be interpreted
 875   * by the browser as if it were inside the tag.
 876   *
 877   * This function exploits preg_match behaviour (since PHP 4.3.5) when used
 878   * with the u modifier, as a fast way to find invalid UTF-8. When the matched
 879   * string contains an invalid byte sequence, it will fail silently.
 880   *
 881   * preg_match may not fail on 4 and 5 octet sequences, even though they
 882   * are not supported by the specification.
 883   *
 884   * The specific preg_match behaviour is present since PHP 4.3.5.
 885   *
 886   * @param $text
 887   *   The text to check.
 888   * @return
 889   *   TRUE if the text is valid UTF-8, FALSE if not.
 890   */
 891  function drupal_validate_utf8($text) {
 892    if (strlen($text) == 0) {
 893      return TRUE;
 894    }
 895    // For performance reasons this logic is duplicated in check_plain().
 896    return (preg_match('/^./us', $text) == 1);
 897  }
 898  
 899  /**
 900   * Since $_SERVER['REQUEST_URI'] is only available on Apache, we
 901   * generate an equivalent using other environment variables.
 902   */
 903  function request_uri() {
 904  
 905    if (isset($_SERVER['REQUEST_URI'])) {
 906      $uri = $_SERVER['REQUEST_URI'];
 907    }
 908    else {
 909      if (isset($_SERVER['argv'])) {
 910        $uri = $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['argv'][0];
 911      }
 912      elseif (isset($_SERVER['QUERY_STRING'])) {
 913        $uri = $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
 914      }
 915      else {
 916        $uri = $_SERVER['SCRIPT_NAME'];
 917      }
 918    }
 919    // Prevent multiple slashes to avoid cross site requests via the FAPI.
 920    $uri = '/'. ltrim($uri, '/');
 921  
 922    return $uri;
 923  }
 924  
 925  /**
 926   * Log a system message.
 927   *
 928   * @param $type
 929   *   The category to which this message belongs. Can be any string, but the
 930   *   general practice is to use the name of the module calling watchdog().
 931   * @param $message
 932   *   The message to store in the log. See t() for documentation
 933   *   on how $message and $variables interact. Keep $message
 934   *   translatable by not concatenating dynamic values into it!
 935   * @param $variables
 936   *   Array of variables to replace in the message on display or
 937   *   NULL if message is already translated or not possible to
 938   *   translate.
 939   * @param $severity
 940   *   The severity of the message, as per RFC 3164. Possible values are
 941   *   WATCHDOG_ERROR, WATCHDOG_WARNING, etc.
 942   * @param $link
 943   *   A link to associate with the message.
 944   *
 945   * @see watchdog_severity_levels()
 946   */
 947  function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
 948    global $user, $base_root;
 949  
 950    // Prepare the fields to be logged
 951    $log_message = array(
 952      'type'        => $type,
 953      'message'     => $message,
 954      'variables'   => $variables,
 955      'severity'    => $severity,
 956      'link'        => $link,
 957      'user'        => $user,
 958      'request_uri' => $base_root . request_uri(),
 959      'referer'     => referer_uri(),
 960      'ip'          => ip_address(),
 961      'timestamp'   => time(),
 962      );
 963  
 964    // Call the logging hooks to log/process the message
 965    foreach (module_implements('watchdog') as $module) {
 966      module_invoke($module, 'watchdog', $log_message);
 967    }
 968  }
 969  
 970  /**
 971   * Set a message which reflects the status of the performed operation.
 972   *
 973   * If the function is called with no arguments, this function returns all set
 974   * messages without clearing them.
 975   *
 976   * @param $message
 977   *   The message should begin with a capital letter and always ends with a
 978   *   period '.'.
 979   * @param $type
 980   *   The type of the message. One of the following values are possible:
 981   *   - 'status'
 982   *   - 'warning'
 983   *   - 'error'
 984   * @param $repeat
 985   *   If this is FALSE and the message is already set, then the message won't
 986   *   be repeated.
 987   */
 988  function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
 989    if ($message) {
 990      if (!isset($_SESSION['messages'])) {
 991        $_SESSION['messages'] = array();
 992      }
 993  
 994      if (!isset($_SESSION['messages'][$type])) {
 995        $_SESSION['messages'][$type] = array();
 996      }
 997  
 998      if ($repeat || !in_array($message, $_SESSION['messages'][$type])) {
 999        $_SESSION['messages'][$type][] = $message;
1000      }
1001    }
1002  
1003    // messages not set when DB connection fails
1004    return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL;
1005  }
1006  
1007  /**
1008   * Return all messages that have been set.
1009   *
1010   * @param $type
1011   *   (optional) Only return messages of this type.
1012   * @param $clear_queue
1013   *   (optional) Set to FALSE if you do not want to clear the messages queue
1014   * @return
1015   *   An associative array, the key is the message type, the value an array
1016   *   of messages. If the $type parameter is passed, you get only that type,
1017   *   or an empty array if there are no such messages. If $type is not passed,
1018   *   all message types are returned, or an empty array if none exist.
1019   */
1020  function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
1021    if ($messages = drupal_set_message()) {
1022      if ($type) {
1023        if ($clear_queue) {
1024          unset($_SESSION['messages'][$type]);
1025        }
1026        if (isset($messages[$type])) {
1027          return array($type => $messages[$type]);
1028        }
1029      }
1030      else {
1031        if ($clear_queue) {
1032          unset($_SESSION['messages']);
1033        }
1034        return $messages;
1035      }
1036    }
1037    return array();
1038  }
1039  
1040  /**
1041   * Perform an access check for a given mask and rule type. Rules are usually
1042   * created via admin/user/rules page.
1043   *
1044   * If any allow rule matches, access is allowed. Otherwise, if any deny rule
1045   * matches, access is denied.  If no rule matches, access is allowed.
1046   *
1047   * @param $type string
1048   *   Type of access to check: Allowed values are:
1049   *     - 'host': host name or IP address
1050   *     - 'mail': e-mail address
1051   *     - 'user': username
1052   * @param $mask string
1053   *   String or mask to test: '_' matches any character, '%' matches any
1054   *   number of characters.
1055   * @return bool
1056   *   TRUE if access is denied, FALSE if access is allowed.
1057   */
1058  function drupal_is_denied($type, $mask) {
1059    // Because this function is called for every page request, both cached
1060    // and non-cached pages, we tried to optimize it as much as possible.
1061    // We deny access if the only matching records in the {access} table have
1062    // status 0 (deny). If any have status 1 (allow), or if there are no
1063    // matching records, we allow access.
1064    $sql = "SELECT 1 FROM {access} WHERE type = '%s' AND LOWER('%s') LIKE LOWER(mask) AND status = %d";
1065    return db_result(db_query_range($sql, $type, $mask, 0, 0, 1)) && !db_result(db_query_range($sql, $type, $mask, 1, 0, 1));
1066  }
1067  
1068  /**
1069   * Generates a default anonymous $user object.
1070   *
1071   * @return Object - the user object.
1072   */
1073  function drupal_anonymous_user($session = '') {
1074    $user = new stdClass();
1075    $user->uid = 0;
1076    $user->hostname = ip_address();
1077    $user->roles = array();
1078    $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
1079    $user->session = $session;
1080    $user->cache = 0;
1081    return $user;
1082  }
1083  
1084  /**
1085   * A string describing a phase of Drupal to load. Each phase adds to the
1086   * previous one, so invoking a later phase automatically runs the earlier
1087   * phases too. The most important usage is that if you want to access the
1088   * Drupal database from a script without loading anything else, you can
1089   * include bootstrap.inc, and call drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE).
1090   *
1091   * @param $phase
1092   *   A constant. Allowed values are:
1093   *     DRUPAL_BOOTSTRAP_CONFIGURATION: initialize configuration.
1094   *     DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE: try to call a non-database cache fetch routine.
1095   *     DRUPAL_BOOTSTRAP_DATABASE: initialize database layer.
1096   *     DRUPAL_BOOTSTRAP_ACCESS: identify and reject banned hosts.
1097   *     DRUPAL_BOOTSTRAP_SESSION: initialize session handling.
1098   *     DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE: load bootstrap.inc and module.inc, start
1099   *       the variable system and try to serve a page from the cache.
1100   *     DRUPAL_BOOTSTRAP_LANGUAGE: identify the language used on the page.
1101   *     DRUPAL_BOOTSTRAP_PATH: set $_GET['q'] to Drupal path of request.
1102   *     DRUPAL_BOOTSTRAP_FULL: Drupal is fully loaded, validate and fix input data.
1103   */
1104  function drupal_bootstrap($phase) {
1105    static $phases = array(DRUPAL_BOOTSTRAP_CONFIGURATION, DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE, DRUPAL_BOOTSTRAP_DATABASE, DRUPAL_BOOTSTRAP_ACCESS, DRUPAL_BOOTSTRAP_SESSION, DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE, DRUPAL_BOOTSTRAP_LANGUAGE, DRUPAL_BOOTSTRAP_PATH, DRUPAL_BOOTSTRAP_FULL), $phase_index = 0;
1106  
1107    while ($phase >= $phase_index && isset($phases[$phase_index])) {
1108      $current_phase = $phases[$phase_index];
1109      unset($phases[$phase_index++]);
1110      _drupal_bootstrap($current_phase);
1111    }
1112  }
1113  
1114  function _drupal_bootstrap($phase) {
1115    global $conf;
1116  
1117    switch ($phase) {
1118  
1119      case DRUPAL_BOOTSTRAP_CONFIGURATION:
1120        drupal_unset_globals();
1121        // Start a page timer:
1122        timer_start('page');
1123        // Initialize the configuration
1124        conf_init();
1125        break;
1126  
1127      case DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE:
1128        // Allow specifying special cache handlers in settings.php, like
1129        // using memcached or files for storing cache information.
1130        require_once variable_get('cache_inc', './includes/cache.inc');
1131        // If the page_cache_fastpath is set to TRUE in settings.php and
1132        // page_cache_fastpath (implemented in the special implementation of
1133        // cache.inc) printed the page and indicated this with a returned TRUE
1134        // then we are done.
1135        if (variable_get('page_cache_fastpath', FALSE) && page_cache_fastpath()) {
1136          exit;
1137        }
1138        break;
1139  
1140      case DRUPAL_BOOTSTRAP_DATABASE:
1141        // Initialize the default database.
1142        require_once  './includes/database.inc';
1143        db_set_active();
1144        // Allow specifying alternate lock implementations in settings.php, like
1145        // those using APC or memcached.
1146        require_once variable_get('lock_inc', './includes/lock.inc');
1147        lock_init();
1148        break;
1149  
1150      case DRUPAL_BOOTSTRAP_ACCESS:
1151        // Deny access to hosts which were banned - t() is not yet available.
1152        if (drupal_is_denied('host', ip_address())) {
1153          header('HTTP/1.1 403 Forbidden');
1154          print 'Sorry, '. check_plain(ip_address()) .' has been banned.';
1155          exit();
1156        }
1157        break;
1158  
1159      case DRUPAL_BOOTSTRAP_SESSION:
1160        require_once variable_get('session_inc', './includes/session.inc');
1161        session_set_save_handler('sess_open', 'sess_close', 'sess_read', 'sess_write', 'sess_destroy_sid', 'sess_gc');
1162        session_start();
1163        break;
1164  
1165      case DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE:
1166        // Initialize configuration variables, using values from settings.php if available.
1167        $conf = variable_init(isset($conf) ? $conf : array());
1168        $cache_mode = variable_get('cache', CACHE_DISABLED);
1169        // Get the page from the cache.
1170        $cache = $cache_mode == CACHE_DISABLED ? '' : page_get_cache();
1171        // If the skipping of the bootstrap hooks is not enforced, call hook_boot.
1172        if (!$cache || $cache_mode != CACHE_AGGRESSIVE) {
1173          // Load module handling.
1174          require_once  './includes/module.inc';
1175          bootstrap_invoke_all('boot');
1176        }
1177        // If there is a cached page, display it.
1178        if ($cache) {
1179          drupal_page_cache_header($cache);
1180          // If the skipping of the bootstrap hooks is not enforced, call hook_exit.
1181          if ($cache_mode != CACHE_AGGRESSIVE) {
1182            bootstrap_invoke_all('exit');
1183          }
1184          // We are done.
1185          exit;
1186        }
1187        // Prepare for non-cached page workflow.
1188        drupal_page_header();
1189        break;
1190  
1191      case DRUPAL_BOOTSTRAP_LANGUAGE:
1192        drupal_init_language();
1193        break;
1194  
1195      case DRUPAL_BOOTSTRAP_PATH:
1196        require_once  './includes/path.inc';
1197        // Initialize $_GET['q'] prior to loading modules and invoking hook_init().
1198        drupal_init_path();
1199        break;
1200  
1201      case DRUPAL_BOOTSTRAP_FULL:
1202        require_once  './includes/common.inc';
1203        _drupal_bootstrap_full();
1204        break;
1205    }
1206  }
1207  
1208  /**
1209   * Enables use of the theme system without requiring database access.
1210   *
1211   * Loads and initializes the theme system for site installs, updates and when
1212   * the site is in off-line mode. This also applies when the database fails.
1213   *
1214   * @see _drupal_maintenance_theme()
1215   */
1216  function drupal_maintenance_theme() {
1217    require_once  './includes/theme.maintenance.inc';
1218    _drupal_maintenance_theme();
1219  }
1220  
1221  /**
1222   * Return the name of the localisation function. Use in code that needs to
1223   * run both during installation and normal operation.
1224   */
1225  function get_t() {
1226    static $t;
1227    if (is_null($t)) {
1228      $t = function_exists('install_main') ? 'st' : 't';
1229    }
1230    return $t;
1231  }
1232  
1233  /**
1234   *  Choose a language for the current page, based on site and user preferences.
1235   */
1236  function drupal_init_language() {
1237    global $language, $user;
1238  
1239    // Ensure the language is correctly returned, even without multilanguage support.
1240    // Useful for eg. XML/HTML 'lang' attributes.
1241    if (variable_get('language_count', 1) == 1) {
1242      $language = language_default();
1243    }
1244    else {
1245      include_once  './includes/language.inc';
1246      $language = language_initialize();
1247    }
1248  }
1249  
1250  /**
1251   * Get a list of languages set up indexed by the specified key
1252   *
1253   * @param $field The field to index the list with.
1254   * @param $reset Boolean to request a reset of the list.
1255   */
1256  function language_list($field = 'language', $reset = FALSE) {
1257    static $languages = NULL;
1258  
1259    // Reset language list
1260    if ($reset) {
1261      $languages = NULL;
1262    }
1263  
1264    // Init language list
1265    if (!isset($languages)) {
1266      if (variable_get('language_count', 1) > 1 || module_exists('locale')) {
1267        $result = db_query('SELECT * FROM {languages} ORDER BY weight ASC, name ASC');
1268        while ($row = db_fetch_object($result)) {
1269          $languages['language'][$row->language] = $row;
1270        }
1271      }
1272      else {
1273        // No locale module, so use the default language only.
1274        $default = language_default();
1275        $languages['language'][$default->language] = $default;
1276      }
1277    }
1278  
1279    // Return the array indexed by the right field
1280    if (!isset($languages[$field])) {
1281      $languages[$field] = array();
1282      foreach ($languages['language'] as $lang) {
1283        // Some values should be collected into an array
1284        if (in_array($field, array('enabled', 'weight'))) {
1285          $languages[$field][$lang->$field][$lang->language] = $lang;
1286        }
1287        else {
1288          $languages[$field][$lang->$field] = $lang;
1289        }
1290      }
1291    }
1292    return $languages[$field];
1293  }
1294  
1295  /**
1296   * Default language used on the site
1297   *
1298   * @param $property
1299   *   Optional property of the language object to return
1300   */
1301  function language_default($property = NULL) {
1302    $language = variable_get('language_default', (object) array('language' => 'en', 'name' => 'English', 'native' => 'English', 'direction' => 0, 'enabled' => 1, 'plurals' => 0, 'formula' => '', 'domain' => '', 'prefix' => '', 'weight' => 0, 'javascript' => ''));
1303    return $property ? $language->$property : $language;
1304  }
1305  
1306  /**
1307   * If Drupal is behind a reverse proxy, we use the X-Forwarded-For header
1308   * instead of $_SERVER['REMOTE_ADDR'], which would be the IP address
1309   * of the proxy server, and not the client's.
1310   *
1311   * @return
1312   *   IP address of client machine, adjusted for reverse proxy.
1313   */
1314  function ip_address() {
1315    static $ip_address = NULL;
1316  
1317    if (!isset($ip_address)) {
1318      $ip_address = $_SERVER['REMOTE_ADDR'];
1319      if (variable_get('reverse_proxy', 0) && array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) {
1320        // If an array of known reverse proxy IPs is provided, then trust
1321        // the XFF header if request really comes from one of them.
1322        $reverse_proxy_addresses = variable_get('reverse_proxy_addresses', array());
1323        if (!empty($reverse_proxy_addresses) && in_array($ip_address, $reverse_proxy_addresses, TRUE)) {
1324          // If there are several arguments, we need to check the most
1325          // recently added one, i.e. the last one.
1326          $ip_address_parts = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
1327          $ip_address = array_pop($ip_address_parts);
1328        }
1329      }
1330    }
1331  
1332    return $ip_address;
1333  }


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7