Skip to content

Commit

Permalink
feat(backup): added verification of backup files
Browse files Browse the repository at this point in the history
  • Loading branch information
thorsten committed Oct 9, 2022
1 parent ac3d7e0 commit 37123ed
Show file tree
Hide file tree
Showing 23 changed files with 662 additions and 131 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -15,6 +15,7 @@ This is a log of major user-visible changes in each phpMyFAQ release.
- added HTTPS support for local Docker development (Thorsten)
- added Monolog v2 as logging solution (Thorsten)
- added REST API v2.2 to fetch groups (Thorsten)
- added verification of backup files (Thorsten)
- migrated from SwiftMailer to Symfony Mailer (Thorsten)
- updated to Bootstrap v5.1 (Thorsten)
- updated to TinyMCE v5.10 (Thorsten)
Expand Down
1 change: 1 addition & 0 deletions composer.json
Expand Up @@ -29,6 +29,7 @@
"ext-filter": "*",
"ext-gd": "*",
"ext-json": "*",
"ext-sodium": "*",
"ext-xml": "*",
"ext-zip": "*",
"ext-xmlwriter": "*",
Expand Down
19 changes: 8 additions & 11 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 23 additions & 41 deletions phpmyfaq/admin/backup.export.php
Expand Up @@ -15,6 +15,7 @@
* @since 2009-08-18
*/

use phpMyFAQ\Backup;
use phpMyFAQ\Database;
use phpMyFAQ\Database\DatabaseHelper;
use phpMyFAQ\Filter;
Expand Down Expand Up @@ -49,11 +50,12 @@
}

if ($user->perm->hasPermission($user->getUserId(), 'backup')) {
$tables = $tableNames = $faqConfig->getDb()->getTableNames(Database::getTablePrefix());
$tablePrefix = (Database::getTablePrefix() !== '') ? Database::getTablePrefix() . '.phpmyfaq' : 'phpmyfaq';
$tables = $faqConfig->getDb()->getTableNames(Database::getTablePrefix());
$tableNames = '';
$majorVersion = substr($faqConfig->getVersion(), 0, 3);

$dbHelper = new DatabaseHelper($faqConfig);
$backup = new Backup($faqConfig, $dbHelper);

$httpHelper = new HttpHelper();
$httpHelper->addHeader();
$httpHelper->addExtraHeader('Content-Type: application/octet-stream');
Expand Down Expand Up @@ -82,49 +84,29 @@
break;
}

$text[] = '-- pmf' . $majorVersion . ': ' . $tableNames;
$text[] = '-- DO NOT REMOVE THE FIRST LINE!';
$text[] = '-- pmftableprefix: ' . Database::getTablePrefix();
$text[] = '-- DO NOT REMOVE THE LINES ABOVE!';
$text[] = '-- Otherwise this backup will be broken.';

