Fixed company subdirs creation during upgrade and company addition.
[fa-stable.git] / admin / db / maintenance_db.inc
1 <?php
2
3 function write_config_db($new = false)
4 {
5         global $path_to_root, $def_coy, $db_connections, $tb_pref_counter;
6         include_once($path_to_root . "/config_db.php");
7
8         if ($new)
9                 $tb_pref_counter++;
10         $n = count($db_connections);
11         $msg = "<?php\n\n";
12         $msg .= "/*Connection Information for the database\n";
13         $msg .= "- \$def_coy is the default company that is pre-selected on login\n\n";
14         $msg .= "- host is the computer ip address or name where the database is the default is localhost assuming that the web server is also the sql server\n\n";
15         $msg .= "- user is the user name under which the database should be accessed - need to change to the mysql (or other DB) user set up for purpose\n";
16         $msg .= "  NB it is not secure to use root as the user with no password - a user with appropriate privileges must be set up\n\n";
17         $msg .= "- password is the password the user of the database requires to be sent to authorise the above database user\n\n";
18         $msg .= "- DatabaseName is the name of the database as defined in the RDMS being used. Typically RDMS allow many databases to be maintained under the same server.\n";
19         $msg .= "  The scripts for MySQL provided use the name logicworks */\n\n\n";
20
21         $msg .= "\$def_coy = " . $def_coy . ";\n\n";
22         $msg .= "\$tb_pref_counter = " . $tb_pref_counter . ";\n\n";
23         $msg .= "\$db_connections = array (\n";
24         $msg .= "\t0 => ";
25         for ($i = 0; $i < $n; $i++)
26         {
27                 if ($i > 0)
28                         $msg .= "\tarray ";
29                 else
30                         $msg .= "array ";
31                 $msg .= "('name' => '" . $db_connections[$i]['name'] . "',\n";
32                 $msg .= "\t\t'host' => '" . $db_connections[$i]['host'] . "',\n";
33                 $msg .= "\t\t'dbuser' => '" . $db_connections[$i]['dbuser'] . "',\n";
34                 $msg .= "\t\t'dbpassword' => '" . $db_connections[$i]['dbpassword'] . "',\n";
35                 $msg .= "\t\t'dbname' => '" . $db_connections[$i]['dbname'] . "',\n";
36                 $msg .= "\t\t'tbpref' => '" . $db_connections[$i]['tbpref'] . "')";
37                 if ($i != $n - 1)
38                         $msg .= ",";
39                 $msg .= "\n\n";
40         }
41         $msg .= "\t);\n?>";
42
43         $filename = $path_to_root . "/config_db.php";
44         // Check if the file exists and is writable first.
45         if (file_exists($filename) && is_writable($filename))
46         {
47                 if (!$zp = fopen($filename, 'w'))
48                 {
49                         return -1;
50                 }
51                 else
52                 {
53                         if (!fwrite($zp, $msg))
54                         {
55                                 fclose($zp);
56                                 return -2;
57                         }
58                         // Close file
59                         fclose($zp);
60                 }
61         }
62         else
63         {
64                 return -3;
65         }
66         return 0;
67 }
68
69 function db_create_db($connection)
70 {
71         $db = mysql_connect($connection["host"] ,
72                 $connection["dbuser"], $connection["dbpassword"]);
73         if (!mysql_select_db($connection["dbname"], $db))
74         {
75                 $sql = "CREATE DATABASE " . $connection["dbname"] . "";
76                 if (!mysql_query($sql))
77                         return 0;
78                 mysql_select_db($connection["dbname"], $db);
79         }
80         return $db;
81 }
82
83 function db_drop_db($connection)
84 {
85         if ($connection["tbpref"] == "")
86         {
87                 $sql = "DROP DATABASE " . $connection["dbname"] . "";
88                 return mysql_query($sql);
89         }
90         else
91         {
92         $res = db_query("show table status");
93         $all_tables = array();
94         while($row = db_fetch($res))
95                 $all_tables[] = $row;
96         // get table structures
97                 foreach ($all_tables as $table)
98                 {
99                         if (strpos($table['Name'], $connection["tbpref"]) === 0)
100                                 db_query("DROP TABLE `".$table['Name'] . "`");
101                 }
102                 //deleting the tables, how??
103                 return true;
104         }
105 }
106
107 function db_import($filename, $connection)
108 {
109         $data_queries = array();
110         $drop_queries = array();
111         $table_queries = array();
112
113         ini_set("max_execution_time", "180");
114         // uncrompress gziped backup files
115         if (strpos($filename, ".gzip") || strpos($filename, ".GZIP"))
116                 $lines = db_ungzip("lines", $filename);
117         elseif (strpos($filename, ".zip") || strpos($filename, ".ZIP"))
118                 $lines = db_unzip("lines", $filename);
119         else
120                 $lines = file("". $filename);
121
122         // divide insert and create sql queries
123         // $table is set to TRUE if the next line belongs to a create sql query
124         $table = false;
125         foreach($lines as $line)
126         {
127                 $line = trim($line);
128
129                 $line = str_replace("0_", $connection["tbpref"], $line);
130                 // $line = str_replace("Y_", "0_", $line);
131                 // the last line did not belong to a 'create' sql query
132                 if (!$table)
133                 {
134
135                         // this line does not, too
136                         if (strtolower(substr($line,0,6)) == "insert")
137                         {
138                                 $data_queries[] = substr($line, 0, strlen($line) - 1);
139
140                         // this line does not, too
141                         }
142                         elseif (strtolower(substr($line,0,6)) == "update")
143                         {
144                                 $data_queries[] = substr($line, 0, strlen($line) - 1);
145
146                         // this line does not, too
147                         }
148                         elseif (strtolower(substr($line, 0, 20)) == "drop table if exists")
149                         {
150                                 $drop_queries[] = substr($line, 0, strlen($line) - 1);
151
152                         // this line does!
153                         }
154                         elseif (strtolower(substr($line, 0, 6)) == "create")
155                         {
156                                 $table = true;
157                                 $table_queries[] = $line . "\n";
158                         }
159                         elseif (strtolower(substr($line, 0, 11)) == "alter table")
160                         {
161                                 $data_queries[] = substr($line, 0, strlen($line) - 1);
162                         }
163
164                 // the current line belongs to a create sql query
165                 }
166                 else
167                 {
168
169                         // create sql query ending in this line
170                         if (strtolower(substr($line, 0, 1)) == ")") {
171                                 $table = false;
172                                 $line = substr($line,0,strlen($line)-1);
173                   }
174                         $table_queries[count($table_queries) - 1] .= $line . "\n";
175                 }
176         }
177
178         $sql_error = false;
179
180         // execute drop tables if exists queries
181         if (is_array($drop_queries))
182         {
183                 foreach($drop_queries as $drop_query)
184                 {
185                         $sql_error = false;
186                         if (!db_query($drop_query))
187                         {
188                                 $sql_error = true;
189                                 //if ($CONF['import_error']) echo nl2br($drop_query)."\n<div class=\"bold_left\">".mysql_error()."</div><br>\n";
190                         }
191                 }
192         }
193
194         // execute create tables queries
195         if (is_array($table_queries))
196         {
197                 foreach($table_queries as $table_query)
198                 {
199                         $sql_error = false;
200                         if (!db_query($table_query))
201                         {
202                                 $sql_error = true;
203                                 //if ($CONF['import_error']) echo nl2br($table_query)."\n<div class=\"bold_left\">".mysql_error()."</div><br>\n";
204                         }
205                 }
206         }
207
208         // execute insert data queries
209         if (is_array($data_queries))
210         {
211                 foreach($data_queries as $data_query)
212                 {
213                         $sql_error = false;
214                         if (!db_query($data_query))
215                         {
216                                 //if ($CONF['import_error']) echo $data_query."\n<div class=\"bold_left\">".mysql_error()."</div><br>\n";
217                                 $sql_error = true;
218                                 return false;
219                         }
220                 }
221         }
222
223         // show number successful executed querys or if an error did occur
224         if ($sql_error == 1)
225                 return false;
226                 //echo "<div class=\"red\">".IM_ERROR.".</div>\n";
227         else
228                 return true;
229                 //echo "<div class=\"green\">".IM_SUCCESS." ".count($table_queries)." ".IM_TABLES." ".count($data_queries)." ".IM_ROWS." (".$import_file.")</div>\n";
230         //$shell_command = C_MYSQL_PATH . " -h $host -u $user -p{$password} $dbname < $filename";
231         //shell_exec($shell_command);
232 }
233
234 // returns the content of the gziped $path backup file. use of $mode see below
235 function db_ungzip($mode, $path)
236 {
237     $file_data = gzfile($path);
238     // returns one string or an array of lines
239     if ($mode != "lines")
240         return implode("",$file_data);
241     else
242         return $file_data;
243 }
244
245 // returns the content of the ziped $path backup file. use of $mode see below
246 function db_unzip($mode, $path)
247 {
248     $all = false;
249     $all = implode("", file($path));
250
251     // convert path to name of ziped file
252     $filename = ereg_replace(".*/", "", $path);
253     $filename = substr($filename, 0, strlen($filename) - 4);
254
255     // compare filname in zip and filename from $_GET
256     if (substr($all, 30, strlen($filename)) != $filename)
257     {
258                 return '';
259         // exit if names differ
260         //echo F_WRONG_FILE.".";
261         //exit;
262     }
263     else
264     {
265         // get the suffix of the filename in hex
266         $crc_bugfix = substr(substr($filename, 0, strlen($filename) - 4), strlen($filename) - 12 - 4);
267         $suffix = false;
268
269         // convert hex to ascii
270         for ($i=0; $i < 12; )
271                 $suffix .= chr($crc_bugfix[$i++] . $crc_bugfix[$i++] . $crc_bugfix[$i++]);
272
273         // remove central directory information (we have always just one ziped file)
274         $comp = substr($all, -(strlen($all) - 30 - strlen($filename)));
275         $comp = substr($comp, 0, (strlen($comp) - 80 - strlen($filename)));
276
277         // fix the crc bugfix (see function save_to_file)
278         $comp = "x\9c" . $comp . $suffix;
279         $file_data = gzuncompress($comp);
280     }
281
282     // returns one string or an array of lines
283     if ($mode != "lines")
284         return $file_data;
285     else
286         return explode("\n", $file_data);
287 }
288
289 // generates a dump of $db database
290 // $drop and $zip tell if to include the drop table statement or dry to pack
291 function db_export($conn, $filename, $zip='no', $comment='')
292 {
293
294         global $app_title, $version, $power_url, $path_to_root;
295
296     $error = false;
297
298     // set max string size before writing to file
299     $max_size = 1048576 * 2; // 2 MB
300     // changes max size if value can be retrieved
301     if (ini_get("memory_limit"))
302         $max_size = 900000 * ini_get("memory_limit");
303
304     // set backupfile name
305     if ($zip == "gzip")
306         $backupfile = $filename . ".gz";
307     elseif ($zip == "zip")
308         $backupfile = $filename . ".zip";
309     else
310         $backupfile = $filename;
311     $company = get_company_pref('coy_name');
312     //create comment
313     $out="# MySQL dump of database '".$conn["dbname"]."' on host '".$conn["host"]."'\n";
314     $out.="# Backup Date and Time: ".date("Y-m-d H:i")."\n";
315     $out.="# Built by " . $app_title . " " . $version ."\n";
316     $out.="# ".$power_url."\n";
317     $out.="# Company: ". @html_entity_decode($company, ENT_COMPAT, $_SESSION['language']->encoding)."\n";
318     $out.="# User: ".$_SESSION["wa_current_user"]->name."\n\n";
319
320         // write users comment
321         if ($comment)
322         {
323                 $out .= "# Comment:\n";
324                 $comment=preg_replace("'\n'","\n# ","# ".$comment);
325                 //$comment=str_replace("\n", "\n# ", $comment);
326                 foreach(explode("\n",$comment) as $line)
327                         $out .= $line."\n";
328                 $out.="\n";
329         }
330
331     //$out.="use ".$db.";\n"; we don't use this option.
332
333     // get auto_increment values and names of all tables
334     $res = db_query("show table status");
335     $all_tables = array();
336     while($row = db_fetch($res))
337     {
338                 //if ($conn["tbpref"] == "" || strpos($row['Name'], $conn["tbpref"]) !== false) replaced
339                 if (($conn["tbpref"] == "" && !preg_match('/[0-9]+_/', $row['Name'])) ||
340                         ($conn["tbpref"] != "" && strpos($row['Name'], $conn["tbpref"]) !== false))
341                 $all_tables[] = $row;
342     }
343         // get table structures
344         foreach ($all_tables as $table)
345         {
346                 $res1 = db_query("SHOW CREATE TABLE `" . $table['Name'] . "`");
347                 $tmp = db_fetch($res1);
348                 $table_sql[$table['Name']] = $tmp["Create Table"];
349         }
350
351         // find foreign keys
352         $fks = array();
353         if (isset($table_sql))
354         {
355                 foreach($table_sql as $tablenme=>$table)
356                 {
357                         $tmp_table=$table;
358                         // save all tables, needed for creating this table in $fks
359                         while (($ref_pos = strpos($tmp_table, " REFERENCES ")) > 0)
360                         {
361                                 $tmp_table = substr($tmp_table, $ref_pos + 12);
362                                 $ref_pos = strpos($tmp_table, "(");
363                                 $fks[$tablenme][] = substr($tmp_table, 0, $ref_pos);
364                         }
365                 }
366         }
367         // order $all_tables
368         $all_tables = order_sql_tables($all_tables, $fks);
369
370         // as long as no error occurred
371         if (!$error)
372         {
373                 //while($row=@mysql_fetch_array($res))
374                 foreach ($all_tables as $row)
375                 {
376                         $tablename = $row['Name'];
377                         $auto_incr[$tablename] = $row['Auto_increment'];
378
379                         $out.="\n\n";
380                         // export tables
381                         $out.="### Structure of table `".$tablename."` ###\n\n";
382
383                         $out.="DROP TABLE IF EXISTS `".$tablename."`;\n\n";
384                         $out.=$table_sql[$tablename];
385
386                         // add auto_increment value
387                         if ($auto_incr[$tablename])
388                                 $out.=" AUTO_INCREMENT=".$auto_incr[$tablename];
389                         $out.=" ;";
390                         $out.="\n\n\n";
391
392                         // export data
393                         if (!$error)
394                         {
395                                 $out.="### Data of table `".$tablename."` ###\n\n";
396
397                                 // check if field types are NULL or NOT NULL
398                                 $res3 = db_query("SHOW COLUMNS FROM `" . $tablename . "`");
399
400                                 $field_type = array();
401                                 for ($j = 0; $j < db_num_rows($res3); $j++)
402                                 {
403                                         $row3 = db_fetch($res3);
404                                         $field_type[] = $row3[2];
405                                 }
406
407                                 $res2 = db_query("SELECT * FROM `" . $tablename . "`");
408                                 for ($j = 0; $j < db_num_rows($res2); $j++)
409                                 {
410                                         $out .= "INSERT INTO `" . $tablename . "` VALUES (";
411                                         $row2 = db_fetch_row($res2);
412                                         // run through each field
413                                         for ($k = 0; $k < $nf = db_num_fields($res2); $k++)
414                                         {
415                                                 $out .= db_escape(@html_entity_decode($row2[$k], ENT_COMPAT, $_SESSION['language']->encoding));
416                                                 if ($k < ($nf - 1))
417                                                         $out .= ", ";
418                                         }
419                                         $out .= ");\n";
420
421                                         // if saving is successful, then empty $out, else set error flag
422                                         if (strlen($out) > $max_size && $zip != "zip")
423                                         {
424                                                 if (save_to_file($backupfile, $zip, $out))
425                                                         $out = "";
426                                                 else
427                                                         $error = true;
428                                         }
429                                 }
430
431                         // an error occurred! Try to delete file and return error status
432                         }
433                         elseif ($error)
434                         {
435                                 @unlink(BACKUP_PATH . $backupfile);
436                                 return false;
437                         }
438
439                         // if saving is successful, then empty $out, else set error flag
440                         if (strlen($out) > $max_size && $zip != "zip")
441                         {
442                                 if (save_to_file($backupfile, $zip, $out))
443                                         $out= "";
444                                 else
445                                         $error = true;
446                         }
447                 }
448
449         // an error occurred! Try to delete file and return error status
450         }
451         else
452         {
453                 @unlink(BACKUP_PATH . $backupfile);
454                 return false;
455         }
456
457         // if (mysql_error()) return "DB_ERROR";
458         //@mysql_close($con);
459
460         //if ($zip == "zip")
461         //      $zip = $time;
462         if (save_to_file($backupfile, $zip, $out))
463         {
464                 $out = "";
465         }
466         else
467         {
468                 @unlink(BACKUP_PATH . $backupfile);
469                 return false;
470         }
471     return $backupfile;
472 }
473
474 // orders the tables in $tables according to the constraints in $fks
475 // $fks musst be filled like this: $fks[tablename][0]=needed_table1; $fks[tablename][1]=needed_table2; ...
476 function order_sql_tables($tables, $fks)
477 {
478         // do not order if no contraints exist
479         if (!count($fks))
480                 return $tables;
481
482         // order
483         $new_tables = array();
484         $existing = array();
485         $modified = true;
486         while (count($tables) && $modified == true)
487         {
488                 $modified = false;
489             foreach ($tables as $key=>$row)
490             {
491                 // delete from $tables and add to $new_tables
492                 if (isset($fks[$row['Name']]))
493                 {
494                         foreach($fks[$row['Name']] as $needed)
495                         {
496                         // go to next table if not all needed tables exist in $existing
497                         if (!in_array($needed,$existing))
498                                 continue 2;
499                     }
500                 }
501             // delete from $tables and add to $new_tables
502                 $existing[] = $row['Name'];
503                         $new_tables[] = $row;
504             prev($tables);
505             unset($tables[$key]);
506             $modified = true;
507
508             }
509         }
510
511         if (count($tables))
512         {
513             // probably there are 'circles' in the constraints, bacause of that no proper backups can be created yet
514             // TODO: this will be fixed sometime later through using 'alter table' commands to add the constraints after generating the tables
515             // until now, just add the lasting tables to $new_tables, return them and print a warning
516             foreach($tables as $row)
517                 $new_tables[] = $row;
518             echo "<div class=\"red_left\">THIS DATABASE SEEMS TO CONTAIN 'RING CONSTRAINTS'. WA DOES NOT SUPPORT THEM. PROBABLY THE FOLOWING BACKUP IS DEFECT!</div>";
519         }
520         return $new_tables;
521 }
522
523 // saves the string in $fileData to the file $backupfile as gz file or not ($zip)
524 // returns backup file name if name has changed (zip), else TRUE. If saving failed, return value is FALSE
525 function save_to_file($backupfile, $zip, $fileData)
526 {
527         global $path_to_root;
528
529     if ($zip == "gzip")
530     {
531         if ($zp = @gzopen(BACKUP_PATH . $backupfile, "a9"))
532         {
533                         @gzwrite($zp, $fileData);
534                         @gzclose($zp);
535                         return true;
536         }
537         else
538         {
539                 return false;
540         }
541
542     // $zip contains the timestamp
543     }
544     elseif ($zip == "zip")
545     {
546         // based on zip.lib.php 2.2 from phpMyBackupAdmin
547         // offical zip format: http://www.pkware.com/appnote.txt
548
549         // End of central directory record
550         $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
551
552         // "local file header" segment
553         $unc_len = strlen($fileData);
554         $crc = crc32($fileData);
555         $zdata = gzcompress($fileData);
556
557         // string needed for decoding (because of crc bug)
558         //$name_suffix = substr($zdata, -4, 4);
559         //$name_suffix2 = "_";
560         //for ($i = 0; $i < 4; $i++)
561         //      $name_suffix2 .= sprintf("%03d", ord($name_suffix[$i]));
562         //$backupfile = substr($backupfile, 0, strlen($backupfile) - 8) . $name_suffix2 . ".sql.zip";
563         $name = substr($backupfile, 0, strlen($backupfile) -4);
564
565         // fix crc bug
566         $zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2);
567         $c_len = strlen($zdata);
568
569         // dos time
570         $timearray = getdate($zip);
571         $dostime = (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) |
572             ($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
573         $dtime = dechex($dostime);
574         $hexdtime = "\x" . $dtime[6] . $dtime[7] . "\x" . $dtime[4].$dtime[5] . "\x" . $dtime[2] . $dtime[3] . "\x" . $dtime[0] . $dtime[1];
575         eval('$hexdtime="' . $hexdtime . '";');
576
577         // ver needed to extract, gen purpose bit flag, compression method, last mod time and date
578         $sub1 = "\x14\x00" . "\x00\x00" . "\x08\x00" . $hexdtime;
579
580         // crc32, compressed filesize, uncompressed filesize
581         $sub2 = pack('V', $crc) . pack('V', $c_len) . pack('V', $unc_len);
582
583         $fr = "\x50\x4b\x03\x04" . $sub1. $sub2;
584
585         // length of filename, extra field length
586         $fr .= pack('v', strlen($name)) . pack('v', 0);
587         $fr .= $name;
588
589         // "file data" segment and "data descriptor" segment (optional but necessary if archive is not served as file)
590         $fr .= $zdata . $sub2;
591
592         // now add to central directory record
593         $cdrec = "\x50\x4b\x01\x02";
594         $cdrec .= "\x00\x00";                // version made by
595         $cdrec .= $sub1 . $sub2;
596
597          // length of filename, extra field length, file comment length, disk number start, internal file attributes, external file attributes - 'archive' bit set, offset
598         $cdrec .= pack('v', strlen($name)) . pack('v', 0) . pack('v', 0) . pack('v', 0) . pack('v', 0) . pack('V', 32) . pack('V',0);
599         $cdrec .= $name;
600
601         // combine data
602         $fileData = $fr . $cdrec . $eof_ctrl_dir;
603
604         // total # of entries "on this disk", total # of entries overall, size of central dir, offset to start of central dir, .zip file comment length
605         $fileData .= pack('v', 1) . pack('v', 1) . pack('V', strlen($cdrec)) . pack('V', strlen($fr)) . "\x00\x00";
606
607         if ($zp = @fopen(BACKUP_PATH . $backupfile, "a"))
608         {
609                         @fwrite($zp, $fileData);
610                         @fclose($zp);
611                         return true;
612         }
613         else
614         {
615                 return false;
616         }
617
618         // uncompressed
619     }
620     else
621     {
622         if ($zp = @fopen(BACKUP_PATH . $backupfile, "a"))
623         {
624                         @fwrite($zp, $fileData);
625                         @fclose($zp);
626                         return true;
627         }
628         else
629         {
630                 return false;
631         }
632     }
633 }
634
635 function create_comp_dirs($comp_path, $comp_subdirs)
636 {
637                 $index = "<?php\nheader(\"Location: ../index.php\");\n?>";
638             $cdir = $comp_path;
639             @mkdir($cdir);
640                 $f = @fopen("$cdir/index.php", "wb");
641                 @fwrite($f, $index);
642                 @fclose($f);
643
644             foreach($comp_subdirs as $dir)
645             {
646                         @mkdir($cdir.'/'.$dir);
647                         $f = @fopen("$cdir/$dir/index.php", "wb");
648                         @fwrite($f, $index);
649                         @fclose($f);
650             }
651 }
652 ?>