bootstrap.inc 28.7 KB
Newer Older
1
<?php
Dries's avatar
   
Dries committed
2
// $Id$
Dries's avatar
 
Dries committed
3

Dries's avatar
   
Dries committed
4
5
6
7
/**
 * @file
 * Functions that need to be loaded on every Drupal request.
 */
Dries's avatar
   
Dries committed
8

9
10
11
12
/**
 * Indicates that the item should never be removed unless explicitly told to
 * using cache_clear_all() with a cache ID.
 */
13
define('CACHE_PERMANENT', 0);
14
15
16
17

/**
 * Indicates that the item should be removed at the next general cache wipe.
 */
18
define('CACHE_TEMPORARY', -1);
Dries's avatar
 
Dries committed
19

20
21
22
/**
 * Indicates that page caching is disabled.
 */
23
define('CACHE_DISABLED', 0);
24
25
26
27

/**
 * Indicates that page caching is enabled, using "normal" mode.
 */
28
define('CACHE_NORMAL', 1);
29
30
31
32
33
34

/**
 * Indicates that page caching is using "aggressive" mode. This bypasses
 * loading any modules for additional speed, which may break functionality in
 * modules that expect to be run on each page load.
 */
35
define('CACHE_AGGRESSIVE', 2);
36

37
38
39
40
/**
 * Indicates a notice-level watchdog event; these are normally notifications
 * of normal system events that have occurred and can usually be safely ignored.
 */
41
define('WATCHDOG_NOTICE', 0);
42
43
44
45
46

/**
 * Indicates a warning-level watchdog event; this can be triggered by an error
 * in a module that does not impact the overall functionality of the site.
 */
47
define('WATCHDOG_WARNING', 1);
48
49
50
51
52

/**
 * Indicates an error-level watchdog event; could be indicative of an attempt
 * to compromise the security of the site, or a serious system error.
 */
53
54
define('WATCHDOG_ERROR', 2);

55
56
57
/**
 * First bootstrap phase: initialize configuration.
 */
58
define('DRUPAL_BOOTSTRAP_CONFIGURATION', 0);
59
60
61
62
63

/**
 * Second bootstrap phase: try to call a non-database cache
 * fetch routine.
 */
64
define('DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE', 1);
65
66
67
68

/**
 * Third bootstrap phase: initialize database layer.
 */
69
define('DRUPAL_BOOTSTRAP_DATABASE', 2);
70
71
72
73

/**
 * Fourth bootstrap phase: identify and reject banned hosts.
 */
74
define('DRUPAL_BOOTSTRAP_ACCESS', 3);
75
76
77
78

/**
 * Fifth bootstrap phase: initialize session handling.
 */
79
define('DRUPAL_BOOTSTRAP_SESSION', 4);
80
81
82
83
84

/**
 * Sixth bootstrap phase: load bootstrap.inc and module.inc, start
 * the variable system and try to serve a page from the cache.
 */
85
define('DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE', 5);
86
87

/**
88
 * Seventh bootstrap phase: find out language of the page.
89
 */
90
91
92
93
94
95
define('DRUPAL_BOOTSTRAP_LANGUAGE', 6);

/**
 * Eighth bootstrap phase: set $_GET['q'] to Drupal path of request.
 */
define('DRUPAL_BOOTSTRAP_PATH', 7);
96
97
98
99
100

/**
 * Final bootstrap phase: Drupal is fully loaded; validate and fix
 * input data.
 */
101
define('DRUPAL_BOOTSTRAP_FULL', 8);
102

103
104
105
/**
 * Role ID for anonymous users; should match what's in the "role" table.
 */
106
define('DRUPAL_ANONYMOUS_RID', 1);
107
108
109
110

/**
 * Role ID for authenticated users; should match what's in the "role" table.
 */
111
112
define('DRUPAL_AUTHENTICATED_RID', 2);

113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
 * No language negotiation. The default language is used.
 */
define('LANGUAGE_NEGOTIATION_NONE', 0);

/**
 * Path based negotiation with fallback to default language
 * if no defined path prefix identified.
 */
define('LANGUAGE_NEGOTIATION_PATH_DEFAULT', 1);

/**
 * Path based negotiation with fallback to user preferences
 * and browser language detection if no defined path prefix
 * identified.
 */
define('LANGUAGE_NEGOTIATION_PATH', 2);

/**
 * Domain based negotiation with fallback to default language
 * if no language identified by domain.
 */
define('LANGUAGE_NEGOTIATION_DOMAIN', 3);

Dries's avatar
   
Dries committed
137
/**
138
 * Start the timer with the specified name. If you start and stop
Dries's avatar
   
Dries committed
139
140
141
142
143
144
145
146
147
148
149
 * the same timer multiple times, the measured intervals will be
 * accumulated.
 *
 * @param name
 *   The name of the timer.
 */
