2014-03-30 21:59:26 +02:00
|
|
|
<?php
|
|
|
|
|
2016-08-25 03:17:58 +02:00
|
|
|
namespace Miniflux\Model\AutoUpdate;
|
2014-03-30 21:59:26 +02:00
|
|
|
|
|
|
|
use ZipArchive;
|
|
|
|
use DirectoryIterator;
|
|
|
|
use RecursiveIteratorIterator;
|
|
|
|
use RecursiveDirectoryIterator;
|
2016-08-25 03:17:58 +02:00
|
|
|
use Miniflux\Model\Config;
|
2014-03-30 21:59:26 +02:00
|
|
|
|
|
|
|
// Get all files of a given directory
|
|
|
|
function get_files_list($directory)
|
|
|
|
{
|
|
|
|
$exclude_list = array(
|
|
|
|
'.git',
|
|
|
|
'data',
|
|
|
|
'scripts',
|
|
|
|
'config.php',
|
2015-04-11 02:34:48 +02:00
|
|
|
'rules',
|
2014-03-30 21:59:26 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
$it = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($directory), RecursiveIteratorIterator::SELF_FIRST);
|
|
|
|
$files = array();
|
|
|
|
|
|
|
|
while ($it->valid()) {
|
|
|
|
if ($it->isFile() && ! is_excluded_path($it->getSubPathname(), $exclude_list)) {
|
|
|
|
$files[] = $it->getSubPathname();
|
|
|
|
}
|
|
|
|
|
|
|
|
$it->next();
|
|
|
|
}
|
|
|
|
|
|
|
|
return $files;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the given path is excluded
|
|
|
|
function is_excluded_path($path, array $exclude_list)
|
|
|
|
{
|
|
|
|
foreach ($exclude_list as $excluded_path) {
|
|
|
|
if (strpos($path, $excluded_path) === 0) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Synchronize 2 directories (copy/remove files)
|
|
|
|
function synchronize($source_directory, $destination_directory)
|
|
|
|
{
|
2015-01-18 00:53:40 +01:00
|
|
|
Config\debug('[SYNCHRONIZE] '.$source_directory.' to '.$destination_directory);
|
2014-03-30 21:59:26 +02:00
|
|
|
|
|
|
|
$src_files = get_files_list($source_directory);
|
|
|
|
$dst_files = get_files_list($destination_directory);
|
|
|
|
|
|
|
|
// Remove files
|
|
|
|
$remove_files = array_diff($dst_files, $src_files);
|
|
|
|
|
|
|
|
foreach ($remove_files as $file) {
|
2014-04-06 04:10:10 +02:00
|
|
|
if ($file !== '.htaccess') {
|
|
|
|
$destination_file = $destination_directory.DIRECTORY_SEPARATOR.$file;
|
2015-01-18 00:53:40 +01:00
|
|
|
Config\debug('[REMOVE] '.$destination_file);
|
2014-03-30 21:59:26 +02:00
|
|
|
|
2014-04-06 04:10:10 +02:00
|
|
|
if (! @unlink($destination_file)) {
|
|
|
|
return false;
|
|
|
|
}
|
2014-03-30 21:59:26 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Overwrite all files
|
|
|
|
foreach ($src_files as $file) {
|
|
|
|
$directory = $destination_directory.DIRECTORY_SEPARATOR.dirname($file);
|
|
|
|
|
|
|
|
if (! is_dir($directory)) {
|
2015-01-18 00:53:40 +01:00
|
|
|
Config\debug('[MKDIR] '.$directory);
|
2014-03-30 21:59:26 +02:00
|
|
|
|
|
|
|
if (! @mkdir($directory, 0755, true)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$source_file = $source_directory.DIRECTORY_SEPARATOR.$file;
|
|
|
|
$destination_file = $destination_directory.DIRECTORY_SEPARATOR.$file;
|
|
|
|
|
2015-01-18 00:53:40 +01:00
|
|
|
Config\debug('[COPY] '.$source_file.' to '.$destination_file);
|
2014-03-30 21:59:26 +02:00
|
|
|
|
|
|
|
if (! @copy($source_file, $destination_file)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Download and unzip the archive
|
|
|
|
function uncompress_archive($url, $download_directory = AUTO_UPDATE_DOWNLOAD_DIRECTORY, $archive_directory = AUTO_UPDATE_ARCHIVE_DIRECTORY)
|
|
|
|
{
|
|
|
|
$archive_file = $download_directory.DIRECTORY_SEPARATOR.'update.zip';
|
|
|
|
|
2015-01-18 00:53:40 +01:00
|
|
|
Config\debug('[DOWNLOAD] '.$url);
|
2014-03-30 21:59:26 +02:00
|
|
|
|
|
|
|
if (($data = @file_get_contents($url)) === false) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (@file_put_contents($archive_file, $data) === false) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-01-18 00:53:40 +01:00
|
|
|
Config\debug('[UNZIP] '.$archive_file);
|
2014-03-30 21:59:26 +02:00
|
|
|
|
|
|
|
$zip = new ZipArchive;
|
|
|
|
|
|
|
|
if (! $zip->open($archive_file)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$zip->extractTo($archive_directory);
|
|
|
|
$zip->close();
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Remove all files for a given directory
|
|
|
|
function cleanup_directory($directory)
|
|
|
|
{
|
2015-01-18 00:53:40 +01:00
|
|
|
Config\debug('[CLEANUP] '.$directory);
|
2014-03-30 21:59:26 +02:00
|
|
|
|
|
|
|
$dir = new DirectoryIterator($directory);
|
|
|
|
|
|
|
|
foreach ($dir as $fileinfo) {
|
|
|
|
if (! $fileinfo->isDot()) {
|
|
|
|
$filename = $fileinfo->getRealPath();
|
|
|
|
|
|
|
|
if ($fileinfo->isFile()) {
|
2016-08-25 03:17:58 +02:00
|
|
|
Config\debug('[REMOVE] '.$filename);
|
2014-03-30 21:59:26 +02:00
|
|
|
@unlink($filename);
|
2016-04-18 01:44:45 +02:00
|
|
|
} else {
|
2014-03-30 21:59:26 +02:00
|
|
|
cleanup_directory($filename);
|
|
|
|
@rmdir($filename);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cleanup all temporary directories
|
|
|
|
function cleanup_directories()
|
|
|
|
{
|
|
|
|
cleanup_directory(AUTO_UPDATE_DOWNLOAD_DIRECTORY);
|
|
|
|
cleanup_directory(AUTO_UPDATE_ARCHIVE_DIRECTORY);
|
|
|
|
cleanup_directory(AUTO_UPDATE_BACKUP_DIRECTORY);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find the archive directory name
|
|
|
|
function find_archive_root($base_directory = AUTO_UPDATE_ARCHIVE_DIRECTORY)
|
|
|
|
{
|
|
|
|
$directory = '';
|
|
|
|
$dir = new DirectoryIterator($base_directory);
|
|
|
|
|
|
|
|
foreach ($dir as $fileinfo) {
|
|
|
|
if (! $fileinfo->isDot() && $fileinfo->isDir()) {
|
|
|
|
$directory = $fileinfo->getFilename();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (empty($directory)) {
|
2015-01-18 00:53:40 +01:00
|
|
|
Config\debug('[FIND ARCHIVE] No directory found');
|
2014-03-30 21:59:26 +02:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$path = $base_directory.DIRECTORY_SEPARATOR.$directory;
|
2015-01-18 00:53:40 +01:00
|
|
|
Config\debug('[FIND ARCHIVE] '.$path);
|
2014-03-30 21:59:26 +02:00
|
|
|
|
|
|
|
return $path;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if everything is setup correctly
|
|
|
|
function check_setup()
|
|
|
|
{
|
2016-04-18 01:44:45 +02:00
|
|
|
if (! class_exists('ZipArchive')) {
|
|
|
|
die('To use this feature, your PHP installation must be able to uncompress zip files!');
|
|
|
|
}
|
2014-03-30 21:59:26 +02:00
|
|
|
|
2016-04-18 01:44:45 +02:00
|
|
|
if (AUTO_UPDATE_DOWNLOAD_DIRECTORY === '') {
|
|
|
|
die('The constant AUTO_UPDATE_DOWNLOAD_DIRECTORY is not set!');
|
|
|
|
}
|
|
|
|
if (AUTO_UPDATE_ARCHIVE_DIRECTORY === '') {
|
|
|
|
die('The constant AUTO_UPDATE_ARCHIVE_DIRECTORY is not set!');
|
|
|
|
}
|
|
|
|
if (AUTO_UPDATE_DOWNLOAD_DIRECTORY === '') {
|
|
|
|
die('The constant AUTO_UPDATE_DOWNLOAD_DIRECTORY is not set!');
|
|
|
|
}
|
2014-03-30 21:59:26 +02:00
|
|
|
|
2016-04-18 01:44:45 +02:00
|
|
|
if (! is_dir(AUTO_UPDATE_DOWNLOAD_DIRECTORY)) {
|
|
|
|
@mkdir(AUTO_UPDATE_DOWNLOAD_DIRECTORY, 0755);
|
|
|
|
}
|
|
|
|
if (! is_dir(AUTO_UPDATE_ARCHIVE_DIRECTORY)) {
|
|
|
|
@mkdir(AUTO_UPDATE_ARCHIVE_DIRECTORY, 0755);
|
|
|
|
}
|
|
|
|
if (! is_dir(AUTO_UPDATE_BACKUP_DIRECTORY)) {
|
|
|
|
@mkdir(AUTO_UPDATE_BACKUP_DIRECTORY, 0755);
|
|
|
|
}
|
2014-03-30 21:59:26 +02:00
|
|
|
|
2016-04-18 01:44:45 +02:00
|
|
|
if (! is_writable(AUTO_UPDATE_DOWNLOAD_DIRECTORY)) {
|
|
|
|
die('Update directories must be writable by your web server user!');
|
|
|
|
}
|
|
|
|
if (! is_writable(__DIR__)) {
|
|
|
|
die('Source files must be writable by your web server user!');
|
|
|
|
}
|
2014-03-30 21:59:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
// Update the source code
|
|
|
|
function execute($url)
|
|
|
|
{
|
|
|
|
check_setup();
|
|
|
|
cleanup_directories();
|
|
|
|
|
|
|
|
if (uncompress_archive($url)) {
|
|
|
|
$update_directory = find_archive_root();
|
|
|
|
|
|
|
|
if ($update_directory) {
|
|
|
|
|
|
|
|
// Backup first
|
|
|
|
if (synchronize(ROOT_DIRECTORY, AUTO_UPDATE_BACKUP_DIRECTORY)) {
|
|
|
|
|
|
|
|
// Update
|
|
|
|
if (synchronize($update_directory, ROOT_DIRECTORY)) {
|
|
|
|
cleanup_directories();
|
|
|
|
return true;
|
2016-04-18 01:44:45 +02:00
|
|
|
} else {
|
2014-03-30 21:59:26 +02:00
|
|
|
// If update failed, rollback
|
|
|
|
synchronize(AUTO_UPDATE_BACKUP_DIRECTORY, ROOT_DIRECTORY);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|