Code coverage for /20081101/includes/bootstrap.inc

Line #Times calledCode
1
<?php
2
// $Id: bootstrap.inc,v 1.243 2008/10/31 02:18:22 dries 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
 */
132367
define('CACHE_PERMANENT', 0);
14
15
/**
16
 * Indicates that the item should be removed at the next general cache
wipe.
17
 */
182367
define('CACHE_TEMPORARY', -1);
19
20
/**
21
 * Indicates that page caching is disabled.
22
 */
232367
define('CACHE_DISABLED', 0);
24
25
/**
26
 * Indicates that page caching is enabled, using "normal" mode.
27
 */
282367
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
 */
352367
define('CACHE_AGGRESSIVE', 2);
36
37
/**
38
 * Log message severity -- Emergency: system is unusable.
39
 *
40
 * @see watchdog()
41
 * @see watchdog_severity_levels()
42
 */
432367
define('WATCHDOG_EMERG', 0);
44
45
/**
46
 * Log message severity -- Alert: action must be taken immediately.
47
 *
48
 * @see watchdog()
49
 * @see watchdog_severity_levels()
50
 */
512367
define('WATCHDOG_ALERT', 1);
52
53
/**
54
 * Log message severity -- Critical: critical conditions.
55
 *
56
 * @see watchdog()
57
 * @see watchdog_severity_levels()
58
 */
592367
define('WATCHDOG_CRITICAL', 2);
60
61
/**
62
 * Log message severity -- Error: error conditions.
63
 *
64
 * @see watchdog()
65
 * @see watchdog_severity_levels()
66
 */
672367
define('WATCHDOG_ERROR', 3);
68
69
/**
70
 * Log message severity -- Warning: warning conditions.
71
 *
72
 * @see watchdog()
73
 * @see watchdog_severity_levels()
74
 */
752367
define('WATCHDOG_WARNING', 4);
76
77
/**
78
 * Log message severity -- Notice: normal but significant condition.
79
 *
80
 * @see watchdog()
81
 * @see watchdog_severity_levels()
82
 */
832367
define('WATCHDOG_NOTICE', 5);
84
85
/**
86
 * Log message severity -- Informational: informational messages.
87
 *
88
 * @see watchdog()
89
 * @see watchdog_severity_levels()
90
 */
912367
define('WATCHDOG_INFO', 6);
92
93
/**
94
 * Log message severity -- Debug: debug-level messages.
95
 *
96
 * @see watchdog()
97
 * @see watchdog_severity_levels()
98
 */
992367
define('WATCHDOG_DEBUG', 7);
100
101
/**
102
 * First bootstrap phase: initialize configuration.
103
 */
1042367
define('DRUPAL_BOOTSTRAP_CONFIGURATION', 0);
105
106
/**
107
 * Second bootstrap phase: try to call a non-database cache
108
 * fetch routine.
109
 */
1102367
define('DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE', 1);
111
112
/**
113
 * Third bootstrap phase: initialize database layer.
114
 */
1152367
define('DRUPAL_BOOTSTRAP_DATABASE', 2);
116
117
/**
118
 * Fourth bootstrap phase: identify and reject banned hosts.
119
 */
1202367
define('DRUPAL_BOOTSTRAP_ACCESS', 3);
121
122
/**
123
 * Fifth bootstrap phase: initialize session handling.
124
 */
1252367
define('DRUPAL_BOOTSTRAP_SESSION', 4);
126
127
/**
128
 * Sixth bootstrap phase: initialize the variable system.
129
 */
1302367
define('DRUPAL_BOOTSTRAP_VARIABLES', 5);
131
132
/**
133
 * Seventh bootstrap phase: load bootstrap.inc and module.inc, start
134
 * the variable system and try to serve a page from the cache.
135
 */
1362367
define('DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE', 6);
137
138
/**
139
 * Eighth bootstrap phase: find out language of the page.
140
 */
1412367
define('DRUPAL_BOOTSTRAP_LANGUAGE', 7);
142
143
/**
144
 * Nineth bootstrap phase: set $_GET['q'] to Drupal path of request.
145
 */
1462367
define('DRUPAL_BOOTSTRAP_PATH', 8);
147
148
/**
149
 * Final bootstrap phase: Drupal is fully loaded; validate and fix
150
 * input data.
151
 */
1522367
define('DRUPAL_BOOTSTRAP_FULL', 9);
153
154
/**
155
 * Role ID for anonymous users; should match what's in the "role" table.
156
 */
1572367
define('DRUPAL_ANONYMOUS_RID', 1);
158
159
/**
160
 * Role ID for authenticated users; should match what's in the "role"
table.
161
 */
1622367
define('DRUPAL_AUTHENTICATED_RID', 2);
163
164
/**
165
 * No language negotiation. The default language is used.
166
 */
1672367
define('LANGUAGE_NEGOTIATION_NONE', 0);
168
169
/**
170
 * Path based negotiation with fallback to default language
171
 * if no defined path prefix identified.
172
 */
1732367
define('LANGUAGE_NEGOTIATION_PATH_DEFAULT', 1);
174
175
/**
176
 * Path based negotiation with fallback to user preferences
177
 * and browser language detection if no defined path prefix
178
 * identified.
179
 */
1802367
define('LANGUAGE_NEGOTIATION_PATH', 2);
181
182
/**
183
 * Domain based negotiation with fallback to default language
184
 * if no language identified by domain.
185
 */
1862367
define('LANGUAGE_NEGOTIATION_DOMAIN', 3);
187
188
/**
189
 * For convenience, define a short form of the request time global.
190
 */
1912367
define('REQUEST_TIME', $_SERVER['REQUEST_TIME']);
192
193
/**
194
 * @name Title text filtering flags
195
 * @{
196
 * Flags for use in drupal_set_title().
197
 */
198
199
/**
200
 * Flag for drupal_set_title(); text is not sanitized, so run
check_plain().
201
 */
2022367
define('CHECK_PLAIN', 0);
203
204
/**
205
 * Flag for drupal_set_title(); text has already been sanitized.
206
 */
2072367
define('PASS_THROUGH', -1);
208
209
/**
210
 * @} End of "Title text filtering flags".
211
 */
212
213
214
/**
215
 * Start the timer with the specified name. If you start and stop
216
 * the same timer multiple times, the measured intervals will be
217
 * accumulated.
218
 *
219
 * @param name
220
 *   The name of the timer.
221
 */
2222367
function timer_start($name) {
2232367
  global $timers;
224
2252367
  $timers[$name]['start'] = microtime(TRUE);
2262367
  $timers[$name]['count'] = isset($timers[$name]['count']) ?
++$timers[$name]['count'] : 1;
2272367
}
228
229
/**
230
 * Read the current timer value without stopping the timer.
231
 *
232
 * @param name
233
 *   The name of the timer.
234
 * @return
235
 *   The current timer value in ms.
236
 */