function timer_start($name) {
  global $timers;

  list($usec, $sec) = explode(' ', microtime());
  $timers[$name]['start'] = (float)$usec + (float)$sec;
150
  $timers[$name]['count'] = isset($timers[$name]['count']) ? ++$timers[$name]['count'] : 1;
Dries's avatar
   
Dries committed
151
152
153
154
155
156
157
158
159
160
161
162
163
}

/**
 * Read the current timer value without stopping the timer.
 *
 * @param name
 *   The name of the timer.
 * @return
 *   The current timer value in ms.
 */
function timer_read($name) {
  global $timers;

164
165
166
167
  if (isset($timers[$name]['start'])) {
    list($usec, $sec) = explode(' ', microtime());
    $stop = (float)$usec + (float)$sec;
    $diff = round(($stop - $timers[$name]['start']) * 1000, 2);
Dries's avatar
   
Dries committed
168

169
170
171
172
    if (isset($timers[$name]['time'])) {
      $diff += $timers[$name]['time'];
    }
    return $diff;
173
  }
Dries's avatar
   
Dries committed
174
175
176
177
178
179
180
181
}

/**
 * Stop the timer with the specified name.
 *
 * @param name
 *   The name of the timer.
 * @return
182
 *   A timer array. The array contains the number of times the
Dries's avatar
   
Dries committed
183
184
185
186
187
188
 *   timer has been started and stopped (count) and the accumulated
 *   timer value in ms (time).
 */
function timer_stop($name) {
  global $timers;

189
  $timers[$name]['time'] = timer_read($name);
Dries's avatar
   
Dries committed
190
191
192
193
  unset($timers[$name]['start']);

  return $timers[$name];
}
194

Dries's avatar
   
Dries committed
195
/**
196
 * Find the appropriate configuration directory.
Dries's avatar
   
Dries committed
197
 *
198
199
 * Try finding a matching configuration directory by stripping the website's
 * hostname from left to right and pathname from right to left. The first
200
 * configuration file found will be used; the remaining will ignored. If no
201
 * configuration file is found, return a default value '$confdir/default'.
Dries's avatar
Dries committed
202
 *
203
 * Example for a fictitious site installed at
204
205
 * http://www.drupal.org:8080/mysite/test/ the 'settings.php' is searched in
 * the following directories:
Dries's avatar
   
Dries committed
206
 *
207
208
209
210
 *  1. $confdir/8080.www.drupal.org.mysite.test
 *  2. $confdir/www.drupal.org.mysite.test
 *  3. $confdir/drupal.org.mysite.test
 *  4. $confdir/org.mysite.test
Dries's avatar
   
Dries committed
211
 *
212
213
214
215
 *  5. $confdir/8080.www.drupal.org.mysite
 *  6. $confdir/www.drupal.org.mysite
 *  7. $confdir/drupal.org.mysite
 *  8. $confdir/org.mysite
Dries's avatar
   
Dries committed
216
 *
217
218
219
220
 *  9. $confdir/8080.www.drupal.org
 * 10. $confdir/www.drupal.org
 * 11. $confdir/drupal.org
 * 12. $confdir/org
Dries's avatar
   
Dries committed
221
 *
222
 * 13. $confdir/default
Dries's avatar
   
Dries committed
223
 */
