diff -urpN domain-HEAD\domain.module domain\domain.module
--- domain-HEAD\domain.module	Sun Jul 06 23:16:39 2008
+++ domain\domain.module	Wed Jul 16 15:11:06 2008
@@ -34,33 +34,31 @@ define('DOMAIN_SITE_GRANT', TRUE);
 /**
  * Implements hook_init()
  *
- * Inititalizes a global $_domain variable and set it based on the
- * third-level domain used to access the page.
+ * Inititalizes a global $_domain variable if necessary (usually that's done in
+ * domain_bootstrap.inc) and loads information on current domain.
+ *
+ * Also handles www stripping, checks the validity of user domains and updates
+ * $conf['site_name'].
  */
 function domain_init() {
   global $_domain, $conf;
-  $_domain = array();
-  // We lower case this, since EXAMPLE.com == example.com.
-  $_subdomain = strtolower(rtrim($_SERVER['HTTP_HOST']));
+
+  // if $_domain is empty start domain bootstrap (by including settings.inc)
+  if (!is_array($_domain) || !isset($_domain['domain_id'])) {
+    require_once drupal_get_path('module', 'domain') .'/settings.inc';
+  }
 
   // Strip the www. off the subdomain, if required by the module settings.
-  $raw_domain = $_subdomain;
-  if (variable_get('domain_www', 0)) {
+  if (variable_get('domain_www', 0) && ($www_replaced = strpos($_subdomain, 'www.')) !== FALSE) {
     $_subdomain = str_replace('www.', '', $_subdomain);
   }
-  // Lookup the active domain against our allowed hosts record.
-  $data = db_fetch_array(db_query("SELECT domain_id FROM {domain} WHERE subdomain = '%s'", $_subdomain));
-  // Get the domain data.
-  $_domain = domain_lookup($data['domain_id']);
 
-  // If return is -1, then the DNS didn't match anything, so use defaults.
-  if ($_domain == -1) {
-    $_domain = domain_default();
-  }
+  // add information from domain_lookup but keep existing values (domain_id and subdomain)
+  $domain = domain_lookup($_domain['domain_id']);
+  $_domain = array_merge($domain, $_domain);
 
-  // If we stripped the www. send the user to the proper domain.  This should only
-  // happen once, on an inbound link or typed URL, so the overhead is acceptable.
-  if ($raw_domain != $_subdomain) {
+  // If we have replaced 'www.' in the url, redirect to the clean domain.
+  if ($www_replaced !== FALSE) {
     drupal_goto(domain_get_uri($_domain));
   }
 
@@ -70,6 +68,7 @@ function domain_init() {
     $_domain = domain_default();
     drupal_goto($_domain['path']);
   }
+
   // Set the site name to the domain-specific name.
   $conf['site_name'] = $_domain['sitename'];
 }
diff -urpN domain-HEAD\domain_bootstrap.inc domain\domain_bootstrap.inc
--- domain-HEAD\domain_bootstrap.inc	Thu Jan 01 01:00:00 1970
+++ domain\domain_bootstrap.inc	Wed Jul 16 14:55:28 2008
@@ -0,0 +1,288 @@
+<?php
+// $Id$
+/**
+ * @file
+ * Domain bootstrap file
+ *
+ * @ingroup domain
+ */
+
+/**
+ * Domain bootstrap phase 1: make sure database is initialised and necessary
+ * modules are loaded.
+ */
+define('DOMAIN_BOOTSTRAP_INIT', 0);
+
+/**
+ * Domain bootstrap phase 2: resolve host and lookup in domain tables
+ */
+define('DOMAIN_BOOTSTRAP_DNRESOLVE', 1);
+
+/**
+ * Domain bootstrap phase 3: invoke bootstrap hook "hook_domain_bootstrap_full"
+ */
+define('DOMAIN_BOOTSTRAP_FULL', 2);
+
+/**
+ * Domain module bootstrap: calls all bootstrap phases
+ *
+ * @param $_bootstrap_modules
+ * Array of modules to be loaded during domain bootstrap and called bootstrap
+ * hooks on
+ *
+ * @todo Change handling of bootstrap errors (atm it uses die() to send a msg
+ * and exit the script; watchdog is not initialised yet and cannot be used.
+ * Maybe php:trigger_error() is an option?
+ */
+function domain_bootstrap($_bootstrap_modules = null) {
+  timer_start('domain_boot');
+  _domain_bootstrap_modules($_bootstrap_modules);
+
+  $phases = array(DOMAIN_BOOTSTRAP_INIT, DOMAIN_BOOTSTRAP_DNRESOLVE, DOMAIN_BOOTSTRAP_FULL);
+
+  foreach ($phases as $phase) {
+    if (!_domain_bootstrap($phase)) {
+      die('Error during domain_bootstrap, phase '. $phase);
+    }
+  }
+  timer_stop('domain_boot');
+}
+// domain bootstrap debug
+function dbd($data) {
+  echo '<pre>'. htmlspecialchars(print_r($data, 1)) .'</pre>';
+}
+
+/**
+ * call individuell bootstrap phases
+ *
+ * @param $phase
+ * Which domain bootstrap to do
+ * @return
+ * TRUE if the bootstrap phase was successful, FALSE otherwise
+ */
+function _domain_bootstrap($phase) {
+  global $_domain;
+  switch ($phase) {
+  case DOMAIN_BOOTSTRAP_INIT:
+    // make sure database is loaded
+    _drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
+    // load bootstrap modules
+    _domain_bootstrap_modules_load();
+    // if essential core module file has not been loaded, bootstrap fails.
+    if (!function_exists('domain_load')) {
+      return false;
+    }
+
+    break;
+
+  case DOMAIN_BOOTSTRAP_DNRESOLVE:
+    // get domain_id
+    $_domain = domain_resolve_host();
+    // if we don't have a valid domain id now, we can't really go on, bootstrap fails.
+    if (empty($_domain) || !is_numeric($_domain['domain_id'])) {
+      return false;
+    }
+    break;
+
+  case DOMAIN_BOOTSTRAP_FULL:
+    _domain_bootstrap_invoke_all('full', $_domain);
+    break;
+  }
+  return true;
+}
+
+/**
+ * Returns list of modules which are called during domain_bootstrap phases and
+ * called respective hooks on
+ *
+ * @param $modules
+ * Pass an array of module names to set or reset the list of modules
+ * @return
+ * Array of module names
+ */
+function _domain_bootstrap_modules($modules = null) {
+  static $bootstrap_modules = array('domain');
+
+  if (is_array($modules)) {
+    // make sure the core domain module is always in the array
+    if (!in_array('domain', $modules)) {
+      $bootstrap_modules = array_merge(array('domain'), $modules);
+    }
+    else {
+      $bootstrap_modules = $modules;
+    }
+  }
+  return $bootstrap_modules;
+}
+
+/**
+ * Tries to load all domain bootstrap modules (see _domain_bootstrap_modules())
+ * and removes all that cannot be loaded
+ *
+ * @return
+ * void
+ */
+function _domain_bootstrap_modules_load() {
+  $modules = _domain_bootstrap_modules();
+
+  foreach ($modules as $key => $module) {
+    if (!drupal_load('module', $module)) {
+      unset($modules[$key]);
+    }
+  }
+  // update bootstrap modules to remove any modules that couldn't be loaded
+  _domain_bootstrap_modules($modules);
+}
+
+/**
+ * Tries to call specified hook on all domain_bootstrap modules.
+ * - hook function names are "{$module}_domain_bootstrap_{$hook}"
+ * - the function is basically a copy of module_invoke_all adjusted to our needs
+ *
+ * @param $hook
+ * Name of bootstrap hook to call
+ * @link http://api.drupal.org/api/function/module_invoke_all/6
+ */
+function _domain_bootstrap_invoke_all() {
+  $args = func_get_args();
+  $hook = $args[0];
+  unset($args[0]);
+  $return = array();
+  foreach (_domain_bootstrap_modules() as $module) {
+    $function = $module .'_domain_bootstrap_'. $hook;
+    if (function_exists($function)) {
+      $result = call_user_func_array($function, $args);
+      if (isset($result) && is_array($result)) {
+        $return = array_merge_recursive($return, $result);
+      }
+      else if (isset($result)) {
+        $return[] = $result;
+      }
+    }
+  }
+
+  return $return;
+}
+
+/**
+ * Tries to match the current (host) domainname to a domain in the {domain}
+ * table and return a respective domain_id
+ *
+ * @param $_domainname
+ * The domainname to match against. Optional.
+ * @return
+ * An array containing a domain_id matching the current domainname
+ */
+function domain_resolve_host($_domainname = "") {
+  if (empty($_domainname)) {
+    $_domainname = domain_current_domainname();
+  }
+
+  return _domain_lookup_simple($_domainname);
+}
+
+/**
+ * Determine current fully qualified domainname
+ *
+ * @return
+ * (String) The current (host) domainname
+ */
+function domain_current_domainname() {
+  // We lower case this, since EXAMPLE.com == example.com.
+  return strtolower(rtrim($_SERVER['HTTP_HOST']));
+}
+
+/**
+ * Determine domain_id matching given $_domainname
+ *
+ * - Runs a lookup against the {domain} table.
+ * - Calls hook_domain_bootstrap_lookup() during the process.
+ *
+ * @param $domainname
+ * The string representation of a {domain} entry.
+ * @param $reset
+ * Set TRUE to ignore cached versions and look the name up again. Optional.
+ *
+ * @return
+ * An array containing a domain_id from {domain} matching the given domainname
+ */
+function _domain_lookup_simple($_domainname, $reset = false) {
+  static $cache = array();
+
+  if (empty($_domainname)) return 0;
+
+  if ( $reset || !isset($cache[$_domainname]) ) {
+    // Lookup the given domainname against our allowed hosts record.
+    $domain = db_fetch_array(db_query_range("SELECT domain_id FROM {domain} WHERE subdomain = '%s' ", $_domainname, 0, 1));
+
+    if ( !is_array($domain) ) {
+      $domain = array();
+    }
+
+    $domain['subdomain'] = $_domainname;
+
+    // invoke hook_domain_bootstrap_lookup()
+    $domain_new = _domain_bootstrap_invoke_all('lookup', $domain);
+    if (is_array($domain_new)) {
+      $domain = array_merge($domain, $domain_new);
+    }
+
+    // no match => use default (0)
+    if (!isset($domain['domain_id'])) {
+      $domain['domain_id'] = 0;
+    }
+
+    $cache[$_domainname] = $domain;
+  }
+  return $cache[$_domainname];
+}
+
+/**
+ * Hook domain_bootstrap_lookup allows to modify the domain record used on the
+ * current page on bootstrap level, that is before it is used anywhere else.
+ *
+ * This for example allows to change the domain_id matched to the current
+ * domainname before related information is retrieved during domain_init().
+ *
+ * Note: Because this function is usually called VERY early many drupal
+ * functions or modules won't be loaded yet.
+ *
+ * In order for this hook to work your module needs to be registered in
+ * domain/settings.inc.
+ *
+ * @param $domain
+ * Array containing current domain (host) name, used during bootstrap and
+ * results of lookup against {domain} table.
+ *
+ * @return
+ * Array containing at least a valid domain_id
+ */
+function hook_domain_bootstrap_lookup($domain) {
+  // match en.example.org to default domain (id:0)
+  if ($domain['_domainname'] == 'en.example.org') {
+    $domain['domain_id'] = 0;
+  }
+
+  return $domain;
+}
+
+/**
+ * Hook hook_domain_bootstrap_full allows to execute code after the domain
+ * bootstrap phases which (usually) is even before drupal's hook_boot().
+ *
+ * This can be used to modify drupal's variables system or prefix database#
+ * tables, as used in modules domain_conf and domain_prefix.
+ *
+ * Note: Because this function is usually called VERY early many drupal
+ * functions or modules won't be loaded yet.
+ *
+ * In order for this hook to work your module needs to be registered in
+ * domain/settings.inc.
+ *
+ * @param $domain
+ * Array containing domain_id for current hostname
+ *
+ * @return
+ * void
+ */
+function hook_domain_bootstrap_full($domain) {}
diff -urpN domain-HEAD\domain_conf\domain_conf.module domain\domain_conf\domain_conf.module
--- domain-HEAD\domain_conf\domain_conf.module	Sun Jul 06 23:16:39 2008
+++ domain\domain_conf\domain_conf.module	Wed Jul 16 15:19:20 2008
@@ -17,6 +17,52 @@
  */
 
 /**
+ * Implementation of hook_domain_bootstrap_full()
+ *
+ * Dynamic domain settings loading: Loads the settings for the current domain.
+ *
+ * This routine was in hook_init(), but there are cases where
+ * the $conf array needs to be loaded in early phases of bootstrap.
+ * In particular, these variables need to be available during variable_init().
+ *
+ * Hook hook_domain_bootstrap_full allows to execute code at domain bootstrap
+ * time which is before drupal's hook_boot() and before variable_init().
+ *
+ * In order for this to work correctly the settings.inc needs to be included
+ * in settings.php (see readme)
+ *
+ * @param $domain
+ * Array containing domain_id for current hostname
+ *
+ * @return void
+ */
+function domain_conf_domain_bootstrap_full($domain) {
+  $check = db_result(db_query("SELECT status FROM {system} WHERE name = 'domain_conf'"));
+  if ($check > 0) {
+    if (!domain_settings_setup_ok()) {
+      drupal_set_message(t('The Domain module is not installed correctly. Please edit your settings.php file as described in <a href="!url">INSTALL.txt</a>', array('!url' => base_path() . drupal_get_path('module', 'domain') .'/INSTALL.txt')), 'error', FALSE);
+      return;
+    }
+    if (!is_numeric($domain['domain_id'])) {
+      drupal_set_message('Domain Configuration: domain_conf_domain_bootstrap_full, no valid id given. ', 'error');
+      return;
+    }
+    else {
+      $data = array();
+      $data = db_fetch_array(db_query("SELECT settings FROM {domain_conf} WHERE domain_id = %d", $domain['domain_id']));
+      if (!empty($data)) {
+        global $conf;
+        $settings = unserialize($data['settings']);
+        // Overwrite the $conf variables.
+        foreach ($settings as $key => $value) {
+          $conf[$key] = $value;
+        }
+      }
+    }
+  }
+}
+
+/**
  * Implements hook_init()
  */
 function domain_conf_init() {
diff -urpN domain-HEAD\domain_conf\settings_domain_conf.inc domain\domain_conf\settings_domain_conf.inc
--- domain-HEAD\domain_conf\settings_domain_conf.inc	Fri Jul 04 21:48:48 2008
+++ domain\domain_conf\settings_domain_conf.inc	Thu Jan 01 01:00:00 1970
@@ -1,54 +0,0 @@
-<?php
-// $Id: settings_domain_conf.inc,v 1.9 2008/07/04 19:48:48 agentken Exp $
-
-/**
- * @file
- * Dynamic domain settings loading.
- *
- * Loads the settings for the current domain.
- *
- * This routine was in hook_init(), but there are cases where
- * the $conf array needs to be loaded in early phases of bootstrap.
- * In particular, these variables need to be available during variable_init().
- *
- * In order for this to work, we must leap ahead in the boostrap process.
- * This step ensures that the database functions are active.
- * Then we run our function, overriding the default $conf variables, as
- * indicated in settings.php
- *
- * @ingroup domain_conf
- */
-
-_drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
-_domain_conf_load();
-
-/**
- * Load the variables for this subdomain
- *
- * @ingroup conf
- */
-function _domain_conf_load($domain = NULL) {
-  $check = db_result(db_query("SELECT status FROM {system} WHERE name = 'domain_conf'"));
-  if ($check > 0) {
-    if (is_null($domain)) {
-      // We lower case this, since EXAMPLE.com == example.com.
-      $_subdomain = strtolower(rtrim($_SERVER['HTTP_HOST']));
-      // Lookup the active domain against our allowed hosts record.
-      $domain = db_fetch_array(db_query("SELECT domain_id FROM {domain} WHERE subdomain = '%s'", $_subdomain));
-    }
-    // If nothing was found, use the default domain.
-    if (!isset($domain['domain_id'])) {
-      $domain['domain_id'] = 0;
-    }
-    $data = array();
-    $data = db_fetch_array(db_query("SELECT settings FROM {domain_conf} WHERE domain_id = %d", $domain['domain_id']));
-    if (!empty($data)) {
-      global $conf;
-      $settings = unserialize($data['settings']);
-      // Overwrite the $conf variables.
-      foreach ($settings as $key => $value) {
-        $conf[$key] = $value;
-      }
-    }
-  }
-}
diff -urpN domain-HEAD\domain_prefix\domain_prefix.module domain\domain_prefix\domain_prefix.module
--- domain-HEAD\domain_prefix\domain_prefix.module	Sun Jul 06 23:16:41 2008
+++ domain\domain_prefix\domain_prefix.module	Wed Jul 16 15:19:31 2008
@@ -26,6 +26,61 @@ define('DOMAIN_PREFIX_DROP', 8);
 define('DOMAIN_PREFIX_UPDATE', 16);
 
 /**
+ * Implementation of hook_domain_bootstrap_full()
+ *
+ * Dynamic domain settings loading: Loads the settings for the current domain.
+ *
+ * This routine was in hook_init(), but there are cases where
+ * the $conf array needs to be loaded in early phases of bootstrap.
+ * In particular, these variables need to be available during variable_init().
+ *
+ * Hook hook_domain_bootstrap_full allows to execute code at domain bootstrap
+ * time which is before drupal's hook_boot() and before variable_init().
+ *
+ * In order for this to work correctly the settings.inc needs to be included
+ * in settings.php (see readme)
+ *
+ * @param $domain
+ * Array containing domain_id for current hostname
+ *
+ * @return void
+ */
+function domain_prefix_domain_bootstrap_full($domain) {
+  $check = db_result(db_query("SELECT status FROM {system} WHERE name = '%s'", 'domain_prefix'));
+  if ($check > 0) {
+    if (!domain_settings_setup_ok()) {
+      drupal_set_message(t('The Domain module is not installed correctly. Please edit your settings.php file as described in <a href="!url">INSTALL.txt</a>', array('!url' => base_path() . drupal_get_path('module', 'domain') .'/INSTALL.txt')), 'error', FALSE);
+      return;
+    }
+    else if (!is_numeric($domain['domain_id'])) {
+      drupal_set_message('Domain Prefix: domain_prefix_domain_bootstrap_full, no valid id given. ', 'error');
+      return;
+    }
+    else {
+      $tables = array();
+      $prefix = 'domain_'. $domain['domain_id'] .'_';
+      $result = db_query("SELECT tablename FROM {domain_prefix} WHERE domain_id = %d AND status > %d", $domain['domain_id'], 1);
+      while ($data = db_fetch_array($result)) {
+        $tables[] = $data['tablename'];
+      }
+      if (!empty($tables)) {
+        global $db_prefix;
+        $new_prefix = array();
+        // There might be global prefixing; if so, prepend the global.
+        if (is_string($db_prefix)) {
+          $new_prefix['default'] = $db_prefix;
+          $prefix = $db_prefix . $prefix;
+        }
+        foreach ($tables as $table) {
+          $new_prefix[$table] = $prefix;
+        }
+        $db_prefix = $new_prefix;
+      }
+    }
+  }
+}
+
+/**
  * Implements hook_menu()
  */
 function domain_prefix_menu() {
diff -urpN domain-HEAD\domain_prefix\settings_domain_prefix.inc domain\domain_prefix\settings_domain_prefix.inc
--- domain-HEAD\domain_prefix\settings_domain_prefix.inc	Fri Jul 04 21:48:48 2008
+++ domain\domain_prefix\settings_domain_prefix.inc	Thu Jan 01 01:00:00 1970
@@ -1,52 +0,0 @@
-<?php
-// $Id: settings_domain_prefix.inc,v 1.10 2008/07/04 19:48:48 agentken Exp $
-
-/**
- * @file
- * Dynamic domain table prefix loading.
- *
- * Loads the table prefixes for the current domain.
- *
- * @ingroup domain_prefix
- */
-
-_drupal_bootstrap(DRUPAL_BOOTSTRAP_DATABASE);
-_domain_prefix_load();
-
-/**
- * Load the prefixes for this subdomain
- *
- * @ingroup prefix
- */
-function _domain_prefix_load($domain = NULL) {
-  $check = db_result(db_query("SELECT status FROM {system} WHERE name = '%s'", 'domain_prefix'));
-  if ($check > 0) {
-    if (is_null($domain)) {
-      // We lower case this, since EXAMPLE.com == example.com.
-      $_subdomain = strtolower(rtrim($_SERVER['HTTP_HOST']));
-      // Lookup the active domain against our allowed hosts record.
-      $domain = db_fetch_array(db_query("SELECT domain_id FROM {domain} WHERE subdomain = '%s'", $_subdomain));
-    }
-    if (isset($domain['domain_id'])) {
-      $tables = array();
-      $prefix = 'domain_'. $domain['domain_id'] .'_';
-      $result = db_query("SELECT tablename FROM {domain_prefix} WHERE domain_id = %d AND status > %d", $domain['domain_id'], 1);
-      while ($data = db_fetch_array($result)) {
-        $tables[] = $data['tablename'];
-      }
-      if (!empty($tables)) {
-        global $db_prefix;
-        $new_prefix = array();
-        // There might be global prefixing; if so, prepend the global.
-        if (is_string($db_prefix)) {
-          $new_prefix['default'] = $db_prefix;
-          $prefix = $db_prefix . $prefix;
-        }
-        foreach ($tables as $table) {
-          $new_prefix[$table] = $prefix;
-        }
-        $db_prefix = $new_prefix;
-      }
-    }
-  }
-}
diff -urpN domain-HEAD\settings.inc domain\settings.inc
--- domain-HEAD\settings.inc	Thu Jan 01 01:00:00 1970
+++ domain\settings.inc	Wed Jul 16 15:01:31 2008
@@ -0,0 +1,51 @@
+<?php
+// $Id$
+/**
+ *
+ * @file
+ * settings.inc
+ *
+ * This file should be included at the bottom of your settings.php file:
+ * <?php
+ * require_once '/sites/all/modules/domain/settings.inc';
+ * ?>
+ * If you have installed the domain module into a different folder than
+ * /sites/all/modules/domain please adjust the path approriately.
+ *
+ * @ingroup domain
+ */
+
+/**
+ * Specify all modules that implement some domain_bootstrap hook, like
+ * hook_domain_bootstrap_lookup() or hook_domain_bootstrap_full()
+ */
+$_bootstrap_modules = array('domain_conf', 'domain_prefix', 'domain_alias');
+
+/**
+ * include boostrap file, setup setup checker function and start bootstrap phases.
+ */
+require_once 'domain_bootstrap.inc';
+domain_settings_setup_ok();
+domain_bootstrap($_bootstrap_modules);
+
+/**
+ * Small helper function to determine whether this file was included correctly in
+ * the user's settings.php
+ *
+ * If it was included at the right time than cache.inc shouldn't be included
+ * yet and the function 'cache_get' not be defined.
+ *
+ * When the function is called first (from within this file) the state is saved
+ * in a static variable, every later call will return that boolean value.
+ *
+ * @return
+ * TRUE if settings.inc was included correctly in settings.php, else FALSE
+ */
+function domain_settings_setup_ok() {
+  static $state = NULL;
+  if ($state === NULL) {
+    $state = !function_exists('cache_get');
+  }
+  return $state;
+}
+
