-
Notifications
You must be signed in to change notification settings - Fork 5
/
domain.bootstrap.inc
391 lines (354 loc) · 12.8 KB
/
domain.bootstrap.inc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
<?php
/**
* @file
* Domain bootstrap file.
*
* The domain bootstrap process is initiated in domain/settings.inc which is
* (supposed to be) included in the user's settings.php and therefore initiated
* during drupal's first bootstrap phase (backdrop_BOOTSTRAP_CONFIGURATION).
*
* The purpose of this is to allow domain-based modules to modify basic drupal
* systems like the variable or database/prefix system - before they are used by
* any other modules.
*
* @ingroup domain
*/
/**
* Domain bootstrap phase 1: makes sure that database is initialized and
* loads all necessary module files.
*/
define('DOMAIN_BOOTSTRAP_INIT', 0);
/**
* Domain bootstrap phase 2: resolves host and does a lookup in the {domain}
* table. Also invokes hook "hook_domain_bootstrap_lookup".
*/
define('DOMAIN_BOOTSTRAP_NAME_RESOLVE', 1);
/**
* Domain bootstrap phase 3: invokes hook_domain_bootstrap_full().
*/
define('DOMAIN_BOOTSTRAP_FULL', 2);
/**
* Domain module bootstrap: calls all bootstrap phases.
*
* Effectively, we add our own bootstrap phase to Drupal core.
* This allows us to do selective configuration changes before Drupal
* has a chance to react. For example, the Domain Conf module allows
* page caching to be set to "on" for example.com but "off" for
* no-cache.example.com.
*
* The big issues here are command-line interactions, which are slightly
* different; and the fact that backdrop_settings_initialize() is needed to
* properly hit the page cache.
*
* The initial check for GLOBALS['base_root'] lets us know that we have
* already run the normal configuration routine and are now free to alter
* settings per-domain.
*
* Note that this problem is largely caused by the need to jump ahead
* in backdrop_bootstrap() in order to have database functions available.
*/
function domain_bootstrap() {
global $_domain;
// Initialize our global variable.
$_domain = [];
// Ensure that core globals are loaded so that we don't break page caching.
// See http://drupal.org/node/1046844 and http://drupal.org/node/1189916.
if (!isset($GLOBALS['base_root'])) {
domain_settings_initialize();
}
$phases = [
DOMAIN_BOOTSTRAP_INIT,
DOMAIN_BOOTSTRAP_NAME_RESOLVE,
DOMAIN_BOOTSTRAP_FULL,
];
foreach ($phases as $phase) {
// We return NULL if the domain module is not enabled. This prevents
// load errors during module enable / disable.
$error = _domain_bootstrap($phase);
if ($error === NULL) {
$_domain['error'] = -1;
break;
}
elseif ($error === FALSE) {
// watchdog() is not available here, and there may be other logging.
// So we have to write an error message global variable and pick them up
// in settings,inc.
$_domain['error'] = $phase;
break;
}
}
}
/**
* Calls individual bootstrap phases.
*
* @param $phase
* The domain bootstrap phase to call.
*
* @return
* Returns TRUE if the bootstrap phase was successful and FALSE otherwise.
*/
function _domain_bootstrap($phase) {
global $_domain;
global $install_state;
switch ($phase) {
case DOMAIN_BOOTSTRAP_INIT:
// Make sure database is loaded.
// The new update handler causes problems here, so we account for it.
// Same with drush or other CLI resources. It turns out that the
// backdrop_is_cli() function is not consistent for php and drush scripts,
// so instead, we check to see if the database driver has been loaded.
// See http://drupal.org/node/1342740 for the latest background.
$new_phase = FALSE;
// If running from drush or update.php, we act differently.
// Prevent $new_phase = TRUE when running drush site-install by checking
// the $install_state global.
if ((function_exists('drush_verify_cli') || function_exists('update_prepare_bootstrap')) && empty($install_state)) {
$new_phase = TRUE;
}
backdrop_bootstrap(BACKDROP_BOOTSTRAP_DATABASE, $new_phase);
// If the Domain Access module has been disabled, or if we're unable to interact with the database, stop loading.
if (function_exists('db_query')) {
$check = (bool) db_query("SELECT status FROM {system} WHERE name = 'domain' AND type = 'module'")->fetchField();
if (empty($check)) {
return NULL;
}
}
else {
return NULL;
}
// Load bootstrap modules registered by Domain Access.
_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_NAME_RESOLVE:
// Get the domain_id of the request.
$_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['domain_id']) || !is_numeric($_domain['domain_id'])) {
return FALSE;
}
break;
case DOMAIN_BOOTSTRAP_FULL:
// Grant access to all affiliates. See http://drupal.org/node/770650
$_domain['site_grant'] = DOMAIN_SITE_GRANT;
// Load all bootstrap processes registered with the module.
_domain_bootstrap_invoke_all('full', $_domain);
break;
}
return TRUE;
}
/**
* Returns a list of modules which are loaded during domain_bootstrap phases.
*
* The domain module is always in the list of modules and has weight -99 so it
* should usually be first one as well.
*
* @param $reset
* If set to TRUE the cached list of modules is updated with the value from
* the {variable} table again. Default value is FALSE. Optional.
*
* @return
* An array of module names.
*/
function _domain_bootstrap_modules($reset = FALSE) {
$modules = backdrop_static(__FUNCTION__, NULL);
if ($reset || is_null($modules)) {
$modules = _domain_bootstrap_modules_get();
if (!is_array($modules)) {
$modules = [];
}
if (!in_array('domain', $modules)) {
$modules['-99'] = 'domain';
}
ksort($modules);
}
return $modules;
}
/**
* Tries to load all domain bootstrap modules.
*
* @see _domain_bootstrap_modules()
*/
function _domain_bootstrap_modules_load() {
$modules = _domain_bootstrap_modules();
foreach ($modules as $module) {
backdrop_load('module', $module);
}
}
/**
* Retrieves the value of the variable 'domain_bootstrap_modules' from the
* {variable} table. This function does not use Drupal's variable system.
*
* @return
* An array containing module names.
*/
function _domain_bootstrap_modules_get() {
return config_get('domain.settings', 'domain_bootstrap_modules');
}
/**
* Tries to call specified hook on all domain_bootstrap modules.
*
* The hook function names are of the following format:
* {$module}_domain_bootstrap_{$hook} where {$module}
* is the name of the module implementing the hook and {$hook}
* is the identifier for the concrete domain bootstrap hook.
*
* This function is basically a copy of module_invoke_all() adjusted to our
* needs.
*
* @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 = [];
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);
}
elseif (isset($result)) {
$return[] = $result;
}
}
}
return $return;
}
/**
* Escape the names of the default tables for variable lookups.
*
* There are a few cases where we must directly query data from the
* primary site's database. Due to table prefixing and Domain Prefix, we
* must ensure that we are querying the correct table.
*
* When using this function, you should insert the $table result directly
* into your query string, or use token replacement syntax. Do not
* enclose the table name in brackets {}, as that defeats the purpose of
* this function.
*
* @param $table
* The name of the base table to lookup.
*
* @return
* A query-safe table name pointing to the primary domain and default
* database..
* @see _domain_conf_load_primary()
*
*/
function domain_get_primary_table($table) {
global $databases;
$db_prefix = isset($databases['default']['default']['prefix']) ? $databases['default']['default']['prefix'] : '';
if (is_string($db_prefix)) {
$table = $db_prefix . $table;
}
elseif (is_array($db_prefix)) {
$table = $db_prefix['default'] . $table;
}
// db_escape_table() relies on a database connection, so we mirror that public
// method here. See DatabaseConnection::escapeTable.
return preg_replace('/[^A-Za-z0-9_.]+/', '', $table);
}
/**
* Replaces the logic from backdrop_settings_initialize() so caching works.
*
* Problem: we cannot run backdrop_settings_initialize() twice, so this logic
* has to be cloned here, otherwise, user logins get corrupted. Without this
* code, core page caching breaks because the url path isn't set properly
* for use as a cache id.
*
* Further, calling backdrop_settings_initialize() will reset $conf to an array
* which destroys caching settings.
*
* @see backdrop_settings_initialize()
*
* @link http://drupal.org/node/1046844
* @link http://drupal.org/node/1189916
*/
function domain_settings_initialize() {
global $base_url, $base_path, $base_root, $cookie_domain;
require_once BACKDROP_ROOT . '/core/includes/config.inc';
$is_https = isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on';
if (isset($base_url)) {
// Parse fixed base URL from settings.php.
$parts = parse_url($base_url);
if (!isset($parts['path'])) {
$parts['path'] = '';
}
$base_path = $parts['path'] . '/';
// 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.
$http_protocol = $is_https ? 'https' : 'http';
$base_root = $http_protocol . '://' . $_SERVER['HTTP_HOST'];
$base_url = $base_root;
// $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not
// be modified by a visitor.
if ($dir = rtrim(dirname($_SERVER['SCRIPT_NAME']), '\/')) {
// Remove "core" directory if present, allowing install.php, update.php,
// cron.php and others to auto-detect a base path.
$core_position = strrpos($dir, '/core');
if ($core_position !== FALSE && strlen($dir) - 5 == $core_position) {
$base_path = substr($dir, 0, $core_position);
}
else {
$base_path = $dir;
}
$base_url .= $base_path;
$base_path .= '/';
}
else {
$base_path = '/';
}
}
if ($cookie_domain) {
// If the user specifies the cookie domain, also use it for session name.
$session_name = $cookie_domain;
}
else {
// Otherwise use $base_url as session name, without the protocol
// to use the same session identifiers across http and https.
[, $session_name] = explode('://', $base_url, 2);
// HTTP_HOST can be modified by a visitor, but we already sanitized it
// in backdrop_settings_initialize().
if (!empty($_SERVER['HTTP_HOST'])) {
$cookie_domain = $_SERVER['HTTP_HOST'];
// Strip leading periods, www., and port numbers from cookie domain.
$cookie_domain = ltrim($cookie_domain, '.');
if (strpos($cookie_domain, 'www.') === 0) {
$cookie_domain = substr($cookie_domain, 4);
}
$cookie_domain = explode(':', $cookie_domain);
$cookie_domain = '.' . $cookie_domain[0];
}
}
// Per RFC 2109, cookie domains must contain at least one dot other than the
// first. For hosts such as 'localhost' or IP Addresses we don't set a cookie domain.
if (count(explode('.', $cookie_domain)) > 2 && !is_numeric(str_replace('.', '', $cookie_domain))) {
ini_set('session.cookie_domain', $cookie_domain);
}
// To prevent session cookies from being hijacked, a user can configure the
// SSL version of their website to only transfer session cookies via SSL by
// using PHP's session.cookie_secure setting. The browser will then use two
// separate session cookies for the HTTPS and HTTP versions of the site. So we
// must use different session identifiers for HTTPS and HTTP to prevent a
// cookie collision.
if ($is_https) {
ini_set('session.cookie_secure', TRUE);
}
// We have set $cookie_domain, so we must match $session_name to it, since
// that's what will happen inside backdrop_settings_initialize() after we run.
// Essentially, we short-circuit the IF routine when this copied code runs
// after our routine.
$session_name = $cookie_domain;
// Now set the session token correctly.
$prefix = ini_get('session.cookie_secure') ? 'SSESS' : 'SESS';
session_name($prefix . substr(hash('sha256', $session_name), 0, 32));
}