224
function conf_path() {
Dries's avatar
Dries committed
225
  static $conf = '';
Dries's avatar
 
Dries committed
226

Dries's avatar
Dries committed
227
228
229
  if ($conf) {
    return $conf;
  }
Dries's avatar
 
Dries committed
230

Dries's avatar
   
Dries committed
231
  $confdir = 'sites';
232
  $uri = explode('/', $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_FILENAME']);
233
  $server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
Dries's avatar
Dries committed
234
235
236
237
238
239
240
  for ($i = count($uri) - 1; $i > 0; $i--) {
    for ($j = count($server); $j > 0; $j--) {
      $dir = implode('.', array_slice($server, -$j)) . implode('.', array_slice($uri, 0, $i));
      if (file_exists("$confdir/$dir/settings.php")) {
        $conf = "$confdir/$dir";
        return $conf;
      }
Dries's avatar
 
Dries committed
241
242
    }
  }
Dries's avatar
Dries committed
243
244
  $conf = "$confdir/default";
  return $conf;
Dries's avatar
 
Dries committed
245
246
}

247
248
249
250
251
/**
 * Unsets all disallowed global variables. See $allowed for what's allowed.
 */
function drupal_unset_globals() {
  if (ini_get('register_globals')) {
252
    $allowed = array('_ENV' => 1, '_GET' => 1, '_POST' => 1, '_COOKIE' => 1, '_FILES' => 1, '_SERVER' => 1, '_REQUEST' => 1, 'access_check' => 1, 'GLOBALS' => 1);
253
    foreach ($GLOBALS as $key => $value) {
254
255
256
257
258
259
260
      if (!isset($allowed[$key])) {
        unset($GLOBALS[$key]);
      }
    }
  }
}

261
262
263
264
/**
 * Loads the configuration and sets the base URL correctly.
 */
function conf_init() {
265
  global $db_url, $db_prefix, $base_url, $base_path, $base_root, $conf, $installed_profile;
266
  $conf = array();
267
  include_once './'. conf_path() .'/settings.php';
268
269
270
271

  if (isset($base_url)) {
    // Parse fixed base URL from settings.php.
    $parts = parse_url($base_url);
272
273
274
275
    if (!isset($parts['path'])) {
      $parts['path'] = '';
    }
    $base_path = $parts['path'] . '/';
276
277
278
279
280
281
282
    // Build $base_root (everything until first slash after "scheme://").
    $base_root = substr($base_url, 0, strlen($base_url) - strlen($parts['path']));
  }
  else {
    // Create base URL
    $base_root = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
    $base_url = $base_root .= '://'. $_SERVER['HTTP_HOST'];
283
    if ($dir = trim(dirname($_SERVER['PHP_SELF']), '\,/')) {
284
285
286
287
288
289
290
291
292
293
      $base_path = "/$dir";
      $base_url .= $base_path;
      $base_path .= '/';
    }
    else {
      $base_path = '/';
    }
  }
}

Dries's avatar
Dries committed
294
295
/**
 * Returns and optionally sets the filename for a system item (module,
296
 * theme, etc.). The filename, whether provided, cached, or retrieved
Dries's avatar
Dries committed
297
298
 * from the database, is only returned if the file exists.
 *
Dries's avatar
Dries committed
299
300
301
302
303
304
305
306
307
308
309
310
 * This function plays a key role in allowing Drupal's resources (modules
 * and themes) to be located in different places depending on a site's
 * configuration. For example, a module 'foo' may legally be be located
 * in any of these three places:
 *
 * modules/foo/foo.module
 * sites/all/modules/foo/foo.module
 * sites/example.com/modules/foo/foo.module
 *
 * Calling drupal_get_filename('module', 'foo') will give you one of
 * the above, depending on where the module is located.
 *
Dries's avatar
Dries committed
311
312
313
314
315
316
317
318
319
320
321
 * @param $type
 *   The type of the item (i.e. theme, theme_engine, module).
 * @param $name
 *   The name of the item for which the filename is requested.
 * @param $filename
 *   The filename of the item if it is to be set explicitly rather
 *   than by consulting the database.
 *
 * @return
 *   The filename of the requested item.
 */
Dries's avatar
Dries committed
322
function drupal_get_filename($type, $name, $filename = NULL) {
Dries's avatar
Dries committed
323
  static $files = array();
Dries's avatar
Dries committed
324
  global $active_db;
Dries's avatar
Dries committed
325

326
  if (!isset($files[$type])) {
Dries's avatar
Dries committed
327
328
329
    $files[$type] = array();
  }

330
  if (!empty($filename) && file_exists($filename)) {
Dries's avatar
Dries committed
331
332
    $files[$type][$name] = $filename;
  }
333
  elseif (isset($files[$type][$name])) {
Dries's avatar
Dries committed
334
335
    // nothing
  }
Dries's avatar
Dries committed
336
337
338
339
340
  // Verify that we have an active database connection, before querying
  // the database.  This is required because this function is called both
  // before we have a database connection (i.e. during installation) and
  // when a database connection fails.
  elseif ($active_db && (($file = db_result(db_query("SELECT filename FROM {system} WHERE name = '%s' AND type = '%s'", $name, $type))) && file_exists($file))) {
Dries's avatar
Dries committed
341
342
343
    $files[$type][$name] = $file;
  }
  else {
Dries's avatar
Dries committed
344
345
    // Fallback to searching the filesystem if the database connection is
    // not established or the requested file is not found.
Steven Wittens's avatar
Steven Wittens committed
346
    $config = conf_path();
Dries's avatar
Dries committed
347
    $dir = (($type == 'theme_engine') ? 'themes/engines' : "${type}s");
Dries's avatar
   
Dries committed
348
    $file = (($type == 'theme_engine') ? "$name.engine" : "$name.$type");
Dries's avatar
Dries committed
349
350
351
352
353
354
355
356
357
358
359
360

    foreach (array("$config/$dir/$file", "$config/$dir/$name/$file", "$dir/$file", "$dir/$name/$file") as $file) {
      if (file_exists($file)) {
        $files[$type][$name] = $file;
        break;
      }
    }
  }

  return $files[$type][$name];
}

Dries's avatar
   
Dries committed
361
362
363
364
365
366
367
/**
 * Load the persistent variable table.
 *
 * The variable table is composed of values that have been saved in the table
 * with variable_set() as well as those explicitly specified in the configuration
 * file.
 */
Dries's avatar
 
Dries committed
368
function variable_init($conf = array()) {
369
  // NOTE: caching the variables improves performance by 20% when serving cached pages.
370
  if ($cached = cache_get('variables', 'cache')) {
Dries's avatar
   
Dries committed
371
372
373
374
375
376
    $variables = unserialize($cached->data);
  }
  else {
    $result = db_query('SELECT * FROM {variable}');
    while ($variable = db_fetch_object($result)) {
      $variables[$variable->name] = unserialize($variable->value);
Dries's avatar
 
Dries committed
377
    }
378
    cache_set('variables', 'cache', serialize($variables));
Dries's avatar
   
Dries committed
379
380
381
382
  }

  foreach ($conf as $name => $value) {
    $variables[$name] = $value;
Dries's avatar
 
Dries committed
383
384
  }

Dries's avatar
   
Dries committed
385
  return $variables;
Dries's avatar
 
Dries committed
386
387
}

Dries's avatar
   
Dries committed
388
389
390
391
392
393
394
395
396
397
/**
 * Return a persistent variable.
 *
 * @param $name
 *   The name of the variable to return.
 * @param $default
 *   The default value to use if this variable has never been set.
 * @return
 *   The value of the variable.
 */
Dries's avatar
 
Dries committed
398
399
400
401
402
403
function variable_get($name, $default) {
  global $conf;

  return isset($conf[$name]) ? $conf[$name] : $default;
}

Dries's avatar
   
Dries committed
404
405
406
407
408
409
410
411
412
/**
 * Set a persistent variable.
 *
 * @param $name
 *   The name of the variable to set.
 * @param $value
 *   The value to set. This can be any PHP data type; these functions take care
 *   of serialization as necessary.
 */
Dries's avatar
 
Dries committed
413
414
415
function variable_set($name, $value) {
  global $conf;

416
  db_lock_table('variable');
Dries's avatar
 
Dries committed
417
418
  db_query("DELETE FROM {variable} WHERE name = '%s'", $name);
  db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, serialize($value));
419
  db_unlock_tables();
Dries's avatar
   
Dries committed
420

421
  cache_clear_all('variables', 'cache');
Dries's avatar
 
Dries committed
422
423
424
425

  $conf[$name] = $value;
}

