Code coverage for /20081101/includes/install.inc

Line #Times calledCode
1
<?php
2
// $Id: install.inc,v 1.74 2008/10/24 18:21:54 dries Exp $
3
4
/**
5
 * Indicates that a module has not been installed yet.
6
 */
7145
define('SCHEMA_UNINSTALLED', -1);
8
9
/**
10
 * Indicates that a module has been installed.
11
 */
12145
define('SCHEMA_INSTALLED', 0);
13
14
/**
15
 * Requirement severity -- Informational message only.
16
 */
17145
define('REQUIREMENT_INFO', -1);
18
19
/**
20
 * Requirement severity -- Requirement successfully met.
21
 */
22145
define('REQUIREMENT_OK', 0);
23
24
/**
25
 * Requirement severity -- Warning condition; proceed but flag warning.
26
 */
27145
define('REQUIREMENT_WARNING', 1);
28
29
/**
30
 * Requirement severity -- Error condition; abort installation.
31
 */
32145
define('REQUIREMENT_ERROR', 2);
33
34
/**
35
 * File permission check -- File exists.
36
 */
37145
define('FILE_EXIST', 1);
38
39
/**
40
 * File permission check -- File is readable.
41
 */
42145
define('FILE_READABLE', 2);
43
44
/**
45
 * File permission check -- File is writable.
46
 */
47145
define('FILE_WRITABLE', 4);
48
49
/**
50
 * File permission check -- File is executable.
51
 */
52145
define('FILE_EXECUTABLE', 8);
53
54
/**
55
 * File permission check -- File does not exist.
56
 */
57145
define('FILE_NOT_EXIST', 16);
58
59
/**
60
 * File permission check -- File is not readable.
61
 */
62145
define('FILE_NOT_READABLE', 32);
63
64
/**
65
 * File permission check -- File is not writable.
66
 */
67145
define('FILE_NOT_WRITABLE', 64);
68
69
/**
70
 * File permission check -- File is not executable.
71
 */
72145
define('FILE_NOT_EXECUTABLE', 128);
73
74
/**
75
 * Initialize the update system by loading all installed module's .install
files.
76
 */
77145
function drupal_load_updates() {
783
  foreach (drupal_get_installed_schema_version(NULL, FALSE, TRUE) as
$module => $schema_version) {
793
    if ($schema_version > -1) {
803
      module_load_install($module);
813
    }
823
  }
833
}
84
85
/**
86
 * Returns an array of available schema versions for a module.
87
 *
88
 * @param $module
89
 *   A module name.
90
 * @return
91
 *   If the module has updates, an array of available updates sorted by
version. 
92
 *   Otherwise, FALSE.
93
 */
94145
function drupal_get_schema_versions($module) {
95139
  $updates = array();
96139
  $functions = get_defined_functions();
97139
  foreach ($functions['user'] as $function) {
98139
    if (strpos($function, $module . '_update_') === 0) {
99139
      $version = substr($function, strlen($module . '_update_'));
100139
      if (is_numeric($version)) {
101139
        $updates[] = $version;
102139
      }
103139
    }
104139
  }
105139
  if (count($updates) == 0) {
106138
    return FALSE;
1070
  }
108
  
109
  // Make sure updates are run in numeric order, not in definition order.
110139
  sort($updates, SORT_NUMERIC);
111
  
112139
  return $updates;
1130
}
114
115
/**
116
 * Returns the currently installed schema version for a module.
117
 *
118
 * @param $module
119
 *   A module name.
120
 * @param $reset
121
 *   Set to TRUE after modifying the system table.
122
 * @param $array
123
 *   Set to TRUE if you want to get information about all modules in the
124
 *   system.
125
 * @return
126
 *   The currently installed schema version.
127
 */
