/[drupal]/drupal/includes/bootstrap.inc
ViewVC logotype

Contents of /drupal/includes/bootstrap.inc

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.321 - (show annotations) (download) (as text)
Thu Nov 5 03:00:21 2009 UTC (3 weeks, 2 days ago) by dries
Branch: MAIN
Changes since 1.320: +163 -118 lines
File MIME type: text/x-php
- Patch #622048 by sun: streamline drupal_bootstrap() and expose the flow to code profilers.
1 <?php
2 // $Id: bootstrap.inc,v 1.320 2009/11/02 03:46:43 webchick Exp $
3
4 /**
5 * @file
6 * Functions that need to be loaded on every Drupal request.
7 */
8
9 /**
10 * The current system version.
11 */
12 define('VERSION', '7.0-dev');
13
14 /**
15 * Core API compatibility.
16 */
17 define('DRUPAL_CORE_COMPATIBILITY', '7.x');
18
19 /**
20 * Minimum supported version of PHP.
21 */
22 define('DRUPAL_MINIMUM_PHP', '5.2.0');
23
24 /**
25 * Minimum recommended value of PHP memory_limit.
26 */
27 define('DRUPAL_MINIMUM_PHP_MEMORY_LIMIT', '16M');
28
29 /**
30 * Minimum supported version of MySQL, if it is used.
31 */
32 define('DRUPAL_MINIMUM_MYSQL', '5.0');
33
34 /**
35 * Minimum supported version of PostgreSQL, if it is used.
36 */
37 define('DRUPAL_MINIMUM_PGSQL', '8.3');
38
39 /**
40 * Indicates that the item should never be removed unless explicitly told to
41 * using cache_clear_all() with a cache ID.
42 */
43 define('CACHE_PERMANENT', 0);
44
45 /**
46 * Indicates that the item should be removed at the next general cache wipe.
47 */
48 define('CACHE_TEMPORARY', -1);
49
50 /**
51 * Indicates that page caching is disabled.
52 */
53 define('CACHE_DISABLED', 0);
54
55 /**
56 * Indicates that page caching is enabled, using "normal" mode.
57 */
58 define('CACHE_NORMAL', 1);
59
60 /**
61 * Log message severity -- Emergency: system is unusable.
62 *
63 * @see watchdog()
64 * @see watchdog_severity_levels()
65 */
66 define('WATCHDOG_EMERG', 0);
67
68 /**
69 * Log message severity -- Alert: action must be taken immediately.
70 *
71 * @see watchdog()
72 * @see watchdog_severity_levels()
73 */
74 define('WATCHDOG_ALERT', 1);
75
76 /**
77 * Log message severity -- Critical: critical conditions.
78 *
79 * @see watchdog()
80 * @see watchdog_severity_levels()
81 */
82 define('WATCHDOG_CRITICAL', 2);
83
84 /**
85 * Log message severity -- Error: error conditions.
86 *
87 * @see watchdog()
88 * @see watchdog_severity_levels()
89 */
90 define('WATCHDOG_ERROR', 3);
91
92 /**
93 * Log message severity -- Warning: warning conditions.
94 *
95 * @see watchdog()
96 * @see watchdog_severity_levels()
97 */
98 define('WATCHDOG_WARNING', 4);
99
100 /**
101 * Log message severity -- Notice: normal but significant condition.
102 *
103 * @see watchdog()
104 * @see watchdog_severity_levels()
105 */
106 define('WATCHDOG_NOTICE', 5);
107
108 /**
109 * Log message severity -- Informational: informational messages.
110 *
111 * @see watchdog()
112 * @see watchdog_severity_levels()
113 */
114 define('WATCHDOG_INFO', 6);
115
116 /**
117 * Log message severity -- Debug: debug-level messages.
118 *
119 * @see watchdog()
120 * @see watchdog_severity_levels()
121 */
122 define('WATCHDOG_DEBUG', 7);
123
124 /**
125 * First bootstrap phase: initialize configuration.
126 */
127 define('DRUPAL_BOOTSTRAP_CONFIGURATION', 0);
128
129 /**
130 * Second bootstrap phase: try to serve a cached page.
131 */
132 define('DRUPAL_BOOTSTRAP_PAGE_CACHE', 1);
133
134 /**
135 * Third bootstrap phase: initialize database layer.
136 */
137 define('DRUPAL_BOOTSTRAP_DATABASE', 2);
138
139 /**
140 * Fourth bootstrap phase: initialize the variable system.
141 */
142 define('DRUPAL_BOOTSTRAP_VARIABLES', 3);
143
144 /**
145 * Fifth bootstrap phase: initialize session handling.
146 */
147 define('DRUPAL_BOOTSTRAP_SESSION', 4);
148
149 /**
150 * Sixth bootstrap phase: set up the page header.
151 */
152 define('DRUPAL_BOOTSTRAP_PAGE_HEADER', 5);
153
154 /**
155 * Seventh bootstrap phase: find out language of the page.
156 */
157 define('DRUPAL_BOOTSTRAP_LANGUAGE', 6);
158
159 /**
160 * Final bootstrap phase: Drupal is fully loaded; validate and fix
161 * input data.
162 */
163 define('DRUPAL_BOOTSTRAP_FULL', 7);
164
165 /**
166 * Role ID for anonymous users; should match what's in the "role" table.
167 */
168 define('DRUPAL_ANONYMOUS_RID', 1);
169
170 /**
171 * Role ID for authenticated users; should match what's in the "role" table.
172 */
173 define('DRUPAL_AUTHENTICATED_RID', 2);
174
175 /**
176 * The number of bytes in a kilobyte. For more information, visit
177 * http://en.wikipedia.org/wiki/Kilobyte.
178 */
179 define('DRUPAL_KILOBYTE', 1024);
180
181 /**
182 * The type of language used to define the content language.
183 */
184 define('LANGUAGE_TYPE_CONTENT', 'language');
185
186 /**
187 * The type of language used to select the user interface.
188 */
189 define('LANGUAGE_TYPE_INTERFACE', 'language_interface');
190
191 /**
192 * The type of language used for URLs.
193 */
194 define('LANGUAGE_TYPE_URL', 'language_url');
195
196 /**
197 * Language written left to right. Possible value of $language->direction.
198 */
199 define('LANGUAGE_LTR', 0);
200
201 /**
202 * Language written right to left. Possible value of $language->direction.
203 */
204 define('LANGUAGE_RTL', 1);
205
206 /**
207 * For convenience, define a short form of the request time global.
208 */
209 define('REQUEST_TIME', $_SERVER['REQUEST_TIME']);
210
211 /**
212 * @name Title text filtering flags
213 * @{
214 * Flags for use in drupal_set_title().
215 */
216
217 /**
218 * Flag for drupal_set_title(); text is not sanitized, so run check_plain().
219 */
220 define('CHECK_PLAIN', 0);
221
222 /**
223 * Flag for drupal_set_title(); text has already been sanitized.
224 */
225 define('PASS_THROUGH', -1);
226
227 /**
228 * Signals that the registry lookup cache should be reset.
229 */
230 define('REGISTRY_RESET_LOOKUP_CACHE', 1);
231
232 /**
233 * Signals that the registry lookup cache should be written to storage.
234 */
235 define('REGISTRY_WRITE_LOOKUP_CACHE', 2);
236
237 /**
238 * @} End of "Title text filtering flags".
239 */
240
241
242 /**
243 * Start the timer with the specified name. If you start and stop the same
244 * timer multiple times, the measured intervals will be accumulated.
245 *
246 * @param name
247 * The name of the timer.
248 */
249 function timer_start($name) {
250 global $timers;
251
252 $timers[$name]['start'] = microtime(TRUE);
253 $timers[$name]['count'] = isset($timers[$name]['count']) ? ++$timers[$name]['count'] : 1;
254 }
255
256 /**
257 * Read the current timer value without stopping the timer.
258 *
259 * @param name
260 * The name of the timer.
261 * @return
262 * The current timer value in ms.
263 */
264 function timer_read($name) {
265 global $timers;
266
267 if (isset($timers[$name]['start'])) {
268 $stop = microtime(TRUE);
269 $diff = round(($stop - $timers[$name]['start']) * 1000, 2);
270
271 if (isset($timers[$name]['time'])) {
272 $diff += $timers[$name]['time'];
273 }
274 return $diff;
275 }
276 return $timers[$name]['time'];
277 }
278
279 /**
280 * Stop the timer with the specified name.
281 *
282 * @param name
283 * The name of the timer.
284 * @return
285 * A timer array. The array contains the number of times the timer has been
286 * started and stopped (count) and the accumulated timer value in ms (time).
287 */
288 function timer_stop($name) {
289 global $timers;
290
291 if (isset($timers[$name]['time'])) {
292 $timers[$name]['time'] += timer_read($name);
293 }
294 else {
295 $timers[$name]['time'] = timer_read($name);
296 }
297 unset($timers[$name]['start']);
298
299 return $timers[$name];
300 }
301
302 /**
303 * Find the appropriate configuration directory.
304 *
305 * Try finding a matching configuration directory by stripping the website's
306 * hostname from left to right and pathname from right to left. The first
307 * configuration file found will be used; the remaining will ignored. If no
308 * configuration file is found, return a default value '$confdir/default'.
309 *
310 * Example for a fictitious site installed at
311 * http://www.drupal.org:8080/mysite/test/ the 'settings.php' is searched in
312 * the following directories:
313 *
314 * 1. $confdir/8080.www.drupal.org.mysite.test
315 * 2. $confdir/www.drupal.org.mysite.test
316 * 3. $confdir/drupal.org.mysite.test
317 * 4. $confdir/org.mysite.test
318 *
319 * 5. $confdir/8080.www.drupal.org.mysite
320 * 6. $confdir/www.drupal.org.mysite
321 * 7. $confdir/drupal.org.mysite
322 * 8. $confdir/org.mysite
323 *
324 * 9. $confdir/8080.www.drupal.org
325 * 10. $confdir/www.drupal.org
326 * 11. $confdir/drupal.org
327 * 12. $confdir/org
328 *
329 * 13. $confdir/default
330 *
331 * If a file named sites.php is present in the $confdir, it will be loaded
332 * prior to scanning for directories. It should define an associative array
333 * named $sites, which maps domains to directories. It should be in the form
334 * of:
335 *
336 * $sites = array(
337 * 'The url to alias' => 'A directory within the sites directory'
338 * );
339 *
340 * For example:
341 *
342 * $sites = array(
343 * 'devexample.com' => 'example.com',
344 * 'localhost/example' => 'example.com',
345 * );
346 *
347 * The above array will cause Drupal to look for a directory named
348 * "example.com" in the sites directory whenever a request comes from
349 * "example.com", "devexample.com", or "localhost/example". That is useful
350 * on development servers, where the domain name may not be the same as the
351 * domain of the live server. Since Drupal stores file paths into the database
352 * (files, system table, etc.) this will ensure the paths are correct while
353 * accessed on development servers.
354 *
355 * @param $require_settings
356 * Only configuration directories with an existing settings.php file
357 * will be recognized. Defaults to TRUE. During initial installation,
358 * this is set to FALSE so that Drupal can detect a matching directory,
359 * then create a new settings.php file in it.
360 * @param reset
361 * Force a full search for matching directories even if one had been
362 * found previously.
363 * @return
364 * The path of the matching directory.
365 */
366 function conf_path($require_settings = TRUE, $reset = FALSE) {
367 $conf = &drupal_static(__FUNCTION__, '');
368
369 if ($conf && !$reset) {
370 return $conf;
371 }
372
373 $confdir = 'sites';
374
375 $sites = array();
376 if (file_exists(DRUPAL_ROOT . '/' . $confdir . '/sites.php')) {
377 // This will overwrite $sites with the desired mappings.
378 include(DRUPAL_ROOT . '/' . $confdir . '/sites.php');
379 }
380
381 $uri = explode('/', $_SERVER['SCRIPT_NAME'] ? $_SERVER['SCRIPT_NAME'] : $_SERVER['SCRIPT_FILENAME']);
382 $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
383 for ($i = count($uri) - 1; $i > 0; $i--) {
384 for ($j = count($server); $j > 0; $j--) {
385 $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
386 if (isset($sites[$dir]) && file_exists(DRUPAL_ROOT . '/' . $confdir . '/' . $sites[$dir])) {
387 $dir = $sites[$dir];
388 }
389 if (file_exists(DRUPAL_ROOT . '/' . $confdir . '/' . $dir . '/settings.php') || (!$require_settings && file_exists(DRUPAL_ROOT . '/' . $confdir . '/' . $dir))) {
390 $conf = "$confdir/$dir";
391 return $conf;
392 }
393 }
394 }
395 $conf = "$confdir/default";
396 return $conf;
397 }
398
399 /**
400 * Set appropriate server variables needed for command line scripts to work.
401 *
402 * This function can be called by command line scripts before bootstrapping
403 * Drupal, to ensure that the page loads with the desired server parameters.
404 * This is because many parts of Drupal assume that they are running in a web
405 * browser and therefore use information from the global PHP $_SERVER variable
406 * that does not get set when Drupal is run from the command line.
407 *
408 * In many cases, the default way in which this function populates the $_SERVER
409 * variable is sufficient, and it can therefore be called without passing in
410 * any input. However, command line scripts running on a multisite installation
411 * (or on any installation that has settings.php stored somewhere other than
412 * the sites/default folder) need to pass in the URL of the site to allow
413 * Drupal to detect the correct location of the settings.php file. Passing in
414 * the 'url' parameter is also required for functions like request_uri() to
415 * return the expected values.
416 *
417 * Most other parameters do not need to be passed in, but may be necessary in
418 * some cases; for example, if Drupal's ip_address() function needs to return
419 * anything but the standard localhost value ('127.0.0.1'), the command line
420 * script should pass in the desired value via the 'REMOTE_ADDR' key.
421 *
422 * @param $variables
423 * (optional) An associative array of variables within $_SERVER that should
424 * be replaced. If the special element 'url' is provided in this array, it
425 * will be used to populate some of the server defaults; it should be set to
426 * the URL of the current page request, excluding any $_GET request but
427 * including the script name (e.g., http://www.example.com/mysite/index.php).
428 *
429 * @see conf_path()
430 * @see request_uri()
431 * @see ip_address()
432 */
433 function drupal_override_server_variables($variables = array()) {
434 // Set defaults based on the provided URL.
435 if (isset($variables['url'])) {
436 $url = parse_url($variables['url']);
437 unset($variables['url']);
438 }
439 else {
440 $url = array();
441 }
442 $url += array(
443 'path' => '',
444 'host' => 'localhost',
445 );
446 $defaults = array(
447 'HTTP_HOST' => $url['host'],
448 'SCRIPT_NAME' => $url['path'],
449 'REMOTE_ADDR' => '127.0.0.1',
450 'REQUEST_METHOD' => 'GET',
451 'SERVER_NAME' => NULL,
452 'SERVER_SOFTWARE' => NULL,
453 'HTTP_USER_AGENT' => NULL,
454 );
455 // Replace elements of the $_SERVER array, as appropriate.
456 $_SERVER = $variables + $_SERVER + $defaults;
457 }
458
459 /**
460 * Initialize PHP environment.
461 */
462 function drupal_environment_initialize() {
463 if (!isset($_SERVER['HTTP_REFERER'])) {
464 $_SERVER['HTTP_REFERER'] = '';
465 }
466 if (!isset($_SERVER['SERVER_PROTOCOL']) || ($_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.0' && $_SERVER['SERVER_PROTOCOL'] != 'HTTP/1.1')) {
467 $_SERVER['SERVER_PROTOCOL'] = 'HTTP/1.0';
468 }
469
470 if (isset($_SERVER['HTTP_HOST'])) {
471 // As HTTP_HOST is user input, ensure it only contains characters allowed
472 // in hostnames. See RFC 952 (and RFC 2181).
473 // $_SERVER['HTTP_HOST'] is lowercased here per specifications.
474 $_SERVER['HTTP_HOST'] = strtolower($_SERVER['HTTP_HOST']);
475 if (!drupal_valid_http_host($_SERVER['HTTP_HOST'])) {
476 // HTTP_HOST is invalid, e.g. if containing slashes it may be an attack.
477 header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
478 exit;
479 }
480 }
481 else {
482 // Some pre-HTTP/1.1 clients will not send a Host header. Ensure the key is
483 // defined for E_ALL compliance.
484 $_SERVER['HTTP_HOST'] = '';
485 }
486
487 // Enforce E_ALL, but allow users to set levels not part of E_ALL.
488 error_reporting(E_ALL | error_reporting());
489
490 // Override PHP settings required for Drupal to work properly.
491 // sites/default/default.settings.php contains more runtime settings.
492 // The .htaccess file contains settings that cannot be changed at runtime.
493
494 // Prevent PHP from generating HTML error messages.
495 ini_set('html_errors', 0);
496 // Don't escape quotes when reading files from the database, disk, etc.
497 ini_set('magic_quotes_runtime', '0');
498 // Use session cookies, not transparent sessions that puts the session id in
499 // the query string.
500 ini_set('session.use_cookies', '1');
501 ini_set('session.use_only_cookies', '1');
502 ini_set('session.use_trans_sid', '0');
503 // Don't send HTTP headers using PHP's session handler.
504 ini_set('session.cache_limiter', 'none');
505 // Use httponly session cookies.
506 ini_set('session.cookie_httponly', '1');
507 }
508
509 /**
510 * Validate that a hostname (for example $_SERVER['HTTP_HOST']) is safe.
511 *
512 * @return
513 * TRUE if only containing valid characters, or FALSE otherwise.
514 */
515 function drupal_valid_http_host($host) {
516 return preg_match('/^\[?(?:[a-zA-Z0-9-:\]_]+\.?)+$/', $host);
517 }
518
519 /**
520 * Loads the configuration and sets the base URL, cookie domain, and
521 * session name correctly.
522 */
523 function drupal_settings_initialize() {
524 global $base_url, $base_path, $base_root;
525
526 // Export the following settings.php variables to the global namespace
527 global $databases, $db_prefix, $cookie_domain, $conf, $installed_profile, $update_free_access, $db_url, $is_https, $base_secure_url, $base_insecure_url;
528 $conf = array();
529
530 if (file_exists(DRUPAL_ROOT . '/' . conf_path() . '/settings.php')) {
531 include_once DRUPAL_ROOT . '/' . conf_path() . '/settings.php';
532 }
533
534 if (isset($base_url)) {
535 // Parse fixed base URL from settings.php.
536 $parts = parse_url($base_url);
537 $http_protocol = $parts['scheme'];
538 if (!isset($parts['path'])) {
539 $parts['path'] = '';
540 }
541 $base_path = $parts['path'] . '/';
542 // Build $base_root (everything until first slash after "scheme://").
543 $base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path']));
544 }
545 else {
546 // Create base URL
547 $http_protocol = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
548 $base_root = $http_protocol . '://' . $_SERVER['HTTP_HOST'];
549
550 $base_url = $base_root;
551
552 // $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not
553 // be modified by a visitor.
554 if ($dir = trim(dirname($_SERVER['SCRIPT_NAME']), '\,/')) {
555 $base_path = "/$dir";
556 $base_url .= $base_path;
557 $base_path .= '/';
558 }
559 else {
560 $base_path = '/';
561 }
562 }
563 $is_https = $http_protocol == 'https';
564 $base_secure_url = str_replace('http://', 'https://', $base_url);
565 $base_insecure_url = str_replace('https://', 'http://', $base_url);
566
567 if ($cookie_domain) {
568 // If the user specifies the cookie domain, also use it for session name.
569 $session_name = $cookie_domain;
570 }
571 else {
572 // Otherwise use $base_url as session name, without the protocol
573 // to use the same session identifiers across http and https.
574 list( , $session_name) = explode('://', $base_url, 2);
575 // We escape the hostname because it can be modified by a visitor.
576 if (!empty($_SERVER['HTTP_HOST'])) {
577 $cookie_domain = check_plain($_SERVER['HTTP_HOST']);
578 }
579 }
580 // Strip leading periods, www., and port numbers from cookie domain.
581 $cookie_domain = ltrim($cookie_domain, '.');
582 if (strpos($cookie_domain, 'www.') === 0) {
583 $cookie_domain = substr($cookie_domain, 4);
584 }
585 $cookie_domain = explode(':', $cookie_domain);
586 $cookie_domain = '.' . $cookie_domain[0];
587 // Per RFC 2109, cookie domains must contain at least one dot other than the
588 // first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
589 if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
590 ini_set('session.cookie_domain', $cookie_domain);
591 }
592 // To prevent session cookies from being hijacked, a user can configure the
593 // SSL version of their website to only transfer session cookies via SSL by
594 // using PHP's session.cookie_secure setting. The browser will then use two
595 // separate session cookies for the HTTPS and HTTP versions of the site. So we
596 // must use different session identifiers for HTTPS and HTTP to prevent a
597 // cookie collision.
598 if ($is_https) {
599 ini_set('session.cookie_secure', TRUE);
600 }
601 $prefix = ini_get('session.cookie_secure') ? 'SSESS' : 'SESS';
602 session_name($prefix . md5($session_name));
603 }
604
605 /**
606 * Returns and optionally sets the filename for a system item (module,
607 * theme, etc.). The filename, whether provided, cached, or retrieved
608 * from the database, is only returned if the file exists.
609 *
610 * This function plays a key role in allowing Drupal's resources (modules
611 * and themes) to be located in different places depending on a site's
612 * configuration. For example, a module 'foo' may legally be be located
613 * in any of these three places:
614 *
615 * modules/foo/foo.module
616 * sites/all/modules/foo/foo.module
617 * sites/example.com/modules/foo/foo.module
618 *
619 * Calling drupal_get_filename('module', 'foo') will give you one of
620 * the above, depending on where the module is located.
621 *
622 * @param $type
623 * The type of the item (i.e. theme, theme_engine, module).
624 * @param $name
625 * The name of the item for which the filename is requested.
626 * @param $filename
627 * The filename of the item if it is to be set explicitly rather
628 * than by consulting the database.
629 *
630 * @return
631 * The filename of the requested item.
632 */
633 function drupal_get_filename($type, $name, $filename = NULL) {
634 // The location of files will not change during the request, so do not use
635 // drupal_static().
636 static $files = array();
637
638 if (!isset($files[$type])) {
639 $files[$type] = array();
640 }
641
642 if (!empty($filename) && file_exists($filename)) {
643 $files[$type][$name] = $filename;
644 }
645 elseif (isset($files[$type][$name])) {
646 // nothing
647 }
648 // Verify that we have an active database connection, before querying
649 // the database. This is required because this function is called both
650 // before we have a database connection (i.e. during installation) and
651 // when a database connection fails.
652 else {
653 try {
654 $file = db_query("SELECT filename FROM {system} WHERE name = :name AND type = :type", array(':name' => $name, ':type' => $type))->fetchField();
655 if (file_exists($file)) {
656 $files[$type][$name] = $file;
657 }
658 }
659 catch (PDOException $e) {
660 // The database table may not exist because Drupal is not yet installed,
661 // or the database might be down. We have a fallback for this case so we
662 // hide the error completely.
663 }
664 // Fallback to searching the filesystem if the database could not find the
665 // file or the file returned by the database is not found.
666 if (!isset($files[$type][$name])) {
667 // We have a consistent directory naming: modules, themes...
668 $dir = $type . 's';
669 if ($type == 'theme_engine') {
670 $dir = 'themes/engines';
671 $mask = "/$name\.engine$/";
672 }
673 elseif ($type == 'theme') {
674 $mask = "/$name\.info$/";
675 }
676 else {
677 $mask = "/$name\.$type$/";
678 }
679
680 if (!function_exists('drupal_system_listing')) {
681 require_once DRUPAL_ROOT . '/includes/common.inc';
682 }
683 $matches = drupal_system_listing($mask, $dir, 'name', 0);
684 if (!empty($matches[$name]->uri)) {
685 $files[$type][$name] = $matches[$name]->uri;
686 }
687 }
688 }
689
690 if (isset($files[$type][$name])) {
691 return $files[$type][$name];
692 }
693 }
694
695 /**
696 * Load the persistent variable table.
697 *
698 * The variable table is composed of values that have been saved in the table
699 * with variable_set() as well as those explicitly specified in the configuration
700 * file.
701 */
702 function variable_initialize($conf = array()) {
703 // NOTE: caching the variables improves performance by 20% when serving cached pages.
704 if ($cached = cache_get('variables', 'cache')) {
705 $variables = $cached->data;
706 }
707 else {
708 $variables = array_map('unserialize', db_query('SELECT name, value FROM {variable}')->fetchAllKeyed());
709 cache_set('variables', $variables);
710 }
711
712 foreach ($conf as $name => $value) {
713 $variables[$name] = $value;
714 }
715
716 return $variables;
717 }
718
719 /**
720 * Return a persistent variable.
721 *
722 * @param $name
723 * The name of the variable to return.
724 * @param $default
725 * The default value to use if this variable has never been set.
726 * @return
727 * The value of the variable.
728 *
729 * @see variable_del(), variable_set()
730 */
731 function variable_get($name, $default = NULL) {
732 global $conf;
733
734 return isset($conf[$name]) ? $conf[$name] : $default;
735 }
736
737 /**
738 * Set a persistent variable.
739 *
740 * @param $name
741 * The name of the variable to set.
742 * @param $value
743 * The value to set. This can be any PHP data type; these functions take care
744 * of serialization as necessary.
745 *
746 * @see variable_del(), variable_get()
747 */
748 function variable_set($name, $value) {
749 global $conf;
750
751 db_merge('variable')->key(array('name' => $name))->fields(array('value' => serialize($value)))->execute();
752
753 cache_clear_all('variables', 'cache');
754
755 $conf[$name] = $value;
756 }
757
758 /**
759 * Unset a persistent variable.
760 *
761 * @param $name
762 * The name of the variable to undefine.
763 *
764 * @see variable_get(), variable_set()
765 */
766 function variable_del($name) {
767 global $conf;
768
769 db_delete('variable')
770 ->condition('name', $name)
771 ->execute();
772 cache_clear_all('variables', 'cache');
773
774 unset($conf[$name]);
775 }
776
777 /**
778 * Retrieve the current page from the cache.
779 *
780 * Note: we do not serve cached pages to authenticated users, or to anonymous
781 * users when $_SESSION is non-empty. $_SESSION may contain status messages
782 * from a form submission, the contents of a shopping cart, or other user-
783 * specific content that should not be cached and displayed to other users.
784 *
785 * @param $check_only
786 * (optional) Set to TRUE to only return whether a previous call found a
787 * cache entry.
788 *
789 * @return
790 * The cache object, if the page was found in the cache, NULL otherwise.
791 */
792 function drupal_page_get_cache($check_only = FALSE) {
793 global $base_root;
794 static $cache_hit = FALSE;
795
796 if ($check_only) {
797 return $cache_hit;
798 }
799
800 if (drupal_page_is_cacheable()) {
801 $cache = cache_get($base_root . request_uri(), 'cache_page');
802 if ($cache !== FALSE) {
803 $cache_hit = TRUE;
804 }
805 return $cache;
806 }
807 }
808
809 /**
810 * Determine the cacheability of the current page.
811 *
812 * @param $allow_caching
813 * Set to FALSE if you want to prevent this page to get cached.
814 *
815 * @return
816 * TRUE if the current page can be cached, FALSE otherwise.
817 */
818 function drupal_page_is_cacheable($allow_caching = NULL) {
819 $allow_caching_static = &drupal_static(__FUNCTION__, TRUE);
820 if (isset($allow_caching)) {
821 $allow_caching_static = $allow_caching;
822 }
823
824 return $allow_caching_static && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')
825 && !drupal_is_cli();
826 }
827
828 /**
829 * Call all init or exit hooks without including all modules.
830 *
831 * @param $hook
832 * The name of the bootstrap hook we wish to invoke.
833 */
834 function bootstrap_invoke_all($hook) {
835 foreach (module_list(TRUE, TRUE) as $module) {
836 drupal_load('module', $module);
837 module_invoke($module, $hook);
838 }
839 }
840
841 /**
842 * Includes a file with the provided type and name. This prevents
843 * including a theme, engine, module, etc., more than once.
844 *
845 * @param $type
846 * The type of item to load (i.e. theme, theme_engine, module).
847 * @param $name
848 * The name of the item to load.
849 *
850 * @return
851 * TRUE if the item is loaded or has already been loaded.
852 */
853 function drupal_load($type, $name) {
854 // Once a file is included this can't be reversed during a request so do not
855 // use drupal_static() here.
856 static $files = array();
857
858 if (isset($files[$type][$name])) {
859 return TRUE;
860 }
861
862 $filename = drupal_get_filename($type, $name);
863
864 if ($filename) {
865 include_once DRUPAL_ROOT . '/' . $filename;
866 $files[$type][$name] = TRUE;
867
868 return TRUE;
869 }
870
871 return FALSE;
872 }
873
874 /**
875 * Set an HTTP response header for the current page.
876 *
877 * Note: When sending a Content-Type header, always include a 'charset' type,
878 * too. This is necessary to avoid security bugs (e.g. UTF-7 XSS).
879 *
880 * @param $name
881 * The HTTP header name, or a status code followed by a reason phrase, e.g.
882 * "404 Not Found".
883 * @param $value
884 * The HTTP header value; if omitted, the specified header is unset.
885 * @param $append
886 * Whether to append the value to an existing header or to replace it.
887 */
888 function drupal_add_http_header($name = NULL, $value = NULL, $append = FALSE) {
889 // The headers as name/value pairs.
890 $headers = &drupal_static(__FUNCTION__, array());
891
892 if (!isset($name)) {
893 return $headers;
894 }
895
896 // Save status codes using the special key ":status".
897 if (preg_match('/^\d{3} /', $name)) {
898 $value = $name;
899 $name = $name_lower = ':status';
900 }
901 else {
902 $name_lower = strtolower($name);
903 }
904 _drupal_set_preferred_header_name($name);
905
906 if (!isset($value)) {
907 $headers[$name_lower] = FALSE;
908 }
909 elseif (isset($headers[$name_lower]) && $append) {
910 // Multiple headers with identical names may be combined using comma (RFC
911 // 2616, section 4.2).
912 $headers[$name_lower] .= ',' . $value;
913 }
914 else {
915 $headers[$name_lower] = $value;
916 }
917 drupal_send_headers(array($name => $headers[$name_lower]), TRUE);
918 }
919
920 /**
921 * Get the HTTP response headers for the current page.
922 *
923 * @param $name
924 * An HTTP header name. If omitted, all headers are returned as name/value
925 * pairs. If an array value is FALSE, the header has been unset.
926 * @return
927 * A string containing the header value, or FALSE if the header has been set,
928 * or NULL if the header has not been set.
929 */
930 function drupal_get_http_header($name = NULL) {
931 $headers = drupal_add_http_header();
932 if (isset($name)) {
933 $name = strtolower($name);
934 return isset($headers[$name]) ? $headers[$name] : NULL;
935 }
936 else {
937 return $headers;
938 }
939 }
940
941 /**
942 * Header names are case-insensitive, but for maximum compatibility they should
943 * follow "common form" (see RFC 2617, section 4.2).
944 */
945 function _drupal_set_preferred_header_name($name = NULL) {
946 static $header_names = array();
947
948 if (!isset($name)) {
949 return $header_names;
950 }
951 $header_names[strtolower($name)] = $name;
952 }
953
954 /**
955 * Send the HTTP response headers previously set using drupal_add_http_header().
956 * Add default headers, unless they have been replaced or unset using
957 * drupal_add_http_header().
958 *
959 * @param $default_headers
960 * An array of headers as name/value pairs.
961 * @param $single
962 * If TRUE and headers have already be sent, send only the specified header.
963 */
964 function drupal_send_headers($default_headers = array(), $only_default = FALSE) {
965 $headers_sent = &drupal_static(__FUNCTION__, FALSE);
966 $headers = drupal_get_http_header();
967 if ($only_default && $headers_sent) {
968 $headers = array();
969 }
970 $headers_sent = TRUE;
971
972 $header_names = _drupal_set_preferred_header_name();
973 foreach ($default_headers as $name => $value) {
974 $name_lower = strtolower($name);
975 if (!isset($headers[$name_lower])) {
976 $headers[$name_lower] = $value;
977 $header_names[$name_lower] = $name;
978 }
979 }
980 foreach ($headers as $name_lower => $value) {
981 if ($name_lower == ':status') {
982 header($_SERVER['SERVER_PROTOCOL'] . ' ' . $value);
983 }
984 // Skip headers that have been unset.
985 elseif ($value) {
986 header($header_names[$name_lower] . ': ' . $value);
987 }
988 }
989 }
990
991 /**
992 * Set HTTP headers in preparation for a page response.
993 *
994 * Authenticated users are always given a 'no-cache' header, and will fetch a
995 * fresh page on every request. This prevents authenticated users from seeing
996 * locally cached pages.
997 *
998 * Also give each page a unique ETag. This will force clients to include both
999 * an If-Modified-Since header and an If-None-Match header when doing
1000 * conditional requests for the page (required by RFC 2616, section 13.3.4),
1001 * making the validation more robust. This is a workaround for a bug in Mozilla
1002 * Firefox that is triggered when Drupal's caching is enabled and the user
1003 * accesses Drupal via an HTTP proxy (see
1004 * https://bugzilla.mozilla.org/show_bug.cgi?id=269303): When an authenticated
1005 * user requests a page, and then logs out and requests the same page again,
1006 * Firefox may send a conditional request based on the page that was cached
1007 * locally when the user was logged in. If this page did not have an ETag
1008 * header, the request only contains an If-Modified-Since header. The date will
1009 * be recent, because with authenticated users the Last-Modified header always
1010 * refers to the time of the request. If the user accesses Drupal via a proxy
1011 * server, and the proxy already has a cached copy of the anonymous page with an
1012 * older Last-Modified date, the proxy may respond with 304 Not Modified, making
1013 * the client think that the anonymous and authenticated pageviews are
1014 * identical.
1015 *
1016 * @see drupal_page_set_cache()
1017 */
1018 function drupal_page_header() {
1019 $headers_sent = &drupal_static(__FUNCTION__, FALSE);
1020 if ($headers_sent) {
1021 return TRUE;
1022 }
1023 $headers_sent = TRUE;
1024
1025 $default_headers = array(
1026 'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT',
1027 'Last-Modified' => gmdate(DATE_RFC1123, REQUEST_TIME),
1028 'Cache-Control' => 'no-cache, must-revalidate, post-check=0, pre-check=0',
1029 'ETag' => '"' . REQUEST_TIME . '"',
1030 );
1031 drupal_send_headers($default_headers);
1032 }
1033
1034 /**
1035 * Set HTTP headers in preparation for a cached page response.
1036 *
1037 * The headers allow as much as possible in proxies and browsers without any
1038 * particular knowledge about the pages. Modules can override these headers
1039 * using drupal_add_http_header().
1040 *
1041 * If the request is conditional (using If-Modified-Since and If-None-Match),
1042 * and the conditions match those currently in the cache, a 304 Not Modified
1043 * response is sent.
1044 */
1045 function drupal_serve_page_from_cache(stdClass $cache) {
1046 // Negotiate whether to use compression.
1047 $page_compression = variable_get('page_compression', TRUE) && extension_loaded('zlib');
1048 $return_compressed = $page_compression && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE;
1049
1050 // Get headers set in hook_boot(). Keys are lower-case.
1051 $hook_boot_headers = drupal_get_http_header();
1052
1053 // Headers generated in this function, that may be replaced or unset using
1054 // drupal_add_http_headers(). Keys are mixed-case.
1055 $default_headers = array();
1056
1057 foreach ($cache->headers as $name => $value) {
1058 // In the case of a 304 response, certain headers must be sent, and the
1059 // remaining may not (see RFC 2616, section 10.3.5). Do not override
1060 // headers set in hook_boot().
1061 $name_lower = strtolower($name);
1062 if (in_array($name_lower, array('content-location', 'expires', 'cache-control', 'vary')) && !isset($hook_boot_headers[$name_lower])) {
1063 drupal_add_http_header($name, $value);
1064 unset($cache->headers[$name]);
1065 }
1066 }
1067
1068 // If a cache is served from a HTTP proxy without hitting the web server,
1069 // the boot and exit hooks cannot be fired, so only allow caching in
1070 // proxies if boot hooks are disabled. If the client send a session cookie,
1071 // do not bother caching the page in a public proxy, because the cached copy
1072 // will only be served to that particular user due to Vary: Cookie, unless
1073 // the Vary header has been replaced or unset in hook_boot() (see below).
1074 $max_age = !variable_get('page_cache_invoke_hooks', TRUE) && (!isset($_COOKIE[session_name()]) || isset($hook_boot_headers['vary'])) ? variable_get('cache_lifetime', 0) : 0;
1075 $default_headers['Cache-Control'] = 'public, max-age=' . $max_age;
1076
1077 // Entity tag should change if the output changes.
1078 $etag = '"' . $cache->created . '-' . intval($return_compressed) . '"';
1079 header('Etag: ' . $etag);
1080
1081 // See if the client has provided the required HTTP headers.
1082 $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
1083 $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;
1084
1085 if ($if_modified_since && $if_none_match
1086 && $if_none_match == $etag // etag must match
1087 && $if_modified_since == $cache->created) { // if-modified-since must match
1088 header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified');
1089 drupal_send_headers($default_headers);
1090 return;
1091 }
1092
1093 // Send the remaining headers.
1094 foreach ($cache->headers as $name => $value) {
1095 drupal_add_http_header($name, $value);
1096 }
1097
1098 $default_headers['Last-Modified'] = gmdate(DATE_RFC1123, $cache->created);
1099
1100 // HTTP/1.0 proxies does not support the Vary header, so prevent any caching
1101 // by sending an Expires date in the past. HTTP/1.1 clients ignores the
1102 // Expires header if a Cache-Control: max-age= directive is specified (see RFC
1103 // 2616, section 14.9.3).
1104 $default_headers['Expires'] = 'Sun, 19 Nov 1978 05:00:00 GMT';
1105
1106 drupal_send_headers($default_headers);
1107
1108 // Allow HTTP proxies to cache pages for anonymous users without a session
1109 // cookie. The Vary header is used to indicates the set of request-header
1110 // fields that fully determines whether a cache is permitted to use the
1111 // response to reply to a subsequent request for a given URL without
1112 // revalidation. If a Vary header has been set in hook_boot(), it is assumed
1113 // that the module knows how to cache the page.
1114 if (!isset($hook_boot_headers['vary']) && !variable_get('omit_vary_cookie')) {
1115 header('Vary: Cookie');
1116 }
1117
1118 if ($page_compression) {
1119 header('Vary: Accept-Encoding', FALSE);
1120 // If page_compression is enabled, the cache contains gzipped data.
1121 if ($return_compressed) {
1122 // $cache->data is already gzip'ed, so make sure zlib.output_compression
1123 // does not compress it once more.
1124 ini_set('zlib.output_compression', '0');
1125 header('Content-Encoding: gzip');
1126 }
1127 else {
1128 // The client does not support compression, so unzip the data in the
1129 // cache. Strip the gzip header and run uncompress.
1130 $cache->data = gzinflate(substr(substr($cache->data, 10), 0, -8));
1131 }
1132 }
1133
1134 print $cache->data;
1135 }
1136
1137 /**
1138 * Define the critical hooks that force modules to always be loaded.
1139 */
1140 function bootstrap_hooks() {
1141 return array('boot', 'exit', 'watchdog');
1142 }
1143
1144 /**
1145 * Unserializes and appends elements from a serialized string.
1146 *
1147 * @param $obj
1148 * The object to which the elements are appended.
1149 * @param $field
1150 * The attribute of $obj whose value should be unserialized.
1151 */
1152 function drupal_unpack($obj, $field = 'data') {
1153 if ($obj->$field && $data = unserialize($obj->$field)) {
1154 foreach ($data as $key => $value) {
1155 if (!isset($obj->$key)) {
1156 $obj->$key = $value;
1157 }
1158 }
1159 }
1160 return $obj;
1161 }
1162
1163 /**
1164 * Encode special characters in a plain-text string for display as HTML.
1165 *
1166 * Uses drupal_validate_utf8 to prevent cross site scripting attacks on
1167 * Internet Explorer 6.
1168 */
1169 function check_plain($text) {
1170 return drupal_validate_utf8($text) ? htmlspecialchars($text, ENT_QUOTES) : '';
1171 }
1172
1173 /**
1174 * Checks whether a string is valid UTF-8.
1175 *
1176 * All functions designed to filter input should use drupal_validate_utf8
1177 * to ensure they operate on valid UTF-8 strings to prevent bypass of the
1178 * filter.
1179 *
1180 * When text containing an invalid UTF-8 lead byte (0xC0 - 0xFF) is presented
1181 * as UTF-8 to Internet Explorer 6, the program may misinterpret subsequent
1182 * bytes. When these subsequent bytes are HTML control characters such as
1183 * quotes or angle brackets, parts of the text that were deemed safe by filters
1184 * end up in locations that are potentially unsafe; An onerror attribute that
1185 * is outside of a tag, and thus deemed safe by a filter, can be interpreted
1186 * by the browser as if it were inside the tag.
1187 *
1188 * The function does not return FALSE for strings containing character codes
1189 * above U+10FFFF, even though these are prohibited by RFC 3629.
1190 *
1191 * @param $text
1192 * The text to check.
1193 * @return
1194 * TRUE if the text is valid UTF-8, FALSE if not.
1195 */
1196 function drupal_validate_utf8($text) {
1197 if (strlen($text) == 0) {
1198 return TRUE;
1199 }
1200 // With the PCRE_UTF8 modifier 'u', preg_match() fails silently on strings
1201 // containing invalid UTF-8 byte sequences. It does not reject character
1202 // codes above U+10FFFF (represented by 4 or more octets), though.
1203 return (preg_match('/^./us', $text) == 1);
1204 }
1205
1206 /**
1207 * Since $_SERVER['REQUEST_URI'] is only available on Apache, we
1208 * generate an equivalent using other environment variables.
1209 */
1210 function request_uri() {
1211
1212 if (isset($_SERVER['REQUEST_URI'])) {
1213 $uri = $_SERVER['REQUEST_URI'];
1214 }
1215 else {
1216 if (isset($_SERVER['argv'])) {
1217 $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['argv'][0];
1218 }
1219 elseif (isset($_SERVER['QUERY_STRING'])) {
1220 $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING'];
1221 }
1222 else {
1223 $uri = $_SERVER['SCRIPT_NAME'];
1224 }
1225 }
1226 // Prevent multiple slashes to avoid cross site requests via the Form API.
1227 $uri = '/' . ltrim($uri, '/');
1228
1229 return $uri;
1230 }
1231
1232 /**
1233 * Log a system message.
1234 *
1235 * @param $type
1236 * The category to which this message belongs.
1237 * @param $message
1238 * The message to store in the log. Keep $message translatable
1239 * by not concatenating dynamic values into it! Variables in the
1240 * message should be added by using placeholder strings alongside
1241 * the variables argument to declare the value of the placeholders.
1242 * See t() for documentation on how $message and $variables interact.
1243 * @param $variables
1244 * Array of variables to replace in the message on display or
1245 * NULL if message is already translated or not possible to
1246 * translate.
1247 * @param $severity
1248 * The severity of the message, as per RFC 3164.
1249 * @param $link
1250 * A link to associate with the message.
1251 *
1252 * @see watchdog_severity_levels()
1253 * @see hook_watchdog()
1254 */
1255 function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
1256 global $user, $base_root;
1257
1258 static $in_error_state = FALSE;
1259
1260 // It is possible that the error handling will itself trigger an error. In that case, we could
1261 // end up in an infinite loop. To avoid that, we implement a simple static semaphore.
1262 if (!$in_error_state) {
1263 $in_error_state = TRUE;
1264
1265 // Prepare the fields to be logged
1266 $log_entry = array(
1267 'type' => $type,
1268 'message' => $message,
1269 'variables' => $variables,
1270 'severity' => $severity,
1271 'link' => $link,
1272 'user' => $user,
1273 'request_uri' => $base_root . request_uri(),
1274 'referer' => $_SERVER['HTTP_REFERER'],
1275 'ip' => ip_address(),
1276 'timestamp' => REQUEST_TIME,
1277 );
1278
1279 // Call the logging hooks to log/process the message
1280 foreach (module_implements('watchdog', TRUE) as $module) {
1281 module_invoke($module, 'watchdog', $log_entry);
1282 }
1283
1284 // It is critical that the semaphore is only cleared here, in the parent
1285 // watchdog() call (not outside the loop), to prevent recursive execution.
1286 $in_error_state = FALSE;
1287 }
1288 }
1289
1290 /**
1291 * Set a message which reflects the status of the performed operation.
1292 *
1293 * If the function is called with no arguments, this function returns all set
1294 * messages without clearing them.
1295 *
1296 * @param $message
1297 * The message should begin with a capital letter and always ends with a
1298 * period '.'.
1299 * @param $type
1300 * The type of the message. One of the following values are possible:
1301 * - 'status'
1302 * - 'warning'
1303 * - 'error'
1304 * @param $repeat
1305 * If this is FALSE and the message is already set, then the message won't
1306 * be repeated.
1307 */
1308 function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
1309 if ($message) {
1310 if (!isset($_SESSION['messages'][$type])) {
1311 $_SESSION['messages'][$type] = array();
1312 }
1313
1314 if ($repeat || !in_array($message, $_SESSION['messages'][$type])) {
1315 $_SESSION['messages'][$type][] = $message;
1316 }
1317
1318 // Mark this page has being not cacheable.
1319 drupal_page_is_cacheable(FALSE);
1320 }
1321
1322 // Messages not set when DB connection fails.
1323 return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL;
1324 }
1325
1326 /**
1327 * Return all messages that have been set.
1328 *
1329 * @param $type
1330 * (optional) Only return messages of this type.
1331 * @param $clear_queue
1332 * (optional) Set to FALSE if you do not want to clear the messages queue
1333 * @return
1334 * An associative array, the key is the message type, the value an array
1335 * of messages. If the $type parameter is passed, you get only that type,
1336 * or an empty array if there are no such messages. If $type is not passed,
1337 * all message types are returned, or an empty array if none exist.
1338 */
1339 function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
1340 if ($messages = drupal_set_message()) {
1341 if ($type) {
1342 if ($clear_queue) {
1343 unset($_SESSION['messages'][$type]);
1344 }
1345 if (isset($messages[$type])) {
1346 return array($type => $messages[$type]);
1347 }
1348 }
1349 else {
1350 if ($clear_queue) {
1351 unset($_SESSION['messages']);
1352 }
1353 return $messages;
1354 }
1355 }
1356 return array();
1357 }
1358
1359 /**
1360 * Check to see if an IP address has been blocked.
1361 *
1362 * Blocked IP addresses are stored in the database by default. However for
1363 * performance reasons we allow an override in settings.php. This allows us
1364 * to avoid querying the database at this critical stage of the bootstrap if
1365 * an administrative interface for IP address blocking is not required.
1366 *
1367 * @param $ip
1368 * IP address to check.
1369 * @return bool
1370 * TRUE if access is denied, FALSE if access is allowed.
1371 */
1372 function drupal_is_denied($ip) {
1373 // Because this function is called on every page request, we first check
1374 // for an array of IP addresses in settings.php before querying the
1375 // database.
1376 $blocked_ips = variable_get('blocked_ips');
1377 $denied = FALSE;
1378 if (isset($blocked_ips) && is_array($blocked_ips)) {
1379 $denied = in_array($ip, $blocked_ips);
1380 }
1381 // Only check if database.inc is loaded already. If
1382 // $conf['page_cache_without_database'] = TRUE; is set in settings.php,
1383 // then the database won't be loaded here so the IPs in the database
1384 // won't be denied. However the user asked explicitly not to use the
1385 // database and also in this case it's quite likely that the user relies
1386 // on higher performance solutions like a firewall.
1387 elseif (function_exists('db_is_active')) {
1388 $denied = (bool)db_query("SELECT 1 FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField();
1389 }
1390 return $denied;
1391 }
1392
1393 /**
1394 * Handle denied users.
1395 *
1396 * @param $ip
1397 * IP address to check. Prints a message and exits if access is denied.
1398 */
1399 function drupal_block_denied($ip) {
1400 // Deny access to blocked IP addresses - t() is not yet available.
1401 if (drupal_is_denied($ip)) {
1402 header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
1403 print 'Sorry, ' . check_plain(ip_address()) . ' has been banned.';
1404 exit();
1405 }
1406 }
1407
1408