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; } } } $conf = "$confdir/default"; return $conf; } /** * Returns and optionally sets the filename for a system item (module, * theme, etc.). The filename, whether provided, cached, or retrieved * from the database, is only returned if the file exists. * * @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. */ function drupal_get_filename($type, $name, $filename = NULL) { static $files = array(); if (!isset($files[$type])) { $files[$type] = array(); } if (!empty($filename) && file_exists($filename)) { $files[$type][$name] = $filename; } elseif (isset($files[$type][$name])) { // nothing } elseif (($file = db_result(db_query("SELECT filename FROM {system} WHERE name = '%s' AND type = '%s'", $name, $type))) && file_exists($file)) { $files[$type][$name] = $file; } else { $config = conf_init(); $dir = (($type == 'theme_engine') ? 'themes/engines' : "${type}s"); $file = (($type == 'theme_engine') ? "$name.engine" : "$name.$type"); 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]; } /** * 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. */ function variable_init($conf = array()) { // NOTE: caching the variables improves performance with 20% when serving cached pages. if ($cached = cache_get('variables')) { $variables = unserialize($cached->data); } else { $result = db_query('SELECT * FROM {variable}'); while ($variable = db_fetch_object($result)) { $variables[$variable->name] = unserialize($variable->value); } cache_set('variables', serialize($variables)); } foreach ($conf as $name => $value) { $variables[$name] = $value; } return $variables; } /** * 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. */ function variable_get($name, $default) { global $conf; return isset($conf[$name]) ? $conf[$name] : $default; } /** * 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. */ function variable_set($name, $value) { global $conf; db_lock_table('variable'); db_query("DELETE FROM {variable} WHERE name = '%s'", $name); db_query("INSERT INTO {variable} (name, value) VALUES ('%s', '%s')", $name, serialize($value)); db_unlock_tables(); cache_clear_all('variables'); $conf[$name] = $value; } /** * Unset a persistent variable. * * @param $name * The name of the variable to undefine. */ function variable_del($name) { global $conf; db_query("DELETE FROM {variable} WHERE name = '%s'", $name); cache_clear_all('variables'); unset($conf[$name]); } /** * Return data from the persistent cache. * * @param $key * The cache ID of the data to retrieve. */ function cache_get($key) { global $user; // Garbage collection necessary when enforcing a minimum cache lifetime $cache_flush = variable_get('cache_flush', 0); if ($cache_flush && ($cache_flush + variable_get('cache_lifetime', 0) <= time())) { // Time to flush old cache data db_query("DELETE FROM {cache} WHERE expire != %d AND expire <= %d", CACHE_PERMANENT, $cache_flush); variable_set('cache_flush', 0); } $cache = db_fetch_object(db_query("SELECT data, created, headers, expire FROM {cache} WHERE cid = '%s'", $key)); if (isset($cache->data)) { // If the data is permanent or we're not enforcing a minimum cache lifetime // always return the cached data. if ($cache->expire == CACHE_PERMANENT || !variable_get('cache_lifetime', 0)) { $cache->data = db_decode_blob($cache->data); } // If enforcing a minimum cache lifetime, validate that the data is // currenly valid for this user before we return it by making sure the // cache entry was created before the timestamp in the current session's // cache timer. The cache variable is loaded into the $user object by // sess_read() in session.inc. else { if ($user->cache > $cache->created) { // This cache data is too old and thus not valid for us, ignore it. return 0; } else { $cache->data = db_decode_blob($cache->data); } } return $cache; } return 0; } /** * Store data in the persistent cache. * * @param $cid * The cache ID of the data to store. * @param $data * The data to store in the cache. Complex data types must be serialized first. * @param $expire * One of the following values: * - CACHE_PERMANENT: Indicates that the item should never be removed unless * explicitly told to using cache_clear_all() with a cache ID. * - CACHE_TEMPORARY: Indicates that the item should be removed at the next * general cache wipe. * - A Unix timestamp: Indicates that the item should be kept at least until * the given time, after which it behaves like CACHE_TEMPORARY. * @param $headers * A string containing HTTP header information for cached pages. */ function cache_set($cid, $data, $expire = CACHE_PERMANENT, $headers = NULL) { db_lock_table('cache'); db_query("UPDATE {cache} SET data = %b, created = %d, expire = %d, headers = '%s' WHERE cid = '%s'", $data, time(), $expire, $headers, $cid); if (!db_affected_rows()) { @db_query("INSERT INTO {cache} (cid, data, created, expire, headers) VALUES ('%s', %b, %d, %d, '%s')", $cid, $data, time(), $expire, $headers); } db_unlock_tables(); } /** * Expire data from the cache. * * @param $cid * If set, the cache ID to delete. Otherwise, all cache entries that can * expire are deleted. * * @param $wildcard * If set to true, the $cid is treated as a substring to match rather than a * complete ID. */ function cache_clear_all($cid = NULL, $wildcard = false) { global $user; if (empty($cid)) { if (variable_get('cache_lifetime', 0)) { // We store the time in the current user's $user->cache variable which // will be saved into the sessions table by sess_write(). We then // simulate that the cache was flushed for this user by not returning // cached data that was cached before the timestamp. $user->cache = time(); $cache_flush = variable_get('cache_flush', 0); if ($cache_flush == 0) { // This is the first request to clear the cache, start a timer. variable_set('cache_flush', time()); } else if (time() > ($cache_flush + variable_get('cache_lifetime', 0))) { // Clear the cache for everyone, cache_flush_delay seconds have // passed since the first request to clear the cache. db_query("DELETE FROM {cache} WHERE expire != %d AND expire < %d", CACHE_PERMANENT, time()); variable_set('cache_flush', 0); } } else { // No minimum cache lifetime, flush all temporary cache entries now. db_query("DELETE FROM {cache} WHERE expire != %d AND expire < %d", CACHE_PERMANENT, time()); } } else { if ($wildcard) { db_query("DELETE FROM {cache} WHERE cid LIKE '%%%s%%'", $cid); } else { db_query("DELETE FROM {cache} WHERE cid = '%s'", $cid); } } } /** * Retrieve the current page from the cache. * * Note, we do not serve cached pages when status messages are waiting (from * a redirected form submission which was completed). * Because the output handler is not activated, the resulting page will not * get cached either. */ function page_get_cache() { global $user, $base_url; $cache = NULL; if (!$user->uid && $_SERVER['REQUEST_METHOD'] == 'GET' && count(drupal_set_message()) == 0) { $cache = cache_get($base_url . request_uri()); if (empty($cache)) { ob_start(); } } return $cache; } /** * Call all init or exit hooks without including all modules. * * @param $hook * The name of the bootstrap hook we wish to invoke. */ function bootstrap_invoke_all($hook) { foreach (module_list(FALSE, TRUE) as $module) { drupal_load('module', $module); module_invoke($module, $hook); } } /** * Includes a file with the provided type and name. This prevents * 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(); if (isset($files[$type][$name])) { return TRUE; } $filename = drupal_get_filename($type, $name); if ($filename) { include_once "./$filename"; $files[$type][$name] = TRUE; return TRUE; } return FALSE; } /** * Set HTTP headers in preparation for a page response. * * @see page_set_cache */ function drupal_page_header() { if (variable_get('cache', 0)) { if ($cache = page_get_cache()) { bootstrap_invoke_all('init'); // Set default values: $date = gmdate('D, d M Y H:i:s', $cache->created) .' GMT'; $etag = '"'. md5($date) .'"'; // Check http headers: $modified_since = isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) ? $_SERVER['HTTP_IF_MODIFIED_SINCE'] == $date : NULL; if (!empty($_SERVER['HTTP_IF_MODIFIED_SINCE']) && ($timestamp = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE'])) > 0) { $modified_since = $cache->created <= $timestamp; } else { $modified_since = NULL; } $none_match = !empty($_SERVER['HTTP_IF_NONE_MATCH']) ? $_SERVER['HTTP_IF_NONE_MATCH'] == $etag : NULL; // The type checking here is very important, be careful when changing entries. if (($modified_since !== NULL || $none_match !== NULL) && $modified_since !== false && $none_match !== false) { header('HTTP/1.0 304 Not Modified'); exit(); } // Send appropriate response: header("Last-Modified: $date"); header("ETag: $etag"); // 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)); } elseif (function_exists('gzencode')) { header('Content-Encoding: gzip'); } // 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; bootstrap_invoke_all('exit'); exit(); } else { header("Expires: Sun, 19 Nov 1978 05:00:00 GMT"); header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache"); } } } /** * Define the critical hooks that force modules to always be loaded. */ function bootstrap_hooks() { return array('init', 'exit'); } /** * 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. */ 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; } /** * Return the URI of the referring page. */ function referer_uri() { if (isset($_SERVER['HTTP_REFERER'])) { return $_SERVER['HTTP_REFERER']; } } /** * Encode special characters in a plain-text string for display as HTML. */ function check_plain($text) { return htmlspecialchars($text, ENT_QUOTES); } /** * Since request_uri() is only available on Apache, we generate an * equivalent using other environment variables. */ function request_uri() { if (isset($_SERVER['REQUEST_URI'])) { $uri = $_SERVER['REQUEST_URI']; } else { if (isset($_SERVER['argv'])) { $uri = $_SERVER['PHP_SELF'] .'?'. $_SERVER['argv'][0]; } else { $uri = $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING']; } } return $uri; } /** * Log a system message. * * @param $type * The category to which this message belongs. * @param $message * The message to store in the log. * @param $severity * The severity of the message. One of the following values: * - WATCHDOG_NOTICE * - WATCHDOG_WARNING * - WATCHDOG_ERROR * @param $link * A link to associate with the message. */ function watchdog($type, $message, $severity = WATCHDOG_NOTICE, $link = NULL) { global $user; $current_db = db_set_active(); 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()); if ($current_db) { db_set_active($current_db); } } /** * Set a message which reflects the status of the performed operation. * * If the function is called with no arguments, this function returns all set * messages without clearing them. * * @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' */ function drupal_set_message($message = NULL, $type = 'status') { if ($message) { if (!isset($_SESSION['messages'])) { $_SESSION['messages'] = array(); } if (!isset($_SESSION['messages'][$type])) { $_SESSION['messages'][$type] = array(); } $_SESSION['messages'][$type][] = $message; } // messages not set when DB connection fails return isset($_SESSION['messages']) ? $_SESSION['messages'] : NULL; } /** * Return all messages that have been set. * * As a side effect, this function clears the message queue. */ function drupal_get_messages() { if ($messages = drupal_set_message()) { unset($_SESSION['messages']); } return $messages; } /** * Perform an access check for a given mask and rule type. Rules are usually created via admin/access/rules page. */ function drupal_is_denied($type, $mask) { $allow = db_fetch_object(db_query("SELECT * FROM {access} WHERE status = 1 AND type = '%s' AND LOWER('%s') LIKE LOWER(mask)", $type, $mask)); $deny = db_fetch_object(db_query("SELECT * FROM {access} WHERE status = 0 AND type = '%s' AND LOWER('%s') LIKE LOWER(mask)", $type, $mask)); return $deny && !$allow; } /** * 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 * phases too. The most important usage is that if you want to access * Drupal database from a script without loading anything else, you can * include bootstrap.inc, and call drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE). * * @param $phase * A constant. Allowed values are: * DRUPAL_BOOTSTRAP_DATABASE: initialize database layer. * DRUPAL_BOOTSTRAP_SESSION: initialize session handling. * DRUPAL_BOOTSTRAP_PAGE_CACHE: load bootstrap.inc and module.inc, start * the variable system and try to serve a page from the cache. * DRUPAL_BOOTSTRAP_FULL: Drupal is fully loaded, validate and fix input * data. */ function drupal_bootstrap($phase) { static $phases = array(DRUPAL_BOOTSTRAP_DATABASE, DRUPAL_BOOTSTRAP_SESSION, DRUPAL_BOOTSTRAP_PAGE_CACHE, DRUPAL_BOOTSTRAP_PATH, DRUPAL_BOOTSTRAP_FULL); while (!is_null($current_phase = array_shift($phases))) { _drupal_bootstrap($current_phase); if ($phase == $current_phase) { return; } } } function _drupal_bootstrap($phase) { global $conf; switch ($phase) { case DRUPAL_BOOTSTRAP_DATABASE: global $db_url, $db_prefix, $base_url, $base_path; $conf = array(); require_once './' . conf_init() .'/settings.php'; require_once './includes/database.inc'; // Initialize the default database. $parts = parse_url($base_url); $base_path = (isset($parts['path']) ? $parts['path'] . '/' : '/'); db_set_active(); break; case DRUPAL_BOOTSTRAP_SESSION: require_once './includes/session.inc'; session_set_save_handler("sess_open", "sess_close", "sess_read", "sess_write", "sess_destroy", "sess_gc"); session_start(); break; case DRUPAL_BOOTSTRAP_PAGE_CACHE: require_once './includes/module.inc'; // Start a page timer: timer_start('page'); // deny access to hosts which were banned. t() is not yet available. if (drupal_is_denied('host', $_SERVER['REMOTE_ADDR'])) { header('HTTP/1.0 403 Forbidden'); print 'Sorry, '. $_SERVER['REMOTE_ADDR']. ' has been banned.'; exit(); } // Initialize configuration variables, using values from conf.php if available. $conf = variable_init(isset($conf) ? $conf : array()); drupal_page_header(); break; case DRUPAL_BOOTSTRAP_PATH: require_once './includes/path.inc'; // Initialize $_GET['q'] prior to loading modules and invoking hook_init(). drupal_init_path(); break; case DRUPAL_BOOTSTRAP_FULL: require_once './includes/common.inc'; _drupal_bootstrap_full(); break; } } /** * Enables use of the theme system without requiring database access. Since * there is not database access no theme will be enabled and the default * themable fuctions will be called. Some themable functions can not be used * 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; require_once './includes/path.inc'; require_once './includes/theme.inc'; require_once './includes/common.inc'; require_once './includes/unicode.inc'; unicode_check(); $theme = ''; }