2372367
function timer_read($name) {
23816
  global $timers;
239
24016
  if (isset($timers[$name]['start'])) {
24116
    $stop = microtime(TRUE);
24216
    $diff = round(($stop - $timers[$name]['start']) * 1000, 2);
243
24416
    if (isset($timers[$name]['time'])) {
2450
      $diff += $timers[$name]['time'];
2460
    }
24716
    return $diff;
2480
  }
2490
}
250
251
/**
252
 * Stop the timer with the specified name.
253
 *
254
 * @param name
255
 *   The name of the timer.
256
 * @return
257
 *   A timer array. The array contains the number of times the
258
 *   timer has been started and stopped (count) and the accumulated
259
 *   timer value in ms (time).
260
 */
2612367
function timer_stop($name) {
2620
  global $timers;
263
2640
  $timers[$name]['time'] = timer_read($name);
2650
  unset($timers[$name]['start']);
266
2670
  return $timers[$name];
2680
}
269
270
/**
271
 * Find the appropriate configuration directory.
272
 *
273
 * Try finding a matching configuration directory by stripping the
website's
274
 * hostname from left to right and pathname from right to left. The first
275
 * configuration file found will be used; the remaining will ignored. If no
276
 * configuration file is found, return a default value '$confdir/default'.
277
 *
278
 * Example for a fictitious site installed at
279
 * http://www.drupal.org:8080/mysite/test/ the 'settings.php' is searched
in
280
 * the following directories:
281
 *
282
 *  1. $confdir/8080.www.drupal.org.mysite.test
283
 *  2. $confdir/www.drupal.org.mysite.test
284
 *  3. $confdir/drupal.org.mysite.test
285
 *  4. $confdir/org.mysite.test
286
 *
287
 *  5. $confdir/8080.www.drupal.org.mysite
288
 *  6. $confdir/www.drupal.org.mysite
289
 *  7. $confdir/drupal.org.mysite
290
 *  8. $confdir/org.mysite
291
 *
292
 *  9. $confdir/8080.www.drupal.org
293
 * 10. $confdir/www.drupal.org
294
 * 11. $confdir/drupal.org
295
 * 12. $confdir/org
296
 *
297
 * 13. $confdir/default
298
 *
299
 * If a file named sites.php is present in the $confdir, it will be loaded
300
 * prior to scanning for directories.  It should define an associative
array
301
 * named $sites, which maps domains to directories.  It should be in the
form
302
 * of:
303
 *
304
 * $sites = array(
305
 *   'The url to alias' => 'A directory within the sites directory'
306
 * );
307
 *
308
 * For example:
309
 *
310
 * $sites = array(
311
 *   'devexample.com' => 'example.com',
312
 *   'localhost/example' => 'example.com',
313
 * );
314
 *
315
 * The above array will cause Drupal to look for a directory named
316
 * "example.com" in the sites directory whenever a request comes from
317
 * "example.com", "devexample.com", or "localhost/example". That is useful
318
 * on development servers, where the domain name may not be the same as the
319
 * domain of the live server.  Since Drupal stores file paths into the
database
320
 * (files, system table, etc.) this will ensure the paths are correct while
321
 * accessed on development servers.
322
 *
323
 * @param $require_settings
324
 *   Only configuration directories with an existing settings.php file
325
 *   will be recognized. Defaults to TRUE. During initial installation,
326
 *   this is set to FALSE so that Drupal can detect a matching directory,
327
 *   then create a new settings.php file in it.
328
 * @param reset
329
 *   Force a full search for matching directories even if one had been
330
 *   found previously.
331
 * @return
332
 *   The path of the matching directory.
333
 */
3342367
function conf_path($require_settings = TRUE, $reset = FALSE) {
3352498
  static $conf = '';
336
3372498
  if ($conf && !$reset) {
3382498
    return $conf;
3390
  }
340
3412367
  $confdir = 'sites';
342
3432367
  $sites = array();
3442367
  if (file_exists(DRUPAL_ROOT . '/' . $confdir . '/sites.php')) {
345
    // This will overwrite $sites with the desired mappings.
3460
    include(DRUPAL_ROOT . '/' . $confdir . '/sites.php');
3470
  }
348
3492367
  $uri = explode('/', $_SERVER['SCRIPT_NAME'] ? $_SERVER['SCRIPT_NAME'] :
$_SERVER['SCRIPT_FILENAME']);
3502367
  if (strpos($_SERVER['HTTP_HOST'], '/') !== FALSE ||
strpos($_SERVER['HTTP_HOST'], '\\') !== FALSE) {
351
    // A HTTP_HOST containing slashes may be an attack and is invalid.
3520
    header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
3530
    exit;
3540
  }
3552367
  $server = explode('.', implode('.', array_reverse(explode(':',
rtrim($_SERVER['HTTP_HOST'], '.')))));
3562367
  for ($i = count($uri) - 1; $i > 0; $i--) {
3572367
    for ($j = count($server); $j > 0; $j--) {
3582367
      $dir = implode('.', array_slice($server, -$j)) . implode('.',
array_slice($uri, 0, $i));
3592367
      if (isset($sites[$dir]) && file_exists(DRUPAL_ROOT . '/' . $confdir .
'/' . $sites[$dir])) {
3600
        $dir = $sites[$dir];
3610
      }
3622367
      if (file_exists(DRUPAL_ROOT . '/' . $confdir . '/' . $dir .
'/settings.php') || (!$require_settings && file_exists(DRUPAL_ROOT . '/' .
$confdir . '/' . $dir))) {
3630
        $conf = "$confdir/$dir";
3640
        return $conf;
3650
      }
3662367
    }
3672367
  }
3682367
  $conf = "$confdir/default";
3692367
  return $conf;
3700
}
371
372
/**
373
 * Initialize variables needed for the rest of the execution.
374
 */
