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 // Please note that the plain sql is used over the Doctrine ORM for
248 // `version` table interactions because it cannot connect due to a
249 // lack of context (this code is ran outside of the OpenEMR context).
250 public function add_version_info() {
251 include dirname(__FILE__
) . "/../../version.php";
252 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) {
253 $this->error_message
= "ERROR. Unable insert version information into database\n" .
254 "<p>".mysqli_error($this->dbh
)." (#".mysqli_errno($this->dbh
).")\n";
260 public function add_initial_user() {
261 if ($this->execute_sql("INSERT INTO groups (id, name, user) VALUES (1,'$this->igroup','$this->iuser')") == FALSE) {
262 $this->error_message
= "ERROR. Unable to add initial user group\n" .
263 "<p>".mysqli_error($this->dbh
)." (#".mysqli_errno($this->dbh
).")\n";
266 $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.
267 $salt=oemr_password_salt(); // Uses the functions defined in library/authentication/password_hashing.php
268 $hash=oemr_password_hash($this->iuserpass
,$salt);
269 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) {
270 $this->error_message
= "ERROR. Unable to add initial user\n" .
271 "<p>".mysqli_error($this->dbh
)." (#".mysqli_errno($this->dbh
).")\n";
276 // Create the new style login credentials with blowfish and salt
277 if ($this->execute_sql("INSERT INTO users_secure (id, username, password, salt) VALUES (1,'$this->iuser','$hash','$salt')") == FALSE) {
278 $this->error_message
= "ERROR. Unable to add initial user login credentials\n" .
279 "<p>".mysqli_error($this->dbh
)." (#".mysqli_errno($this->dbh
).")\n";
282 // Add the official openemr users (services)
283 if ($this->load_file($this->additional_users
,"Additional Official Users") == FALSE) return FALSE;
289 * Create site directory if it is missing.
290 * @global string $GLOBALS['OE_SITE_DIR'] contains the name of the site directory to create
291 * @return name of the site directory or False
293 public function create_site_directory() {
294 if (!file_exists($GLOBALS['OE_SITE_DIR'])) {
295 $source_directory = $GLOBALS['OE_SITES_BASE'] . "/" . $this->source_site_id
;
296 $destination_directory = $GLOBALS['OE_SITE_DIR'];
297 if ( ! $this->recurse_copy( $source_directory, $destination_directory ) ) {
298 $this->error_message
= "unable to copy directory: '$source_directory' to '$destination_directory'. " . $this->error_message
;
305 public function write_configuration_file() {
306 @touch
($this->conffile
); // php bug
307 $fd = @fopen
($this->conffile
, 'w');
309 $this->error_message
= 'unable to open configuration file for writing: ' . $this->conffile
;
318 $it_died = 0; //fmg: variable keeps running track of any errors
320 fwrite($fd,$string) or $it_died++
;
321 fwrite($fd,"\$host\t= '$this->server';\n") or $it_died++
;
322 fwrite($fd,"\$port\t= '$this->port';\n") or $it_died++
;
323 fwrite($fd,"\$login\t= '$this->login';\n") or $it_died++
;
324 fwrite($fd,"\$pass\t= '$this->pass';\n") or $it_died++
;
325 fwrite($fd,"\$dbase\t= '$this->dbname';\n\n") or $it_died++
;
326 fwrite($fd,"//Added ability to disable\n") or $it_died++
;
327 fwrite($fd,"//utf8 encoding - bm 05-2009\n") or $it_died++
;
328 fwrite($fd,"global \$disable_utf8_flag;\n") or $it_died++
;
329 fwrite($fd,"\$disable_utf8_flag = false;\n") or $it_died++
;
334 $sqlconf["host"]= $host;
335 $sqlconf["port"] = $port;
336 $sqlconf["login"] = $login;
337 $sqlconf["pass"] = $pass;
338 $sqlconf["dbase"] = $dbase;
339 //////////////////////////
340 //////////////////////////
341 //////////////////////////
342 //////DO NOT TOUCH THIS///
343 $config = 1; /////////////
344 //////////////////////////
345 //////////////////////////
346 //////////////////////////
349 ?
><?php
// done just for coloring
351 fwrite($fd,$string) or $it_died++
;
352 fclose($fd) or $it_died++
;
354 //it's rather irresponsible to not report errors when writing this file.
356 $this->error_message
= "ERROR. Couldn't write $it_died lines to config file '$this->conffile'.\n";
363 public function insert_globals() {
364 function xl($s) { return $s; }
365 require(dirname(__FILE__
) . '/../globals.inc.php');
366 foreach ($GLOBALS_METADATA as $grpname => $grparr) {
367 foreach ($grparr as $fldid => $fldarr) {
368 list($fldname, $fldtype, $flddef, $flddesc) = $fldarr;
369 if (is_array($fldtype) ||
substr($fldtype, 0, 2) !== 'm_') {
370 $res = $this->execute_sql("SELECT count(*) AS count FROM globals WHERE gl_name = '$fldid'");
371 $row = mysqli_fetch_array($res, MYSQLI_ASSOC
);
372 if (empty($row['count'])) {
373 $this->execute_sql("INSERT INTO globals ( gl_name, gl_index, gl_value ) " .
374 "VALUES ( '$fldid', '0', '$flddef' )");
382 public function install_gacl()
384 $install_results_1 = $this->get_require_contents($this->gaclSetupScript1
);
385 if (! $install_results_1 ) {
386 $this->error_message
= "install_gacl failed: unable to require gacl script 1";
390 $install_results_2 = $this->get_require_contents($this->gaclSetupScript2
);
391 if (! $install_results_2 ) {
392 $this->error_message
= "install_gacl failed: unable to require gacl script 2";
395 $this->debug_message
.= $install_results_1 . $install_results_2;
399 public function quick_install() {
400 // Validation of OpenEMR user settings
401 // (applicable if not cloning from another database)
402 if (empty($this->clone_database
)) {
403 if ( ! $this->login_is_valid() ) {
406 if ( ! $this->iuser_is_valid() ) {
409 if ( ! $this->user_password_is_valid() ) {
413 // Validation of mysql database password
414 if ( ! $this->password_is_valid() ) {
417 if (! $this->no_root_db_access
) {
418 // Connect to mysql via root user
419 if (! $this->root_database_connection() ) {
422 // Create the dumpfile
423 // (applicable if cloning from another database)
424 if (! empty($this->clone_database
)) {
425 if ( ! $this->create_dumpfiles() ) {
429 // Create the site directory
430 // (applicable if mirroring another local site)
431 if ( ! empty($this->source_site_id
) ) {
432 if ( ! $this->create_site_directory() ) {
437 if (! $this->user_database_connection()) {
438 // Re-connect to mysql via root user
439 if (! $this->root_database_connection() ) {
442 // Create the mysql database
443 if ( ! $this->create_database()) {
446 // Grant user privileges to the mysql database
447 if ( ! $this->grant_privileges() ) {
453 // Connect to mysql via created user
454 if ( ! $this->user_database_connection() ) {
457 // Build the database
458 if ( ! $this->load_dumpfiles() ) {
461 // Write the sql configuration file
462 if ( ! $this->write_configuration_file() ) {
465 // Load the version information, globals settings,
466 // initial user, and set up gacl access controls.
467 // (applicable if not cloning from another database)
468 if (empty($this->clone_database
)) {
469 if ( ! $this->add_version_info() ) {
472 if ( ! $this->insert_globals() ) {
475 if ( ! $this->add_initial_user() ) {
478 if ( ! $this->install_gacl()) {
486 private function execute_sql( $sql ) {
487 $this->error_message
= '';
488 if ( ! $this->dbh
) {
489 $this->user_database_connection();
491 $results = mysqli_query($this->dbh
, $sql);
495 $error_mes = mysqli_error($this->dbh
);
496 $this->error_message
= "unable to execute SQL: '$sql' due to: " . $error_mes;
497 error_log("ERROR IN OPENEMR INSTALL: Unable to execute SQL: ".$sql." due to: ".$error_mes);
502 private function connect_to_database( $server, $user, $password, $port, $dbname='' )
504 if ($server == "localhost")
505 $dbh = mysqli_connect($server, $user, $password, $dbname);
507 $dbh = mysqli_connect($server, $user, $password, $dbname, $port);
511 private function set_sql_strict()
513 // Turn off STRICT SQL
514 return $this->execute_sql("SET sql_mode = ''");
517 private function set_collation()
519 if ($this->collate
) {
520 return $this->execute_sql("SET NAMES 'utf8'");
526 * innitialize $this->dumpfiles, an array of the dumpfiles that will
527 * be loaded into the database, including the correct translation
529 * The keys are the paths of the dumpfiles, and the values are the titles
532 private function initialize_dumpfile_list() {
533 if ( $this->clone_database
) {
534 $this->dumpfiles
= array( $this->get_backup_filename() => 'clone database' );
536 $dumpfiles = array( $this->main_sql
=> 'Main' );
537 if (! empty($this->development_translations
)) {
538 // Use the online development translation set
539 $dumpfiles[ $this->devel_translation_sql
] = "Online Development Language Translations (utf8)";
542 // Use the local translation set
543 $dumpfiles[ $this->translation_sql
] = "Language Translation (utf8)";
545 if ($this->ippf_specific
) {
546 $dumpfiles[ $this->ippf_sql
] = "IPPF Layout";
548 // Load ICD-9 codes if present.
549 if (file_exists( $this->icd9
)) {
550 $dumpfiles[ $this->icd9
] = "ICD-9";
552 // Load CVX codes if present
553 if (file_exists( $this->cvx
)) {
554 $dumpfiles[ $this->cvx
] = "CVX Immunization Codes";
556 $this->dumpfiles
= $dumpfiles;
558 return $this->dumpfiles
;
561 // http://www.php.net/manual/en/function.include.php
562 private function get_require_contents($filename) {
563 if (is_file($filename)) {
566 $contents = ob_get_contents();
575 * Directory copy logic borrowed from a user comment at
576 * http://www.php.net/manual/en/function.copy.php
577 * @param string $src name of the directory to copy
578 * @param string $dst name of the destination to copy to
579 * @return bool indicating success
581 private function recurse_copy($src, $dst) {
582 $dir = opendir($src);
583 if ( ! @mkdir
($dst) ) {
584 $this->error_message
= "unable to create directory: '$dst'";
587 while(false !== ($file = readdir($dir))) {
588 if ($file != '.' && $file != '..') {
589 if (is_dir($src . '/' . $file)) {
590 $this->recurse_copy($src . '/' . $file, $dst . '/' . $file);
593 copy($src . '/' . $file, $dst . '/' . $file);
603 * dump a site's database to a temporary file.
604 * @param string $source_site_id the site_id of the site to dump
605 * @return filename of the backup
607 private function dumpSourceDatabase() {
608 global $OE_SITES_BASE;
609 $source_site_id = $this->source_site_id
;
611 include("$OE_SITES_BASE/$source_site_id/sqlconf.php");
613 if (empty($config)) die("Source site $source_site_id has not been set up!");
615 $backup_file = $this->get_backup_filename();
616 $cmd = "mysqldump -u " . escapeshellarg($login) .
617 " -p" . escapeshellarg($pass) .
618 " --opt --skip-extended-insert --quote-names -r $backup_file " .
619 escapeshellarg($dbase);
621 $tmp0 = exec($cmd, $tmp1=array(), $tmp2);
622 if ($tmp2) die("Error $tmp2 running \"$cmd\": $tmp0 " . implode(' ', $tmp1));
628 * @return filename of the source backup database for cloning
630 private function get_backup_filename() {
631 if (stristr(PHP_OS
, 'WIN')) {
632 $backup_file = 'C:/windows/temp/setup_dump.sql';
635 $backup_file = '/tmp/setup_dump.sql';
643 This file is free software: you can redistribute it and/or modify it under the
644 terms of the GNU General Public License as publish by the Free Software
647 This file is distributed in the hope that it will be useful, but WITHOUT ANY
648 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
649 PARTICULAR PURPOSE. See the GNU Gneral Public License for more details.
651 You should have received a copy of the GNU General Public Licence along with
652 this file. If not see <http://www.gnu.org/licenses/>.