128145
function drupal_get_installed_schema_version($module, $reset = FALSE,
$array = FALSE) {
129141
  static $versions = array();
130
131141
  if ($reset) {
132136
    $versions = array();
133136
  }
134
135141
  if (!$versions) {
136141
    $versions = array();
137141
    $result = db_query("SELECT name, schema_version FROM {system} WHERE
type = '%s'", 'module');
138141
    while ($row = db_fetch_object($result)) {
139141
      $versions[$row->name] = $row->schema_version;
140141
    }
141141
  }
142
143141
  return $array ? $versions : $versions[$module];
1440
}
145
146
/**
147
 * Update the installed version information for a module.
148
 *
149
 * @param $module
150
 *   A module name.
151
 * @param $version
152
 *   The new schema version.
153
 */
154145
function drupal_set_installed_schema_version($module, $version) {
155137
  db_query("UPDATE {system} SET schema_version = %d WHERE name = '%s'",
$version, $module);
156137
}
157
158
/**
159
 * Loads the install profile definition, extracting its defined name.
160
 *
161
 * @return
162
 *   The name defined in the profile's _profile_details() hook.
163
 */
164145
function drupal_install_profile_name() {
1650
  global $profile;
1660
  static $name = NULL;
167
1680
  if (!isset($name)) {
169
    // Load profile details.
1700
    $function = $profile . '_profile_details';
1710
    if (function_exists($function)) {
1720
      $details = $function();
1730
    }
1740
    $name = isset($details['name']) ? $details['name'] : 'Drupal';
1750
  }
176
1770
  return $name;
1780
}
179
180
/**
181
 * Auto detect the base_url with PHP predefined variables.
182
 *
183
 * @param $file
184
 *   The name of the file calling this function so we can strip it out of
185
 *   the URI when generating the base_url.
186
 * @return
187
 *   The auto-detected $base_url that should be configured in settings.php
188
 */
189145
function drupal_detect_baseurl($file = 'install.php') {
1900
  global $profile;
1910
  $proto = $_SERVER['HTTPS'] ? 'https://' : 'http://';
1920
  $host = $_SERVER['SERVER_NAME'];
1930
  $port = ($_SERVER['SERVER_PORT'] == 80 ? '' : ':' .
$_SERVER['SERVER_PORT']);
1940
  $uri = preg_replace("/\?.*/", '', $_SERVER['REQUEST_URI']);
1950
  $dir = str_replace("/$file", '', $uri);
196
1970
  return "$proto$host$port$dir";
1980
}
199
200
/**
201
 * Detect all supported databases that are compiled into PHP.
202
 *
203
 * @return
204
 *  An array of database types compiled into PHP.
205
 */
206145
function drupal_detect_database_types() {
2070
  $databases = array();
208
209
  // We define a driver as a directory in /includes/database that in turn
210
  // contains a database.inc file.  That allows us to drop in additional
drivers
211
  // without modifying the installer.
212
  // Because we have no registry yet, we need to also include the
install.inc
213
  // file for the driver explicitly.
214
2150
  foreach (file_scan_directory(DRUPAL_ROOT . '/includes/database',
'/^[a-z]*$/i', array('.', '..', 'CVS'), 0, FALSE) as $file) {
2160
    include_once "{$file->filename}/install.inc";      
2170
    include_once "{$file->filename}/database.inc"; 
2180
    $drivers[$file->basename] = $file->filename;
2190
  }
220
2210
  foreach ($drivers as $driver => $file) {
2220
    $class = 'DatabaseInstaller_' . $driver;
2230
    $installer = new $class();
2240
    if ($installer->installable()) {
2250
      $databases[$driver] = $installer->name();
2260
    }
2270
  }
228
229
  // Usability: unconditionnally put the MySQL driver on top.
2300
  if (isset($databases['mysql'])) {
2310
    $mysql_database = $databases['mysql'];
2320
    unset($databases['mysql']);
2330
    $databases = array('mysql' => $mysql_database) + $databases;
2340
  }
235
  
2360
  return $databases;
2370
}
238
239145
abstract class DatabaseInstaller {
240
  protected $success = array();
241
  protected $tests = array(
242
    'testCreate' => array(
243
      'query' => 'CREATE TABLE drupal_install_test (id int NULL)',
244
      'success' => 'CREATE',
245
      'message' => 'Failed to create a test table on your %name database
server with the command %query. %name reports the following message:
%error.<ul><li>Are you sure the configured username has the necessary %name
permissions to create tables in the database?</li></ul>For more help, see
the <a href="http://drupal.org/node/258">Installation and upgrading
handbook</a>. If you are unsure what these terms mean you should probably
contact your hosting provider.',
246
      'fatal' => TRUE,
247
    ),
248
    'testInsert' => array(
249
      'query' => 'INSERT INTO drupal_install_test (id) VALUES (1)',
250
      'success' => 'INSERT',
251
      'message' => 'Failed to insert a value into a test table on your
%name database server. We tried inserting a value with the command %query
and %name reported the following error: %error.',
252
    ),
253
    'testUpdate' => array(
254
      'query' => 'UPDATE drupal_install_test SET id = 2',
255
      'success' => 'UPDATE',
256
      'message' => 'Failed to update a value in a test table on your %name
database server. We tried updating a value with the command %query and
%name reported the following error: %error.',
257
    ),
258
    'testDelete' => array(
259
      'query' => 'DELETE FROM drupal_install_test',
260
      'success' => 'DELETE',
261
      'message' => 'Failed to delete a value from a test table on your
%name database server. We tried deleting a value with the command %query
and %name reported the following error: %error.',
262
    ),
263
    'testDrop' => array(
264
      'query' => 'DROP TABLE drupal_install_test',
265
      'success' => 'DELETE',
266
      'message' => 'Failed to drop a test table from your %name database
server. We tried dropping a table with the command %query and %name
reported the following error %error.',
267
    ),
268
  );
269
  public $error = FALSE;
270
271
  protected function hasPdoDriver() {
2720
    return in_array($this->pdoDriver, PDO::getAvailableDrivers());
2730
  }
274
275
  public function installable() {
2760
    return $this->hasPdoDriver();
2770
  }
278
279
  abstract public function name();
280
281
  public function test() {
2820
    $return = $this->testConnect();
2830
    if ($return === FALSE) {
2840
      return FALSE;
2850
    }
2860
    foreach ($this->tests as $test) {
2870
      $return = $this->runTestQuery($test['query'], $test['success'],
$test['message'], !empty($tests['fatal']));
2880
      if ($return === FALSE) {
2890
        return FALSE;
2900
      }
2910
    }
2920
    return $this->success;
2930
  }
294
295
  /**
296
   * Check if we can connect to the database.
297
   *
298
   * @return
299
   *  FALSE on failure.
300
   */
301
  protected function testConnect() {
302
    try {
3030
      db_set_active();
3040
      $this->success[] = 'CONNECT';
305
    }
3060
    catch (Exception $e) {
3070
      drupal_set_message(st('Failed to connect to your %name database
server. %name reports the following message: %error.<ul><li>Are you sure
you have the correct username and password?</li><li>Are you sure that you
have typed the correct database hostname?</li><li>Are you sure that the
database server is running?</li></ul>For more help, see the <a
href="http://drupal.org/node/258">Installation and upgrading handbook</a>.
If you are unsure what these terms mean you should probably contact your
hosting provider.', array('%error' => $e->getMessage(), 'name' =>
$this->name())), 'error');
3080
      return FALSE;
309
    }
3100
  }
311
312
  protected function runTestQuery($query, $success, $message, $fatal =
FALSE) {
313
    try {
3140
      db_query($query);
3150
      $this->success[] = $success;
316
    }
3170
    catch (Exception $e) {
3180
      drupal_set_message(st($message, array('%query' => $query, '%error' =>
$e->getMessage(), '%name' => $this->name())), 'error');
3190
      $this->error = TRUE;
3200
      if ($fatal) {
3210
        return FALSE;
3220
      }
323
    }
3240
  }
325
}
326
327
/**
328
 * Replace values in settings.php with values in the submitted array.
329
 *
330
 * @param $settings
331
 *   An array of settings that need to be updated.
332
 */
