/[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.320 - (show annotations) (download) (as text)
Mon Nov 2 03:46:43 2009 UTC (3 weeks, 6 days ago) by webchick
Branch: MAIN
Changes since 1.319: +7 -6 lines
File MIME type: text/x-php
#341140 follow-up by JohnAlbin and chx:  drupal_get_filename() fails for PHPTemplate themes when database is down.
1 <?php
2 // $Id: bootstrap.inc,v 1.319 2009/11/02 03:12:05 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 * @return
786 * The cache object, if the page was found in the cache, NULL otherwise.
787 */
788 function drupal_page_get_cache() {
789 global $base_root;
790
791 if (drupal_page_is_cacheable()) {
792 return cache_get($base_root . request_uri(), 'cache_page');
793 }
794 }
795
796 /**
797 * Determine the cacheability of the current page.
798 *
799 * @param $allow_caching
800 * Set to FALSE if you want to prevent this page to get cached.
801 * @return
802 * TRUE if the current page can be cached, FALSE otherwise.
803 */
804 function drupal_page_is_cacheable($allow_caching = NULL) {
805 $allow_caching_static = &drupal_static(__FUNCTION__, TRUE);
806 if (isset($allow_caching)) {
807 $allow_caching_static = $allow_caching;
808 }
809
810 return $allow_caching_static && ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD')
811 && !drupal_is_cli();
812 }
813
814 /**
815 * Call all init or exit hooks without including all modules.
816 *
817 * @param $hook
818 * The name of the bootstrap hook we wish to invoke.
819 */
820 function bootstrap_invoke_all($hook) {
821 foreach (module_list(TRUE, TRUE) as $module) {
822 drupal_load('module', $module);
823 module_invoke($module, $hook);
824 }
825 }
826
827 /**
828 * Includes a file with the provided type and name. This prevents
829 * including a theme, engine, module, etc., more than once.
830 *
831 * @param $type
832 * The type of item to load (i.e. theme, theme_engine, module).
833 * @param $name
834 * The name of the item to load.
835 *
836 * @return
837 * TRUE if the item is loaded or has already been loaded.
838 */
839 function drupal_load($type, $name) {
840 // Once a file is included this can't be reversed during a request so do not
841 // use drupal_static() here.
842 static $files = array();
843
844 if (isset($files[$type][$name])) {
845 return TRUE;
846 }
847
848 $filename = drupal_get_filename($type, $name);
849
850 if ($filename) {
851 include_once DRUPAL_ROOT . '/' . $filename;
852 $files[$type][$name] = TRUE;
853
854 return TRUE;
855 }
856
857 return FALSE;
858 }
859
860 /**
861 * Set an HTTP response header for the current page.
862 *
863 * Note: When sending a Content-Type header, always include a 'charset' type,
864 * too. This is necessary to avoid security bugs (e.g. UTF-7 XSS).
865 *
866 * @param $name
867 * The HTTP header name, or a status code followed by a reason phrase, e.g.
868 * "404 Not Found".
869 * @param $value
870 * The HTTP header value; if omitted, the specified header is unset.
871 * @param $append
872 * Whether to append the value to an existing header or to replace it.
873 */
874 function drupal_add_http_header($name = NULL, $value = NULL, $append = FALSE) {
875 // The headers as name/value pairs.
876 $headers = &drupal_static(__FUNCTION__, array());
877
878 if (!isset($name)) {
879 return $headers;
880 }
881
882 // Save status codes using the special key ":status".
883 if (preg_match('/^\d{3} /', $name)) {
884 $value = $name;
885 $name = $name_lower = ':status';
886 }
887 else {
888 $name_lower = strtolower($name);
889 }
890 _drupal_set_preferred_header_name($name);
891
892 if (!isset($value)) {
893 $headers[$name_lower] = FALSE;
894 }
895 elseif (isset($headers[$name_lower]) && $append) {
896 // Multiple headers with identical names may be combined using comma (RFC
897 // 2616, section 4.2).
898 $headers[$name_lower] .= ',' . $value;
899 }
900 else {
901 $headers[$name_lower] = $value;
902 }
903 drupal_send_headers(array($name => $headers[$name_lower]), TRUE);
904 }
905
906 /**
907 * Get the HTTP response headers for the current page.
908 *
909 * @param $name
910 * An HTTP header name. If omitted, all headers are returned as name/value
911 * pairs. If an array value is FALSE, the header has been unset.
912 * @return
913 * A string containing the header value, or FALSE if the header has been set,
914 * or NULL if the header has not been set.
915 */
916 function drupal_get_http_header($name = NULL) {
917 $headers = drupal_add_http_header();
918 if (isset($name)) {
919 $name = strtolower($name);
920 return isset($headers[$name]) ? $headers[$name] : NULL;
921 }
922 else {
923 return $headers;
924 }
925 }
926
927 /**
928 * Header names are case-insensitive, but for maximum compatibility they should
929 * follow "common form" (see RFC 2617, section 4.2).
930 */
931 function _drupal_set_preferred_header_name($name = NULL) {
932 static $header_names = array();
933
934 if (!isset($name)) {
935 return $header_names;
936 }
937 $header_names[strtolower($name)] = $name;
938 }
939
940 /**
941 * Send the HTTP response headers previously set using drupal_add_http_header().
942 * Add default headers, unless they have been replaced or unset using
943 * drupal_add_http_header().
944 *
945 * @param $default_headers
946 * An array of headers as name/value pairs.
947 * @param $single
948 * If TRUE and headers have already be sent, send only the specified header.
949 */
950 function drupal_send_headers($default_headers = array(), $only_default = FALSE) {
951 $headers_sent = &drupal_static(__FUNCTION__, FALSE);
952 $headers = drupal_get_http_header();
953 if ($only_default && $headers_sent) {
954 $headers = array();
955 }
956 $headers_sent = TRUE;
957
958 $header_names = _drupal_set_preferred_header_name();
959 foreach ($default_headers as $name => $value) {
960 $name_lower = strtolower($name);
961 if (!isset($headers[$name_lower])) {
962 $headers[$name_lower] = $value;
963 $header_names[$name_lower] = $name;
964 }
965 }
966 foreach ($headers as $name_lower => $value) {
967 if ($name_lower == ':status') {
968 header($_SERVER['SERVER_PROTOCOL'] . ' ' . $value);
969 }
970 // Skip headers that have been unset.
971 elseif ($value) {
972 header($header_names[$name_lower] . ': ' . $value);
973 }
974 }
975 }
976
977 /**
978 * Set HTTP headers in preparation for a page response.
979 *
980 * Authenticated users are always given a 'no-cache' header, and will fetch a
981 * fresh page on every request. This prevents authenticated users from seeing
982 * locally cached pages.
983 *
984 * Also give each page a unique ETag. This will force clients to include both
985 * an If-Modified-Since header and an If-None-Match header when doing
986 * conditional requests for the page (required by RFC 2616, section 13.3.4),
987 * making the validation more robust. This is a workaround for a bug in Mozilla
988 * Firefox that is triggered when Drupal's caching is enabled and the user
989 * accesses Drupal via an HTTP proxy (see
990 * https://bugzilla.mozilla.org/show_bug.cgi?id=269303): When an authenticated
991 * user requests a page, and then logs out and requests the same page again,
992 * Firefox may send a conditional request based on the page that was cached
993 * locally when the user was logged in. If this page did not have an ETag
994 * header, the request only contains an If-Modified-Since header. The date will
995 * be recent, because with authenticated users the Last-Modified header always
996 * refers to the time of the request. If the user accesses Drupal via a proxy
997 * server, and the proxy already has a cached copy of the anonymous page with an
998 * older Last-Modified date, the proxy may respond with 304 Not Modified, making
999 * the client think that the anonymous and authenticated pageviews are
1000 * identical.
1001 *
1002 * @see drupal_page_set_cache()
1003 */
1004 function drupal_page_header() {
1005 $headers_sent = &drupal_static(__FUNCTION__, FALSE);
1006 if ($headers_sent) {
1007 return TRUE;
1008 }
1009 $headers_sent = TRUE;
1010
1011 $default_headers = array(
1012 'Expires' => 'Sun, 19 Nov 1978 05:00:00 GMT',
1013 'Last-Modified' => gmdate(DATE_RFC1123, REQUEST_TIME),
1014 'Cache-Control' => 'no-cache, must-revalidate, post-check=0, pre-check=0',
1015 'ETag' => '"' . REQUEST_TIME . '"',
1016 );
1017 drupal_send_headers($default_headers);
1018 }
1019
1020 /**
1021 * Set HTTP headers in preparation for a cached page response.
1022 *
1023 * The headers allow as much as possible in proxies and browsers without any
1024 * particular knowledge about the pages. Modules can override these headers
1025 * using drupal_add_http_header().
1026 *
1027 * If the request is conditional (using If-Modified-Since and If-None-Match),
1028 * and the conditions match those currently in the cache, a 304 Not Modified
1029 * response is sent.
1030 */
1031 function drupal_serve_page_from_cache(stdClass $cache) {
1032 // Negotiate whether to use compression.
1033 $page_compression = variable_get('page_compression', TRUE) && extension_loaded('zlib');
1034 $return_compressed = $page_compression && isset($_SERVER['HTTP_ACCEPT_ENCODING']) && strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') !== FALSE;
1035
1036 // Get headers set in hook_boot(). Keys are lower-case.
1037 $hook_boot_headers = drupal_get_http_header();
1038
1039 // Headers generated in this function, that may be replaced or unset using
1040 // drupal_add_http_headers(). Keys are mixed-case.
1041 $default_headers = array();
1042
1043 foreach ($cache->headers as $name => $value) {
1044 // In the case of a 304 response, certain headers must be sent, and the
1045 // remaining may not (see RFC 2616, section 10.3.5). Do not override
1046 // headers set in hook_boot().
1047 $name_lower = strtolower($name);
1048 if (in_array($name_lower, array('content-location', 'expires', 'cache-control', 'vary')) && !isset($hook_boot_headers[$name_lower])) {
1049 drupal_add_http_header($name, $value);
1050 unset($cache->headers[$name]);
1051 }
1052 }
1053
1054 // If a cache is served from a HTTP proxy without hitting the web server,
1055 // the boot and exit hooks cannot be fired, so only allow caching in
1056 // proxies if boot hooks are disabled. If the client send a session cookie,
1057 // do not bother caching the page in a public proxy, because the cached copy
1058 // will only be served to that particular user due to Vary: Cookie, unless
1059 // the Vary header has been replaced or unset in hook_boot() (see below).
1060 $max_age = !variable_get('page_cache_invoke_hooks', TRUE) && (!isset($_COOKIE[session_name()]) || isset($hook_boot_headers['vary'])) ? variable_get('cache_lifetime', 0) : 0;
1061 $default_headers['Cache-Control'] = 'public, max-age=' . $max_age;
1062
1063 // Entity tag should change if the output changes.
1064 $etag = '"' . $cache->created . '-' . intval($return_compressed) . '"';
1065 header('Etag: ' . $etag);
1066
1067 // See if the client has provided the required HTTP headers.
1068 $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
1069 $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;
1070
1071 if ($if_modified_since && $if_none_match
1072 && $if_none_match == $etag // etag must match
1073 && $if_modified_since == $cache->created) { // if-modified-since must match
1074 header($_SERVER['SERVER_PROTOCOL'] . ' 304 Not Modified');
1075 drupal_send_headers($default_headers);
1076 return;
1077 }
1078
1079 // Send the remaining headers.
1080 foreach ($cache->headers as $name => $value) {
1081 drupal_add_http_header($name, $value);
1082 }
1083
1084 $default_headers['Last-Modified'] = gmdate(DATE_RFC1123, $cache->created);
1085
1086 // HTTP/1.0 proxies does not support the Vary header, so prevent any caching
1087 // by sending an Expires date in the past. HTTP/1.1 clients ignores the
1088 // Expires header if a Cache-Control: max-age= directive is specified (see RFC
1089 // 2616, section 14.9.3).
1090 $default_headers['Expires'] = 'Sun, 19 Nov 1978 05:00:00 GMT';
1091
1092 drupal_send_headers($default_headers);
1093
1094 // Allow HTTP proxies to cache pages for anonymous users without a session
1095 // cookie. The Vary header is used to indicates the set of request-header
1096 // fields that fully determines whether a cache is permitted to use the
1097 // response to reply to a subsequent request for a given URL without
1098 // revalidation. If a Vary header has been set in hook_boot(), it is assumed
1099 // that the module knows how to cache the page.
1100 if (!isset($hook_boot_headers['vary']) && !variable_get('omit_vary_cookie')) {
1101 header('Vary: Cookie');
1102 }
1103
1104 if ($page_compression) {
1105 header('Vary: Accept-Encoding', FALSE);
1106 // If page_compression is enabled, the cache contains gzipped data.
1107 if ($return_compressed) {
1108 // $cache->data is already gzip'ed, so make sure zlib.output_compression
1109 // does not compress it once more.
1110 ini_set('zlib.output_compression', '0');
1111 header('Content-Encoding: gzip');
1112 }
1113 else {
1114 // The client does not support compression, so unzip the data in the
1115 // cache. Strip the gzip header and run uncompress.
1116 $cache->data = gzinflate(substr(substr($cache->data, 10), 0, -8));
1117 }
1118 }
1119
1120 print $cache->data;
1121 }
1122
1123 /**
1124 * Define the critical hooks that force modules to always be loaded.
1125 */
1126 function bootstrap_hooks() {
1127 return array('boot', 'exit', 'watchdog');
1128 }
1129
1130 /**
1131 * Unserializes and appends elements from a serialized string.
1132 *
1133 * @param $obj
1134 * The object to which the elements are appended.
1135 * @param $field
1136 * The attribute of $obj whose value should be unserialized.
1137 */
1138 function drupal_unpack($obj, $field = 'data') {
1139 if ($obj->$field && $data = unserialize($obj->$field)) {
1140 foreach ($data as $key => $value) {
1141 if (!isset($obj->$key)) {
1142 $obj->$key = $value;
1143 }
1144 }
1145 }
1146 return $obj;
1147 }
1148
1149 /**
1150 * Encode special characters in a plain-text string for display as HTML.
1151 *
1152 * Uses drupal_validate_utf8 to prevent cross site scripting attacks on
1153 * Internet Explorer 6.
1154 */
1155 function check_plain($text) {
1156 return drupal_validate_utf8($text) ? htmlspecialchars($text, ENT_QUOTES) : '';
1157 }
1158
1159 /**
1160 * Checks whether a string is valid UTF-8.
1161 *
1162 * All functions designed to filter input should use drupal_validate_utf8
1163 * to ensure they operate on valid UTF-8 strings to prevent bypass of the
1164 * filter.
1165 *
1166 * When text containing an invalid UTF-8 lead byte (0xC0 - 0xFF) is presented
1167 * as UTF-8 to Internet Explorer 6, the program may misinterpret subsequent
1168 * bytes. When these subsequent bytes are HTML control characters such as
1169 * quotes or angle brackets, parts of the text that were deemed safe by filters
1170 * end up in locations that are potentially unsafe; An onerror attribute that
1171 * is outside of a tag, and thus deemed safe by a filter, can be interpreted
1172 * by the browser as if it were inside the tag.
1173 *
1174 * The function does not return FALSE for strings containing character codes
1175 * above U+10FFFF, even though these are prohibited by RFC 3629.
1176 *
1177 * @param $text
1178 * The text to check.
1179 * @return
1180 * TRUE if the text is valid UTF-8, FALSE if not.
1181 */
1182 function drupal_validate_utf8($text) {
1183 if (strlen($text) == 0) {
1184 return TRUE;
1185 }
1186 // With the PCRE_UTF8 modifier 'u', preg_match() fails silently on strings
1187 // containing invalid UTF-8 byte sequences. It does not reject character
1188 // codes above U+10FFFF (represented by 4 or more octets), though.
1189 return (preg_match('/^./us', $text) == 1);
1190 }
1191
1192 /**
1193 * Since $_SERVER['REQUEST_URI'] is only available on Apache, we
1194 * generate an equivalent using other environment variables.
1195 */
1196 function request_uri() {
1197
1198 if (isset($_SERVER['REQUEST_URI'])) {
1199 $uri = $_SERVER['REQUEST_URI'];
1200 }
1201 else {
1202 if (isset($_SERVER['argv'])) {
1203 $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['argv'][0];
1204 }
1205 elseif (isset($_SERVER['QUERY_STRING'])) {
1206 $uri = $_SERVER['SCRIPT_NAME'] . '?' . $_SERVER['QUERY_STRING'];
1207 }
1208 else {
1209 $uri = $_SERVER['SCRIPT_NAME'];
1210 }
1211 }
1212 // Prevent multiple slashes to avoid cross site requests via the Form API.
1213 $uri = '/' . ltrim($uri, '/');
1214
1215 return $uri;
1216 }
1217
1218 /**
1219 * Log a system message.
1220 *
1221 * @param $type
1222 * The category to which this message belongs.
1223 * @param $message
1224 * The message to store in the log. Keep $message translatable
1225 * by not concatenating dynamic values into it! Variables in the
1226 * message should be added by using placeholder strings alongside
1227 * the variables argument to declare the value of the placeholders.
1228 * See t() for documentation on how $message and $variables interact.
1229 * @param $variables
1230 * Array of variables to replace in the message on display or
1231 * NULL if message is already translated or not possible to
1232 * translate.
1233 * @param $severity
1234 * The severity of the message, as per RFC 3164.
1235 * @param $link
1236 * A link to associate with the message.
1237 *
1238 * @see watchdog_severity_levels()
1239 * @see hook_watchdog()
1240 */
1241 function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
1242 global $user, $base_root;
1243
1244 static $in_error_state = FALSE;
1245
1246 // It is possible that the error handling will itself trigger an error. In that case, we could
1247 // end up in an infinite loop. To avoid that, we implement a simple static semaphore.
1248 if (!$in_error_state) {
1249 $in_error_state = TRUE;
1250
1251 // Prepare the fields to be logged
1252 $log_entry = array(
1253 'type' => $type,
1254 'message' => $message,
1255 'variables' => $variables,
1256 'severity' => $severity,
1257 'link' => $link,
1258 'user' => $user,
1259 'request_uri' => $base_root . request_uri(),
1260 'referer' => $_SERVER['HTTP_REFERER'],
1261 'ip' => ip_address(),
1262 'timestamp' => REQUEST_TIME,
1263 );
1264
1265 // Call the logging hooks to log/process the message
1266 foreach (module_implements('watchdog', TRUE) as $module) {
1267 module_invoke($module, 'watchdog', $log_entry);
1268 }
1269
1270 // It is critical that the semaphore is only cleared here, in the parent
1271 // watchdog() call (not outside the loop), to prevent recursive execution.
1272 $in_error_state = FALSE;
1273 }
1274 }
1275
1276 /**
1277 * Set a message which reflects the status of the performed operation.
1278 *
1279 * If the function is called with no arguments, this function returns all set
1280 * messages without clearing them.
1281 *
1282 * @param $message
1283 * The message should begin with a capital letter and always ends with a
1284 * period '.'.
1285 * @param $type
1286 * The type of the message. One of the following values are possible:
1287 * - 'status'
1288 * - 'warning'
1289 * - 'error'
1290 * @param $repeat
1291 * If this is FALSE and the message is already set, then the message won't
1292 * be repeated.
1293 */
1294 function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
1295 if ($message) {
1296 if (!isset($_SESSION['messages'][$type])) {
1297 $_SESSION['messages'][$type] = array();
1298 }
1299
1300 if ($repeat || !in_array($message, $_SESSION['messages'][$type])) {
1301 $_SESSION['messages'][$type][] = $message;
1302 }
1303
1304 // Mark this page has being not cacheable.
1305 drupal_page_is_cacheable(FALSE);
1306 }
1307
1308 // Messages not set when DB connection fails.
1309 return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL;
1310 }
1311
1312 /**
1313 * Return all messages that have been set.
1314 *
1315 * @param $type
1316 * (optional) Only return messages of this type.
1317 * @param $clear_queue
1318 * (optional) Set to FALSE if you do not want to clear the messages queue
1319 * @return
1320 * An associative array, the key is the message type, the value an array
1321 * of messages. If the $type parameter is passed, you get only that type,
1322 * or an empty array if there are no such messages. If $type is not passed,
1323 * all message types are returned, or an empty array if none exist.
1324 */
1325 function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
1326 if ($messages = drupal_set_message()) {
1327 if ($type) {
1328 if ($clear_queue) {
1329 unset($_SESSION['messages'][$type]);
1330 }
1331 if (isset($messages[$type])) {
1332 return array($type => $messages[$type]);
1333 }
1334 }
1335 else {
1336 if ($clear_queue) {
1337 unset($_SESSION['messages']);
1338 }
1339 return $messages;
1340 }
1341 }
1342 return array();
1343 }
1344
1345 /**
1346 * Check to see if an IP address has been blocked.
1347 *
1348 * Blocked IP addresses are stored in the database by default. However for
1349 * performance reasons we allow an override in settings.php. This allows us
1350 * to avoid querying the database at this critical stage of the bootstrap if
1351 * an administrative interface for IP address blocking is not required.
1352 *
1353 * @param $ip
1354 * IP address to check.
1355 * @return bool
1356 * TRUE if access is denied, FALSE if access is allowed.
1357 */
1358 function drupal_is_denied($ip) {
1359 // Because this function is called on every page request, we first check
1360 // for an array of IP addresses in settings.php before querying the
1361 // database.
1362 $blocked_ips = variable_get('blocked_ips');
1363 $denied = FALSE;
1364 if (isset($blocked_ips) && is_array($blocked_ips)) {
1365 $denied = in_array($ip, $blocked_ips);
1366 }
1367 // Only check if database.inc is loaded already. If
1368 // $conf['page_cache_without_database'] = TRUE; is set in settings.php,
1369 // then the database won't be loaded here so the IPs in the database
1370 // won't be denied. However the user asked explicitly not to use the
1371 // database and also in this case it's quite likely that the user relies
1372 // on higher performance solutions like a firewall.
1373 elseif (function_exists('db_is_active')) {
1374 $denied = (bool)db_query("SELECT 1 FROM {blocked_ips} WHERE ip = :ip", array(':ip' => $ip))->fetchField();
1375 }
1376 return $denied;
1377 }
1378
1379 /**
1380 * Handle denied users.
1381 *
1382 * @param $ip
1383 * IP address to check. Prints a message and exits if access is denied.
1384 */
1385 function drupal_block_denied($ip) {
1386 // Deny access to blocked IP addresses - t() is not yet available.
1387 if (drupal_is_denied($ip)) {
1388 header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
1389 print 'Sorry, ' . check_plain(ip_address()) . ' has been banned.';
1390 exit();
1391 }
1392 }
1393
1394 /**
1395 * Generates a default anonymous $user object.
1396 *
1397 * @return Object - the user object.
1398 */
1399 function drupal_anonymous_user($session = '') {
1400 $user = new stdClass();
1401 $user->uid = 0;
1402 $user->hostname = ip_address();
1403 $user->roles = array();
1404 $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
1405 $user->session = $session;