2021-08-16 20:48:26 +00:00
|
|
|
<?php
|
2021-09-02 20:14:32 +00:00
|
|
|
/**
|
|
|
|
* @brief pacKman, a plugin for Dotclear 2
|
2021-11-01 10:39:02 +00:00
|
|
|
*
|
2021-09-02 20:14:32 +00:00
|
|
|
* @package Dotclear
|
|
|
|
* @subpackage Plugin
|
2021-11-01 10:39:02 +00:00
|
|
|
*
|
2021-09-02 20:14:32 +00:00
|
|
|
* @author Jean-Christian Denis
|
2021-11-01 10:39:02 +00:00
|
|
|
*
|
2021-09-02 20:14:32 +00:00
|
|
|
* @copyright Jean-Christian Denis
|
|
|
|
* @copyright GPL-2.0 https://www.gnu.org/licenses/gpl-2.0.html
|
|
|
|
*/
|
2022-12-17 21:12:10 +00:00
|
|
|
declare(strict_types=1);
|
|
|
|
|
2022-12-18 23:21:11 +00:00
|
|
|
namespace Dotclear\Plugin\pacKman;
|
2021-08-16 20:48:26 +00:00
|
|
|
|
2023-03-06 00:17:30 +00:00
|
|
|
use dcCore;
|
2022-12-17 21:12:10 +00:00
|
|
|
use dcModules;
|
2023-03-11 17:46:23 +00:00
|
|
|
use Exception;
|
2022-12-17 21:12:10 +00:00
|
|
|
use files;
|
|
|
|
use fileUnzip;
|
|
|
|
use path;
|
|
|
|
|
|
|
|
class Core
|
2021-08-16 20:48:26 +00:00
|
|
|
{
|
2021-11-08 21:19:07 +00:00
|
|
|
public static function quote_exclude(array $exclude): array
|
2021-08-23 13:07:29 +00:00
|
|
|
{
|
2023-03-11 17:46:23 +00:00
|
|
|
foreach (My::EXCLUDED_FILES as $k => $v) {
|
2021-08-23 13:07:29 +00:00
|
|
|
$exclude[$k] = '#(^|/)(' . str_replace(
|
|
|
|
['.', '*'],
|
|
|
|
['\.', '.*?'],
|
|
|
|
trim($v)
|
|
|
|
) . ')(/|$)#';
|
|
|
|
}
|
|
|
|
|
|
|
|
return $exclude;
|
|
|
|
}
|
|
|
|
|
2022-11-12 20:42:05 +00:00
|
|
|
public static function getPackages(string $root): array
|
2021-08-23 13:07:29 +00:00
|
|
|
{
|
2021-10-23 14:46:03 +00:00
|
|
|
$res = [];
|
2021-08-23 13:07:29 +00:00
|
|
|
|
|
|
|
$cache = self::getCache() . '/';
|
|
|
|
if (!is_dir($root) || !is_readable($root)) {
|
|
|
|
return $res;
|
|
|
|
}
|
|
|
|
|
2021-11-01 10:39:02 +00:00
|
|
|
$files = files::scanDir($root);
|
2021-10-23 14:46:03 +00:00
|
|
|
$zip_files = [];
|
2021-11-01 10:39:02 +00:00
|
|
|
foreach ($files as $file) {
|
2021-08-23 13:07:29 +00:00
|
|
|
if (!preg_match('#(^|/)(.*?)\.zip(/|$)#', $file)) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
$zip_files[] = $file;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (empty($zip_files)) {
|
|
|
|
return $res;
|
|
|
|
}
|
|
|
|
|
2023-03-06 00:17:30 +00:00
|
|
|
$sandboxes = [
|
|
|
|
'theme' => clone dcCore::app()->themes,
|
|
|
|
'plugin' => clone dcCore::app()->plugins,
|
|
|
|
];
|
|
|
|
|
2021-08-23 13:07:29 +00:00
|
|
|
$i = 0;
|
2021-11-01 10:39:02 +00:00
|
|
|
foreach ($zip_files as $zip_file) {
|
2023-03-06 00:17:30 +00:00
|
|
|
$zip_file = $root . DIRECTORY_SEPARATOR . $zip_file;
|
|
|
|
$zip = new fileUnzip($zip_file);
|
|
|
|
$zip->getList(false, '#(^|/)(__MACOSX|\.svn|\.hg.*|\.git.*|\.DS_Store|\.directory|Thumbs\.db)(/|$)#');
|
2021-08-23 13:07:29 +00:00
|
|
|
|
|
|
|
$zip_root_dir = $zip->getRootDir();
|
2023-03-06 00:17:30 +00:00
|
|
|
$define = '';
|
2021-08-23 13:07:29 +00:00
|
|
|
if ($zip_root_dir != false) {
|
2023-03-06 00:17:30 +00:00
|
|
|
$target = dirname($zip_file);
|
|
|
|
$destination = $target . DIRECTORY_SEPARATOR . $zip_root_dir;
|
|
|
|
$define = $zip_root_dir . '/' . dcModules::MODULE_FILE_DEFINE;
|
|
|
|
$init = $zip_root_dir . '/' . dcModules::MODULE_FILE_INIT;
|
|
|
|
$has_define = $zip->hasFile($define);
|
2021-08-23 13:07:29 +00:00
|
|
|
} else {
|
2023-03-06 00:17:30 +00:00
|
|
|
$target = dirname($zip_file) . DIRECTORY_SEPARATOR . preg_replace('/\.([^.]+)$/', '', basename($zip_file));
|
|
|
|
$destination = $target;
|
|
|
|
$define = dcModules::MODULE_FILE_DEFINE;
|
|
|
|
$init = dcModules::MODULE_FILE_INIT;
|
|
|
|
$has_define = $zip->hasFile($define);
|
2021-08-23 13:07:29 +00:00
|
|
|
}
|
|
|
|
|
2023-03-06 00:17:30 +00:00
|
|
|
if ($zip->isEmpty()) {
|
|
|
|
$zip->close();
|
|
|
|
|
2021-08-23 13:07:29 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2023-03-06 00:17:30 +00:00
|
|
|
if (!$has_define) {
|
|
|
|
$zip->close();
|
2021-08-23 13:07:29 +00:00
|
|
|
|
2023-03-06 00:17:30 +00:00
|
|
|
continue;
|
2021-08-23 13:07:29 +00:00
|
|
|
}
|
2023-03-06 00:17:30 +00:00
|
|
|
|
|
|
|
foreach ($sandboxes as $type => $sandbox) {
|
|
|
|
try {
|
|
|
|
files::makeDir($destination, true);
|
|
|
|
|
|
|
|
// can't load twice _init.php file !
|
|
|
|
$unlink = false;
|
|
|
|
if ($zip->hasFile($init)
|
2023-03-11 17:46:23 +00:00
|
|
|
// && !dcCore::app()->plugins->getDefine(basename($destination))->isDefined()
|
|
|
|
// && !dcCore::app()->themes->getDefine(basename($destination))->isDefined()
|
2023-03-06 00:17:30 +00:00
|
|
|
) {
|
|
|
|
$unlink = true;
|
|
|
|
$zip->unzip($init, $destination . DIRECTORY_SEPARATOR . dcModules::MODULE_FILE_INIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
$zip->unzip($define, $destination . DIRECTORY_SEPARATOR . dcModules::MODULE_FILE_DEFINE);
|
|
|
|
|
|
|
|
$sandbox->resetModulesList();
|
|
|
|
$sandbox->requireDefine($destination, basename($destination));
|
|
|
|
|
|
|
|
if ($unlink) {
|
|
|
|
unlink($destination . DIRECTORY_SEPARATOR . dcModules::MODULE_FILE_INIT);
|
|
|
|
}
|
|
|
|
|
|
|
|
unlink($destination . DIRECTORY_SEPARATOR . dcModules::MODULE_FILE_DEFINE);
|
|
|
|
|
|
|
|
$new_errors = $sandbox->getErrors();
|
|
|
|
if (!empty($new_errors)) {
|
|
|
|
$new_errors = implode(" \n", $new_errors);
|
|
|
|
|
|
|
|
throw new Exception($new_errors);
|
|
|
|
}
|
|
|
|
|
|
|
|
$module = $sandbox->getDefine(basename($destination));
|
|
|
|
if (!$module->isDefined() || $module->get('type') != $type) {
|
|
|
|
throw new Exception('bad module type');
|
|
|
|
}
|
|
|
|
|
|
|
|
$res[$i] = $module->dump();
|
|
|
|
$res[$i]['root'] = $zip_file;
|
|
|
|
$i++;
|
|
|
|
|
|
|
|
$zip->close();
|
|
|
|
files::deltree($destination);
|
|
|
|
} catch (Exception $e) {
|
|
|
|
$zip->close();
|
|
|
|
files::deltree($destination);
|
|
|
|
|
|
|
|
continue;
|
|
|
|
}
|
2022-02-01 20:39:38 +00:00
|
|
|
}
|
2021-08-23 13:07:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $res;
|
|
|
|
}
|
|
|
|
|
2021-11-08 21:19:07 +00:00
|
|
|
public static function pack(array $info, string $root, array $files, bool $overwrite = false, array $exclude = [], bool $nocomment = false, bool $fixnewline = false): bool
|
2021-08-23 13:07:29 +00:00
|
|
|
{
|
|
|
|
if (!($info = self::getInfo($info))
|
2021-10-23 14:46:03 +00:00
|
|
|
|| !($root = self::getRoot($root))
|
|
|
|
) {
|
2021-08-23 13:07:29 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
$exclude = self::getExclude($exclude);
|
|
|
|
|
2021-11-01 10:39:02 +00:00
|
|
|
foreach ($files as $file) {
|
2021-08-23 13:07:29 +00:00
|
|
|
if (!($file = self::getFile($file, $info))
|
2021-10-23 14:46:03 +00:00
|
|
|
|| !($dest = self::getOverwrite($overwrite, $root, $file))
|
|
|
|
) {
|
2021-08-23 13:07:29 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
@set_time_limit(300);
|
|
|
|
$fp = fopen($dest, 'wb');
|
|
|
|
|
2021-09-02 20:13:21 +00:00
|
|
|
if ($nocomment) {
|
2022-12-18 23:21:11 +00:00
|
|
|
Filezip::$remove_comment = true;
|
2021-09-02 20:13:21 +00:00
|
|
|
}
|
|
|
|
if ($fixnewline) {
|
2022-12-18 23:21:11 +00:00
|
|
|
Filezip::$fix_newline = true;
|
2021-09-02 20:13:21 +00:00
|
|
|
}
|
2022-12-18 23:21:11 +00:00
|
|
|
$zip = new Filezip($fp);
|
2021-08-23 13:07:29 +00:00
|
|
|
|
2021-11-01 10:39:02 +00:00
|
|
|
foreach ($exclude as $e) {
|
2021-08-23 13:07:29 +00:00
|
|
|
$zip->addExclusion($e);
|
|
|
|
}
|
|
|
|
$zip->addDirectory(
|
|
|
|
path::real($info['root']),
|
|
|
|
$info['id'],
|
|
|
|
true
|
|
|
|
);
|
|
|
|
|
|
|
|
$zip->write();
|
|
|
|
$zip->close();
|
|
|
|
unset($zip);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2021-11-08 21:19:07 +00:00
|
|
|
private static function getRoot(string $root): string
|
2021-08-23 13:07:29 +00:00
|
|
|
{
|
2021-11-08 21:19:07 +00:00
|
|
|
$root = (string) path::real($root);
|
2021-08-23 13:07:29 +00:00
|
|
|
if (!is_dir($root) || !is_writable($root)) {
|
2022-11-26 21:20:11 +00:00
|
|
|
throw new Exception(__('Directory is not writable'));
|
2021-08-23 13:07:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $root;
|
|
|
|
}
|
|
|
|
|
2021-11-08 21:19:07 +00:00
|
|
|
private static function getInfo(array $info): array
|
2021-08-23 13:07:29 +00:00
|
|
|
{
|
2021-11-01 10:39:02 +00:00
|
|
|
if (!isset($info['root'])
|
|
|
|
|| !isset($info['id'])
|
2021-10-23 14:46:03 +00:00
|
|
|
|| !is_dir($info['root'])
|
|
|
|
) {
|
2022-11-26 21:20:11 +00:00
|
|
|
throw new Exception(__('Failed to get module info'));
|
2021-08-23 13:07:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $info;
|
|
|
|
}
|
|
|
|
|
2021-11-08 21:19:07 +00:00
|
|
|
private static function getExclude(array $exclude): array
|
2021-08-23 13:07:29 +00:00
|
|
|
{
|
2023-03-11 17:46:23 +00:00
|
|
|
$exclude = array_merge(My::EXCLUDED_FILES, $exclude);
|
2021-08-23 13:07:29 +00:00
|
|
|
|
|
|
|
return self::quote_exclude($exclude);
|
|
|
|
}
|
|
|
|
|
2021-11-08 21:19:07 +00:00
|
|
|
private static function getFile(string $file, array $info): ?string
|
2021-08-23 13:07:29 +00:00
|
|
|
{
|
|
|
|
if (empty($file) || empty($info)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
$file = str_replace(
|
|
|
|
[
|
|
|
|
'%type%',
|
|
|
|
'%id%',
|
|
|
|
'%version%',
|
|
|
|
'%author%',
|
2022-11-26 21:22:42 +00:00
|
|
|
'%time%',
|
2021-08-23 13:07:29 +00:00
|
|
|
],
|
|
|
|
[
|
|
|
|
$info['type'],
|
|
|
|
$info['id'],
|
|
|
|
$info['version'],
|
|
|
|
$info['author'],
|
2022-11-26 21:22:42 +00:00
|
|
|
time(),
|
2021-08-23 13:07:29 +00:00
|
|
|
],
|
|
|
|
$file
|
|
|
|
);
|
|
|
|
$parts = explode('/', $file);
|
2021-11-01 10:39:02 +00:00
|
|
|
foreach ($parts as $i => $part) {
|
2021-08-23 13:07:29 +00:00
|
|
|
$parts[$i] = files::tidyFileName($part);
|
|
|
|
}
|
2021-11-01 10:39:02 +00:00
|
|
|
|
2021-08-23 13:07:29 +00:00
|
|
|
return implode('/', $parts) . '.zip';
|
|
|
|
}
|
|
|
|
|
2021-11-08 21:19:07 +00:00
|
|
|
private static function getOverwrite(bool $overwrite, string $root, string$file): ?string
|
2021-08-23 13:07:29 +00:00
|
|
|
{
|
|
|
|
$path = $root . '/' . $file;
|
|
|
|
if (file_exists($path) && !$overwrite) {
|
|
|
|
// don't break loop
|
|
|
|
//throw new Exception('File already exists');
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $path;
|
|
|
|
}
|
|
|
|
|
2021-11-08 21:19:07 +00:00
|
|
|
private static function getCache(): string
|
2021-08-23 13:07:29 +00:00
|
|
|
{
|
|
|
|
$c = DC_TPL_CACHE . '/packman';
|
|
|
|
if (!file_exists($c)) {
|
2022-12-20 00:33:20 +00:00
|
|
|
@files::makeDir($c);
|
2021-08-23 13:07:29 +00:00
|
|
|
}
|
|
|
|
if (!is_writable($c)) {
|
2022-11-26 21:20:11 +00:00
|
|
|
throw new Exception(__('Failed to get temporary directory'));
|
2021-08-23 13:07:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return $c;
|
|
|
|
}
|
2021-11-06 13:56:29 +00:00
|
|
|
}
|