Dries's avatar
   
Dries committed
426
427
428
429
430
431
/**
 * Unset a persistent variable.
 *
 * @param $name
 *   The name of the variable to undefine.
 */
Dries's avatar
 
Dries committed
432
433
434
435
function variable_del($name) {
  global $conf;

  db_query("DELETE FROM {variable} WHERE name = '%s'", $name);
436
  cache_clear_all('variables', 'cache');
Dries's avatar
 
Dries committed
437
438
439
440

  unset($conf[$name]);
}

441

Dries's avatar
   
Dries committed
442
443
444
/**
 * Retrieve the current page from the cache.
 *
445
 * Note: we do not serve cached pages when status messages are waiting (from
Dries's avatar
   
Dries committed
446
447
 * a redirected form submission which was completed).
 */
Dries's avatar
 
Dries committed
448
function page_get_cache() {
449
  global $user, $base_root;
Dries's avatar
 
Dries committed
450
451

  $cache = NULL;
452

453
  if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && count(drupal_set_message()) == 0) {
454
    $cache = cache_get($base_root . request_uri(), 'cache_page');
Dries's avatar
 
Dries committed
455
456
457
458
459
460
461
462
463

    if (empty($cache)) {
      ob_start();
    }
  }

  return $cache;
}

Dries's avatar
Dries committed
464
465
466
/**
 * Call all init or exit hooks without including all modules.
 *
467
 * @param $hook
Dries's avatar
Dries committed
468
469
 *   The name of the bootstrap hook we wish to invoke.
 */
470
function bootstrap_invoke_all($hook) {
471
  foreach (module_list(TRUE, TRUE) as $module) {
Dries's avatar
Dries committed
472
    drupal_load('module', $module);
473
    module_invoke($module, $hook);
Dries's avatar
Dries committed
474
475
476
477
 }
}

/**
478
 * Includes a file with the provided type and name. This prevents
Dries's avatar
Dries committed
479
480
481
482
483
484
485
486
487
488
489
490
491
 * including a theme, engine, module, etc., more than once.
 *
 * @param $type
 *   The type of item to load (i.e. theme, theme_engine, module).
 * @param $name
 *   The name of the item to load.
 *
 * @return
 *   TRUE if the item is loaded or has already been loaded.
 */
