Spike PHPCoverage Details: bootstrap.inc

Line #FrequencySource Line
1 <?php
2 // $Id: bootstrap.inc,v 1.206 2008/01/10 22:47:17 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) {
2331  static $conf = '';
234 
2351  if ($conf && !$reset) {
2361    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 
3731  if (!isset($files[$type])) {
374     $files[$type] = array();
375   }
376 
3771  if (!empty($filename) && file_exists($filename)) {
3781    $files[$type][$name] = $filename;
379   }
3801  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 
4051  if (isset($files[$type][$name])) {
4061    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) {
4481  global $conf;
449 
4501  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) {
4631  global $conf;
464 
4651  $serialized_value = serialize($value);
4661  db_query("UPDATE {variable} SET value = '%s' WHERE name = '%s'", $serialized_value, $name);
4671  if (!db_affected_rows()) {
4681    @db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, $serialized_value);
469   }
470 
4711  cache_clear_all('variables', 'cache');
472 
4731  $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) {
4831  global $conf;
484 
4851  db_query("DELETE FROM {variable} WHERE name = '%s'", $name);
4861  cache_clear_all('variables', 'cache');
487 
4881  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 function page_get_cache() {
499   global $user, $base_root;
500 
501   $cache = NULL;
502 
503   if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && count(drupal_set_message()) == 0) {
504     $cache = cache_get($base_root . request_uri(), 'cache_page');
505 
506     if (empty($cache)) {
507       ob_start();
508     }
509   }
510 
511   return $cache;
512 }
513 
514 /**
515  * Call all init or exit hooks without including all modules.
516  *
517  * @param $hook
518  *   The name of the bootstrap hook we wish to invoke.
519  */
520 function bootstrap_invoke_all($hook) {
521   foreach (module_list(TRUE, TRUE) as $module) {
522     drupal_load('module', $module);
523     module_invoke($module, $hook);
524   }
525 }
526 
527 /**
528  * Includes a file with the provided type and name. This prevents
529  * including a theme, engine, module, etc., more than once.
530  *
531  * @param $type
532  *   The type of item to load (i.e. theme, theme_engine, module).
533  * @param $name
534  *   The name of the item to load.
535  *
536  * @return
537  *   TRUE if the item is loaded or has already been loaded.
538  */
539 function drupal_load($type, $name) {
540   static $files = array();
541 
5421  if (isset($files[$type][$name])) {
5431    return TRUE;
544   }
545 
546   $filename = drupal_get_filename($type, $name);
547 
548   if ($filename) {
549     include_once "./$filename";
550     $files[$type][$name] = TRUE;
551 
552     return TRUE;
553   }
554 
555   return FALSE;
556 }
557 
558 /**
559  * Set HTTP headers in preparation for a page response.
560  *
561  * Authenticated users are always given a 'no-cache' header, and will
562  * fetch a fresh page on every request.  This prevents authenticated
563  * users seeing locally cached pages that show them as logged out.
564  *
565  * @see page_set_cache()
566  */
567 function drupal_page_header() {
568   header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
569   header("Last-Modified: ". gmdate("D, d M Y H:i:s") ." GMT");
570   header("Cache-Control: store, no-cache, must-revalidate");
571   header("Cache-Control: post-check=0, pre-check=0", FALSE);
572 }
573 
574 /**
575  * Set HTTP headers in preparation for a cached page response.
576  *
577  * The general approach here is that anonymous users can keep a local
578  * cache of the page, but must revalidate it on every request.  Then,
579  * they are given a '304 Not Modified' response as long as they stay
580  * logged out and the page has not been modified.
581  *
582  */
583 function drupal_page_cache_header($cache) {
584   // Set default values:
585   $last_modified = gmdate('D, d M Y H:i:s', $cache->created) .' GMT';
586   $etag = '"'. md5($last_modified) .'"';
587 
588   // See if the client has provided the required HTTP headers:
589   $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
590   $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;
591 
592   if ($if_modified_since && $if_none_match
593       && $if_none_match == $etag // etag must match
594       && $if_modified_since == $last_modified) {  // if-modified-since must match
595     header('HTTP/1.1 304 Not Modified');
596     // All 304 responses must send an etag if the 200 response for the same object contained an etag
597     header("Etag: $etag");
598     exit();
599   }
600 
601   // Send appropriate response:
602   header("Last-Modified: $last_modified");
603   header("ETag: $etag");
604 
605   // The following headers force validation of cache:
606   header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
607   header("Cache-Control: must-revalidate");
608 
609   if (variable_get('page_compression', TRUE)) {
610     // Determine if the browser accepts gzipped data.
611     if (@strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === FALSE && function_exists('gzencode')) {
612       // Strip the gzip header and run uncompress.
613       $cache->data = gzinflate(substr(substr($cache->data, 10), 0, -8));
614     }
615     elseif (function_exists('gzencode')) {
616       header('Content-Encoding: gzip');
617     }
618   }
619 
620   // Send the original request's headers. We send them one after
621   // another so PHP's header() function can deal with duplicate
622   // headers.
623   $headers = explode("\n", $cache->headers);
624   foreach ($headers as $header) {
625     header($header);
626   }
627 
628   print $cache->data;
629 }
630 
631 /**
632  * Define the critical hooks that force modules to always be loaded.
633  */
634 function bootstrap_hooks() {
6351  return array('boot', 'exit');
636 }
637 
638 /**
639  * Unserializes and appends elements from a serialized string.
640  *
641  * @param $obj
642  *   The object to which the elements are appended.
643  * @param $field
644  *   The attribute of $obj whose value should be unserialized.
645  */
646 function drupal_unpack($obj, $field = 'data') {
6471  if ($obj->$field && $data = unserialize($obj->$field)) {
648     foreach ($data as $key => $value) {
649       if (!isset($obj->$key)) {
650         $obj->$key = $value;
651       }
652     }
653   }
6541  return $obj;
655 }
656 
657 /**
658  * Return the URI of the referring page.
659  */
660 function referer_uri() {
6611  if (isset($_SERVER['HTTP_REFERER'])) {
6621    return $_SERVER['HTTP_REFERER'];
663   }
664 }
665 
666 /**
667  * Encode special characters in a plain-text string for display as HTML.
668  *
669  * Uses drupal_validate_utf8 to prevent cross site scripting attacks on
670  * Internet Explorer 6.
671  */
672 function check_plain($text) {
6731  return drupal_validate_utf8($text) ? htmlspecialchars($text, ENT_QUOTES) : '';
674 }
675 
676 /**
677  * Checks whether a string is valid UTF-8.
678  *
679  * All functions designed to filter input should use drupal_validate_utf8
680  * to ensure they operate on valid UTF-8 strings to prevent bypass of the
681  * filter.
682  *
683  * When text containing an invalid UTF-8 lead byte (0xC0 - 0xFF) is presented
684  * as UTF-8 to Internet Explorer 6, the program may misinterpret subsequent
685  * bytes. When these subsequent bytes are HTML control characters such as
686  * quotes or angle brackets, parts of the text that were deemed safe by filters
687  * end up in locations that are potentially unsafe; An onerror attribute that
688  * is outside of a tag, and thus deemed safe by a filter, can be interpreted
689  * by the browser as if it were inside the tag.
690  *
691  * This function exploits preg_match behaviour (since PHP 4.3.5) when used
692  * with the u modifier, as a fast way to find invalid UTF-8. When the matched
693  * string contains an invalid byte sequence, it will fail silently.
694  *
695  * preg_match may not fail on 4 and 5 octet sequences, even though they
696  * are not supported by the specification.
697  *
698  * The specific preg_match behaviour is present since PHP 4.3.5.
699  *
700  * @param $text
701  *   The text to check.
702  * @return
703  *   TRUE if the text is valid UTF-8, FALSE if not.
704  */
705 function drupal_validate_utf8($text) {
7061  if (strlen($text) == 0) {
707     return TRUE;
708   }
7091  return (preg_match('/^./us', $text) == 1);
710 }
711 
712 /**
713  * Since $_SERVER['REQUEST_URI'] is only available on Apache, we
714  * generate an equivalent using other environment variables.
715  */
716 function request_uri() {
717 
7181  if (isset($_SERVER['REQUEST_URI'])) {
7191    $uri = $_SERVER['REQUEST_URI'];
720   }
721   else {
722     if (isset($_SERVER['argv'])) {
723       $uri = $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['argv'][0];
724     }
725     elseif (isset($_SERVER['QUERY_STRING'])) {
726       $uri = $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
727     }
728     else {
729       $uri = $_SERVER['SCRIPT_NAME'];
730     }
731   }
732 
7331  return $uri;
734 }
735 
736 /**
737  * Log a system message.
738  *
739  * @param $type
740  *   The category to which this message belongs.
741  * @param $message
742  *   The message to store in the log. See t() for documentation
743  *   on how $message and $variables interact. Keep $message
744  *   translatable by not concatenating dynamic values into it!
745  * @param $variables
746  *   Array of variables to replace in the message on display or
747  *   NULL if message is already translated or not possible to
748  *   translate.
749  * @param $severity
750  *   The severity of the message, as per RFC 3164
751  * @param $link
752  *   A link to associate with the message.
753  *
754  * @see watchdog_severity_levels()
755  */
756 function watchdog($type, $message, $variables = array(), $severity = WATCHDOG_NOTICE, $link = NULL) {
7571  global $user, $base_root;
758 
759   // Prepare the fields to be logged
760   $log_message = array(
7611    'type'        => $type,
762     'message'     => $message,
763     'variables'   => $variables,
764     'severity'    => $severity,
765     'link'        => $link,
766     'user'        => $user,
767     'request_uri' => $base_root . request_uri(),
768     'referer'     => referer_uri(),
769     'ip'          => ip_address(),
770     'timestamp'   => time(),
771     );
772 
773   // Call the logging hooks to log/process the message
7741  foreach (module_implements('watchdog', TRUE) as $module) {
7751    module_invoke($module, 'watchdog', $log_message);
776   }
777 }
778 
779 /**
780  * Set a message which reflects the status of the performed operation.
781  *
782  * If the function is called with no arguments, this function returns all set
783  * messages without clearing them.
784  *
785  * @param $message
786  *   The message should begin with a capital letter and always ends with a
787  *   period '.'.
788  * @param $type
789  *   The type of the message. One of the following values are possible:
790  *   - 'status'
791  *   - 'warning'
792  *   - 'error'
793  * @param $repeat
794  *   If this is FALSE and the message is already set, then the message won't
795  *   be repeated.
796  */
797 function drupal_set_message($message = NULL, $type = 'status', $repeat = TRUE) {
798   if ($message) {
799     if (!isset($_SESSION['messages'])) {
800       $_SESSION['messages'] = array();
801     }
802 
803     if (!isset($_SESSION['messages'][$type])) {
804       $_SESSION['messages'][$type] = array();
805     }
806 
807     if ($repeat || !in_array($message, $_SESSION['messages'][$type])) {
808       $_SESSION['messages'][$type][] = $message;
809     }
810   }
811 
812   // messages not set when DB connection fails
813   return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL;
814 }
815 
816 /**
817  * Return all messages that have been set.
818  *
819  * @param $type
820  *   (optional) Only return messages of this type.
821  * @param $clear_queue
822  *   (optional) Set to FALSE if you do not want to clear the messages queue
823  * @return
824  *   An associative array, the key is the message type, the value an array
825  *   of messages. If the $type parameter is passed, you get only that type,
826  *   or an empty array if there are no such messages. If $type is not passed,
827  *   all message types are returned, or an empty array if none exist.
828  */
829 function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
830   if ($messages = drupal_set_message()) {
831     if ($type) {
832       if ($clear_queue) {
833         unset($_SESSION['messages'][$type]);
834       }
835       if (isset($messages[$type])) {
836         return array($type => $messages[$type]);
837       }
838     }
839     else {
840       if ($clear_queue) {
841         unset($_SESSION['messages']);
842       }
843       return $messages;
844     }
845   }
846   return array();
847 }
848 
849 /**
850  * Perform an access check for a given mask and rule type. Rules are usually
851  * created via admin/user/rules page.
852  *
853  * If any allow rule matches, access is allowed. Otherwise, if any deny rule
854  * matches, access is denied.  If no rule matches, access is allowed.
855  *
856  * @param $type string
857  *   Type of access to check: Allowed values are:
858  *     - 'host': host name or IP address
859  *     - 'mail': e-mail address
860  *     - 'user': username
861  * @param $mask string
862  *   String or mask to test: '_' matches any character, '%' matches any
863  *   number of characters.
864  * @return bool
865  *   TRUE if access is denied, FALSE if access is allowed.
866  */
867 function drupal_is_denied($type, $mask) {
868   // Because this function is called for every page request, both cached
869   // and non-cached pages, we tried to optimize it as much as possible.
870   // We deny access if the only matching records in the {access} table have
871   // status 0 (deny). If any have status 1 (allow), or if there are no
872   // matching records, we allow access.
873   $sql = "SELECT 1 FROM {access} WHERE type = '%s' AND LOWER('%s') LIKE LOWER(mask) AND status = %d";
874   return db_result(db_query_range($sql, $type, $mask, 0, 0, 1)) && !db_result(db_query_range($sql, $type, $mask, 1, 0, 1));
875 }
876 
877 /**
878  * Generates a default anonymous $user object.
879  *
880  * @return Object - the user object.
881  */
882 function drupal_anonymous_user($session = '') {
883   $user = new stdClass();
884   $user->uid = 0;
885   $user->hostname = ip_address();
886   $user->roles = array();
887   $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
888   $user->session = $session;
889   $user->cache = 0;
890   return $user;
891 }
892 
893 /**
894  * A string describing a phase of Drupal to load. Each phase adds to the
895  * previous one, so invoking a later phase automatically runs the earlier
896  * phases too. The most important usage is that if you want to access the
897  * Drupal database from a script without loading anything else, you can
898  * include bootstrap.inc, and call drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE).
899  *
900  * @param $phase
901  *   A constant. Allowed values are:
902  *     DRUPAL_BOOTSTRAP_CONFIGURATION: initialize configuration.
903  *     DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE: try to call a non-database cache fetch routine.
904  *     DRUPAL_BOOTSTRAP_DATABASE: initialize database layer.
905  *     DRUPAL_BOOTSTRAP_ACCESS: identify and reject banned hosts.
906  *     DRUPAL_BOOTSTRAP_SESSION: initialize session handling.
907  *     DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE: load bootstrap.inc and module.inc, start
908  *       the variable system and try to serve a page from the cache.
909  *     DRUPAL_BOOTSTRAP_LANGUAGE: identify the language used on the page.
910  *     DRUPAL_BOOTSTRAP_PATH: set $_GET['q'] to Drupal path of request.
911  *     DRUPAL_BOOTSTRAP_FULL: Drupal is fully loaded, validate and fix input data.
912  */
913 function drupal_bootstrap($phase) {
914   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;
915 
9161  while ($phase >= $phase_index && isset($phases[$phase_index])) {
917     $current_phase = $phases[$phase_index];
918     unset($phases[$phase_index++]);
919     _drupal_bootstrap($current_phase);
920   }
921 }
922 
923 function _drupal_bootstrap($phase) {
924   global $conf;
925 
926   switch ($phase) {
927 
928     case DRUPAL_BOOTSTRAP_CONFIGURATION:
929       drupal_unset_globals();
930       // Start a page timer:
931       timer_start('page');
932       // Initialize the configuration
933       conf_init();
934       break;
935 
936     case DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE:
937       // Allow specifying special cache handlers in settings.php, like
938       // using memcached or files for storing cache information.
939       require_once variable_get('cache_inc', './includes/cache.inc');
940       // If the page_cache_fastpath is set to TRUE in settings.php and
941       // page_cache_fastpath (implemented in the special implementation of
942       // cache.inc) printed the page and indicated this with a returned TRUE
943       // then we are done.
944       if (variable_get('page_cache_fastpath', FALSE) && page_cache_fastpath()) {
945         exit;
946       }
947       break;
948 
949     case DRUPAL_BOOTSTRAP_DATABASE:
950       // Initialize the default database.
951       require_once './includes/database.inc';
952       db_set_active();
953       break;
954 
955     case DRUPAL_BOOTSTRAP_ACCESS:
956       // Deny access to hosts which were banned - t() is not yet available.
957       if (drupal_is_denied('host', ip_address())) {
958         header('HTTP/1.1 403 Forbidden');
959         print 'Sorry, '. check_plain(ip_address()) .' has been banned.';
960         exit();
961       }
962       break;
963 
964     case DRUPAL_BOOTSTRAP_SESSION:
965       require_once variable_get('session_inc', './includes/session.inc');
966       session_set_save_handler('sess_open', 'sess_close', 'sess_read', 'sess_write', 'sess_destroy_sid', 'sess_gc');
967       session_start();
968       break;
969 
970     case DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE:
971       // Initialize configuration variables, using values from settings.php if available.
972       $conf = variable_init(isset($conf) ? $conf : array());
973       // Load module handling.
974       require_once './includes/module.inc';
975       $cache_mode = variable_get('cache', CACHE_DISABLED);
976       // Get the page from the cache.
977       $cache = $cache_mode == CACHE_DISABLED ? '' : page_get_cache();
978       // If the skipping of the bootstrap hooks is not enforced, call hook_boot.
979       if ($cache_mode != CACHE_AGGRESSIVE) {
980         bootstrap_invoke_all('boot');
981       }
982       // If there is a cached page, display it.
983       if ($cache) {
984         drupal_page_cache_header($cache);
985         // If the skipping of the bootstrap hooks is not enforced, call hook_exit.
986         if ($cache_mode != CACHE_AGGRESSIVE) {
987           bootstrap_invoke_all('exit');
988         }
989         // We are done.
990         exit;
991       }
992       // Prepare for non-cached page workflow.
993       drupal_page_header();
994       break;
995 
996     case DRUPAL_BOOTSTRAP_LANGUAGE:
997       drupal_init_language();
998       break;
999 
1000     case DRUPAL_BOOTSTRAP_PATH:
1001       require_once './includes/path.inc';
1002       // Initialize $_GET['q'] prior to loading modules and invoking hook_init().
1003       drupal_init_path();
1004       break;
1005 
1006     case DRUPAL_BOOTSTRAP_FULL:
1007       require_once './includes/common.inc';
1008       _drupal_bootstrap_full();
1009       break;
1010   }
1011 }
1012 
1013 /**
1014  * Enables use of the theme system without requiring database access.
1015  *
1016  * Loads and initializes the theme system for site installs, updates and when
1017  * the site is in off-line mode. This also applies when the database fails.
1018  *
1019  * @see _drupal_maintenance_theme()
1020  */
1021 function drupal_maintenance_theme() {
1022   require_once './includes/theme.maintenance.inc';
1023   _drupal_maintenance_theme();
1024 }
1025 
1026 /**
1027  * Return the name of the localisation function. Use in code that needs to
1028  * run both during installation and normal operation.
1029  */
1030 function get_t() {
10311  static $t;
10321  if (is_null($t)) {
1033     $t = function_exists('install_main') ? 'st' : 't';
1034   }
10351  return $t;
1036 }
1037 
1038 /**
1039  *  Choose a language for the current page, based on site and user preferences.
1040  */
1041 function drupal_init_language() {
1042   global $language, $user;
1043 
1044   // Ensure the language is correctly returned, even without multilanguage support.
1045   // Useful for eg. XML/HTML 'lang' attributes.
1046   if (variable_get('language_count', 1) == 1) {
1047     $language = language_default();
1048   }
1049   else {
1050     include_once './includes/language.inc';
1051     $language = language_initialize();
1052   }
1053 }
1054 
1055 /**
1056  * Get a list of languages set up indexed by the specified key
1057  *
1058  * @param $field The field to index the list with.
1059  * @param $reset Boolean to request a reset of the list.
1060  */
1061 function language_list($field = 'language', $reset = FALSE) {
1062   static $languages = NULL;
1063 
1064   // Reset language list
1065   if ($reset) {
1066     $languages = NULL;
1067   }
1068 
1069   // Init language list
1070   if (!isset($languages)) {
1071     if (variable_get('language_count', 1) > 1 || module_exists('locale')) {
1072       $result = db_query('SELECT * FROM {languages} ORDER BY weight ASC, name ASC');
1073       while ($row = db_fetch_object($result)) {
1074         $languages['language'][$row->language] = $row;
1075       }
1076     }
1077     else {
1078       // No locale module, so use the default language only.
1079       $default = language_default();
1080       $languages['language'][$default->language] = $default;
1081     }
1082   }
1083 
1084   // Return the array indexed by the right field
1085   if (!isset($languages[$field])) {
1086     $languages[$field] = array();
1087     foreach ($languages['language'] as $lang) {
1088       // Some values should be collected into an array
1089       if (in_array($field, array('enabled', 'weight'))) {
1090         $languages[$field][$lang->$field][$lang->language] = $lang;
1091       }
1092       else {
1093         $languages[$field][$lang->$field] = $lang;
1094       }
1095     }
1096   }
1097   return $languages[$field];
1098 }
1099 
1100 /**
1101  * Default language used on the site
1102  *
1103  * @param $property
1104  *   Optional property of the language object to return
1105  */
1106 function language_default($property = NULL) {
1107   $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' => ''));
1108   return $property ? $language->$property : $language;
1109 }
1110 
1111 /**
1112  * If Drupal is behind a reverse proxy, we use the X-Forwarded-For header
1113  * instead of $_SERVER['REMOTE_ADDR'], which would be the IP address
1114  * of the proxy server, and not the client's.
1115  *
1116  * @return
1117  *   IP address of client machine, adjusted for reverse proxy.
1118  */
1119 function ip_address() {
11201  static $ip_address = NULL;
1121 
11221  if (!isset($ip_address)) {
1123     $ip_address = $_SERVER['REMOTE_ADDR'];
1124     if (variable_get('reverse_proxy', 0) && array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER)) {
1125       // If an array of known reverse proxy IPs is provided, then trust
1126       // the XFF header if request really comes from one of them.
1127       $reverse_proxy_addresses = variable_get('reverse_proxy_addresses', array());
1128       if (!empty($reverse_proxy_addresses) && in_array($ip_address, $reverse_proxy_addresses, TRUE)) {
1129         // If there are several arguments, we need to check the most
1130         // recently added one, i.e. the last one.
1131         $ip_address = array_pop(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']));
1132       }
1133     }
1134   }
1135 
11361  return $ip_address;
1137 }