3752367
function drupal_initialize_variables() {
3762367
  if (!isset($_SERVER['HTTP_REFERER'])) {
3772367
    $_SERVER['HTTP_REFERER'] = '';
3782367
  }
3792367
  if (!isset($_SERVER['SERVER_PROTOCOL']) || ($_SERVER['SERVER_PROTOCOL']
!= 'HTTP/1.0' && $_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.1')) {
3800
    $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.0';
3810
  }
382
  // Enforce E_ALL, but allow users to set levels not part of E_ALL.
3832367
  error_reporting(E_ALL | error_reporting());
384
  // Prevent PHP from generating HTML errors messages.
3852367
  ini_set('html_errors', 0);
3862367
}
387
388
/**
389
 * Loads the configuration and sets the base URL, cookie domain, and
390
 * session name correctly.
391
 */
3922367
function conf_init() {
3932367
  global $base_url, $base_path, $base_root;
394
395
  // Export the following settings.php variables to the global namespace
3962367
  global $databases, $db_prefix, $cookie_domain, $conf, $installed_profile,
$update_free_access;
3972367
  $conf = array();
398
3992367
  if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) {
4002367
    include_once DRUPAL_ROOT . '/' . conf_path() . '/settings.php';
4012367
  }
402
4032367
  if (isset($base_url)) {
404
    // Parse fixed base URL from settings.php.
4050
    $parts = parse_url($base_url);
4060
    if (!isset($parts['path'])) {
4070
      $parts['path'] = '';
4080
    }
4090
    $base_path = $parts['path'] . '/';
410
    // Build $base_root (everything until first slash after "scheme://").
4110
    $base_root = substr($base_url, 0, strlen($base_url) -
strlen($parts['path']));
4120
  }
413
  else {
414
    // Create base URL
4152367
    $base_root = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ?
'https' : 'http';
416
417
    // As $_SERVER['HTTP_HOST'] is user input, ensure it only contains
418
    // characters allowed in hostnames.
4192367
    $base_url = $base_root .= '://' . preg_replace('/[^a-z0-9-:._]/i', '',
$_SERVER['HTTP_HOST']);
420
421
    // $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'],
not
422
    // be modified by a visitor.
4232367
    if ($dir = trim(dirname($_SERVER['SCRIPT_NAME']), '\,/')) {
4242367
      $base_path = "/$dir";
4252367
      $base_url .= $base_path;
4262367
      $base_path .= '/';
4272367
    }
428
    else {
4290
      $base_path = '/';
430
    }
431
  }
432
4332367
  if ($cookie_domain) {
434
    // If the user specifies the cookie domain, also use it for session
name.
4350
    $session_name = $cookie_domain;
4360
  }
437
  else {
438
    // Otherwise use $base_url as session name, without the protocol
439
    // to use the same session identifiers across http and https.
4402367
    list( , $session_name) = explode('://', $base_url, 2);
441
    // We escape the hostname because it can be modified by a visitor.
4422367
    if (!empty($_SERVER['HTTP_HOST'])) {
4432367
      $cookie_domain = check_plain($_SERVER['HTTP_HOST']);
4442367
    }
445
  }
446
  // To prevent session cookies from being hijacked, a user can configure
the
447
  // SSL version of their website to only transfer session cookies via SSL
by
448
  // using PHP's session.cookie_secure setting. The browser will then use
two
449
  // separate session cookies for the HTTPS and HTTP versions of the site.
So we
450
  // must use different session identifiers for HTTPS and HTTP to prevent a
451
  // cookie collision.
4522367
  if (ini_get('session.cookie_secure')) {
4530
    $session_name .= 'SSL';
4540
  }
455
  // Strip leading periods, www., and port numbers from cookie domain.
4562367
  $cookie_domain = ltrim($cookie_domain, '.');
4572367
  if (strpos($cookie_domain, 'www.') === 0) {
4580
    $cookie_domain = substr($cookie_domain, 4);
4590
  }
4602367
  $cookie_domain = explode(':', $cookie_domain);
4612367
  $cookie_domain = '.' . $cookie_domain[0];
462
  // Per RFC 2109, cookie domains must contain at least one dot other than
the
463
  // first. For hosts such as 'localhost' or IP Addresses we don't set a
cookie domain.
4642367
  if (count(explode('.', $cookie_domain)) > 2 &&
!is_numeric(str_replace('.', '', $cookie_domain))) {
4650
    ini_set('session.cookie_domain', $cookie_domain);
4660
  }
4672367
  session_name('SESS' . md5($session_name));
4682367
}
469
470
/**
471
 * Returns and optionally sets the filename for a system item (module,
472
 * theme, etc.). The filename, whether provided, cached, or retrieved
473
 * from the database, is only returned if the file exists.
474
 *
475
 * This function plays a key role in allowing Drupal's resources (modules
476
 * and themes) to be located in different places depending on a site's
477
 * configuration. For example, a module 'foo' may legally be be located
478
 * in any of these three places:
479
 *
480
 * modules/foo/foo.module
481
 * sites/all/modules/foo/foo.module
482
 * sites/example.com/modules/foo/foo.module
483
 *
484
 * Calling drupal_get_filename('module', 'foo') will give you one of
485
 * the above, depending on where the module is located.
486
 *
487
 * @param $type
488
 *   The type of the item (i.e. theme, theme_engine, module).
489
 * @param $name
490
 *   The name of the item for which the filename is requested.
491
 * @param $filename
492
 *   The filename of the item if it is to be set explicitly rather
493
 *   than by consulting the database.
494
 *
495
 * @return
496
 *   The filename of the requested item.
497
 */
4982367
function drupal_get_filename($type, $name, $filename = NULL) {
4992496
  static $files = array();
500
5012496
  if (!isset($files[$type])) {
5022366
    $files[$type] = array();
5032366
  }
504
5052496
  if (!empty($filename) && file_exists($filename)) {
5062496
    $files[$type][$name] = $filename;
5072496
  }
5082496
  elseif (isset($files[$type][$name])) {
509
    // nothing
5102496
  }
511
  // Verify that we have an active database connection, before querying
512
  // the database.  This is required because this function is called both
513
  // before we have a database connection (i.e. during installation) and
514
  // when a database connection fails.
51516
  elseif (db_is_active() && (($file = db_query("SELECT filename FROM
{system} WHERE name = :name AND type = :type", array(':name' => $name,
':type' => $type))->fetchField()) && file_exists($file))) {
51616
    $files[$type][$name] = $file;
51716
  }
518
  else {
519
    // Fallback to searching the filesystem if the database connection is
520
    // not established or the requested file is not found.
5210
    $config = conf_path();
5220
    $dir = (($type == 'theme_engine') ? 'themes/engines' : "${type}s");
5230
    $file = (($type == 'theme_engine') ? "$name.engine" : "$name.$type");
524
5250
    foreach (array("$config/$dir/$file", "$config/$dir/$name/$file",
"$dir/$file", "$dir/$name/$file") as $file) {
5260
      if (file_exists($file)) {
5270
        $files[$type][$name] = $file;
5280
        break;
5290
      }
5300
    }
531
  }
532
5332496
  if (isset($files[$type][$name])) {
5342496
    return $files[$type][$name];
5350
  }
5360
}
537
538
/**
539
 * Load the persistent variable table.
540
 *
541
 * The variable table is composed of values that have been saved in the
table
542
 * with variable_set() as well as those explicitly specified in the
configuration
543
 * file.
544
 */
