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