function drupal_load($type, $name) {
  static $files = array();

492
  if (isset($files[$type][$name])) {
Dries's avatar
Dries committed
493
494
495
496
497
498
    return TRUE;
  }

  $filename = drupal_get_filename($type, $name);

  if ($filename) {
499
    include_once "./$filename";
Dries's avatar
Dries committed
500
501
502
503
504
505
506
507
    $files[$type][$name] = TRUE;

    return TRUE;
  }

  return FALSE;
}

Dries's avatar
   
Dries committed
508
509
/**
 * Set HTTP headers in preparation for a page response.
510
 *
511
512
513
514
 * Authenticated users are always given a 'no-cache' header, and will
 * fetch a fresh page on every request.  This prevents authenticated
 * users seeing locally cached pages that show them as logged out.
 *
515
 * @see page_set_cache
Dries's avatar
   
Dries committed
516
 */
Dries's avatar
 
Dries committed
517
function drupal_page_header() {
518
519
  header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT");
520
  header("Cache-Control: store, no-cache, must-revalidate");
521
522
  header("Cache-Control: post-check=0, pre-check=0", FALSE);
}
Dries's avatar
   
Dries committed
523

524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
/**
 * Set HTTP headers in preparation for a cached page response.
 *
 * The general approach here is that anonymous users can keep a local
 * cache of the page, but must revalidate it on every request.  Then,
 * they are given a '304 Not Modified' response as long as they stay
 * logged out and the page has not been modified.
 *
 */
