science-ation/config/backuprestore.php

349 lines
13 KiB
PHP

<?
/*
* This file is part of the 'Science Fair In A Box' project
* SFIAB Website: http://www.sfiab.ca
*
* Copyright (C) 2008 James Grant <james@lightbox.org>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
?>
<?
require ('../common.inc.php');
require_once ('../user.inc.php');
user_auth_required('committee', 'config');
// make sure backup/restore folder exists, and htaccess it to deny access
if (!file_exists('../data/backuprestore'))
mkdir('../data/backuprestore');
if (!file_exists('../data/backuprestore/.htaccess'))
file_put_contents('../data/backuprestore/.htaccess', "Order Deny,Allow\r\nDeny From All\r\n");
if (get_value_from_array($_GET, 'action') == 'backup') {
$ts = time();
$dump = '#SFIAB SQL BACKUP: ' . date('r', $ts) . "\n";
$dump .= '#SFIAB VERSION: ' . $config['version'] . "\n";
$dump .= '#SFIAB DB VERSION: ' . $config['DBVERSION'] . "\n";
$dump .= '#SFIAB FAIR NAME: ' . $config['fairname'] . "\n";
$dump .= "#-------------------------------------------------\n";
$tableq = $pdo->prepare("SHOW TABLES FROM ?");
$tableq->execute($DBNAME);
while ($tr = $tableq->fetch(PDO::FETCH_NUM)) {
$table = $tr[0];
$dump .= "#TABLE: $table\n";
$columnq = $pdo->prepare("SHOW COLUMNS FROM ?");
$columnq->execute($table);
$str = "INSERT INTO `$table` (";
unset($fields);
$fields = array();
while ($cr = $columnq->fetch(PDO::FETCH_OBJ)) {
$str .= '`' . $cr->Field . '`,';
$fields[] = $cr->Field;
}
$str = substr($str, 0, -1);
$str .= ') VALUES (';
$dataq = $pdo->prepare("SELECT * FROM `$table` ORDER BY ?");
$dataq->execute([$fields[0]]);
while ($data = $dataq->fetch(PDO::FETCH_OBJ)) {
$insertstr = $str;
foreach ($fields AS $field) {
if (is_null($data->$field))
$insertstr .= 'NULL,';
else {
$escaped = str_replace('\\', '\\\\', $data->$field);
$escaped = str_replace("'", "''", $escaped);
$escaped = str_replace("\n", '\n', $escaped);
$escaped = str_replace("\r", '\r', $escaped);
$insertstr .= "'" . $escaped . "',";
}
}
$insertstr = substr($insertstr, 0, -1);
$insertstr .= ');';
$dump .= $insertstr . "\n";
}
}
header('Content-Type: text/sql');
header('Content-Disposition: attachment; filename=sfiab_backup_' . date('Y-m-d-H-i-s', $ts) . '.sql');
header('Content-Length: ' . strlen($dump));
// Make IE with SSL work
header('Pragma: public');
echo $dump;
} else if (get_value_from_array($_POST, 'action') == 'restore') {
echo send_header('Database Backup/Restore',
array('Committee Main' => 'committee_main.php',
'SFIAB Configuration' => 'config/index.php'),
'backup_restore');
echo i18n('Processing file: %1', array($_FILES['restore']['name'])) . "<br />\n";
echo "<br />\n";
do {
// hmm just some random filename
$tmpfilename = md5(rand() . time() . $_FILES['restore']['name']);
} while (file_exists("../data/backuprestore/$tmpfilename"));
move_uploaded_file($_FILES['restore']['tmp_name'], "../data/backuprestore/$tmpfilename");
$fp = fopen("../data/backuprestore/$tmpfilename", 'r');
for ($x = 0; $x < 4; $x++) {
$line = fgets($fp, 1024);
$hdr[$x] = split(':', trim($line), 2);
}
fclose($fp);
if (trim($hdr[0][0]) == '#SFIAB SQL BACKUP') {
echo "<table class=\"tableview\">\n";
$now = date('r');
echo '<tr><th>' . i18n('Information') . '</th><th>' . i18n('Restore File') . '</th><th>' . i18n('Live System') . "</th></tr>\n";
if (trim($hdr[0][1]) < trim($now))
$cl = 'happy';
else {
$cl = 'error';
$err = true;
}
echo "<tr class=\"$cl\"><td>" . i18n('Date/Time') . '</td><td>' . $hdr[0][1] . "</td><td>$now</td></tr>\n";
if (version_compare(trim($hdr[1][1]), $config['version']) == 0)
$cl = 'happy';
else {
$cl = 'error';
$err = true;
}
echo "<tr class=\"$cl\"><td>" . i18n('SFIAB Version') . '</td><td>' . $hdr[1][1] . '</td><td>' . $config['version'] . "</td></tr>\n";
if (version_compare(trim($hdr[2][1]), $config['DBVERSION']) == 0)
$cl = 'happy';
else {
$cl = 'error';
$err = true;
}
echo "<tr class=\"$cl\"><td>" . i18n('Database Version') . '</td><td>' . $hdr[2][1] . '</td><td>' . $config['DBVERSION'] . "</td></tr>\n";
if (trim($hdr[3][1]) == $config['fairname'])
$cl = 'happy';
else {
$cl = 'error';
$err = true;
}
echo "<tr class=\"$cl\"><td>" . i18n('Fair Name') . '</td><td>' . $hdr[3][1] . '</td><td>' . $config['fairname'] . "</td></tr>\n";
echo "</table>\n";
echo "<br />\n";
if ($err) {
echo error(i18n('Warning, there are discrepencies between the restore file and your current live system. Proceed at your own risk!'));
}
echo "<form method=\"post\" action=\"backuprestore.php\">\n";
echo "<input type=\"hidden\" name=\"action\" value=\"restoreproceed\">\n";
echo '<input type="hidden" name="filename" value="' . $_FILES['restore']['name'] . "\">\n";
echo "<input type=\"hidden\" name=\"realfilename\" value=\"$tmpfilename\">\n";
echo '<input type="submit" value="' . i18n('Proceed with Database Restore') . '">';
echo "</form>\n";
echo "<br />\n";
echo '<a href="backuprestore.php">';
echo i18n('If you are not going to proceed, please click here to clean up the temporary files which may contain confidential information!');
echo "</a>\n";
} else {
echo error(i18n('This file is NOT a SFIAB SQL BACKUP file'));
echo i18n('Only backups created with the SFIAB Backup Creator can be used to restore from.');
echo "<br />\n";
}
send_footer();
} else if (get_value_from_array($_POST, 'action') == 'restoreproceed') {
echo send_header('Database Backup/Restore',
array('Committee Main' => 'committee_main.php',
'SFIAB Configuration' => 'config/index.php'),
'backup_restore');
// make sure the filename's good before we used it
if (mb_ereg('^[a-z0-9]{32}$', $_POST['realfilename']) && file_exists('../data/backuprestore/' . $_POST['realfilename'])) {
$filename = $_POST['realfilename'];
echo i18n('Proceeding with database restore from %1', array($_POST['filename'])) . '...';
$lines = file("../data/backuprestore/$filename");
$err = false;
echo '<pre>';
foreach ($lines AS $line) {
$line = trim($line);
if (mb_ereg('^#TABLE: (.*)', $line, $args)) {
// empty out the table
$sql = "TRUNCATE TABLE $args[1]";
// echo $sql."\n";
$stmt = $pdo->prepare($sql);
$stmt->execute();
} else if (mb_ereg('^#', $line)) {
// just skip it
} else {
// insert the new data
$stmt = $pdo->prepare($line);
$stmt->execute();
if ($pdo->errorInfo()) {
echo $line . "\n";
echo $pdo->errorInfo() . "\n";
$err = true;
}
}
}
echo '</pre>';
if ($err) {
echo error(i18n('An error occured while importing the restore database'));
} else
echo happy(i18n('Database successfully restored'));
unlink("../data/backuprestore/$filename");
} else
echo error(i18n('Invalid filename'));
send_footer();
} else if (get_value_from_array($_POST, 'action') == 'clean_judges') {
// select all judges
$query = $pdo->prepare('SELECT * FROM users WHERE types LIKE "judge"');
$query->execute();
show_pdo_errors_if_any($pdo);
// Go through each judge and test:
while ($judge = $query->fetch(PDO::FETCH_ASSOC)) {
// if they are deleted
if ($judge['deleted'] == 'yes') {
// Make types an array if it isn't already. Allows user_purge function to work properly
if (!is_array($judge['types'])) {
$judge['types'] = array($judge['types']);
}
user_purge($judge, 'judge');
} else {
// Find max year of judge
$max_year_query = $pdo->prepare('SELECT year FROM users WHERE uid =? ORDER BY year DESC limit 1');
$max_year_query->execute([$judge['uid']]);
$judge_max_year = $max_year_query->fetch(PDO::FETCH_ASSOC);
// Grab old judge info.
// Old judge info consists of all entries in the database that are not the most recent for the specific judge
$deletable = $pdo->prepare('SELECT * FROM users WHERE uid =? AND year NOT LIKE ?');
$deletable->execute([$judge['uid'],$judge_max_year['year']]);
// and if they have old data from previous fair years
if ($deletable->rowCount() > 0) {
// delete old data one by one
while ($old_judge_data = $deletable->fetch(PDO::FETCH_ASSOC)) {
if (!isset($old_judge_data['type']) && !is_array($old_judge_data['type'])) {
$old_judge_data['types'] = array($old_judge_data['types']);
}
user_purge($old_judge_data, 'judge');
}
}
}
}
echo send_header('Database Backup/Restore',
array('Committee Main' => 'committee_main.php',
'SFIAB Configuration' => 'config/index.php'),
'backup_restore');
$stmt = $pdo->prepare('OPTIMIZE TABLE users, users_judge');
$stmt->execute();
if ($pdo->errorInfo()[0] == 0) {
echo happy(i18n('Old judge data purged.'));
} else {
error(i18n($pdo->errorInfo()[0]));
}
} else if (get_value_from_array($_POST, 'action') == 'clean_parents') {
$query_parents = $pdo->prepare('SELECT * FROM users WHERE types LIKE "parent" AND year !=?');
$query_parents->execute([$config['FAIRYEAR']]);
while ($parent = $query_parents->fetch(PDO::FETCH_ASSOC)) {
if (!is_array($parent['types'])) {
$parent['types'] = array($parent['types']);
}
user_purge($parent, 'parent');
}
echo send_header('Database Backup/Restore',
array('Committee Main' => 'committee_main.php',
'SFIAB Configuration' => 'config/index.php'),
'backup_restore');
$stmt = $pdo->prepare('OPTIMIZE TABLE users, users_parent');
$stmt->execute();
if ($pdo->errorInfo()[0] == 0) {
echo happy(i18n('Old parent data purged.'));
} else {
error(i18n($pdo->errorInfo()[0]));
}
} else {
echo send_header('Database Backup/Restore',
array('Committee Main' => 'committee_main.php',
'SFIAB Configuration' => 'config/index.php'),
'backup_restore');
// we try to remove temp files every time we load this page, who knows, maybe they navigated away
// last time instead of clicking the link to come back here
$dh = opendir('../data/backuprestore');
$removed = false;
while ($fn = readdir($dh)) {
if (mb_ereg('[a-z0-9]{32}', $fn)) {
unlink("../data/backuprestore/$fn");
$removed = true;
}
}
closedir($dh);
if ($removed) {
echo happy(i18n('Temporary files successfully removed'));
}
echo '<h3>' . i18n('Backup Database') . "</h3>\n";
echo '<a href="backuprestore.php?action=backup">' . i18n('Create Database Backup File') . "</a><br />\n";
echo "<br /><br />\n";
echo "<hr />\n";
echo '<h3>' . i18n('Restore Database') . "</h3>\n";
echo error(i18n('WARNING: Importing a backup will completely DESTROY all data currently in the database and replace it with what is in the backup file'));
echo "<form method=\"post\" action=\"backuprestore.php\" enctype=\"multipart/form-data\">\n";
echo "<input type=\"hidden\" name=\"action\" value=\"restore\">\n";
echo "<input type=\"file\" name=\"restore\">\n";
echo '<input type="submit" value="' . i18n('Upload Restore File') . "\">\n";
echo "</form>\n";
echo '<br>';
echo '<h3>' . i18n('Clean Database') . "</h3>\n";
echo error(i18n('WARNING: Cleaning the database COMPLETELY DELETES old data on users'));
echo '<font size = 4 color="red"> RECOMMENDED: Backup database before using the below buttons</font><br><br>';
echo "<font color=\"red\"> Remove Old Judge Data <ul> <li> All information about who has judged in past fairs will be lost
\t <li> All deleted judges will be purged from the system </ul></font>";
echo "<br><font color=\"red\"> Remove Old Emergency Contact / Parent Data<ul> <li> All parent information or other emergency contact information from all previous fair years will be purged from the system
\t \t\t\t\t\t\t\t\t\t\t\t\t<li> It will no longer be possible to email any emergency contacts from previous fair years once the button is clicked </ul></font><br>";
echo '<table>';
echo '<tr><td style = "width: 46%">';
echo "<form method=\"post\" action=\"backuprestore.php\" enctype=\"multipart/form-data\">\n";
echo "<input type=\"hidden\" name=\"action\" value=\"clean_judges\">\n";
echo '<input type="submit" onClick="return confirmClick(\'Are you sure you wish to purge old judge data?\')" value="' . i18n('Remove Old Judge Data') . "\">\n";
echo '</form>';
echo '</td><td>';
echo "<form method=\"post\" action=\"backuprestore.php\" enctype=\"multipart/form-data\">\n";
echo "<input type=\"hidden\" name=\"action\" value=\"clean_parents\">\n";
echo '<input type="submit" onClick="return confirmClick(\'Are you sure you wish to purge old parent data?\')" value="' . i18n('Remove Old Emergency Contact / Parent Data') . "\">\n";
echo '</form>';
send_footer();
}
?>