Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
269 changes: 139 additions & 130 deletions index.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@
*
*/

class UpdateException extends \Exception {
protected $data;

public function __construct($data) {
$this->data = $data;
}

public function getData() {
return $this->data;
}
}

class Updater {
/** @var array */
private $configValues = [];
Expand Down Expand Up @@ -79,6 +91,7 @@ private function getExpectedElementsList() {
'updater',
// Files
'index.html',
'indie.json',
'.user.ini',
'console.php',
'cron.php',
Expand All @@ -102,18 +115,18 @@ private function getExpectedElementsList() {
* @param string $folder
* @return RecursiveIteratorIterator
*/
private function getRecursiveDirectoryIterator($folder = __DIR__ . '/../') {
private function getRecursiveDirectoryIterator($folder = null) {
if ($folder === null) {
$folder = __DIR__ . '/../';
}
return new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($folder, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
}

/**
* Returns the files are are unexpected. If none unexpected files are found
* an empty array is returned.
*
* @return array
* Checks for files that are unexpected.
*/
public function checkForExpectedFilesAndFolders() {
$expectedElements = $this->getExpectedElementsList();
Expand All @@ -124,13 +137,13 @@ public function checkForExpectedFilesAndFolders() {
}
}

return $unexpectedElements;
if (count($unexpectedElements) !== 0) {
throw new UpdateException($unexpectedElements);
}
}

/**
* Returns the files that are not writable
*
* @return array
* Checks for files that are not writable
*/
public function checkWritePermissions() {
// TODO: Exclude data folder
Expand All @@ -140,14 +153,15 @@ public function checkWritePermissions() {
$notWriteablePaths[] = $path;
}
}
return $notWriteablePaths;
if(count($notWriteablePaths) > 0) {
throw new UpdateException($notWriteablePaths);
}
}

/**
* Sets the maintenance mode to the defined value
*
* @param bool $state
* @return bool Whether it was possible to write to the config file or not
*/
public function setMaintenanceMode($state) {
/** @var array $CONFIG */
Expand All @@ -159,7 +173,9 @@ public function setMaintenanceMode($state) {
$content .= var_export($CONFIG, true);
$content .= ";\n";
$state = file_put_contents($configFileName, $content);
return $state !== false;
if ($state === false) {
throw new \Exception('Could not write to config.php');
}
}

/**
Expand Down Expand Up @@ -545,81 +561,44 @@ public function moveNewVersionInPlace() {
// TODO: Note when a step started and when one ended, also to prevent multiple people at the same time accessing the updater
if(isset($_POST['step'])) {
set_time_limit(0);
switch($_POST['step']) {
case '1':
$unexpectedElements = $updater->checkForExpectedFilesAndFolders();
if(count($unexpectedElements) !== 0) {
echo(json_encode(['proceed' => false, 'response' => $unexpectedElements]));
} else {
echo(json_encode(['proceed' => true]));
}
break;
case '2':
$notWriteableFiles = $updater->checkWritePermissions();
if(count($notWriteableFiles) > 0) {
echo(json_encode(['proceed' => false, 'response' => $notWriteableFiles]));
} else {
echo(json_encode(['proceed' => true]));
}
break;
case '3':
$success = $updater->setMaintenanceMode(true);
if($success === true) {
echo(json_encode(['proceed' => true]));
} else {
echo(json_encode(['proceed' => false]));
}
break;
case '4':
try {
try {

switch ($_POST['step']) {
case '1':
$updater->checkForExpectedFilesAndFolders();
break;
case '2':
$updater->checkWritePermissions();
break;
case '3':
$updater->setMaintenanceMode(true);
break;
case '4':
$updater->createBackup();
echo(json_encode(['proceed' => true]));
} catch (\Exception $e) {
echo(json_encode(['proceed' => false, 'response' => $e->getMessage()]));
}
break;
case '5':
try {
break;
case '5':
$updater->downloadUpdate();
echo(json_encode(['proceed' => true]));
} catch (\Exception $e) {
echo(json_encode(['proceed' => false, 'response' => $e->getMessage()]));
}
break;
case '6':
try {
break;
case '6':
$updater->extractDownload();
echo(json_encode(['proceed' => true]));
} catch (\Exception $e) {
echo(json_encode(['proceed' => false, 'response' => $e->getMessage()]));
}
break;
case '7':
// TODO: If it fails after step 7: Rollback
try {
break;
case '7':
// TODO: If it fails after step 7: Rollback
$updater->replaceEntryPoints();
echo(json_encode(['proceed' => true]));
} catch (\Exception $e) {
echo(json_encode(['proceed' => false, 'response' => $e->getMessage()]));
}
break;
case '8':
try {
break;
case '8':
$updater->deleteOldFiles();
echo(json_encode(['proceed' => true]));
} catch (\Exception $e) {
echo(json_encode(['proceed' => false, 'response' => $e->getMessage()]));
}
break;
case '9':
try {
break;
case '9':
$updater->moveNewVersionInPlace();
$updater->setMaintenanceMode(false);
echo(json_encode(['proceed' => true]));
} catch (\Exception $e) {
echo(json_encode(['proceed' => false, 'response' => $e->getMessage()]));
}
break;
break;
}
echo(json_encode(['proceed' => true]));
} catch (UpdateException $e) {
echo(json_encode(['proceed' => false, 'response' => $e->getData()]));
} catch (\Exception $e) {
echo(json_encode(['proceed' => false, 'response' => $e->getMessage()]));
}

die();
Expand Down Expand Up @@ -655,71 +634,101 @@ function addStepText(text) {
document.getElementById('progress').innerHTML = previousValue + "\r" + text;
}

function performStep(number) {
function performStep(number, callback) {
var httpRequest = new XMLHttpRequest();
httpRequest.open("POST", window.location.href, false);
httpRequest.open("POST", window.location.href);
httpRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
httpRequest.onreadystatechange = function () {
if (httpRequest.readyState != 4) { // 4 - request done
return;
}

if (httpRequest.status != 200) {
// failure
}

callback(JSON.parse(httpRequest.responseText));
};
httpRequest.send("step="+number);
var data = httpRequest.responseText;
return JSON.parse(data);
}

// Step 1: Check for expected files
addStepText('Start: Check for expected files');
var response = performStep(1);
if(response.proceed === true) {
addStepText('Success: Check for expected files has succeeded');

// Step 2: Check for write permissions
addStepText('Start: Check for write permissions');
response = performStep(2);
if(response.proceed === true) {
addStepText('Success: Check for write permissions');
var performStepCallbacks = {
1: function(response) {
if(response.proceed === true) {
addStepText('Success: Check for expected files has succeeded');

response = performStep(3);
// Step 2: Check for write permissions
addStepText('Start: Check for write permissions');
performStep(2, performStepCallbacks[2])
} else {
addStepText('Error: Check for all expected files failed. The following extra files have been found:');
response['response'].forEach(function(file) {
addStepText("\t"+file);
});
}
},
2: function(response) {
if(response.proceed === true) {
addStepText('Success: Check for write permissions');

performStep(3, performStepCallbacks[3]);
} else {
addStepText('Error: Check for all write permissions failed. The following places can not be written to:');
response['response'].forEach(function(file) {
addStepText("\t"+file);
});
}
},
3: function(response) {
if(response.proceed === true) {
// TODO: Put into loop and make non-rendering blocking
addStepText('Enabled maintenance mode');

addStepText('Start: Create backup');
performStep(4);
addStepText('Done: Create backup');

addStepText('Start: Download update');
performStep(5);
addStepText('Done: Download update');

addStepText('Start: Extract update');
performStep(6);
addStepText('Done: Extract update');

addStepText('Start: Replace Entry Points');
performStep(7);
addStepText('Done: Replace Entry Points');

addStepText('Start: Delete old files');
performStep(8);
addStepText('Done: Delete old files');

addStepText('Start: Move new files in place');
performStep(9);
addStepText('Done: Move new files in place');
addStepText('!!! Update done !!!');
performStep(4, performStepCallbacks[4]);
} else {
addStepText('Error: Could not enable maintenance mode in config.php');
}
} else {
addStepText('Error: Check for all write permissions failed. The following places can not be written to:');
response['response'].forEach(function(file) {
addStepText("\t"+file);
});
},
4: function(response) {
addStepText('Done: Create backup');

addStepText('Start: Download update');
performStep(5, performStepCallbacks[5]);
},
5: function(response) {
addStepText('Done: Download update');

addStepText('Start: Extract update');
performStep(6, performStepCallbacks[6]);
},
6: function(response) {
addStepText('Done: Extract update');

addStepText('Start: Replace Entry Points');
performStep(7, performStepCallbacks[7]);
},
7: function(response) {
addStepText('Done: Replace Entry Points');

addStepText('Start: Delete old files');
performStep(8, performStepCallbacks[8]);
},
8: function(response) {
addStepText('Done: Delete old files');

addStepText('Start: Move new files in place');
performStep(9, performStepCallbacks[9]);
},
9: function(response) {
addStepText('Done: Move new files in place');
addStepText('!!! Update done !!!');
}
} else {
addStepText('Error: Check for all expected files failed. The following extra files have been found:');
response['response'].forEach(function(file) {
addStepText("\t"+file);
});
}
};

// Step 1: Check for expected files
addStepText('Start: Check for expected files');
performStep(1, performStepCallbacks[1]);
</script>
<?php endif; ?>

Expand Down