333145
function drupal_rewrite_settings($settings = array(), $prefix = '') {
3340
  $default_settings = 'sites/default/default.settings.php';
3350
  $settings_file = conf_path(FALSE, TRUE) . '/' . $prefix . 'settings.php';
336
337
  // Build list of setting names and insert the values into the global
namespace.
3380
  $keys = array();
3390
  foreach ($settings as $setting => $data) {
3400
    $GLOBALS[$setting] = $data['value'];
3410
    $keys[] = $setting;
3420
  }
343
3440
  $buffer = NULL;
3450
  $first = TRUE;
3460
  if ($fp = fopen(DRUPAL_ROOT . '/' . $default_settings, 'r')) {
347
    // Step line by line through settings.php.
3480
    while (!feof($fp)) {
3490
      $line = fgets($fp);
3500
      if ($first && substr($line, 0, 5) != '<?php') {
3510
        $buffer = "<?php\n\n";
3520
      }
3530
      $first = FALSE;
354
      // Check for constants.
3550
      if (substr($line, 0, 7) == 'define(') {
3560
        preg_match('/define\(\s*[\'"]([A-Z_-]+)[\'"]\s*,(.*?)\);/', $line,
$variable);
3570
        if (in_array($variable[1], $keys)) {
3580
          $setting = $settings[$variable[1]];
3590
          $buffer .= str_replace($variable[2], " '" . $setting['value'] .
"'", $line);
3600
          unset($settings[$variable[1]]);
3610
          unset($settings[$variable[2]]);
3620
        }
363
        else {
3640
          $buffer .= $line;
365
        }
3660
      }
367
      // Check for variables.
3680
      elseif (substr($line, 0, 1) == '$') {
3690
        preg_match('/\$([^ ]*) /', $line, $variable);
3700
        if (in_array($variable[1], $keys)) {
371
          // Write new value to settings.php in the following format:
372
          //    $'setting' = 'value'; // 'comment'
3730
          $setting = $settings[$variable[1]];
3740
          $buffer .= '$' . $variable[1] . " = " .
var_export($setting['value'], TRUE) . ";" . (!empty($setting['comment']) ?
' // ' . $setting['comment'] . "\n" : "\n");
3750
          unset($settings[$variable[1]]);
3760
        }
377
        else {
3780
          $buffer .= $line;
379
        }
3800
      }
381
      else {
3820
        $buffer .= $line;
383
      }
3840
    }
3850
    fclose($fp);
386
387
    // Add required settings that were missing from settings.php.
3880
    foreach ($settings as $setting => $data) {
3890
      if ($data['required']) {
3900
        $buffer .= "\$$setting = " . var_export($data['value'], TRUE) .
";\n";
3910
      }
3920
    }
393
3940
    $fp = fopen(DRUPAL_ROOT . '/' . $settings_file, 'w');
3950
    if ($fp && fwrite($fp, $buffer) === FALSE) {
3960
      drupal_set_message(st('Failed to modify %settings, please verify the
file permissions.', array('%settings' => $settings_file)), 'error');
3970
    }
3980
  }
399
  else {
4000
    drupal_set_message(st('Failed to open %settings, please verify the file
permissions.', array('%settings' => $default_settings)), 'error');
401
  }
4020
}
403
404
/**
405
 * Get list of all .install files.
406
 *
407
 * @param $module_list
408
 *   An array of modules to search for their .install files.
409
 */
410145
function drupal_get_install_files($module_list = array()) {
4112
  $installs = array();
4122
  foreach ($module_list as $module) {
4132
    $installs = array_merge($installs, drupal_system_listing('/'. $module .
'.install$/', 'modules'));
4142
  }
4152
  return $installs;
4160
}
417
418
/**
419
 * Get a list of modules required by an installation profile.
420
 *
421
 * @param profile
422
 *   Name of profile.
423
 * @param locale
424
 *   Name of locale used (if any).
425
 * @return
426
 *   The list of modules to install.
427
 */
428145
function drupal_get_profile_modules($profile, $locale = 'en') {
429134
  $profile_file = "./profiles/$profile/$profile.profile";
430134
  require_once($profile_file);
431
432
  // Get a list of modules required by this profile.
433134
  $function = $profile . '_profile_modules';
434134
  return array_merge(drupal_required_modules(), $function(), ($locale !=
'en' ? array('locale') : array()));
4350
}
436
437
/**
438
 * Verify an install profile for installation.
439
 *
440
 * @param $profile
441
 *   Name of install profile to verify.
442
 * @param $locale
443
 *   Name of locale used (if any).
444
 * @return
445
 *   The list of modules to install.
446
 */
447145
function drupal_verify_profile($profile, $locale) {
4480
  include_once DRUPAL_ROOT . '/includes/file.inc';
4490
  include_once DRUPAL_ROOT . '/includes/common.inc';
450
4510
  $profile_file = DRUPAL_ROOT . "/profiles/$profile/$profile.profile";
452
4530
  if (!isset($profile) || !file_exists($profile_file)) {
4540
    install_no_profile_error();
4550
  }
456
4570
  $module_list = drupal_get_profile_modules($profile, $locale);
458
459
  // Get a list of modules that exist in Drupal's assorted subdirectories.
4600
  $present_modules = array();
4610
  foreach (drupal_system_listing('/\.module$/', 'modules', 'name', 0) as
$present_module) {
4620
    $present_modules[] = $present_module->name;
4630
  }
464
465
  // Verify that all of the profile's required modules are present.
4660
  $missing_modules = array_diff($module_list, $present_modules);
467
4680
  $requirements = array();
469
  
4700
  if (count($missing_modules)) {
4710
    $modules = array();
4720
    foreach ($missing_modules as $module) {
4730
      $modules[] = '<span class="admin-missing">' . drupal_ucfirst($module)
. '</span>';
4740
    }
4750
    $requirements['required_modules'] = array(
4760
      'title'       => st('Required modules'),
4770
      'value'       => st('Required modules not found.'),
4780
      'severity'    => REQUIREMENT_ERROR,
4790
      'description' => st('The following modules are required but were not
found. Please move them into the appropriate modules subdirectory, such as
<em>sites/all/modules</em>. Missing modules: !modules', array('!modules' =>
implode(', ', $modules))),
480
    );
4810
  }
4820
  return $requirements;
4830
}
484
485
/**
486
 * Calls the install function for a given list of modules.
487
 *
488
 * @param $module_list
489
 *   The modules to install.
490
 */
491145
function drupal_install_modules($module_list = array()) {
492136
  $files = module_rebuild_cache();
493136
  $module_list = array_flip(array_values($module_list));
494
  do {
495136
    $moved = FALSE;
496136
    foreach ($module_list as $module => $weight) {
497136
      $file = $files[$module];
498136
      if (isset($file->info['dependencies']) &&
is_array($file->info['dependencies'])) {
499136
        foreach ($file->info['dependencies'] as $dependency) {
5005
          if (isset($module_list[$dependency]) && $module_list[$module] <
$module_list[$dependency] +1) {
5011
            $module_list[$module] = $module_list[$dependency] +1;
5021
            $moved = TRUE;
5031
          }
5045
        }
505136
      }
506136
    }
5070
  } while ($moved);
508136
  asort($module_list);
509136
  $module_list = array_keys($module_list);
510136
  $modules_installed = array_filter($module_list,
'_drupal_install_module');
511136
  if (!empty($modules_installed)) {
512136
    module_invoke_all('modules_installed', $modules_installed);
513136
  }
514136
  module_enable($module_list);
515136
}
516
517
/**
518
 * Callback to install an individual install profile module.
519
 *
520
 * Used during installation to install modules one at a time and then
521
 * enable them, or to install a number of modules at one time
522
 * from admin/build/modules.
523
 *
524
 * @param $module
525
 *   The machine name of the module to install.
526
 * @return
527
 *   TRUE if the module got installed.
528
 */
529145
function _drupal_install_module($module) {
530136
  if (drupal_get_installed_schema_version($module, TRUE) ==
SCHEMA_UNINSTALLED) {
531136
    module_load_install($module);
532136
    module_invoke($module, 'install');
533136
    $versions = drupal_get_schema_versions($module);
534136
    drupal_set_installed_schema_version($module, $versions ? max($versions)
: SCHEMA_INSTALLED);
535136
    return TRUE;
5360
  }
537134
}
538
539
/**
540
 * Manually include all files for the active database.
541
 *
542
 * Because we have no registry yet, we need to manually include the
543
 * necessary database include files.
544
 */
545145
function drupal_install_init_database() {
546134
  static $included = FALSE;
547
  
548134
  if (!$included) {
549134
    $connection_info = Database::getConnectionInfo();
550134
    $driver = $connection_info['default']['driver'];
551134
    require_once DRUPAL_ROOT . '/includes/database/query.inc';
552134
    require_once DRUPAL_ROOT . '/includes/database/select.inc';
553134
    require_once DRUPAL_ROOT . '/includes/database/schema.inc';
554134
    foreach (glob(DRUPAL_ROOT . '/includes/database/' . $driver . '/*.inc')
as $include_file) {
555134
      require_once $include_file;
556134
    }
557134
  }
558134
}
559
560
/**
561
 * Callback to install the system module.
562
 *
563
 * Separated from the installation of other modules so core system
564
 * functions can be made available while other modules are installed.
565
 */
566145
function drupal_install_system() {
567134
  $system_path = dirname(drupal_get_filename('module', 'system', NULL));
568134
  require_once DRUPAL_ROOT . '/' . $system_path . '/system.install';
569134
  drupal_install_init_database();
570134
  module_invoke('system', 'install');
571
  
572134
  $system_versions = drupal_get_schema_versions('system');
573134
  $system_version = $system_versions ? max($system_versions) :
SCHEMA_INSTALLED;
574134
  db_query("INSERT INTO {system} (filename, name, type, owner, status,
bootstrap, schema_version) VALUES('%s', '%s', '%s', '%s', %d, %d, %d)",
$system_path . '/system.module', 'system', 'module', '', 1, 0,
$system_version);
575
  // Now that we've installed things properly, bootstrap the full Drupal
environment
576134
  drupal_install_init_database();
577134
  drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);
578134
  module_rebuild_cache();
579134
}
580
581
/**
582
 * Calls the uninstall function and updates the system table for a given
module.
583
 *
584
 * @param $module_list
585
 *   The modules to uninstall.
586
 */
587145
function drupal_uninstall_modules($module_list = array()) {
5881
  foreach ($module_list as $module) {
589
    // First, retrieve all the module's menu paths from db.
5901
    drupal_load('module', $module);
5911
    $paths = module_invoke($module, 'menu');
592
593
    // Uninstall the module.
5941
    module_load_install($module);
5951
    module_invoke($module, 'uninstall');
596
597
    // Now remove the menu links for all paths declared by this module.
5981
    if (!empty($paths)) {
5991
      $paths = array_keys($paths);
600
      // Clean out the names of load functions.
6011
      foreach ($paths as $index => $path) {
6021
        $parts = explode('/', $path, MENU_MAX_PARTS);
6031
        foreach ($parts as $k => $part) {
6041
          if (preg_match('/^%[a-z_]*$/', $part)) {
6051
            $parts[$k] = '%';
6061
          }
6071
        }
6081
        $paths[$index] = implode('/', $parts);
6091
      }
6101
      $placeholders = implode(', ', array_fill(0, count($paths), "'%s'"));
611
6121
      $result = db_query('SELECT * FROM {menu_links} WHERE router_path IN
(' . $placeholders . ') AND external = 0 ORDER BY depth DESC', $paths);
613
      // Remove all such items. Starting from those with the greatest depth
will
614
      // minimize the amount of re-parenting done by menu_link_delete().
6151
      while ($item = db_fetch_array($result)) {
6160
        _menu_delete_item($item, TRUE);
6170
      }
6181
    }
619
6201
    drupal_set_installed_schema_version($module, SCHEMA_UNINSTALLED);
6211
  }
622
6231
  if (!empty($module_list)) {
624
    // Call hook_module_uninstall to let other modules act
6251
    module_invoke_all('modules_uninstalled', $module_list);
6261
  }
6271
}
628
629
/**
630
 * Verify the state of the specified file.
631
 *
632
 * @param $file
633
 *   The file to check for.
634
 * @param $mask
635
 *   An optional bitmask created from various FILE_* constants.
636
 * @param $type
637
 *   The type of file. Can be file (default), dir, or link.
638
 * @return
639
 *   TRUE on success or FALSE on failure. A message is set for the latter.
640
 */
641145
function drupal_verify_install_file($file, $mask = NULL, $type = 'file') {
6423
  $return = TRUE;
643
  // Check for files that shouldn't be there.
6443
  if (isset($mask) && ($mask & FILE_NOT_EXIST) && file_exists($file)) {
6450
    return FALSE;
6460
  }
647
  // Verify that the file is the type of file it is supposed to be.
6483
  if (isset($type) && file_exists($file)) {
6493
    $check = 'is_' . $type;
6503
    if (!function_exists($check) || !$check($file)) {
6510
      $return = FALSE;
6520
    }
6533
  }
654
655
  // Verify file permissions.
6563
  if (isset($mask)) {
6573
    $masks = array(FILE_EXIST, FILE_READABLE, FILE_WRITABLE,
FILE_EXECUTABLE, FILE_NOT_READABLE, FILE_NOT_WRITABLE,
FILE_NOT_EXECUTABLE);
6583
    foreach ($masks as $current_mask) {
6593
      if ($mask & $current_mask) {
660
        switch ($current_mask) {
6613
          case FILE_EXIST:
6623
            if (!file_exists($file)) {
6630
              if ($type == 'dir') {
6640
                drupal_install_mkdir($file, $mask);
6650
              }
6660
              if (!file_exists($file)) {
6670
                $return = FALSE;
6680
              }
6690
            }
6703
            break;
6713
          case FILE_READABLE:
6723
            if (!is_readable($file) && !drupal_install_fix_file($file,
$mask)) {
6730
              $return = FALSE;
6740
            }
6753
            break;
6763
          case FILE_WRITABLE:
6770
            if (!is_writable($file) && !drupal_install_fix_file($file,
$mask)) {
6780
              $return = FALSE;
6790
            }
6800
            break;
6813
          case FILE_EXECUTABLE:
6820
            if (!is_executable($file) && !drupal_install_fix_file($file,
$mask)) {
6830
              $return = FALSE;
6840
            }
6850
            break;
6863
          case FILE_NOT_READABLE:
6870
            if (is_readable($file) && !drupal_install_fix_file($file,
$mask)) {
6880
              $return = FALSE;
6890
            }
6900
            break;
6913
          case FILE_NOT_WRITABLE:
6923
            if (is_writable($file) && !drupal_install_fix_file($file,
$mask)) {
6933
              $return = FALSE;
6943
            }
6953
            break;
6960
          case FILE_NOT_EXECUTABLE:
6970
            if (is_executable($file) && !drupal_install_fix_file($file,
$mask)) {
6980
              $return = FALSE;
6990
            }
7000
            break;
7010
        }
7023
      }
7033
    }
7043
  }
7053
  return $return;
7060
}
707
708
/**
709
 * Create a directory with specified permissions.
710
 *
711
 * @param $file
712
 *  The name of the directory to create;
713
 * @param $mask
714
 *  The permissions of the directory to create.
715
 * @param $message
716
 *  (optional) Whether to output messages. Defaults to TRUE.
717
 * @return
718
 *  TRUE/FALSE whether or not the directory was successfully created.
719
 */
720145
function drupal_install_mkdir($file, $mask, $message = TRUE) {
7210
  $mod = 0;
7220
  $masks = array(FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE,
FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE);
7230
  foreach ($masks as $m) {
7240
    if ($mask & $m) {
725
      switch ($m) {
7260
        case FILE_READABLE:
7270
          $mod += 444;
7280
          break;
7290
        case FILE_WRITABLE:
7300
          $mod += 222;
7310
          break;
7320
        case FILE_EXECUTABLE:
7330
          $mod += 111;
7340
          break;
7350
      }
7360
    }
7370
  }
738
7390
  if (@mkdir($file, intval("0$mod", 8))) {
7400
    return TRUE;
7410
  }
742
  else {
7430
    return FALSE;
744
  }
7450
}
746
747
/**
748
 * Attempt to fix file permissions.
749
 *
750
 * The general approach here is that, because we do not know the security
751
 * setup of the webserver, we apply our permission changes to all three
752
 * digits of the file permission (i.e. user, group and all).
753
 *
754
 * To ensure that the values behave as expected (and numbers don't carry
755
 * from one digit to the next) we do the calculation on the octal value
756
 * using bitwise operations. This lets us remove, for example, 0222 from
757
 * 0700 and get the correct value of 0500.
758
 *
759
 * @param $file
760
 *  The name of the file with permissions to fix.
761
 * @param $mask
762
 *  The desired permissions for the file.
763
 * @param $message
764
 *  (optional) Whether to output messages. Defaults to TRUE.
765
 * @return
766
 *  TRUE/FALSE whether or not we were able to fix the file's permissions.
767
 */
768145
function drupal_install_fix_file($file, $mask, $message = TRUE) {
769
  // If $file does not exist, fileperms() issues a PHP warning.
7703
  if (!file_exists($file)) {
7710
    return FALSE;
7720
  }
773
7743
  $mod = fileperms($file) & 0777;
7753
  $masks = array(FILE_READABLE, FILE_WRITABLE, FILE_EXECUTABLE,
FILE_NOT_READABLE, FILE_NOT_WRITABLE, FILE_NOT_EXECUTABLE);
776
777
  // FILE_READABLE, FILE_WRITABLE, and FILE_EXECUTABLE permission strings
778
  // can theoretically be 0400, 0200, and 0100 respectively, but to be safe
779
  // we set all three access types in case the administrator intends to
780
  // change the owner of settings.php after installation.
7813
  foreach ($masks as $m) {
7823
    if ($mask & $m) {
783
      switch ($m) {
7843
        case FILE_READABLE:
7853
          if (!is_readable($file)) {
7860
            $mod |= 0444;
7870
          }
7883
          break;
7893
        case FILE_WRITABLE:
7900
          if (!is_writable($file)) {
7910
            $mod |= 0222;
7920
          }
7930
          break;
7943
        case FILE_EXECUTABLE:
7950
          if (!is_executable($file)) {
7960
            $mod |= 0111;
7970
          }
7980
          break;
7993
        case FILE_NOT_READABLE:
8000
          if (is_readable($file)) {
8010
            $mod &= ~0444;
8020
          }
8030
          break;
8043
        case FILE_NOT_WRITABLE:
8053
          if (is_writable($file)) {
8063
            $mod &= ~0222;
8073
          }
8083
          break;
8090
        case FILE_NOT_EXECUTABLE:
8100
          if (is_executable($file)) {
8110
            $mod &= ~0111;
8120
          }
8130
          break;
8140
      }
8153
    }
8163
  }
817
818
  // chmod() will work if the web server is running as owner of the file.
819
  // If PHP safe_mode is enabled the currently executing script must also
820
  // have the same owner.
8213
  if (@chmod($file, $mod)) {
8220
    return TRUE;
8230
  }
824
  else {
8253
    return FALSE;
826
  }
8270
}
828
829
830
/**
831
 * Send the user to a different installer page. 
832
 *
833
 * This issues an on-site HTTP redirect. Messages (and errors) are erased.
834
 *
835
 * @param $path
836
 *   An installer path.
837
 */
838145
function install_goto($path) {
8390
  global $base_url;
8400
  header('Location: ' . $base_url . '/' . $path);
8410
  header('Cache-Control: no-cache'); // Not a permanent redirect.
8420
  exit();
8430
}
844
845
/**
846
 * Functional equivalent of t(), used when some systems are not available.
847
 *
848
 * Used during the install process, when database, theme, and localization
849
 * system is possibly not yet available.
850
 *
851
 * @see t()
852
 */
853145
function st($string, $args = array()) {
854134
  static $locale_strings = NULL;
855134
  global $profile, $install_locale;
856
857134
  if (!isset($locale_strings)) {
858134
    $locale_strings = array();
859134
    $filename = 'profiles/' . $profile . '/translations/' . $install_locale
. '.po';
860134
    if (file_exists(DRUPAL_ROOT . '/' . $filename)) {
8610
      require_once DRUPAL_ROOT . '/includes/locale.inc';
8620
      $file = (object) array('filepath' => $filename);
8630
      _locale_import_read_po('mem-store', $file);
8640
      $locale_strings = _locale_import_one_string('mem-report');
8650
    }
866134
  }
867
868134
  require_once DRUPAL_ROOT . '/includes/theme.inc';
869
  // Transform arguments before inserting them
870134
  foreach ($args as $key => $value) {
8710
    switch ($key[0]) {
872
      // Escaped only
8730
      case '@':
8740
        $args[$key] = check_plain($value);
8750
        break;
876
      // Escaped and placeholder
8770
      case '%':
8780
      default:
8790
        $args[$key] = '<em>' . check_plain($value) . '</em>';
8800
        break;
881
      // Pass-through
8820
      case '!':
8830
    }
8840
  }
885134
  return strtr((!empty($locale_strings[$string]) ? $locale_strings[$string]
: $string), $args);
8860
}
887
888
/**
889
 * Check an install profile's requirements.
890
 *
891
 * @param $profile
892
 *   Name of install profile to check.
893
 * @return
894
 *   Array of the install profile's requirements.
895
 */
896145
function drupal_check_profile($profile) {
8970
  include_once DRUPAL_ROOT . '/includes/file.inc';
898
8990
  $profile_file = DRUPAL_ROOT . "/profiles/$profile/$profile.profile";
900
9010
  if (!isset($profile) || !file_exists($profile_file)) {
9020
    install_no_profile_error();
9030
  }
904
9050
  require_once $profile_file;
906
907
  // Get a list of modules required by this profile.
9080
  $function = $profile . '_profile_modules';
9090
  $module_list = array_unique(array_merge(drupal_required_modules(),
$function()));
910
911
  // Get a list of all .install files.
9120
  $installs = drupal_get_install_files($module_list);
913
914
  // Collect requirement testing results
9150
  $requirements = array();
9160
  foreach ($installs as $install) {
9170
    require_once DRUPAL_ROOT . '/' . $install->filename;
9180
    $function = $install->name. '_requirements';
9190
    if (function_exists($function)) {
9200
      $requirements = array_merge($requirements, $function('install'));
9210
    }
9220
  }
9230
  return $requirements;
9240
}
925
926
/**
927
 * Extract highest severity from requirements array.
928
 *
929
 * @param $requirements
930
 *   An array of requirements, in the same format as is returned by 
931
 *   hook_requirements().
932
 * @return
933
 *   The highest severity in the array.
934
 */
935145
function drupal_requirements_severity(&$requirements) {
9362
  $severity = REQUIREMENT_OK;
9372
  foreach ($requirements as $requirement) {
9382
    if (isset($requirement['severity'])) {
9392
      $severity = max($severity, $requirement['severity']);
9402
    }
9412
  }
9422
  return $severity;
9430
}
944
945
/**
946
 * Check a module's requirements.
947
 *
948
 * @param $module
949
 *   Machine name of module to check.
950
 * @return
951
 *   TRUE/FALSE depending on the requirements are in place.
952
 */
953145
function drupal_check_module($module) {
954
  // Include install file
9552
  $install = drupal_get_install_files(array($module));
9562
  if (isset($install[$module])) {
9572
    require_once DRUPAL_ROOT . '/' . $install[$module]->filename;
958
959
    // Check requirements
9602
    $requirements = module_invoke($module, 'requirements', 'install');
9612
    if (is_array($requirements) &&
drupal_requirements_severity($requirements) == REQUIREMENT_ERROR) {
962
      // Print any error messages
9630
      foreach ($requirements as $requirement) {
9640
        if (isset($requirement['severity']) && $requirement['severity'] ==
REQUIREMENT_ERROR) {
9650
          $message = $requirement['description'];
9660
          if (isset($requirement['value']) && $requirement['value']) {
9670
            $message .= ' (' . t('Currently using !item !version',
array('!item' => $requirement['title'], '!version' =>
$requirement['value'])) . ')';
9680
          }
9690
          drupal_set_message($message, 'error');
9700
        }
9710
      }
9720
      return FALSE;
9730
    }
9742
  }
9752
  return TRUE;
9760
}
977145