2 /* Copyright © 2010 by Andrew Moore */
3 /* Licensing information appears at the end of this file. */
8 public function __construct($cgi_variables)
10 // Installation variables
11 // For a good explanation of these variables, see documentation in
12 // the contrib/util/installScripts/InstallerAuto.php file.
13 $this->iuser
= isset($cgi_variables['iuser']) ?
($cgi_variables['iuser']) : '';
14 $this->iuserpass
= isset($cgi_variables['iuserpass']) ?
($cgi_variables['iuserpass']) : '';
15 $this->iuname
= isset($cgi_variables['iuname']) ?
($cgi_variables['iuname']) : '';
16 $this->iufname
= isset($cgi_variables['iufname']) ?
($cgi_variables['iufname']) : '';
17 $this->igroup
= isset($cgi_variables['igroup']) ?
($cgi_variables['igroup']) : '';
18 $this->server
= isset($cgi_variables['server']) ?
($cgi_variables['server']) : ''; // mysql server (usually localhost)
19 $this->loginhost
= isset($cgi_variables['loginhost']) ?
($cgi_variables['loginhost']) : ''; // php/apache server (usually localhost)
20 $this->port
= isset($cgi_variables['port']) ?
($cgi_variables['port']): '';
21 $this->root
= isset($cgi_variables['root']) ?
($cgi_variables['root']) : '';
22 $this->rootpass
= isset($cgi_variables['rootpass']) ?
($cgi_variables['rootpass']) : '';
23 $this->login
= isset($cgi_variables['login']) ?
($cgi_variables['login']) : '';
24 $this->pass
= isset($cgi_variables['pass']) ?
($cgi_variables['pass']) : '';
25 $this->dbname
= isset($cgi_variables['dbname']) ?
($cgi_variables['dbname']) : '';
26 $this->collate
= isset($cgi_variables['collate']) ?
($cgi_variables['collate']) : '';
27 $this->site
= isset($cgi_variables['site']) ?
($cgi_variables['site']) : '';
28 $this->source_site_id
= isset($cgi_variables['source_site_id']) ?
($cgi_variables['source_site_id']) : '';
29 $this->clone_database
= isset($cgi_variables['clone_database']) ?
($cgi_variables['clone_database']) : '';
30 $this->no_root_db_access
= isset($cgi_variables['no_root_db_access']) ?
($cgi_variables['no_root_db_access']) : ''; // no root access to database. user/privileges pre-configured
31 $this->development_translations
= isset($cgi_variables['development_translations']) ?
($cgi_variables['development_translations']) : '';
32 // Make this true for IPPF.
33 $this->ippf_specific
= false;
35 // Record name of sql access file
36 $GLOBALS['OE_SITES_BASE'] = dirname(__FILE__
) . '/../../sites';
37 $GLOBALS['OE_SITE_DIR'] = $GLOBALS['OE_SITES_BASE'] . '/' . $this->site
;
38 $this->conffile
= $GLOBALS['OE_SITE_DIR'] . '/sqlconf.php';
40 // Record names of sql table files
41 $this->main_sql
= dirname(__FILE__
) . '/../../sql/database.sql';
42 $this->translation_sql
= dirname(__FILE__
) . '/../../contrib/util/language_translations/currentLanguage_utf8.sql';
43 $this->devel_translation_sql
= "http://opensourceemr.com/cvs/languageTranslations_utf8.sql";
44 $this->ippf_sql
= dirname(__FILE__
) . "/../../sql/ippf_layout.sql";
45 $this->icd9
= dirname(__FILE__
) . "/../../sql/icd9.sql";
46 $this->cvx
= dirname(__FILE__
) . "/../../sql/cvx_codes.sql";
47 $this->additional_users
= dirname(__FILE__
) . "/../../sql/official_additional_users.sql";
49 // Record name of php-gacl installation files
50 $this->gaclSetupScript1
= dirname(__FILE__
) . "/../../gacl/setup.php";
51 $this->gaclSetupScript2
= dirname(__FILE__
) . "/../../acl_setup.php";
53 // Prepare the dumpfile list
54 $this->initialize_dumpfile_list();
56 // Entities to hold error and debug messages
57 $this->error_message
= '';
58 $this->debug_message
= '';
60 // Entity to hold sql connection
64 public function login_is_valid()
66 if (($this->login
== '') ||
(! isset($this->login
))) {
67 $this->error_message
= "login is invalid: '$this->login'";
74 public function char_is_valid($input_text)
76 // to prevent php injection
78 if ($input_text == '') {
82 if (preg_match('@[\\\\;()<>/\'"]@', $input_text)) {
89 public function iuser_is_valid()
91 if (strpos($this->iuser
, " ")) {
92 $this->error_message
= "Initial user is invalid: '$this->iuser'";
99 public function password_is_valid()
101 if ($this->pass
== "" ||
!isset($this->pass
)) {
102 $this->error_message
= "The password for the new database account is invalid: '$this->pass'";
109 public function user_password_is_valid()
111 if ($this->iuserpass
== "" ||
!isset($this->iuserpass
)) {
112 $this->error_message
= "The password for the user is invalid: '$this->iuserpass'";
119 public function root_database_connection()
121 $this->dbh
= $this->connect_to_database($this->server
, $this->root
, $this->rootpass
, $this->port
);
123 if (! $this->set_sql_strict()) {
124 $this->error_message
= 'unable to set strict sql setting';
130 $this->error_message
= 'unable to connect to database as root';
135 public function user_database_connection()
137 $this->dbh
= $this->connect_to_database($this->server
, $this->login
, $this->pass
, $this->port
, $this->dbname
);
139 $this->error_message
= "unable to connect to database as user: '$this->login'";
143 if (! $this->set_sql_strict()) {
144 $this->error_message
= 'unable to set strict sql setting';
148 if (! $this->set_collation()) {
149 $this->error_message
= 'unable to set sql collation';
153 if (! mysqli_select_db($this->dbh
, $this->dbname
)) {
154 $this->error_message
= "unable to select database: '$this->dbname'";
161 public function create_database()
163 $sql = "create database $this->dbname";
164 if ($this->collate
) {
165 $sql .= " character set utf8 collate $this->collate";
166 $this->set_collation();
169 return $this->execute_sql($sql);
172 public function drop_database()
174 $sql = "drop database if exists $this->dbname";
175 return $this->execute_sql($sql);
178 public function grant_privileges()
180 return $this->execute_sql("GRANT ALL PRIVILEGES ON $this->dbname.* TO '$this->login'@'$this->loginhost' IDENTIFIED BY '$this->pass'");
183 public function disconnect()
185 return mysqli_close($this->dbh
);
189 * This method creates any dumpfiles necessary.
190 * This is actually only done if we're cloning an existing site
191 * and we need to dump their database into a file.
192 * @return bool indicating success
194 public function create_dumpfiles()
196 return $this->dumpSourceDatabase();
199 public function load_dumpfiles()
201 $sql_results = ''; // information string which is returned
202 foreach ($this->dumpfiles
as $filename => $title) {
203 $sql_results_temp = '';
204 $sql_results_temp = $this->load_file($filename, $title);
205 if ($sql_results_temp == false) {
209 $sql_results .= $sql_results_temp;
215 public function load_file($filename, $title)
217 $sql_results = ''; // information string which is returned
218 $sql_results .= "Creating $title tables...\n";
219 $fd = fopen($filename, 'r');
221 $this->error_message
= "ERROR. Could not open dumpfile '$filename'.\n";
228 // Settings to drastically speed up installation with InnoDB
229 if (! $this->execute_sql("SET autocommit=0;")) {
233 if (! $this->execute_sql("START TRANSACTION;")) {
238 $line = fgets($fd, 1024);
239 $line = rtrim($line);
240 if (substr($line, 0, 2) == "--") { // Kill comments
244 if (substr($line, 0, 1) == "#") { // Kill comments
252 $query = $query.$line; // Check for full query
253 $chr = substr($query, strlen($query)-1, 1);
254 if ($chr == ";") { // valid query, execute
255 $query = rtrim($query, ";");
256 if (! $this->execute_sql($query)) {
264 // Settings to drastically speed up installation with InnoDB
265 if (! $this->execute_sql("COMMIT;")) {
269 if (! $this->execute_sql("SET autocommit=1;")) {
273 $sql_results .= "OK<br>\n";
278 // Please note that the plain sql is used over the Doctrine ORM for
279 // `version` table interactions because it cannot connect due to a
280 // lack of context (this code is ran outside of the OpenEMR context).
281 public function add_version_info()
283 include dirname(__FILE__
) . "/../../version.php";
284 if ($this->execute_sql("UPDATE version SET v_major = '$v_major', v_minor = '$v_minor', v_patch = '$v_patch', v_realpatch = '$v_realpatch', v_tag = '$v_tag', v_database = '$v_database', v_acl = '$v_acl'") == false) {
285 $this->error_message
= "ERROR. Unable insert version information into database\n" .
286 "<p>".mysqli_error($this->dbh
)." (#".mysqli_errno($this->dbh
).")\n";
293 public function add_initial_user()
295 if ($this->execute_sql("INSERT INTO groups (id, name, user) VALUES (1,'$this->igroup','$this->iuser')") == false) {
296 $this->error_message
= "ERROR. Unable to add initial user group\n" .
297 "<p>".mysqli_error($this->dbh
)." (#".mysqli_errno($this->dbh
).")\n";
301 $password_hash = "NoLongerUsed"; // This is the value to insert into the password column in the "users" table. password details are now being stored in users_secure instead.
302 $salt=oemr_password_salt(); // Uses the functions defined in library/authentication/password_hashing.php
303 $hash=oemr_password_hash($this->iuserpass
, $salt);
304 if ($this->execute_sql("INSERT INTO users (id, username, password, authorized, lname, fname, facility_id, calendar, cal_ui) VALUES (1,'$this->iuser','$password_hash',1,'$this->iuname','$this->iufname',3,1,3)") == false) {
305 $this->error_message
= "ERROR. Unable to add initial user\n" .
306 "<p>".mysqli_error($this->dbh
)." (#".mysqli_errno($this->dbh
).")\n";
310 // Create the new style login credentials with blowfish and salt
311 if ($this->execute_sql("INSERT INTO users_secure (id, username, password, salt) VALUES (1,'$this->iuser','$hash','$salt')") == false) {
312 $this->error_message
= "ERROR. Unable to add initial user login credentials\n" .
313 "<p>".mysqli_error($this->dbh
)." (#".mysqli_errno($this->dbh
).")\n";
317 // Add the official openemr users (services)
318 if ($this->load_file($this->additional_users
, "Additional Official Users") == false) {
326 * Create site directory if it is missing.
327 * @global string $GLOBALS['OE_SITE_DIR'] contains the name of the site directory to create
328 * @return name of the site directory or False
330 public function create_site_directory()
332 if (!file_exists($GLOBALS['OE_SITE_DIR'])) {
333 $source_directory = $GLOBALS['OE_SITES_BASE'] . "/" . $this->source_site_id
;
334 $destination_directory = $GLOBALS['OE_SITE_DIR'];
335 if (! $this->recurse_copy($source_directory, $destination_directory)) {
336 $this->error_message
= "unable to copy directory: '$source_directory' to '$destination_directory'. " . $this->error_message
;
344 public function write_configuration_file()
346 @touch
($this->conffile
); // php bug
347 $fd = @fopen
($this->conffile
, 'w');
349 $this->error_message
= 'unable to open configuration file for writing: ' . $this->conffile
;
359 $it_died = 0; //fmg: variable keeps running track of any errors
361 fwrite($fd, $string) or $it_died++
;
362 fwrite($fd, "\$host\t= '$this->server';\n") or $it_died++
;
363 fwrite($fd, "\$port\t= '$this->port';\n") or $it_died++
;
364 fwrite($fd, "\$login\t= '$this->login';\n") or $it_died++
;
365 fwrite($fd, "\$pass\t= '$this->pass';\n") or $it_died++
;
366 fwrite($fd, "\$dbase\t= '$this->dbname';\n\n") or $it_died++
;
367 fwrite($fd, "//Added ability to disable\n") or $it_died++
;
368 fwrite($fd, "//utf8 encoding - bm 05-2009\n") or $it_died++
;
369 fwrite($fd, "global \$disable_utf8_flag;\n") or $it_died++
;
370 fwrite($fd, "\$disable_utf8_flag = false;\n") or $it_died++
;
375 $sqlconf["host"]= $host;
376 $sqlconf["port"] = $port;
377 $sqlconf["login"] = $login;
378 $sqlconf["pass"] = $pass;
379 $sqlconf["dbase"] = $dbase;
380 //////////////////////////
381 //////////////////////////
382 //////////////////////////
383 //////DO NOT TOUCH THIS///
384 $config = 1; /////////////
385 //////////////////////////
386 //////////////////////////
387 //////////////////////////
390 ?
><?php
// done just for coloring
392 fwrite($fd, $string) or $it_died++
;
393 fclose($fd) or $it_died++
;
395 //it's rather irresponsible to not report errors when writing this file.
397 $this->error_message
= "ERROR. Couldn't write $it_died lines to config file '$this->conffile'.\n";
404 public function insert_globals()
410 require(dirname(__FILE__
) . '/../globals.inc.php');
411 foreach ($GLOBALS_METADATA as $grpname => $grparr) {
412 foreach ($grparr as $fldid => $fldarr) {
413 list($fldname, $fldtype, $flddef, $flddesc) = $fldarr;
414 if (is_array($fldtype) ||
substr($fldtype, 0, 2) !== 'm_') {
415 $res = $this->execute_sql("SELECT count(*) AS count FROM globals WHERE gl_name = '$fldid'");
416 $row = mysqli_fetch_array($res, MYSQLI_ASSOC
);
417 if (empty($row['count'])) {
418 $this->execute_sql("INSERT INTO globals ( gl_name, gl_index, gl_value ) " .
419 "VALUES ( '$fldid', '0', '$flddef' )");
428 public function install_gacl()
430 $install_results_1 = $this->get_require_contents($this->gaclSetupScript1
);
431 if (! $install_results_1) {
432 $this->error_message
= "install_gacl failed: unable to require gacl script 1";
436 $install_results_2 = $this->get_require_contents($this->gaclSetupScript2
);
437 if (! $install_results_2) {
438 $this->error_message
= "install_gacl failed: unable to require gacl script 2";
442 $this->debug_message
.= $install_results_1 . $install_results_2;
446 public function quick_install()
448 // Validation of OpenEMR user settings
449 // (applicable if not cloning from another database)
450 if (empty($this->clone_database
)) {
451 if (! $this->login_is_valid()) {
455 if (! $this->iuser_is_valid()) {
459 if (! $this->user_password_is_valid()) {
464 // Validation of mysql database password
465 if (! $this->password_is_valid()) {
469 if (! $this->no_root_db_access
) {
470 // Connect to mysql via root user
471 if (! $this->root_database_connection()) {
475 // Create the dumpfile
476 // (applicable if cloning from another database)
477 if (! empty($this->clone_database
)) {
478 if (! $this->create_dumpfiles()) {
483 // Create the site directory
484 // (applicable if mirroring another local site)
485 if (! empty($this->source_site_id
)) {
486 if (! $this->create_site_directory()) {
492 if (! $this->user_database_connection()) {
493 // Re-connect to mysql via root user
494 if (! $this->root_database_connection()) {
498 // Create the mysql database
499 if (! $this->create_database()) {
503 // Grant user privileges to the mysql database
504 if (! $this->grant_privileges()) {
512 // Connect to mysql via created user
513 if (! $this->user_database_connection()) {
517 // Build the database
518 if (! $this->load_dumpfiles()) {
522 // Write the sql configuration file
523 if (! $this->write_configuration_file()) {
527 // Load the version information, globals settings,
528 // initial user, and set up gacl access controls.
529 // (applicable if not cloning from another database)
530 if (empty($this->clone_database
)) {
531 if (! $this->add_version_info()) {
535 if (! $this->insert_globals()) {
539 if (! $this->add_initial_user()) {
543 if (! $this->install_gacl()) {
551 private function execute_sql($sql)
553 $this->error_message
= '';
555 $this->user_database_connection();
558 $results = mysqli_query($this->dbh
, $sql);
562 $error_mes = mysqli_error($this->dbh
);
563 $this->error_message
= "unable to execute SQL: '$sql' due to: " . $error_mes;
564 error_log("ERROR IN OPENEMR INSTALL: Unable to execute SQL: ".$sql." due to: ".$error_mes);
569 private function connect_to_database($server, $user, $password, $port, $dbname = '')
571 if ($server == "localhost") {
572 $dbh = mysqli_connect($server, $user, $password, $dbname);
574 $dbh = mysqli_connect($server, $user, $password, $dbname, $port);
580 private function set_sql_strict()
582 // Turn off STRICT SQL
583 return $this->execute_sql("SET sql_mode = ''");
586 private function set_collation()
588 if ($this->collate
) {
589 return $this->execute_sql("SET NAMES 'utf8'");
596 * innitialize $this->dumpfiles, an array of the dumpfiles that will
597 * be loaded into the database, including the correct translation
599 * The keys are the paths of the dumpfiles, and the values are the titles
602 private function initialize_dumpfile_list()
604 if ($this->clone_database
) {
605 $this->dumpfiles
= array( $this->get_backup_filename() => 'clone database' );
607 $dumpfiles = array( $this->main_sql
=> 'Main' );
608 if (! empty($this->development_translations
)) {
609 // Use the online development translation set
610 $dumpfiles[ $this->devel_translation_sql
] = "Online Development Language Translations (utf8)";
612 // Use the local translation set
613 $dumpfiles[ $this->translation_sql
] = "Language Translation (utf8)";
616 if ($this->ippf_specific
) {
617 $dumpfiles[ $this->ippf_sql
] = "IPPF Layout";
620 // Load ICD-9 codes if present.
621 if (file_exists($this->icd9
)) {
622 $dumpfiles[ $this->icd9
] = "ICD-9";
625 // Load CVX codes if present
626 if (file_exists($this->cvx
)) {
627 $dumpfiles[ $this->cvx
] = "CVX Immunization Codes";
630 $this->dumpfiles
= $dumpfiles;
633 return $this->dumpfiles
;
636 // http://www.php.net/manual/en/function.include.php
637 private function get_require_contents($filename)
639 if (is_file($filename)) {
642 $contents = ob_get_contents();
652 * Directory copy logic borrowed from a user comment at
653 * http://www.php.net/manual/en/function.copy.php
654 * @param string $src name of the directory to copy
655 * @param string $dst name of the destination to copy to
656 * @return bool indicating success
658 private function recurse_copy($src, $dst)
660 $dir = opendir($src);
661 if (! @mkdir
($dst)) {
662 $this->error_message
= "unable to create directory: '$dst'";
666 while (false !== ($file = readdir($dir))) {
667 if ($file != '.' && $file != '..') {
668 if (is_dir($src . '/' . $file)) {
669 $this->recurse_copy($src . '/' . $file, $dst . '/' . $file);
671 copy($src . '/' . $file, $dst . '/' . $file);
682 * dump a site's database to a temporary file.
683 * @param string $source_site_id the site_id of the site to dump
684 * @return filename of the backup
686 private function dumpSourceDatabase()
688 global $OE_SITES_BASE;
689 $source_site_id = $this->source_site_id
;
691 include("$OE_SITES_BASE/$source_site_id/sqlconf.php");
693 if (empty($config)) {
694 die("Source site $source_site_id has not been set up!");
697 $backup_file = $this->get_backup_filename();
698 $cmd = "mysqldump -u " . escapeshellarg($login) .
699 " -p" . escapeshellarg($pass) .
700 " --opt --skip-extended-insert --quote-names -r $backup_file " .
701 escapeshellarg($dbase);
703 $tmp0 = exec($cmd, $tmp1 = array(), $tmp2);
705 die("Error $tmp2 running \"$cmd\": $tmp0 " . implode(' ', $tmp1));
712 * @return filename of the source backup database for cloning
714 private function get_backup_filename()
716 if (stristr(PHP_OS
, 'WIN')) {
717 $backup_file = 'C:/windows/temp/setup_dump.sql';
719 $backup_file = '/tmp/setup_dump.sql';
727 This file is free software: you can redistribute it and/or modify it under the
728 terms of the GNU General Public License as publish by the Free Software
731 This file is distributed in the hope that it will be useful, but WITHOUT ANY
732 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
733 PARTICULAR PURPOSE. See the GNU Gneral Public License for more details.
735 You should have received a copy of the GNU General Public Licence along with
736 this file. If not see <http://www.gnu.org/licenses/>.