function drupal_page_cache_header($cache) {
  // Set default values:
  $last_modified = gmdate('D, d M Y H:i:s', $cache->created) .' GMT';
  $etag = '"'.md5($last_modified).'"';

  // See if the client has provided the required HTTP headers:
  $if_modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? stripslashes($_SERVER['HTTP_IF_MODIFIED_SINCE']) : FALSE;
  $if_none_match = isset($_SERVER['HTTP_IF_NONE_MATCH']) ? stripslashes($_SERVER['HTTP_IF_NONE_MATCH']) : FALSE;

  if ($if_modified_since && $if_none_match
      && $if_none_match == $etag // etag must match
      && $if_modified_since == $last_modified) {  // if-modified-since must match
    header('HTTP/1.1 304 Not Modified');
    // All 304 responses must send an etag if the 200 response for the same object contained an etag
    header("Etag: $etag");
    exit();
  }
550

551
552
553
  // Send appropriate response:
  header("Last-Modified: $last_modified");
  header("ETag: $etag");
554

555
556
557
  // The following headers force validation of cache:
  header("Expires: Sun, 19 Nov 1978 05:00:00 GMT");
  header("Cache-Control: must-revalidate");
558

559
560
561
562
  // Determine if the browser accepts gzipped data.
  if (@strpos($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip') === FALSE && function_exists('gzencode')) {
    // Strip the gzip header and run uncompress.
    $cache->data = gzinflate(substr(substr($cache->data, 10), 0, -8));
563
  }
564
565
  elseif (function_exists('gzencode')) {
    header('Content-Encoding: gzip');
Dries's avatar
 
Dries committed
566
  }
567
568
569
570
571
572
573
574
575
576

  // Send the original request's headers. We send them one after
  // another so PHP's header() function can deal with duplicate
  // headers.
  $headers = explode("\n", $cache->headers);
  foreach ($headers as $header) {
    header($header);
  }

  print $cache->data;
Dries's avatar
 
Dries committed
577
578
}

Dries's avatar
   
Dries committed
579
580
581
/**
 * Define the critical hooks that force modules to always be loaded.
 */
Dries's avatar
 
Dries committed
582
function bootstrap_hooks() {
583
  return array('boot', 'exit');
Dries's avatar
 
Dries committed
584
585
}

Dries's avatar
   
Dries committed
586
587
588
589
590
591
592
593
/**
 * Unserializes and appends elements from a serialized string.
 *
 * @param $obj
 *   The object to which the elements are appended.
 * @param $field
 *   The attribute of $obj whose value should be unserialized.
 */
Dries's avatar
   
Dries committed
594
595
596
597
598
599
600
601
602
603
604
function drupal_unpack($obj, $field = 'data') {
  if ($obj->$field && $data = unserialize($obj->$field)) {
    foreach ($data as $key => $value) {
      if (!isset($obj->$key)) {
        $obj->$key = $value;
      }
    }
  }
  return $obj;
}

Dries's avatar
   
Dries committed
605
606
607
/**
 * Return the URI of the referring page.
 */
Dries's avatar
 
Dries committed
608
function referer_uri() {
609
  if (isset($_SERVER['HTTP_REFERER'])) {
610
    return $_SERVER['HTTP_REFERER'];
Dries's avatar
 
Dries committed
611
612
613
  }
}

Dries's avatar
Dries committed
614
615
616
617
618
619
620
/**
 * Encode special characters in a plain-text string for display as HTML.
 */
function check_plain($text) {
  return htmlspecialchars($text, ENT_QUOTES);
}

Dries's avatar
   
Dries committed
621
/**
622
623
 * Since $_SERVER['REQUEST_URI'] is only available on Apache, we
 * generate an equivalent using other environment variables.
Dries's avatar
   
Dries committed
624
 */
Dries's avatar
 
Dries committed
625
function request_uri() {
626
627
628
629
630
631
632

  if (isset($_SERVER['REQUEST_URI'])) {
    $uri = $_SERVER['REQUEST_URI'];
  }
  else {
    if (isset($_SERVER['argv'])) {
      $uri = $_SERVER['PHP_SELF'] .'?'. $_SERVER['argv'][0];
633
634
    }
    else {
635
      $uri = $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING'];
636
    }
Dries's avatar
 
Dries committed
637
  }
638

639
  return $uri;
Dries's avatar
 
Dries committed
640
}
Dries's avatar
Dries committed
641

Dries's avatar
   
Dries committed
642
643
644
645
646
647
648
/**
 * Log a system message.
 *
 * @param $type
 *   The category to which this message belongs.
 * @param $message
 *   The message to store in the log.
649
650
651
652
653
 * @param $severity
 *   The severity of the message. One of the following values:
 *   - WATCHDOG_NOTICE
 *   - WATCHDOG_WARNING
 *   - WATCHDOG_ERROR
Dries's avatar
   
Dries committed
654
655
656
 * @param $link
 *   A link to associate with the message.
 */
657
function watchdog($type, $message, $severity = WATCHDOG_NOTICE, $link = NULL) {
658
  global $user, $base_root;
659
660
661

  $current_db = db_set_active();

662
663
664
665
  // Note: log the exact, entire absolute URL.
  $request_uri = $base_root . request_uri();

  db_query("INSERT INTO {watchdog} (uid, type, message, severity, link, location, referer, hostname, timestamp) VALUES (%d, '%s', '%s', %d, '%s', '%s', '%s', '%s', %d)", $user->uid, $type, $message, $severity, $link, $request_uri, referer_uri(), $_SERVER['REMOTE_ADDR'], time());
666
667
668
669

  if ($current_db) {
    db_set_active($current_db);
  }
Dries's avatar
   
Dries committed
670
671
}

Dries's avatar
   
Dries committed
672
/**
673
 * Set a message which reflects the status of the performed operation.
Dries's avatar
   
Dries committed
674
 *
675
676
 * If the function is called with no arguments, this function returns all set
 * messages without clearing them.
Dries's avatar
   
Dries committed
677
 *
678
679
680
681
682
683
684
 * @param $message
 *   The message should begin with a capital letter and always ends with a
 *   period '.'.
 * @param $type
 *   The type of the message. One of the following values are possible:
 *   - 'status'
 *   - 'error'
Dries's avatar
   
Dries committed
685
686
 */
function drupal_set_message($message = NULL, $type = 'status') {
687
  if ($message) {
Dries's avatar
   
Dries committed
688
689
690
691
692
693
694
695
696
    if (!isset($_SESSION['messages'])) {
      $_SESSION['messages'] = array();
    }

    if (!isset($_SESSION['messages'][$type])) {
      $_SESSION['messages'][$type] = array();
    }

    $_SESSION['messages'][$type][] = $message;
697
698
  }

699
700
  // messages not set when DB connection fails
  return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL;
701
702
}

Dries's avatar
   
Dries committed
703
704
705
/**
 * Return all messages that have been set.
 *
706
707
 * @param $type
 *   (optional) Only return messages of this type.
708
709
 * @param $clear_queue
 *   (optional) Set to FALSE if you do not want to clear the messages queue
Dries's avatar
   
Dries committed
710
 */
711
function drupal_get_messages($type = NULL, $clear_queue = TRUE) {
712
  if ($messages = drupal_set_message()) {
713
    if ($type) {
714
715
716
      if ($clear_queue) {
         unset($_SESSION['messages'][$type]);
      }
717
718
719
      return array($type => $messages[$type]);
    }
    else {
720
721
722
      if ($clear_queue) {
         unset($_SESSION['messages']);
      }
723
724
      return $messages;
    }
725
  }
726
  return array();
727
728
}

Dries's avatar
   
Dries committed
729
/**
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
 * Perform an access check for a given mask and rule type. Rules are usually
 * created via admin/user/rules page.
 *
 * If any allow rule matches, access is allowed. Otherwise, if any deny rule
 * matches, access is denied.  If no rule matches, access is allowed.
 *
 * @param $type string
 *   Type of access to check: Allowed values are:
 *     - 'host': host name or IP address
 *     - 'mail': e-mail address
 *     - 'user': username
 * @param $mask string
 *   String or mask to test: '_' matches any character, '%' matches any
 *   number of characters.
 * @return bool
 *   TRUE if access is denied, FALSE if access is allowed.
Dries's avatar
   
Dries committed
746
 */
747
function drupal_is_denied($type, $mask) {
748
749
750
751
752
753
754
755
756
757
758
759
  // Because this function is called for every page request, both cached
  // and non-cached pages, we tried to optimize it as much as possible.
  // We deny access if the only matching records in the {access} table have
  // status 0. If any have status 1, or if there are no matching records,
  // we allow access. So, select matching records in decreasing order of
  // 'status', returning NOT(status) for the first. If any have status 1,
  // they come first, and we return NOT(status) = 0 (allowed). Otherwise,
  // if we have some with status 0, we return 1 (denied). If no matching
  // records, we get no return from db_result, so we return (bool)NULL = 0
  // (allowed).
  // The use of ORDER BY / LIMIT is more efficient than "MAX(status) = 0"
  // in PostgreSQL <= 8.0.
760
  return (bool) db_result(db_query_range("SELECT CASE WHEN status=1 THEN 0 ELSE 1 END FROM {access} WHERE type = '%s' AND LOWER('%s') LIKE LOWER(mask) ORDER BY status DESC", $type, $mask, 0, 1));
Dries's avatar
   
Dries committed
761
762
}

763
/**
764
 * Generates a default anonymous $user object.
765
766
767
 *
 * @return Object - the user object.
 */
768
function drupal_anonymous_user($session = '') {
769
770
771
772
773
  $user = new stdClass();
  $user->uid = 0;
  $user->hostname = $_SERVER['REMOTE_ADDR'];
  $user->roles = array();
  $user->roles[DRUPAL_ANONYMOUS_RID] = 'anonymous user';
774
  $user->session = $session;
775
  $user->cache = 0;
776
777
778
  return $user;
}

779
780
781
/**
 * A string describing a phase of Drupal to load. Each phase adds to the
 * previous one, so invoking a later phase automatically runs the earlier
782
 * phases too. The most important usage is that if you want to access the
783
 * Drupal database from a script without loading anything else, you can
784
 * include bootstrap.inc, and call drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE).
785
786
 *
 * @param $phase
787
 *   A constant. Allowed values are:
788
789
 *     DRUPAL_BOOTSTRAP_CONFIGURATION: initialize configuration.
 *     DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE: try to call a non-database cache fetch routine.
790
 *     DRUPAL_BOOTSTRAP_DATABASE: initialize database layer.
791
 *     DRUPAL_BOOTSTRAP_ACCESS: identify and reject banned hosts.
792
 *     DRUPAL_BOOTSTRAP_SESSION: initialize session handling.
793
 *     DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE: load bootstrap.inc and module.inc, start
794
 *       the variable system and try to serve a page from the cache.
795
 *     DRUPAL_BOOTSTRAP_LANGUAGE: identify the language used on the page.
796
 *     DRUPAL_BOOTSTRAP_PATH: set $_GET['q'] to Drupal path of request.
797
 *     DRUPAL_BOOTSTRAP_FULL: Drupal is fully loaded, validate and fix input data.
798
799
 */
function drupal_bootstrap($phase) {
800
  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);
801

802
  while (!is_null($current_phase = array_shift($phases))) {
803
804
805
806
807
808
    _drupal_bootstrap($current_phase);
    if ($phase == $current_phase) {
      return;
    }
  }
}
Dries's avatar
   