5452367
function variable_init($conf = array()) {
546
  // NOTE: caching the variables improves performance by 20% when serving
cached pages.
5472498
  if ($cached = cache_get('variables', 'cache')) {
5482498
    $variables = $cached->data;
5492498
  }
550
  else {
551255
    $variables = array_map('unserialize', db_query('SELECT name, value FROM
{variable}')->fetchAllKeyed());
552255
    cache_set('variables', $variables);
553
  }
554
5552498
  foreach ($conf as $name => $value) {
5560
    $variables[$name] = $value;
5570
  }
558
5592498
  return $variables;
5600
}
561
562
/**
563
 * Return a persistent variable.
564
 *
565
 * @param $name
566
 *   The name of the variable to return.
567
 * @param $default
568
 *   The default value to use if this variable has never been set.
569
 * @return
570
 *   The value of the variable.
571
 */
5722367
function variable_get($name, $default) {
5732498
  global $conf;
574
5752498
  return isset($conf[$name]) ? $conf[$name] : $default;
5760
}
577
578
/**
579
 * Set a persistent variable.
580
 *
581
 * @param $name
582
 *   The name of the variable to set.
583
 * @param $value
584
 *   The value to set. This can be any PHP data type; these functions take
care
585
 *   of serialization as necessary.
586
 */
5872367
function variable_set($name, $value) {
588301
  global $conf;
589
590301
  db_merge('variable')->key(array('name' => $name))->fields(array('value'
=> serialize($value)))->execute();
591
592301
  cache_clear_all('variables', 'cache');
593
594301
  $conf[$name] = $value;
595301
}
596
597
/**
598
 * Unset a persistent variable.
599
 *
600
 * @param $name
601
 *   The name of the variable to undefine.
602
 */
6032367
function variable_del($name) {
604161
  global $conf;
605
606161
  db_delete('variable')
607161
    ->condition('name', $name)
608161
    ->execute();
609161
  cache_clear_all('variables', 'cache');
610
611161
  unset($conf[$name]);
612161
}
613
614
615
/**
616
 * Retrieve the current page from the cache.
617
 *
618
 * Note: we do not serve cached pages when status messages are waiting
(from
619
 * a redirected form submission which was completed).
620
 */
6212367
function page_get_cache() {
6222
  global $user, $base_root;
623
6242
  $cache = NULL;
625
6262
  if (!$user->uid && ($_SERVER['REQUEST_METHOD'] == 'GET' ||
$_SERVER['REQUEST_METHOD'] == 'HEAD') && count(drupal_set_message()) == 0)
{
6272
    $cache = cache_get($base_root . request_uri(), 'cache_page');
628
6292
    if (empty($cache)) {
6301
      ob_start();
6311
    }
6322
  }
633
6342
  return $cache;
6350
}
636
637
/**
638
 * Call all init or exit hooks without including all modules.
639
 *
640
 * @param $hook
641
 *   The name of the bootstrap hook we wish to invoke.
642
 */
6432367
function bootstrap_invoke_all($hook) {
6442367
  foreach (module_list(TRUE, TRUE) as $module) {
6450
    module_invoke($module, $hook);
6460
  }
6472367
}
648
649
/**
650
 * Includes a file with the provided type and name. This prevents
651
 * including a theme, engine, module, etc., more than once.
652
 *
653
 * @param $type
654
 *   The type of item to load (i.e. theme, theme_engine, module).
655
 * @param $name
656
 *   The name of the item to load.
657
 *
658
 * @return
659
 *   TRUE if the item is loaded or has already been loaded.
660
 */
6612367
function drupal_load($type, $name) {
6622496
  static $files = array();
663
6642496
  if (isset($files[$type][$name])) {
665134
    return TRUE;
6660
  }
667
6682438
  $filename = drupal_get_filename($type, $name);
669
6702438
  if ($filename) {
6712438
    include_once DRUPAL_ROOT . '/' . $filename;
6722438
    $files[$type][$name] = TRUE;
673
6742438
    return TRUE;
6750
  }
676
6770
  return FALSE;
6780
}
679
680
/**
681
 * Set HTTP headers in preparation for a page response.
682
 *
683
 * Authenticated users are always given a 'no-cache' header, and will
684
 * fetch a fresh page on every request.  This prevents authenticated
685
 * users seeing locally cached pages that show them as logged out.
686
 *
687
 * @see page_set_cache()
688
 */
6892367
function drupal_page_header() {
6902366
  header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
6912366
  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
6922366
  header("Cache-Control: store, no-cache, must-revalidate");
6932366
  header("Cache-Control: post-check=0, pre-check=0", FALSE);
6942366
}
695
696
/**
697
 * Set HTTP headers in preparation for a cached page response.
698
 *
699
 * The general approach here is that anonymous users can keep a local
700
 * cache of the page, but must revalidate it on every request.  Then,
701
 * they are given a '304 Not Modified' response as long as they stay
702
 * logged out and the page has not been modified.
703
 *
704
 */
7052367
function drupal_page_cache_header($cache) {
706
  // Set default values:
7071
  $last_modified = gmdate('D, d M Y H:i:s', $cache->created) . ' GMT';
7081
  $etag = '"' . md5($last_modified) . '"';
709
710
  // See if the client has provided the required HTTP headers:
7111
  $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ?
stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
7121
  $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ?
stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;
713
7141
  if ($if_modified_since && $if_none_match
7150
      && $if_none_match == $etag // etag must match
7161
      && $if_modified_since == $last_modified) {  // if-modified-since must
match
7170
    header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified');
718
    // All 304 responses must send an etag if the 200 response for the same
object contained an etag
7190
    header("Etag: $etag");
7200
    return;
7210
  }
722
723
  // Send appropriate response:
7241
  header("Last-Modified: $last_modified");
7251
  header("ETag: $etag");
726
727
  // The following headers force validation of cache:
7281
  header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
7291
  header("Cache-Control: must-revalidate");
730
7311
  if (variable_get('page_compression', TRUE)) {
732
    // Determine if the browser accepts gzipped data.
7331
    if (@strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === FALSE &&
function_exists('gzencode')) {
734
      // Strip the gzip header and run uncompress.
7351
      $cache->data = gzinflate(substr(substr($cache->data, 10), 0, -8));
7361
    }
7370
    elseif (function_exists('gzencode')) {
7380
      header('Content-Encoding: gzip');
7390
    }
7401
  }
741
742
  // Send the original request's headers. We send them one after
743
  // another so PHP's header() function can deal with duplicate
744
  // headers.
7451
  $headers = explode("\n", $cache->headers);
7461
  foreach ($headers as $header) {
7471
    header($header);
7481
  }
749
7501
  print $cache->data;
7510
}
752
753
/**
754
 * Define the critical hooks that force modules to always be loaded.
755
 */
7562367
function bootstrap_hooks() {
757154
  return array('boot', 'exit');
7580
}
759
760
/**
761
 * Unserializes and appends elements from a serialized string.
762
 *
763
 * @param $obj
764
 *   The object to which the elements are appended.
765
 * @param $field
766
 *   The attribute of $obj whose value should be unserialized.
767
 */
7682367
function drupal_unpack($obj, $field = 'data') {
7692405
  if ($obj->$field && $data = unserialize($obj->$field)) {
770164
    foreach ($data as $key => $value) {
771164
      if (!isset($obj->$key)) {
772164
        $obj->$key = $value;
773164
      }
774164
    }
775164
  }
7762405
  return $obj;
7770
}
778
779
/**
780
 * Encode special characters in a plain-text string for display as HTML.
781
 *
782
 * Uses drupal_validate_utf8 to prevent cross site scripting attacks on
783
 * Internet Explorer 6.
784
 */
7852367
function check_plain($text) {
7862497
  return drupal_validate_utf8($text) ? htmlspecialchars($text, ENT_QUOTES)
: '';
7870
}
788
789
/**
790
 * Checks whether a string is valid UTF-8.
791
 *
792
 * All functions designed to filter input should use drupal_validate_utf8
793
 * to ensure they operate on valid UTF-8 strings to prevent bypass of the
794
 * filter.
795
 *
796
 * When text containing an invalid UTF-8 lead byte (0xC0 - 0xFF) is
presented
797
 * as UTF-8 to Internet Explorer 6, the program may misinterpret subsequent
798
 * bytes. When these subsequent bytes are HTML control characters such as
799
 * quotes or angle brackets, parts of the text that were deemed safe by
filters
800
 * end up in locations that are potentially unsafe; An onerror attribute
that
801
 * is outside of a tag, and thus deemed safe by a filter, can be
interpreted
802
 * by the browser as if it were inside the tag.
803
 *
804
 * This function exploits preg_match behaviour (since PHP 4.3.5) when used
805
 * with the u modifier, as a fast way to find invalid UTF-8. When the
matched
806
 * string contains an invalid byte sequence, it will fail silently.
807
 *
808
 * preg_match may not fail on 4 and 5 octet sequences, even though they
809
 * are not supported by the specification.
810
 *
811
 * The specific preg_match behaviour is present since PHP 4.3.5.
812
 *
813
 * @param $text
814
 *   The text to check.
815
 * @return
816
 *   TRUE if the text is valid UTF-8, FALSE if not.
817
 */
8182367
function drupal_validate_utf8($text) {
8192497
  if (strlen($text) == 0) {
8201762
    return TRUE;
8210
  }
8222497
  return (preg_match('/^./us', $text) == 1);
8230
}
824
825
/**
826
 * Since $_SERVER['REQUEST_URI'] is only available on Apache, we
827
 * generate an equivalent using other environment variables.
828
 */
8292367
function request_uri() {
830
8312152
  if (isset($_SERVER['REQUEST_URI'])) {
8322152
    $uri = $_SERVER['REQUEST_URI'];
8332152
  }
834
  else {
8350
    if (isset($_SERVER['argv'])) {
8360
      $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['argv'][0];
8370
    }
8380
    elseif (isset($_SERVER['QUERY_STRING'])) {
8390
      $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING'];
8400
    }
841
    else {
8420
      $uri = $_SERVER['SCRIPT_NAME'];
843
    }
844
  }
845
8462152
  return $uri;
8470
}
848
849
/**
850
 * Log a system message.
851
 *
852
 * @param $type
853
 *   The category to which this message belongs.
854
 * @param $message
855
 *   The message to store in the log. See t() for documentation
856
 *   on how $message and $variables interact. Keep $message
857
 *   translatable by not concatenating dynamic values into it!
858
 * @param $variables
859
 *   Array of variables to replace in the message on display or
860
 *   NULL if message is already translated or not possible to
861
 *   translate.
862
 * @param $severity
863
 *   The severity of the message, as per RFC 3164
864
 * @param $link
865
 *   A link to associate with the message.
866
 *
867
 * @see watchdog_severity_levels()
868
 */
8692367
function watchdog($type, $message, $variables = array(), $severity =
WATCHDOG_NOTICE, $link = NULL) {
870704
  global $user, $base_root;
871
872704
  static $in_error_state = FALSE;
873
874
  // It is possible that the error handling will itself trigger an error. 
In that case, we could
875
  // end up in an infinite loop.  To avoid that, we implement a simple
static semaphore.
876704
  if (!$in_error_state) {
877704
    $in_error_state = TRUE;
878
879
    // Prepare the fields to be logged
880
    $log_message = array(
881704
      'type'        => $type,
882704
      'message'     => $message,
883704
      'variables'   => $variables,
884704
      'severity'    => $severity,
885704
      'link'        => $link,
886704
      'user'        => $user,
887704
      'request_uri' => $base_root . request_uri(),
888704
      'referer'     => $_SERVER['HTTP_REFERER'],
889704
      'ip'          => ip_address(),
890704
      'timestamp'   => REQUEST_TIME,
891704
    );
892
893
    // Call the logging hooks to log/process the message
894704
    foreach (module_implements('watchdog', TRUE) as $module) {
895704
      module_invoke($module, 'watchdog', $log_message);
896704
    }
897704
  }
898704
  $in_error_state = FALSE;
899704
}
900
901
/**
902
 * Set a message which reflects the status of the performed operation.
903
 *
904
 * If the function is called with no arguments, this function returns all
set
905
 * messages without clearing them.
906
 *
907
 * @param $message
908
 *   The message should begin with a capital letter and always ends with a
909
 *   period '.'.
910
 * @param $type
911
 *   The type of the message. One of the following values are possible:
912
 *   - 'status'
913
 *   - 'warning'
914
 *   - 'error'
915
 * @param $repeat
916
 *   If this is FALSE and the message is already set, then the message
won't
917
 *   be repeated.
918
 */
9192367
function drupal_set_message($message = NULL, $type = 'status', $repeat =
TRUE) {
9202181
  if ($message) {
921385
    if (!isset($_SESSION['messages'])) {
922385
      $_SESSION['messages'] = array();
923385
    }
924
925385
    if (!isset($_SESSION['messages'][$type])) {
926385
      $_SESSION['messages'][$type] = array();
927385
    }
928
929385
    if ($repeat || !in_array($message, $_SESSION['messages'][$type])) {
930385
      $_SESSION['messages'][$type][] = $message;
931385
    }
932385
  }
933
934
  // messages not set when DB connection fails
9352181
  return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL;
9360
}
937
938
/**
939
 * Return all messages that have been set.
940
 *
941
 * @param $type
942
 *   (optional) Only return messages of this type.
943
 * @param $clear_queue
944
 *   (optional) Set to FALSE if you do not want to clear the messages queue
945
 * @return
946
 *   An associative array, the key is the message type, the value an array
947
 *   of messages. If the $type parameter is passed, you get only that type,
948
 *   or an empty array if there are no such messages. If $type is not
passed,
949
 *   all message types are returned, or an empty array if none exist.
950
 */
9512367
function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
9521871
  if ($messages = drupal_set_message()) {
953347
    if ($type) {
9540
      if ($clear_queue) {
9550
        unset($_SESSION['messages'][$type]);
9560
      }
9570
      if (isset($messages[$type])) {
9580
        return array($type => $messages[$type]);
9590
      }
9600
    }
961
    else {
962347
      if ($clear_queue) {
963347
        unset($_SESSION['messages']);
964347
      }
965347
      return $messages;
966
    }
9670
  }
9681525
  return array();
9690
}
970
971
/**
972
 * Check to see if an IP address has been blocked.
973
 *
974
 * Blocked IP addresses are stored in the database by default. However for
975
 * performance reasons we allow an override in settings.php. This allows us
976
 * to avoid querying the database at this critical stage of the bootstrap
if
977
 * an administrative interface for IP address blocking is not required.
978
 *
979
 * @param $ip string
980
 *   IP address to check.
981
 * @return bool
982
 *   TRUE if access is denied, FALSE if access is allowed.
983
 */
9842367
function drupal_is_denied($ip) {
985
  // Because this function is called on every page request, we first check
986
  // for an array of IP addresses in settings.php before querying the
987
  // database.
9882367
  $blocked_ips = variable_get('blocked_ips', NULL);
9892367
  if (isset($blocked_ips) && is_array($blocked_ips)) {
9900
    return in_array($ip, $blocked_ips);
9910
  }
992
  else {
9932367
    return (bool)db_query("SELECT 1 FROM {blocked_ips} WHERE ip = :ip",
array(':ip' => $ip))->fetchField();
994
  }
9950
}
996
997
/**
998
 * Generates a default anonymous $user object.
999
 *
1000
 * @return Object - the user object.
1001
 */
10022367
function drupal_anonymous_user($session = '') {
1003870
  $user = new stdClass();
1004870
  $user->uid = 0;
1005870
  $user->hostname = ip_address();
1006870
  $user->roles = array();
1007870
  $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
1008870
  $user->session = $session;
1009870
  $user->cache = 0;
1010870
  return $user;
10110
}
1012
1013
/**
1014
 * A string describing a phase of Drupal to load. Each phase adds to the
1015
 * previous one, so invoking a later phase automatically runs the earlier
1016
 * phases too. The most important usage is that if you want to access the
1017
 * Drupal database from a script without loading anything else, you can
1018
 * include bootstrap.inc, and call
drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE).
1019
 *
1020
 * @param $phase
1021
 *   A constant. Allowed values are:
1022
 *     DRUPAL_BOOTSTRAP_CONFIGURATION: initialize configuration.
1023
 *     DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE: try to call a non-database cache
fetch routine.
1024
 *     DRUPAL_BOOTSTRAP_DATABASE: initialize database layer.
1025
 *     DRUPAL_BOOTSTRAP_ACCESS: identify and reject banned hosts.
1026
 *     DRUPAL_BOOTSTRAP_SESSION: initialize session handling.
1027
 *     DRUPAL_BOOTSTRAP_VARIABLES: initialize variable handling.
1028
 *     DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE: load bootstrap.inc and module.inc,
start
1029
 *       the variable system and try to serve a page from the cache.
1030
 *     DRUPAL_BOOTSTRAP_LANGUAGE: identify the language used on the page.
1031
 *     DRUPAL_BOOTSTRAP_PATH: set $_GET['q'] to Drupal path of request.
1032
 *     DRUPAL_BOOTSTRAP_FULL: Drupal is fully loaded, validate and fix
input data.
1033
 */
10342367
function drupal_bootstrap($phase = NULL) {
10352497
  static $phases = array(DRUPAL_BOOTSTRAP_CONFIGURATION,
DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE, DRUPAL_BOOTSTRAP_DATABASE,
DRUPAL_BOOTSTRAP_ACCESS, DRUPAL_BOOTSTRAP_SESSION,
DRUPAL_BOOTSTRAP_VARIABLES, DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE,
DRUPAL_BOOTSTRAP_LANGUAGE, DRUPAL_BOOTSTRAP_PATH, DRUPAL_BOOTSTRAP_FULL),
$completed_phase = -1;
1036
10372497
  if (isset($phase)) {
10382497
    while ($phases && $phase > $completed_phase) {
10392367
      $current_phase = array_shift($phases);
10402367
      _drupal_bootstrap($current_phase);
10412367
      $completed_phase = $current_phase;
10422367
    }
10432496
  }
10442496
  return $completed_phase;
10450
}
1046
1047
/**
1048
 * Return the current bootstrap phase for this Drupal process.  The
1049
 * current phase is the one most recently completed by
1050
 * drupal_bootstrap().
1051
 *
1052
 * @see drupal_bootstrap
1053
 */
10542367
function drupal_get_bootstrap_phase() {
1055136
  return drupal_bootstrap();
10560
}
1057
10582367
function _drupal_bootstrap($phase) {
10592367
  global $conf;
1060
1061
  switch ($phase) {
1062
10632367
    case DRUPAL_BOOTSTRAP_CONFIGURATION:
10642367
      drupal_initialize_variables();
1065
      // Start a page timer:
10662367
      timer_start('page');
1067
      // Initialize the configuration
10682367
      conf_init();
10692367
      break;
1070
10712367
    case DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE:
1072
      // Allow specifying special cache handlers in settings.php, like
1073
      // using memcached or files for storing cache information.
10742367
      require_once DRUPAL_ROOT . '/' . variable_get('cache_inc',
'includes/cache.inc');
1075
      // If the page_cache_fastpath is set to TRUE in settings.php and
1076
      // page_cache_fastpath (implemented in the special implementation of
1077
      // cache.inc) printed the page and indicated this with a returned
TRUE
1078
      // then we are done.
10792367
      if (variable_get('page_cache_fastpath', FALSE) &&
page_cache_fastpath()) {
10800
        exit;
10810
      }
10822367
      break;
1083
10842367
    case DRUPAL_BOOTSTRAP_DATABASE:
1085
      // Initialize the database system.  Note that the connection
1086
      // won't be initialized until it is actually requested.
10872367
      require_once DRUPAL_ROOT . '/includes/database/database.inc';
1088
      // Register autoload functions so that we can access classes and
interfaces.
10892367
      spl_autoload_register('drupal_autoload_class');
10902367
      spl_autoload_register('drupal_autoload_interface');
10912367
      break;
1092
10932367
    case DRUPAL_BOOTSTRAP_ACCESS:
1094
      // Deny access to blocked IP addresses - t() is not yet available.
10952367
      if (drupal_is_denied(ip_address())) {
10960
        header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
10970
        print 'Sorry, ' . check_plain(ip_address()) . ' has been banned.';
10980
        exit();
10990
      }
11002367
      break;
1101
11022367
    case DRUPAL_BOOTSTRAP_SESSION:
11032367
      require_once DRUPAL_ROOT . '/' . variable_get('session_inc',
'includes/session.inc');
11042367
      session_set_save_handler('_sess_open', '_sess_close', '_sess_read',
'_sess_write', '_sess_destroy_sid', '_sess_gc');
11052367
      session_start();
11062367
      break;
1107
11082367
    case DRUPAL_BOOTSTRAP_VARIABLES:
1109
      // Initialize configuration variables, using values from settings.php
if available.
11102367
      $conf = variable_init(isset($conf) ? $conf : array());
11112367
      break;
1112
11132367
    case DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE:
1114
      // Load module handling.
11152367
      require_once DRUPAL_ROOT . '/includes/module.inc';
11162367
      $cache_mode = variable_get('cache', CACHE_DISABLED);
1117
      // Get the page from the cache.
11182367
      $cache = $cache_mode == CACHE_DISABLED ? '' : page_get_cache();
1119
      // If the skipping of the bootstrap hooks is not enforced, call
hook_boot.
11202367
      if ($cache_mode != CACHE_AGGRESSIVE) {
11212367
        bootstrap_invoke_all('boot');
11222367
      }
1123
      // If there is a cached page, display it.
11242367
      if ($cache) {
11251
        drupal_page_cache_header($cache);
1126
        // If the skipping of the bootstrap hooks is not enforced, call
hook_exit.
11270
        if ($cache_mode != CACHE_AGGRESSIVE) {
11280
          bootstrap_invoke_all('exit');
11290
        }
1130
        // We are done.
11310
        exit;
11320
      }
1133
      // Prepare for non-cached page workflow.
11342366
      drupal_page_header();
11352366
      break;
1136
11372366
    case DRUPAL_BOOTSTRAP_LANGUAGE:
11382366
      drupal_init_language();
11392366
      break;
1140
11412366
    case DRUPAL_BOOTSTRAP_PATH:
11422366
      require_once DRUPAL_ROOT . '/includes/path.inc';
1143
      // Initialize $_GET['q'] prior to loading modules and invoking
hook_init().
11442366
      drupal_init_path();
11452366
      break;
1146
11472366
    case DRUPAL_BOOTSTRAP_FULL:
11482366
      require_once DRUPAL_ROOT . '/includes/common.inc';
11492366
      _drupal_bootstrap_full();
11502366
      break;
11510
  }
11522367
}
1153
1154
/**
1155
 * Enables use of the theme system without requiring database access.
1156
 *
1157
 * Loads and initializes the theme system for site installs, updates and
when
1158
 * the site is in offline mode. This also applies when the database fails.
1159
 *
1160
 * @see _drupal_maintenance_theme()
1161
 */
11622367
function drupal_maintenance_theme() {
11630
  require_once DRUPAL_ROOT . '/includes/theme.maintenance.inc';
11640
  _drupal_maintenance_theme();
11650
}
1166
1167
/**
1168
 * Return the name of the localisation function. Use in code that needs to
1169
 * run both during installation and normal operation.
1170
 */
11712367
function get_t() {
11722496
  static $t;
11732496
  if (is_null($t)) {
11742366
    $t = function_exists('install_main') ? 'st' : 't';
11752366
  }
11762496
  return $t;
11770
}
1178
1179
/**
1180
 *  Choose a language for the current page, based on site and user
preferences.
1181
 */
11822367
function drupal_init_language() {
11832367
  global $language, $user;
1184
1185
  // Ensure the language is correctly returned, even without multilanguage
support.
1186
  // Useful for eg. XML/HTML 'lang' attributes.
11872367
  if (variable_get('language_count', 1) == 1) {
11882294
    $language = language_default();
11892294
  }
1190
  else {
119173
    include_once DRUPAL_ROOT . '/includes/language.inc';
119273
    $language = language_initialize();
1193
  }
11942367
}
1195
1196
/**
1197
 * Get a list of languages set up indexed by the specified key
1198
 *
1199
 * @param $field The field to index the list with.
1200
 * @param $reset Boolean to request a reset of the list.
1201
 */
12022367
function language_list($field = 'language', $reset = FALSE) {
1203106
  static $languages = NULL;
1204
1205
  // Reset language list
1206106
  if ($reset) {
12079
    $languages = NULL;
12089
  }
1209
1210
  // Init language list
1211106
  if (!isset($languages)) {
1212106
    if (variable_get('language_count', 1) > 1 || module_exists('locale')) {
121389
      $languages['language'] = db_query('SELECT * FROM {languages} ORDER BY
weight ASC, name ASC')->fetchAllAssoc('language');
121489
    }
1215
    else {
1216
      // No locale module, so use the default language only.
121717
      $default = language_default();
121817
      $languages['language'][$default->language] = $default;
1219
    }
1220106
  }
1221
1222
  // Return the array indexed by the right field
1223106
  if (!isset($languages[$field])) {
122474
    $languages[$field] = array();
122574
    foreach ($languages['language'] as $lang) {
1226
      // Some values should be collected into an array
122774
      if (in_array($field, array('enabled', 'weight'))) {
122874
        $languages[$field][$lang->$field][$lang->language] = $lang;
122974
      }
1230
      else {
12310
        $languages[$field][$lang->$field] = $lang;
1232
      }
123374
    }
123474
  }
1235106
  return $languages[$field];
12360
}
1237
1238
/**
1239
 * Default language used on the site
1240
 *
1241
 * @param $property
1242
 *   Optional property of the language object to return
1243
 */
12442367
function language_default($property = NULL) {
12452365
  $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' => ''));
12462365
  return $property ? $language->$property : $language;
12470
}
1248
1249
/**
1250
 * If Drupal is behind a reverse proxy, we use the X-Forwarded-For header
1251
 * instead of $_SERVER['REMOTE_ADDR'], which would be the IP address of
1252
 * the proxy server, and not the client's.  If Drupal is run in a cluster
1253
 * we use the X-Cluster-Client-Ip header instead.
1254
 *
1255
 * @param $reset
1256
 *   Reset the current IP address saved in static.
1257
 * @return
1258
 *   IP address of client machine, adjusted for reverse proxy and/or
cluster
1259
 *   environments.
1260
 */
12612367
function ip_address($reset = FALSE) {
12622497
  static $ip_address = NULL;
1263
12642497
  if (!isset($ip_address) || $reset) {
12652368
    $ip_address = $_SERVER['REMOTE_ADDR'];
1266
12672368
    if (variable_get('reverse_proxy', 0)) {
12681
      if (array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) {
1269
        // If an array of known reverse proxy IPs is provided, then trust
1270
        // the XFF header if request really comes from one of them.
12711
        $reverse_proxy_addresses = variable_get('reverse_proxy_addresses',
array());
12721
        if (!empty($reverse_proxy_addresses) && in_array($ip_address,
$reverse_proxy_addresses, TRUE)) {
1273
          // If there are several arguments, we need to check the most
1274
          // recently added one, i.e. the last one.
12751
          $ip_address_parts = explode(',',
$_SERVER['HTTP_X_FORWARDED_FOR']);
12761
          $ip_address = array_pop($ip_address_parts);
12771
        }
12781
      }
1279
1280
      // When Drupal is run in a cluster environment, REMOTE_ADDR contains
the IP
1281
      // address of a server in the cluster, while the IP address of the
client is
1282
      // stored in HTTP_X_CLUSTER_CLIENT_IP.
12831
      if (array_key_exists('HTTP_X_CLUSTER_CLIENT_IP', $_SERVER)) {
12841
        $ip_address = $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
12851
      }
12861
    }
12872368
  }
1288
12892497
  return $ip_address;
12900
}
1291
1292
/**
1293
 * @ingroup schemaapi
1294
 * @{
1295
 */
1296
1297
/**
1298
 * Get the schema definition of a table, or the whole database schema.
1299
 *
1300
 * The returned schema will include any modifications made by any
1301
 * module that implements hook_schema_alter().
1302
 *
1303
 * @param $table
1304
 *   The name of the table. If not given, the schema of all tables is
returned.
1305
 * @param $rebuild
1306
 *   If true, the schema will be rebuilt instead of retrieved from the
cache.
1307
 */
13082367
function drupal_get_schema($table = NULL, $rebuild = FALSE) {
1309647
  static $schema = array();
1310
1311647
  if (empty($schema) || $rebuild) {
1312
    // Try to load the schema from cache.
1313647
    if (!$rebuild && $cached = cache_get('schema')) {
1314513
      $schema = $cached->data;
1315513
    }
1316
    // Otherwise, rebuild the schema cache.
1317
    else {
1318134
      $schema = array();
1319
      // Load the .install files to get hook_schema.
1320
      // On some databases this function may be called before bootstrap has
1321
      // been completed, so we force the functions we need to load just in
case.
1322134
      if (drupal_function_exists('module_load_all_includes')) {
1323
1324
        // There is currently a bug in module_list() where it caches what
it
1325
        // was last called with, which is not always what you want.
1326
        // module_load_all_includes() calls module_list(), but if this
function
1327
        // is called very early in the bootstrap process then it will be
1328
        // uninitialized and therefore return no modules.  Instead, we have
to
1329
        // "prime" module_list() here to to values we want, specifically
1330
        // "yes rebuild the list and don't limit to bootstrap".
1331
        // TODO: Remove this call after http://drupal.org/node/222109 is
fixed.
1332134
        module_list(TRUE, FALSE);
1333134
        module_load_all_includes('install');
1334134
      }
1335
1336
      // Invoke hook_schema for all modules.
1337134
      foreach (module_implements('schema') as $module) {
1338134
        $current = module_invoke($module, 'schema');
1339134
        require_once DRUPAL_ROOT . '/includes/common.inc';
1340134
        if (drupal_function_exists('_drupal_initialize_schema')) {
1341134
          _drupal_initialize_schema($module, $current);
1342134
        }
1343
1344134
        $schema = array_merge($schema, $current);
1345134
      }
1346
1347134
      if (drupal_function_exists('drupal_alter')) {
1348134
        drupal_alter('schema', $schema);
1349134
      }
1350
1351134
      if (drupal_get_bootstrap_phase() == DRUPAL_BOOTSTRAP_FULL) {
1352134
        cache_set('schema', $schema);
1353134
      }
1354
    }
1355647
  }
1356
1357647
  if (!isset($table)) {
1358134
    return $schema;
13590
  }
1360624
  elseif (isset($schema[$table])) {
1361624
    return $schema[$table];
13620
  }
1363
  else {
13640
    return FALSE;
1365
  }
13660
}
1367
1368
/**
1369
 * @} End of "ingroup schemaapi".
1370
 */
1371
1372
1373
/**
1374
 * @ingroup registry
1375
 * @{
1376
 */
1377
1378
/**
1379
 * Confirm that a function is available.
1380
 *
1381
 * If the function is already available, this function does nothing.
1382
 * If the function is not available, it tries to load the file where the
1383
 * function lives. If the file is not available, it returns false, so that
it
1384
 * can be used as a drop-in replacement for function_exists().
1385
 *
1386
 * @param $function
1387
 *   The name of the function to check or load.
1388
 * @return
1389
 *   TRUE if the function is now available, FALSE otherwise.
1390
 */
13912367
function drupal_function_exists($function) {
13922496
  static $checked = array();
1393
13942496
  if (defined('MAINTENANCE_MODE')) {
13950
    return function_exists($function);
13960
  }
1397
13982496
  if (isset($checked[$function])) {
13992378
    return $checked[$function];
14000
  }
14012496
  $checked[$function] = FALSE;
1402
14032496
  if (function_exists($function)) {
14042496
    registry_mark_code('function', $function);
14052496
    $checked[$function] = TRUE;
14062496
    return TRUE;
14070
  }
1408
14091528
  $checked[$function] = _registry_check_code('function', $function);
1410
14111528
  return $checked[$function];
14120
}
1413
1414
/**
1415
 * Confirm that an interface is available.
1416
 *
1417
 * This function parallels drupal_function_exists(), but is rarely
1418
 * called directly. Instead, it is registered as an spl_autoload()
1419
 * handler, and PHP calls it for us when necessary.
1420
 *
1421
 * @param $interface
1422
 *   The name of the interface to check or load.
1423
 * @return
1424
 *   TRUE if the interface is currently available, FALSE otherwise.
1425
 */
14262367
function drupal_autoload_interface($interface) {
14272370
  return _registry_check_code('interface', $interface);
14280
}
1429
1430
/**
1431
 * Confirm that a class is available.
1432
 *
1433
 * This function parallels drupal_function_exists(), but is rarely
1434
 * called directly. Instead, it is registered as an spl_autoload()
1435
 * handler, and PHP calls it for us when necessary.
1436
 *
1437
 * @param $class
1438
 *   The name of the class to check or load.
1439
 * @return
1440
 *   TRUE if the class is currently available, FALSE otherwise.
1441
 */
14422367
function drupal_autoload_class($class) {
14432489
  return _registry_check_code('class', $class);
14440
}
1445
1446
/**
1447
 * Helper to check for a resource in the registry.
1448
 */
14492367
function _registry_check_code($type, $name) {
14502490
  $file = db_query("SELECT filename FROM {registry} WHERE name = :name AND
type = :type", array(
14512490
      ':name' => $name,
14522490
      ':type' => $type,
14532490
    ))
14542490
    ->fetchField();
14552490
  if ($file) {
14562348
    require_once DRUPAL_ROOT . '/' . $file;
14572348
    registry_mark_code($type, $name);
14582348
    return TRUE;
14590
  }
14602376
  return FALSE;
14610
}
1462
1463
/**
1464
 * Collect the resources used for this request.
1465
 *
1466
 * @param $type
1467
 *   The type of resource.
1468
 * @param $name
1469
 *   The name of the resource.
1470
 * @param $return
1471
 *   Boolean flag to indicate whether to return the resources.
1472
 */
14732367
function registry_mark_code($type, $name, $return = FALSE) {
14742496
  static $resources = array();
1475
14762496
  if ($type && $name) {
14772496
    if (!isset($resources[$type])) {
14782366
      $resources[$type] = array();
14792366
    }
14802496
    if (!in_array($name, $resources[$type])) {
14812496
      $resources[$type][] = $name;
14822496
    }
14832496
  }
1484
14852496
  if ($return) {
14861756
    return $resources;
14870
  }
14882496
}
1489
1490
/**
1491
 * Rescan all enabled modules and rebuild the registry.
1492
 *
1493
 * Rescans all code in modules or includes directory, storing a mapping of
1494
 * each function, file, and hook implementation in the database.
1495
 */
14962367
function registry_rebuild() {
1497146
  require_once DRUPAL_ROOT . '/includes/registry.inc';
1498146
  _registry_rebuild();
1499146
}
1500
1501
/**
1502
 * Save the files required by the registry for this path.
1503
 */
15042367
function registry_cache_path_files() {
15051756
  if ($used_code = registry_mark_code(NULL, NULL, TRUE)) {
15061756
    $files = array();
15071756
    $type_sql = array();
15081756
    $params = array();
1509
15101756
    $select = db_select('registry')->distinct();
15111756
    $select->addField('registry', 'filename');
1512
1513
    // This creates a series of 2-clause AND conditions that are then ORed
together.
15141756
    $ors = db_or();
15151756
    foreach ($used_code as $type => $names) {
15161756
      $and = db_and()
15171756
        ->condition('name', $names, 'IN')
15181756
        ->condition('type', $type);
15191756
      $ors->condition($and);
15201756
    }
15211756
    $select->condition($ors);
15221756
    $files = $select->execute()->fetchCol();
1523
15241756
    if ($files) {
15251756
      sort($files);
1526
      // Only write this to cache if the file list we are going to cache
1527
      // is different to what we loaded earlier in the request.
15281756
      if ($files != registry_load_path_files(TRUE)) {
15291400
        $menu = menu_get_item();
15301400
        cache_set('registry:' . $menu['path'], implode(';', $files),
'cache_registry');
15311400
      }
15321756
    }
15331756
  }
15341756
}
1535
1536
/**
1537
 * registry_load_path_files
1538
 */
15392367
function registry_load_path_files($return = FALSE) {
15402346
  static $file_cache_data = array();
15412346
  if ($return) {
15421756
    sort($file_cache_data);
15431756
    return $file_cache_data;
15440
  }
15452333
  $menu = menu_get_item();
15462333
  $cache = cache_get('registry:' . $menu['path'], 'cache_registry');
15472333
  if (!empty($cache->data)) {
15481647
    foreach(explode(';', $cache->data) as $file) {
15491647
      require_once DRUPAL_ROOT . '/' . $file;
15501647
      $file_cache_data[] = $file;
15511647
    }
15521647
  }
15532333
}
1554
1555
/**
1556
 * @} End of "ingroup registry".
1557
 */
15582367