comparison includes/bootstrap.inc @ 1:c1f4ac30525a 6.0

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