Dries committed
809

810
811
function _drupal_bootstrap($phase) {
  global $conf;
Dries's avatar
 
Dries committed
812

813
  switch ($phase) {
814

815
    case DRUPAL_BOOTSTRAP_CONFIGURATION:
816
      drupal_unset_globals();
817
818
      // Initialize the configuration
      conf_init();
819
      break;
820

821
    case DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE:
822
      _drupal_cache_init($phase);
823
      break;
824

825
    case DRUPAL_BOOTSTRAP_DATABASE:
826
      // Initialize the default database.
827
      require_once './includes/database.inc';
828
829
      db_set_active();
      break;
830

831
832
833
    case DRUPAL_BOOTSTRAP_ACCESS:
      // Deny access to hosts which were banned - t() is not yet available.
      if (drupal_is_denied('host', $_SERVER['REMOTE_ADDR'])) {
834
        header('HTTP/1.1 403 Forbidden');
835
836
837
838
839
        print 'Sorry, '. $_SERVER['REMOTE_ADDR']. ' has been banned.';
        exit();
      }
      break;

840
    case DRUPAL_BOOTSTRAP_SESSION:
841
      require_once variable_get('session_inc', './includes/session.inc');
842
      session_set_save_handler('sess_open', 'sess_close', 'sess_read', 'sess_write', 'sess_destroy_sid', 'sess_gc');
843
844
      session_start();
      break;
845

846
    case DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE:
847
848
849
850
      // Initialize configuration variables, using values from settings.php if available.
      $conf = variable_init(isset($conf) ? $conf : array());

      _drupal_cache_init($phase);
851

852
853
854
      // Start a page timer:
      timer_start('page');

855
856
      drupal_page_header();
      break;
857

858
859
860
861
    case DRUPAL_BOOTSTRAP_LANGUAGE:
      drupal_init_language();
      break;

862
863
864
865
866
867
    case DRUPAL_BOOTSTRAP_PATH:
      require_once './includes/path.inc';
      // Initialize $_GET['q'] prior to loading modules and invoking hook_init().
      drupal_init_path();
      break;

868
    case DRUPAL_BOOTSTRAP_FULL:
869
870
871
872
      require_once './includes/common.inc';
      _drupal_bootstrap_full();
      break;
  }
Dries's avatar
   
Dries committed
873
874
}

