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'";
73 public function char_is_valid($input_text)
75 // to prevent php injection
77 if ($input_text == '')
81 if (preg_match('@[\\\\;()<>/\'"]@', $input_text))
88 public function iuser_is_valid()
90 if ( strpos($this->iuser
, " ") ) {
91 $this->error_message
= "Initial user is invalid: '$this->iuser'";
97 public function password_is_valid()
99 if ( $this->pass
== "" ||
!isset($this->pass
) ) {
100 $this->error_message
= "The password for the new database account is invalid: '$this->pass'";
106 public function user_password_is_valid()
108 if ( $this->iuserpass
== "" ||
!isset($this->iuserpass
) ) {
109 $this->error_message
= "The password for the user is invalid: '$this->iuserpass'";
115 public function root_database_connection()
117 $this->dbh
= $this->connect_to_database( $this->server
, $this->root
, $this->rootpass
, $this->port
);
119 if (! $this->set_sql_strict()) {
120 $this->error_message
= 'unable to set strict sql setting';
125 $this->error_message
= 'unable to connect to database as root';
130 public function user_database_connection()
132 $this->dbh
= $this->connect_to_database( $this->server
, $this->login
, $this->pass
, $this->port
, $this->dbname
);
133 if ( ! $this->dbh
) {
134 $this->error_message
= "unable to connect to database as user: '$this->login'";
137 if ( ! $this->set_sql_strict() ) {
138 $this->error_message
= 'unable to set strict sql setting';
141 if ( ! $this->set_collation() ) {
142 $this->error_message
= 'unable to set sql collation';
145 if ( ! mysqli_select_db($this->dbh
, $this->dbname
) ) {
146 $this->error_message
= "unable to select database: '$this->dbname'";
152 public function create_database() {
153 $sql = "create database $this->dbname";
154 if ($this->collate
) {
155 $sql .= " character set utf8 collate $this->collate";
156 $this->set_collation();
158 return $this->execute_sql($sql);
161 public function drop_database() {
162 $sql = "drop database if exists $this->dbname";
163 return $this->execute_sql($sql);
166 public function grant_privileges() {
167 return $this->execute_sql( "GRANT ALL PRIVILEGES ON $this->dbname.* TO '$this->login'@'$this->loginhost' IDENTIFIED BY '$this->pass'" );
170 public function disconnect() {
171 return mysqli_close($this->dbh
);
175 * This method creates any dumpfiles necessary.
176 * This is actually only done if we're cloning an existing site
177 * and we need to dump their database into a file.
178 * @return bool indicating success
180 public function create_dumpfiles() {
181 return $this->dumpSourceDatabase();
184 public function load_dumpfiles() {
185 $sql_results = ''; // information string which is returned
186 foreach ($this->dumpfiles
as $filename => $title) {
187 $sql_results_temp = '';
188 $sql_results_temp = $this->load_file($filename,$title);
189 if ($sql_results_temp == FALSE) return FALSE;
190 $sql_results .= $sql_results_temp;
195 public function load_file($filename,$title) {
196 $sql_results = ''; // information string which is returned
197 $sql_results .= "Creating $title tables...\n";
198 $fd = fopen($filename, 'r');
200 $this->error_message
= "ERROR. Could not open dumpfile '$filename'.\n";
206 // Settings to drastically speed up installation with InnoDB
207 if ( ! $this->execute_sql("SET autocommit=0;") ){
210 if ( ! $this->execute_sql("START TRANSACTION;") ){
215 $line = fgets($fd,1024);
216 $line = rtrim($line);
217 if (substr($line,0,2) == "--") // Kill comments
219 if (substr($line,0,1) == "#") // Kill comments
223 $query = $query.$line; // Check for full query
224 $chr = substr($query,strlen($query)-1,1);
225 if ($chr == ";") { // valid query, execute
226 $query = rtrim($query,";");
227 if ( ! $this->execute_sql( $query ) ){
234 // Settings to drastically speed up installation with InnoDB
235 if ( ! $this->execute_sql("COMMIT;") ){
238 if ( ! $this->execute_sql("SET autocommit=1;") ){
242 $sql_results .= "OK<br>\n";
247 public function add_version_info() {
248 include dirname(__FILE__
) . "/../../version.php";
249 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) {
250 $this->error_message
= "ERROR. Unable insert version information into database\n" .
251 "<p>".mysqli_error($this->dbh
)." (#".mysqli_errno($this->dbh
).")\n";
257 public function add_initial_user() {
258 if ($this->execute_sql("INSERT INTO groups (id, name, user) VALUES (1,'$this->igroup','$this->iuser')") == FALSE) {
259 $this->error_message
= "ERROR. Unable to add initial user group\n" .
260 "<p>".mysqli_error($this->dbh
)." (#".mysqli_errno($this->dbh
).")\n";
263 $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.
264 $salt=oemr_password_salt(); // Uses the functions defined in library/authentication/password_hashing.php
265 $hash=oemr_password_hash($this->iuserpass
,$salt);
266 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) {
267 $this->error_message
= "ERROR. Unable to add initial user\n" .
268 "<p>".mysqli_error($this->dbh
)." (#".mysqli_errno($this->dbh
).")\n";
273 // Create the new style login credentials with blowfish and salt
274 if ($this->execute_sql("INSERT INTO users_secure (id, username, password, salt) VALUES (1,'$this->iuser','$hash','$salt')") == FALSE) {
275 $this->error_message
= "ERROR. Unable to add initial user login credentials\n" .
276 "<p>".mysqli_error($this->dbh
)." (#".mysqli_errno($this->dbh
).")\n";
279 // Add the official openemr users (services)
280 if ($this->load_file($this->additional_users
,"Additional Official Users") == FALSE) return FALSE;
286 * Create site directory if it is missing.
287 * @global string $GLOBALS['OE_SITE_DIR'] contains the name of the site directory to create
288 * @return name of the site directory or False
290 public function create_site_directory() {
291 if (!file_exists($GLOBALS['OE_SITE_DIR'])) {
292 $source_directory = $GLOBALS['OE_SITES_BASE'] . "/" . $this->source_site_id
;
293 $destination_directory = $GLOBALS['OE_SITE_DIR'];
294 if ( ! $this->recurse_copy( $source_directory, $destination_directory ) ) {
295 $this->error_message
= "unable to copy directory: '$source_directory' to '$destination_directory'. " . $this->error_message
;
302 public function write_configuration_file() {
303 @touch
($this->conffile
); // php bug
304 $fd = @fopen
($this->conffile
, 'w');
306 $this->error_message
= 'unable to open configuration file for writing: ' . $this->conffile
;
315 $it_died = 0; //fmg: variable keeps running track of any errors
317 fwrite($fd,$string) or $it_died++
;
318 fwrite($fd,"\$host\t= '$this->server';\n") or $it_died++
;
319 fwrite($fd,"\$port\t= '$this->port';\n") or $it_died++
;
320 fwrite($fd,"\$login\t= '$this->login';\n") or $it_died++
;
321 fwrite($fd,"\$pass\t= '$this->pass';\n") or $it_died++
;
322 fwrite($fd,"\$dbase\t= '$this->dbname';\n\n") or $it_died++
;
323 fwrite($fd,"//Added ability to disable\n") or $it_died++
;
324 fwrite($fd,"//utf8 encoding - bm 05-2009\n") or $it_died++
;
325 fwrite($fd,"global \$disable_utf8_flag;\n") or $it_died++
;
326 fwrite($fd,"\$disable_utf8_flag = false;\n") or $it_died++
;
331 $sqlconf["host"]= $host;
332 $sqlconf["port"] = $port;
333 $sqlconf["login"] = $login;
334 $sqlconf["pass"] = $pass;
335 $sqlconf["dbase"] = $dbase;
336 //////////////////////////
337 //////////////////////////
338 //////////////////////////
339 //////DO NOT TOUCH THIS///
340 $config = 1; /////////////
341 //////////////////////////
342 //////////////////////////
343 //////////////////////////
346 ?
><?php
// done just for coloring
348 fwrite($fd,$string) or $it_died++
;
349 fclose($fd) or $it_died++
;
351 //it's rather irresponsible to not report errors when writing this file.
353 $this->error_message
= "ERROR. Couldn't write $it_died lines to config file '$this->conffile'.\n";
360 public function insert_globals() {
361 function xl($s) { return $s; }
362 require(dirname(__FILE__
) . '/../globals.inc.php');
363 foreach ($GLOBALS_METADATA as $grpname => $grparr) {
364 foreach ($grparr as $fldid => $fldarr) {
365 list($fldname, $fldtype, $flddef, $flddesc) = $fldarr;
366 if (is_array($fldtype) ||
substr($fldtype, 0, 2) !== 'm_') {
367 $res = $this->execute_sql("SELECT count(*) AS count FROM globals WHERE gl_name = '$fldid'");
368 $row = mysqli_fetch_array($res, MYSQLI_ASSOC
);
369 if (empty($row['count'])) {
370 $this->execute_sql("INSERT INTO globals ( gl_name, gl_index, gl_value ) " .
371 "VALUES ( '$fldid', '0', '$flddef' )");
379 public function install_gacl()
381 $install_results_1 = $this->get_require_contents($this->gaclSetupScript1
);
382 if (! $install_results_1 ) {
383 $this->error_message
= "install_gacl failed: unable to require gacl script 1";
387 $install_results_2 = $this->get_require_contents($this->gaclSetupScript2
);
388 if (! $install_results_2 ) {
389 $this->error_message
= "install_gacl failed: unable to require gacl script 2";
392 $this->debug_message
.= $install_results_1 . $install_results_2;
396 public function quick_install() {
397 // Validation of OpenEMR user settings
398 // (applicable if not cloning from another database)
399 if (empty($this->clone_database
)) {
400 if ( ! $this->login_is_valid() ) {
403 if ( ! $this->iuser_is_valid() ) {
406 if ( ! $this->user_password_is_valid() ) {
410 // Validation of mysql database password
411 if ( ! $this->password_is_valid() ) {
414 if (! $this->no_root_db_access
) {
415 // Connect to mysql via root user
416 if (! $this->root_database_connection() ) {
419 // Create the dumpfile
420 // (applicable if cloning from another database)
421 if (! empty($this->clone_database
)) {
422 if ( ! $this->create_dumpfiles() ) {
426 // Create the site directory
427 // (applicable if mirroring another local site)
428 if ( ! empty($this->source_site_id
) ) {
429 if ( ! $this->create_site_directory() ) {
434 if (! $this->user_database_connection()) {
435 // Re-connect to mysql via root user
436 if (! $this->root_database_connection() ) {
439 // Create the mysql database
440 if ( ! $this->create_database()) {
443 // Grant user privileges to the mysql database
444 if ( ! $this->grant_privileges() ) {
450 // Connect to mysql via created user
451 if ( ! $this->user_database_connection() ) {
454 // Build the database
455 if ( ! $this->load_dumpfiles() ) {
458 // Write the sql configuration file
459 if ( ! $this->write_configuration_file() ) {
462 // Load the version information, globals settings,
463 // initial user, and set up gacl access controls.
464 // (applicable if not cloning from another database)
465 if (empty($this->clone_database
)) {
466 if ( ! $this->add_version_info() ) {
469 if ( ! $this->insert_globals() ) {
472 if ( ! $this->add_initial_user() ) {
475 if ( ! $this->install_gacl()) {
483 private function execute_sql( $sql ) {
484 $this->error_message
= '';
485 if ( ! $this->dbh
) {
486 $this->user_database_connection();
488 $results = mysqli_query($this->dbh
, $sql);
492 $error_mes = mysqli_error($this->dbh
);
493 $this->error_message
= "unable to execute SQL: '$sql' due to: " . $error_mes;
494 error_log("ERROR IN OPENEMR INSTALL: Unable to execute SQL: ".$sql." due to: ".$error_mes);
499 private function connect_to_database( $server, $user, $password, $port, $dbname='' )
501 if ($server == "localhost")
502 $dbh = mysqli_connect($server, $user, $password, $dbname);
504 $dbh = mysqli_connect($server, $user, $password, $dbname, $port);
508 private function set_sql_strict()
510 // Turn off STRICT SQL
511 return $this->execute_sql("SET sql_mode = ''");
514 private function set_collation()
516 if ($this->collate
) {
517 return $this->execute_sql("SET NAMES 'utf8'");
523 * innitialize $this->dumpfiles, an array of the dumpfiles that will
524 * be loaded into the database, including the correct translation
526 * The keys are the paths of the dumpfiles, and the values are the titles
529 private function initialize_dumpfile_list() {
530 if ( $this->clone_database
) {
531 $this->dumpfiles
= array( $this->get_backup_filename() => 'clone database' );
533 $dumpfiles = array( $this->main_sql
=> 'Main' );
534 if (! empty($this->development_translations
)) {
535 // Use the online development translation set
536 $dumpfiles[ $this->devel_translation_sql
] = "Online Development Language Translations (utf8)";
539 // Use the local translation set
540 $dumpfiles[ $this->translation_sql
] = "Language Translation (utf8)";
542 if ($this->ippf_specific
) {
543 $dumpfiles[ $this->ippf_sql
] = "IPPF Layout";
545 // Load ICD-9 codes if present.
546 if (file_exists( $this->icd9
)) {
547 $dumpfiles[ $this->icd9
] = "ICD-9";
549 // Load CVX codes if present
550 if (file_exists( $this->cvx
)) {
551 $dumpfiles[ $this->cvx
] = "CVX Immunization Codes";
553 $this->dumpfiles
= $dumpfiles;
555 return $this->dumpfiles
;
558 // http://www.php.net/manual/en/function.include.php
559 private function get_require_contents($filename) {
560 if (is_file($filename)) {
563 $contents = ob_get_contents();
572 * Directory copy logic borrowed from a user comment at
573 * http://www.php.net/manual/en/function.copy.php
574 * @param string $src name of the directory to copy
575 * @param string $dst name of the destination to copy to
576 * @return bool indicating success
578 private function recurse_copy($src, $dst) {
579 $dir = opendir($src);
580 if ( ! @mkdir
($dst) ) {
581 $this->error_message
= "unable to create directory: '$dst'";
584 while(false !== ($file = readdir($dir))) {
585 if ($file != '.' && $file != '..') {
586 if (is_dir($src . '/' . $file)) {
587 $this->recurse_copy($src . '/' . $file, $dst . '/' . $file);
590 copy($src . '/' . $file, $dst . '/' . $file);
600 * dump a site's database to a temporary file.
601 * @param string $source_site_id the site_id of the site to dump
602 * @return filename of the backup
604 private function dumpSourceDatabase() {
605 global $OE_SITES_BASE;
606 $source_site_id = $this->source_site_id
;
608 include("$OE_SITES_BASE/$source_site_id/sqlconf.php");
610 if (empty($config)) die("Source site $source_site_id has not been set up!");
612 $backup_file = $this->get_backup_filename();
613 $cmd = "mysqldump -u " . escapeshellarg($login) .
614 " -p" . escapeshellarg($pass) .
615 " --opt --skip-extended-insert --quote-names -r $backup_file " .
616 escapeshellarg($dbase);
618 $tmp0 = exec($cmd, $tmp1=array(), $tmp2);
619 if ($tmp2) die("Error $tmp2 running \"$cmd\": $tmp0 " . implode(' ', $tmp1));
625 * @return filename of the source backup database for cloning
627 private function get_backup_filename() {
628 if (stristr(PHP_OS
, 'WIN')) {
629 $backup_file = 'C:/windows/temp/setup_dump.sql';
632 $backup_file = '/tmp/setup_dump.sql';
640 This file is free software: you can redistribute it and/or modify it under the
641 terms of the GNU General Public License as publish by the Free Software
644 This file is distributed in the hope that it will be useful, but WITHOUT ANY
645 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
646 PARTICULAR PURPOSE. See the GNU Gneral Public License for more details.
648 You should have received a copy of the GNU General Public Licence along with
649 this file. If not see <http://www.gnu.org/licenses/>.