switch ($action) {
case 'backup_content':
$header = sprintf(
'Content-Disposition: attachment; filename=%s',
urlencode(
sprintf(
'%s-data.%s.sql',
$tablePrefix,
date('Y-m-d-H-i-s')
)
)
);
$httpHelper->addExtraHeader($header);
foreach (explode(' ', $tableNames) as $table) {
echo implode("\r\n", $text);
if ('' !== $table) {
$text = $dbHelper->buildInsertQueries('SELECT * FROM ' . $table, $table);
}
$backupQueries = $backup->generateBackupQueries($tableNames);
try {
$backupFileName = $backup->createBackup(Backup::BACKUP_TYPE_DATA, $backupQueries);
$header = sprintf('Content-Disposition: attachment; filename=%s', urlencode($backupFileName));
$httpHelper->addExtraHeader($header);

echo $backupQueries;
} catch (SodiumException $e) {
// Handle exception
}
break;
case 'backup_logs':
$header = sprintf(
'Content-Disposition: attachment; filename=%s',
urlencode(
sprintf(
'%s-logs.%s.sql',
$tablePrefix,
date('Y-m-d-H-i-s')
)
)
);
$httpHelper->addExtraHeader($header);
foreach (explode(' ', $tableNames) as $table) {
echo implode("\r\n", $text);
if ('' !== $table) {
$text = $dbHelper->buildInsertQueries('SELECT * FROM ' . $table, $table);
}
$backupQueries = $backup->generateBackupQueries($tableNames);
try {
$backupFileName = $backup->createBackup(Backup::BACKUP_TYPE_LOGS, $backupQueries);
$header = sprintf('Content-Disposition: attachment; filename=%s', urlencode($backupFileName));
$httpHelper->addExtraHeader($header);

echo $backupQueries;
} catch (SodiumException $e) {
// Handle exception
}
break;
}
Expand Down
37 changes: 30 additions & 7 deletions phpmyfaq/admin/backup.import.php
Expand Up @@ -15,11 +15,13 @@
* @since 2003-02-24
*/

use phpMyFAQ\Backup;
use phpMyFAQ\Component\Alert;
use phpMyFAQ\Database;
use phpMyFAQ\Database\DatabaseHelper;
use phpMyFAQ\Filter;
use phpMyFAQ\Strings;
use phpMyFAQ\Translation;

if (!defined('IS_VALID_PHPMYFAQ')) {
http_response_code(400);
Expand All @@ -37,7 +39,7 @@
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">
<i aria-hidden="true" class="fa fa-download"></i>
<?= $PMF_LANG['ad_csv_rest'] ?>
<?= Translation::get('ad_csv_rest') ?>
</h1>
</div>
<?php
Expand All @@ -47,20 +49,38 @@
$ok = 1;
$fileInfo = new finfo(FILEINFO_MIME_ENCODING);

$dbHelper = new DatabaseHelper($faqConfig);
$backup = new Backup($faqConfig, $dbHelper);

if ('utf-8' !== $fileInfo->file($_FILES['userfile']['tmp_name'])) {
echo 'This file is not UTF-8 encoded.<br>';
$ok = 0;
}

$handle = fopen($_FILES['userfile']['tmp_name'], 'r');
$backupData = fgets($handle, 65536);
$versionFound = Strings::substr($backupData, 0, 9);
$versionExpected = '-- pmf' . substr($faqConfig->getVersion(), 0, 3);
$queries = [];

$fileName = $_FILES['userfile']['name'];

try {
$verification = $backup->verifyBackup(file_get_contents($_FILES['userfile']['tmp_name']), $fileName);
if ($verification) {
$ok = 1;
} else {
$ok = 0;
}
} catch (SodiumException $e) {
echo 'This file cannot be verified.<br>';
$ok = 0;
}

if ($versionFound !== $versionExpected) {
printf(
'%s (Version check failure: "%s" found, "%s" expected)',
$PMF_LANG['ad_csv_no'],
Translation::get('ad_csv_no'),
$versionFound,
$versionExpected
);
Expand All @@ -78,7 +98,7 @@

if ($ok == 1) {
$tablePrefix = '';
printf("<p>%s</p>\n", $PMF_LANG['ad_csv_prepare']);
printf("<p>%s</p>\n", Translation::get('ad_csv_prepare'));
while ($backupData = fgets($handle, 65536)) {
$backupData = trim($backupData);
$backupPrefixPattern = '-- pmftableprefix:';
Expand All @@ -93,11 +113,14 @@

$k = 0;
$g = 0;
printf("<p>%s</p>\n", $PMF_LANG['ad_csv_process']);

printf("<p>%s</p>\n", Translation::get('ad_csv_process'));

$numTables = count($queries);
$kg = '';
for ($i = 0; $i < $numTables; ++$i) {
$queries[$i] = DatabaseHelper::alignTablePrefix($queries[$i], $tablePrefix, Database::getTablePrefix());

$kg = $faqConfig->getDb()->query($queries[$i]);
if (!$kg) {
printf(
Expand All @@ -119,9 +142,9 @@
printf(
'<p class="alert alert-success">%d %s %d %s</p>',
$g,
$PMF_LANG['ad_csv_of'],
Translation::get('ad_csv_of'),
$numTables,
$PMF_LANG['ad_csv_suc']
Translation::get('ad_csv_suc')
);
}
} else {
Expand All @@ -138,5 +161,5 @@
echo Alert::danger('ad_csv_no', $errorMessage);
}
} else {
echo $PMF_LANG['err_NotAuth'];
echo Translation::get('err_NotAuth');
}

0 comments on commit 37123ed

Please sign in to comment.