875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
/**
 * Initialize the caching strategy, which loads at different stages within
 * Drupal's bootstrap process.
 */
function _drupal_cache_init($phase) {
  require_once variable_get('cache_inc', './includes/cache.inc');

  if ($phase == DRUPAL_BOOTSTRAP_EARLY_PAGE_CACHE && variable_get('page_cache_fastpath', 0)) {
    if (page_cache_fastpath()) {
      exit();
    }
  }
  elseif ($phase == DRUPAL_BOOTSTRAP_LATE_PAGE_CACHE) {
    if ($cache = page_get_cache()) {
      if (variable_get('cache', CACHE_DISABLED) == CACHE_AGGRESSIVE) {
        drupal_page_cache_header($cache);
        exit();
      }
      elseif (variable_get('cache', CACHE_DISABLED) == CACHE_NORMAL) {
        require_once './includes/module.inc';
895
        bootstrap_invoke_all('boot');
896
897
898
899
900
901
902
903
904
        drupal_page_cache_header($cache);
        bootstrap_invoke_all('exit');
        exit();
      }
    }
    require_once './includes/module.inc';
  }
}

905
906
907
/**
 * Enables use of the theme system without requiring database access. Since
 * there is not database access no theme will be enabled and the default
908
 * themeable functions will be called. Some themeable functions can not be used
909
910
911
912
913
 * without the full Drupal API loaded. For example, theme_page() is
 * unavailable and theme_maintenance_page() must be used in its place.
 */
function drupal_maintenance_theme() {
  global $theme;
914
  require_once './includes/path.inc';
915
916
917
  require_once './includes/theme.inc';
  require_once './includes/common.inc';
  require_once './includes/unicode.inc';
918
  require_once './modules/filter/filter.module';
919
  unicode_check();
920
921
  drupal_add_css(drupal_get_path('module', 'system') .'/defaults.css', 'module');
  drupal_add_css(drupal_get_path('module', 'system') .'/system.css', 'module');
922
923
  $theme = '';
}
924
925
926
927
928
929
930
931
932
933
934
935

/**
 * Return the name of the localisation function. Use in code that needs to
 * run both during installation and normal operation.
 */
function get_t() {
  static $t;
  if (is_null($t)) {
    $t = function_exists('install_main') ? 'st' : 't';
  }
  return $t;
}
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997

/**
 *  Choose a language for the current page, based on site and user preferences.
 */
function drupal_init_language() {
  global $language, $user;

  // Ensure the language is correctly returned, even without multilanguage support.
  // Useful for eg. XML/HTML 'lang' attributes.
  if (variable_get('language_count', 1) == 1) {
    $language = language_default();
  }
  else {
    include_once './includes/language.inc';
    $language = language_initialize();
  }
}

/**
 * Get a list of languages set up indexed by the specified key
 *
 * @param $field The field to index the list with.
 * @param $reset Boolean to request a reset of the list.
 */
function language_list($field = 'language', $reset = FALSE) {
  static $languages = NULL;

  // Reset language list
  if ($reset) {
    $languages = NULL;
  }

  // Init language list
  if (!isset($languages)) {
    $result = db_query('SELECT * FROM {languages} ORDER BY weight ASC, name ASC');
    while ($row = db_fetch_object($result)) {
      $languages['language'][$row->language] = $row;
    }
  }

  // Return the array indexed by the right field
  if (!isset($languages[$field])) {
    $languages[$field] = array();
    foreach($languages['language'] as $lang) {
      // Some values should be collected into an array
      if (in_array($field, array('enabled', 'weight'))) {
        $languages[$field][$lang->$field][$lang->language] = $lang;
      }
      else {
        $languages[$field][$lang->$field] = $lang;
      }
    }
  }
  return $languages[$field];
}

/**
 * Default language used on the site
 */
function language_default() {
  return variable_get('language_default', (object) array('language' => 'en', 'name' => 'English', 'direction' => 0, 'native' => 'English'));
}