diff --git a/composer.json b/composer.json index ec1d512445..bdc9e01290 100644 --- a/composer.json +++ b/composer.json @@ -17,7 +17,7 @@ "guzzlehttp/guzzle": "^7.5.1", "league/oauth2-google": "^4.0.1", "nikic/php-parser": "^4.14.0", - "pear/archive_tar": "~1.4.14", + "pear/archive_tar": "^1.6.0", "pelago/emogrifier": "^7.2.0", "psr/log": "^3.0.0", "scssphp/scssphp": "^1.12.1", diff --git a/composer.lock b/composer.lock index d2d5c710ba..aee9ef9cee 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "0da5da3b165955f386268e6dd8db2a8d", + "content-hash": "9225d70a5057480846b969102fc8b814", "packages": [ { "name": "apereo/phpcas", @@ -842,21 +842,21 @@ }, { "name": "pear/archive_tar", - "version": "1.4.14", + "version": "1.6.0", "source": { "type": "git", "url": "https://github.com/pear/Archive_Tar.git", - "reference": "4d761c5334c790e45ef3245f0864b8955c562caa" + "reference": "dc3285537f1832da8ddbbe45f5a007248b6cc00e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/4d761c5334c790e45ef3245f0864b8955c562caa", - "reference": "4d761c5334c790e45ef3245f0864b8955c562caa", + "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/dc3285537f1832da8ddbbe45f5a007248b6cc00e", + "reference": "dc3285537f1832da8ddbbe45f5a007248b6cc00e", "shasum": "" }, "require": { "pear/pear-core-minimal": "^1.10.0alpha2", - "php": ">=5.2.0" + "php": ">=5.4.0" }, "require-dev": { "phpunit/phpunit": "*" @@ -882,7 +882,7 @@ "./" ], "license": [ - "BSD-3-Clause" + "BSD-2-Clause" ], "authors": [ { @@ -908,17 +908,7 @@ "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Archive_Tar", "source": "https://github.com/pear/Archive_Tar" }, - "funding": [ - { - "url": "https://github.com/mrook", - "type": "github" - }, - { - "url": "https://www.patreon.com/michielrook", - "type": "patreon" - } - ], - "time": "2021-07-20T13:53:39+00:00" + "time": "2025-07-19T14:49:16+00:00" }, { "name": "pear/console_getopt", diff --git a/datamodels/2.x/itop-backup/backup.php b/datamodels/2.x/itop-backup/backup.php index 49c5252e65..d9ed4eee68 100644 --- a/datamodels/2.x/itop-backup/backup.php +++ b/datamodels/2.x/itop-backup/backup.php @@ -114,8 +114,7 @@ function ExecuteMainOperation($oP) exit; } - $sDefaultBackupFileName = SetupUtils::GetTmpDir().'/'."__DB__-%Y-%m-%d"; - $sBackupFile = utils::ReadParam('backup_file', $sDefaultBackupFileName, true, 'raw_data'); + $sBackupFile = utils::ReadParam('backup_file', BACKUP_DEFAULT_FORMAT, true, 'raw_data'); // Interpret strftime specifications (like %Y) and database placeholders $oBackup = new MyDBBackup($oP); diff --git a/datamodels/2.x/itop-backup/dbrestore.class.inc.php b/datamodels/2.x/itop-backup/dbrestore.class.inc.php index ca211cc9c6..5bb0f1ef5b 100644 --- a/datamodels/2.x/itop-backup/dbrestore.class.inc.php +++ b/datamodels/2.x/itop-backup/dbrestore.class.inc.php @@ -119,29 +119,25 @@ public function RestoreFromCompressedBackup($sFile, $sEnvironment = 'production' IssueLog::Info('Backup Restore - LOCK acquired, executing...'); $bReadonlyBefore = SetupUtils::EnterMaintenanceMode(MetaModel::GetConfig()); + $sDataDir = static::GetTmpDir($this->oConfig); // Here is the directory + try { //safe zone for db backup => cron is stopped/ itop in readonly $this->LogInfo("Starting restore of ".basename($sFile)); $sNormalizedFile = strtolower(basename($sFile)); - if (substr($sNormalizedFile, -4) == '.zip') { - $this->LogInfo('zip file detected'); - $oArchive = new ZipArchiveEx(); - $oArchive->open($sFile); - } elseif (substr($sNormalizedFile, -7) == '.tar.gz') { + if (str_ends_with($sNormalizedFile, '.tar.gz')) { $this->LogInfo('tar.gz file detected'); $oArchive = new TarGzArchive($sFile); + if (!$oArchive->extractTo($sDataDir)) { + throw new BackupException('Failed to extract archive.'); + } } else { throw new BackupException('Unsupported format for a backup file: '.$sFile); } // Load the database // - $sDataDir = APPROOT.'data/tmp-backup-'.rand(10000, getrandmax()); - - SetupUtils::builddir($sDataDir); // Here is the directory - $oArchive->extractTo($sDataDir); - $sDataFile = $sDataDir.'/itop-dump.sql'; $this->LoadDatabase($sDataFile); @@ -177,12 +173,6 @@ public function RestoreFromCompressedBackup($sFile, $sEnvironment = 'production' rename($sSourceFilePath, $sDestinationFilePath); } - try { - SetupUtils::rrmdir($sDataDir); - } catch (Exception $e) { - throw new BackupException("Can't remove data dir", 0, $e); - } - $oEnvironment = new RunTimeEnvironment($sEnvironment); $oEnvironment->CompileFrom($sEnvironment); } finally { @@ -192,6 +182,12 @@ public function RestoreFromCompressedBackup($sFile, $sEnvironment = 'production' //we are in the scope of main process that needs to handle/keep readonly mode. $this->LogInfo("Keep maintenance mode after restore"); } + + try { + SetupUtils::rrmdir($sDataDir); + } catch (Exception $e) { + throw new BackupException("Can't remove tmp folder", previous: $e); + } } } finally { IssueLog::Info('Backup Restore - LOCK released.'); diff --git a/datamodels/2.x/itop-backup/module.itop-backup.php b/datamodels/2.x/itop-backup/module.itop-backup.php index 239156e833..8d2dd0dc88 100644 --- a/datamodels/2.x/itop-backup/module.itop-backup.php +++ b/datamodels/2.x/itop-backup/module.itop-backup.php @@ -51,6 +51,7 @@ 'retention_count' => 5, 'enabled' => true, 'itop_backup_incident' => '', + 'backup_tmpdir' => 'data/', ], ] ); diff --git a/lib/composer/installed.json b/lib/composer/installed.json index 7b246762bb..2c85b1e4a1 100644 --- a/lib/composer/installed.json +++ b/lib/composer/installed.json @@ -869,22 +869,22 @@ }, { "name": "pear/archive_tar", - "version": "1.4.14", - "version_normalized": "1.4.14.0", + "version": "1.6.0", + "version_normalized": "1.6.0.0", "source": { "type": "git", "url": "https://github.com/pear/Archive_Tar.git", - "reference": "4d761c5334c790e45ef3245f0864b8955c562caa" + "reference": "dc3285537f1832da8ddbbe45f5a007248b6cc00e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/4d761c5334c790e45ef3245f0864b8955c562caa", - "reference": "4d761c5334c790e45ef3245f0864b8955c562caa", + "url": "https://api.github.com/repos/pear/Archive_Tar/zipball/dc3285537f1832da8ddbbe45f5a007248b6cc00e", + "reference": "dc3285537f1832da8ddbbe45f5a007248b6cc00e", "shasum": "" }, "require": { "pear/pear-core-minimal": "^1.10.0alpha2", - "php": ">=5.2.0" + "php": ">=5.4.0" }, "require-dev": { "phpunit/phpunit": "*" @@ -894,7 +894,7 @@ "ext-xz": "Lzma2 compression support.", "ext-zlib": "Gzip compression support." }, - "time": "2021-07-20T13:53:39+00:00", + "time": "2025-07-19T14:49:16+00:00", "type": "library", "extra": { "branch-alias": { @@ -912,7 +912,7 @@ "./" ], "license": [ - "BSD-3-Clause" + "BSD-2-Clause" ], "authors": [ { @@ -938,16 +938,6 @@ "issues": "http://pear.php.net/bugs/search.php?cmd=display&package_name[]=Archive_Tar", "source": "https://github.com/pear/Archive_Tar" }, - "funding": [ - { - "url": "https://github.com/mrook", - "type": "github" - }, - { - "url": "https://www.patreon.com/michielrook", - "type": "patreon" - } - ], "install-path": "../pear/archive_tar" }, { diff --git a/lib/composer/installed.php b/lib/composer/installed.php index 1a90038ddd..637054cb09 100644 --- a/lib/composer/installed.php +++ b/lib/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'combodo/itop', 'pretty_version' => 'dev-develop', 'version' => 'dev-develop', - 'reference' => 'c88ba664db4ec5622838a0ee00768e3bc3381d4e', + 'reference' => 'd92eacce81e5f653ee9fc6fe21724fde392e9865', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -22,7 +22,7 @@ 'combodo/itop' => array( 'pretty_version' => 'dev-develop', 'version' => 'dev-develop', - 'reference' => 'c88ba664db4ec5622838a0ee00768e3bc3381d4e', + 'reference' => 'd92eacce81e5f653ee9fc6fe21724fde392e9865', 'type' => 'project', 'install_path' => __DIR__ . '/../../', 'aliases' => array(), @@ -119,9 +119,9 @@ 'dev_requirement' => false, ), 'pear/archive_tar' => array( - 'pretty_version' => '1.4.14', - 'version' => '1.4.14.0', - 'reference' => '4d761c5334c790e45ef3245f0864b8955c562caa', + 'pretty_version' => '1.6.0', + 'version' => '1.6.0.0', + 'reference' => 'dc3285537f1832da8ddbbe45f5a007248b6cc00e', 'type' => 'library', 'install_path' => __DIR__ . '/../pear/archive_tar', 'aliases' => array(), diff --git a/lib/pear/archive_tar/.travis.yml b/lib/pear/archive_tar/.travis.yml deleted file mode 100644 index f103381b10..0000000000 --- a/lib/pear/archive_tar/.travis.yml +++ /dev/null @@ -1,29 +0,0 @@ -sudo: false -language: php -matrix: - fast_finish: true - allow_failures: - - php: nightly - include: - - php: 5.2 - dist: precise - - php: 5.3 - dist: precise - - php: 5.4 - dist: trusty - - php: 5.5 - dist: trusty - - php: 5.6 - - php: 7.0 - - php: 7.1 - - php: 7.2 - - php: 7.3 - - php: 7.4 - - php: nightly -install: -# - pear upgrade --force --alldeps pear/pear - - pear install -f package.xml -script: - - pear version - - pear run-tests -qr tests/ - - for i in `find tests/ -name '*.out'`; do echo "$i"; cat "$i"; done diff --git a/lib/pear/archive_tar/Archive/Tar.php b/lib/pear/archive_tar/Archive/Tar.php index 3356ad6ac1..7ce09491de 100644 --- a/lib/pear/archive_tar/Archive/Tar.php +++ b/lib/pear/archive_tar/Archive/Tar.php @@ -48,27 +48,6 @@ define('ARCHIVE_TAR_ATT_SEPARATOR', 90001); define('ARCHIVE_TAR_END_BLOCK', pack("a512", '')); -if (!function_exists('gzopen') && function_exists('gzopen64')) { - function gzopen($filename, $mode, $use_include_path = 0) - { - return gzopen64($filename, $mode, $use_include_path); - } -} - -if (!function_exists('gztell') && function_exists('gztell64')) { - function gztell($zp) - { - return gztell64($zp); - } -} - -if (!function_exists('gzseek') && function_exists('gzseek64')) { - function gzseek($zp, $offset, $whence = SEEK_SET) - { - return gzseek64($zp, $offset, $whence); - } -} - /** * Creates a (compressed) Tar archive * @@ -244,7 +223,7 @@ public function __construct($p_tarname, $p_compress = null, $buffer_length = 512 "a8checksum/a1typeflag/a100link/a6magic/a2version/" . "a32uname/a32gname/a8devmajor/a8devminor/a131prefix"; } else { - $this->_fmt = "Z100filename/Z8mode/Z8uid/Z8gid/Z12size/Z12mtime/" . + $this->_fmt = "Z100filename/Z8mode/Z8uid/Z8gid/a12size/Z12mtime/" . "Z8checksum/Z1typeflag/Z100link/Z6magic/Z2version/" . "Z32uname/Z32gname/Z8devmajor/Z8devminor/Z131prefix"; } @@ -280,7 +259,7 @@ public function __destruct() * single string with names separated by a single * blank space. * - * @return true on success, false on error. + * @return bool true on success, false on error. * @see createModify() */ public function create($p_filelist) @@ -300,7 +279,7 @@ public function create($p_filelist) * single string with names separated by a single * blank space. * - * @return true on success, false on error. + * @return bool true on success, false on error. * @see createModify() * @access public */ @@ -443,7 +422,7 @@ public function createModify($p_filelist, $p_add_dir, $p_remove_dir = '') * each element in the list, when * relevant. * - * @return true on success, false on error. + * @return bool true on success, false on error. */ public function addModify($p_filelist, $p_add_dir, $p_remove_dir = '') { @@ -496,7 +475,7 @@ public function addModify($p_filelist, $p_add_dir, $p_remove_dir = '') * gid => the group ID of the file * (default = 0 = root) * - * @return true on success, false on error. + * @return bool true on success, false on error. */ public function addString($p_filename, $p_string, $p_datetime = false, $p_params = array()) { @@ -622,7 +601,7 @@ public function extractInString($p_filename) * @param boolean $p_preserve Preserve user/group ownership of files * @param boolean $p_symlinks Allow symlinks. * - * @return true on success, false on error. + * @return bool true on success, false on error. * @see extractModify() */ public function extractList($p_filelist, $p_path = '', $p_remove_path = '', $p_preserve = false, $p_symlinks = true) @@ -660,7 +639,7 @@ public function extractList($p_filelist, $p_path = '', $p_remove_path = '', $p_p * list of parameters, in the format attribute code + attribute values : * $arch->setAttribute(ARCHIVE_TAR_ATT_SEPARATOR, ','); * - * @return true on success, false on error. + * @return bool true on success, false on error. */ public function setAttribute() { @@ -991,48 +970,45 @@ public function _writeBlock($p_binary_data, $p_len = null) { if (is_resource($this->_file)) { if ($p_len === null) { - if ($this->_compress_type == 'gz') { - @gzputs($this->_file, $p_binary_data); - } else { - if ($this->_compress_type == 'bz2') { - @bzwrite($this->_file, $p_binary_data); - } else { - if ($this->_compress_type == 'lzma2') { - @xzwrite($this->_file, $p_binary_data); - } else { - if ($this->_compress_type == 'none') { - @fputs($this->_file, $p_binary_data); - } else { - $this->_error( - 'Unknown or missing compression type (' - . $this->_compress_type . ')' - ); - } - } - } - } - } else { - if ($this->_compress_type == 'gz') { - @gzputs($this->_file, $p_binary_data, $p_len); - } else { - if ($this->_compress_type == 'bz2') { - @bzwrite($this->_file, $p_binary_data, $p_len); - } else { - if ($this->_compress_type == 'lzma2') { - @xzwrite($this->_file, $p_binary_data, $p_len); - } else { - if ($this->_compress_type == 'none') { - @fputs($this->_file, $p_binary_data, $p_len); - } else { - $this->_error( - 'Unknown or missing compression type (' - . $this->_compress_type . ')' - ); - } - } - } - } + switch ($this->_compress_type) + { + case 'gz': + $bytes = @gzwrite($this->_file, $p_binary_data); + break; + case 'bz2': + $bytes = @bzwrite($this->_file, $p_binary_data); + break; + case 'lzma2': + $bytes = @xzwrite($this->_file, $p_binary_data); + break; + case 'none': + $bytes = @fwrite($this->_file, $p_binary_data); + break; + default: + $this->_error('Unknown or missing compression type (' . $this->_compress_type . ')'); + return false; + } + } else { + switch ($this->_compress_type) + { + case 'gz': + $bytes = @gzwrite($this->_file, $p_binary_data, $p_len); + break; + case 'bz2': + $bytes = @bzwrite($this->_file, $p_binary_data, $p_len); + break; + case 'lzma2': + $bytes = @xzwrite($this->_file, $p_binary_data, $p_len); + break; + case 'none': + $bytes = @fwrite($this->_file, $p_binary_data, $p_len); + break; + default: + $this->_error('Unknown or missing compression type (' . $this->_compress_type . ')'); + return false; + } } + return $bytes !== false; } return true; } @@ -1117,7 +1093,7 @@ public function _writeFooter() if (is_resource($this->_file)) { // ----- Write the last 0 filled block for end of archive $v_binary_data = pack('a1024', ''); - $this->_writeBlock($v_binary_data); + return $this->_writeBlock($v_binary_data); } return true; } @@ -1279,7 +1255,9 @@ public function _addFile($p_filename, &$p_header, $p_add_dir, $p_remove_dir, $v_ $pack_format = sprintf('a%d', $this->buffer_length); } $v_binary_data = pack($pack_format, "$v_buffer"); - $this->_writeBlock($v_binary_data); + if(!$this->_writeBlock($v_binary_data)) { + return false; + } } fclose($v_file); @@ -1341,7 +1319,9 @@ public function _addString($p_filename, $p_string, $p_datetime = false, $p_param $i = 0; while (($v_buffer = substr($p_string, (($i++) * 512), 512)) != '') { $v_binary_data = pack("a512", $v_buffer); - $this->_writeBlock($v_binary_data); + if (!$this->_writeBlock($v_binary_data)) { + return false; + } } return true; @@ -2115,7 +2095,7 @@ public function _extractList( if ($v_extract_file) { if ($v_header['typeflag'] == "5") { if (!@file_exists($v_header['filename'])) { - if (!@mkdir($v_header['filename'], 0777)) { + if (!@mkdir($v_header['filename'], 0775)) { $this->_error( 'Unable to create directory {' . $v_header['filename'] . '}' @@ -2448,7 +2428,7 @@ public function _dirCheck($p_dir) return false; } - if (!@mkdir($p_dir, 0777)) { + if (!@mkdir($p_dir, 0775)) { $this->_error("Unable to create directory '$p_dir'"); return false; } diff --git a/lib/pear/archive_tar/SECURITY.md b/lib/pear/archive_tar/SECURITY.md new file mode 100644 index 0000000000..b34d974fec --- /dev/null +++ b/lib/pear/archive_tar/SECURITY.md @@ -0,0 +1,5 @@ +# Reporting Security Issues + +The Archive_Tar team and PEAR community take security bugs seriously. We appreciate your efforts to responsibly disclose your findings, and will make every effort to acknowledge your contributions. + +To report a security issue, please use the GitHub Security Advisory ["Report a Vulnerability"](https://github.com/pear/Archive_Tar/security/advisories/new) tab. diff --git a/lib/pear/archive_tar/composer.json b/lib/pear/archive_tar/composer.json index e464d9d7b7..09c996b1b7 100644 --- a/lib/pear/archive_tar/composer.json +++ b/lib/pear/archive_tar/composer.json @@ -7,7 +7,7 @@ "tar" ], "homepage": "https://github.com/pear/Archive_Tar", - "license": "BSD-3-Clause", + "license": "BSD-2-Clause", "authors": [ { "name": "Vincent Blavet", @@ -23,7 +23,7 @@ } ], "require": { - "php": ">=5.2.0", + "php": ">=5.4.0", "pear/pear-core-minimal": "^1.10.0alpha2" }, "suggest": { diff --git a/lib/pear/archive_tar/package.xml b/lib/pear/archive_tar/package.xml index d4f20bd4b0..93ba5d3256 100644 --- a/lib/pear/archive_tar/package.xml +++ b/lib/pear/archive_tar/package.xml @@ -24,6 +24,12 @@ Also Lzma2 compressed archives are supported with xz extension. Michiel Rook mrook mrook@php.net + no + + + Drew Webber + mcdruid + drew@mcdruid.co.uk yes @@ -32,11 +38,10 @@ Also Lzma2 compressed archives are supported with xz extension. stig@php.net no - 2021-07-20 - + 2025-07-19 - 1.4.14 - 1.4.0 + 1.6.0 + 1.6.0 stable @@ -44,7 +49,11 @@ Also Lzma2 compressed archives are supported with xz extension. New BSD License -* Properly fix symbolic link path traversal (CVE-2021-32610) +This release drops support for PHP 5.4 and 5.5. + +* PR #51: big file support +* PR #53: Fix return value of _writeBlock +* PR #58: Remove gzopen/gztell/gzseek shim @@ -65,15 +74,65 @@ Also Lzma2 compressed archives are supported with xz extension. - 5.2.0 + 5.6.0 - 1.9.0 + 1.10.0 + + + 1.6.0 + 1.6.0 + + + stable + stable + + 2025-07-19 + New BSD License + + This release drops support for PHP 5.4 and 5.5. + + * PR #51: big file support + * PR #53: Fix return value of _writeBlock + * PR #58: Remove gzopen/gztell/gzseek shim + + + + + 1.5.0 + 1.5.0 + + + stable + stable + + 2024-03-16 + New BSD License + + * PR #42: fix @return true... to @return bool true... on some functions + * PR #46: use 775 default for mkdirs, to avoid world-write + + + + + 1.4.14 + 1.4.0 + + + stable + stable + + 2021-02-16 + New BSD License + + * Properly fix symbolic link path traversal (CVE-2021-32610) + + 1.4.13 diff --git a/setup/backup.class.inc.php b/setup/backup.class.inc.php index 86edd3e978..b15bce964f 100644 --- a/setup/backup.class.inc.php +++ b/setup/backup.class.inc.php @@ -201,20 +201,22 @@ public function CreateCompressedBackup($sTargetFile, $sSourceConfigFile = null) $oArchive = new ITopArchiveTar($sTargetFile.'.tar.gz'); - $sTmpFolder = APPROOT.'data/tmp-backup-'.rand(10000, getrandmax()); + $sTmpFolder = static::GetTmpDir($this->oConfig); $aFiles = $this->PrepareFilesToBackup($sSourceConfigFile, $sTmpFolder); $sFilesList = var_export($aFiles, true); $this->LogInfo("backup: adding to archive files '$sFilesList'"); $bArchiveCreationResult = $oArchive->createModify($aFiles, '', $sTmpFolder); + + $this->LogInfo("backup: removing tmp folder '$sTmpFolder'"); + SetupUtils::rrmdir($sTmpFolder); + if (!$bArchiveCreationResult) { + @unlink($sTargetFile.'.tar.gz'); $sErrorMsg = 'Cannot backup : unable to create archive'; $this->LogError($sErrorMsg); throw new BackupException($sErrorMsg); } - - $this->LogInfo("backup: removing tmp folder '$sTmpFolder'"); - SetupUtils::rrmdir($sTmpFolder); } /** @@ -603,6 +605,18 @@ public static function MakeSafeMySQLCommand($sMySQLBinDir, string $sCmd) return '"'.$sMySQLCommand.'"'; } + + /** + * Return a directory name for temporary backup files. + */ + public static function GetTmpDir(Config $oConfig): string + { + $sTmpDir = @tempnam($oConfig->GetModuleSetting('itop-backup', 'backup_tmpdir', 'data/') , 'itop-backup-'); + unlink($sTmpDir); // I need a directory, not a file... + SetupUtils::builddir($sTmpDir); + + return $sTmpDir; + } } class TarGzArchive implements BackupArchive diff --git a/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php b/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php index ab1eaa1b12..ecfe61393c 100644 --- a/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php +++ b/tests/php-unit-tests/src/BaseTestCase/ItopTestCase.php @@ -187,9 +187,7 @@ public static function GetAppRoot() return APPROOT; } - $sAppRootPath = static::GetFirstDirUpContainingFile(__DIR__, 'approot.inc.php'); - - return $sAppRootPath.'/'; + return static::GetFirstDirUpContainingFile(__DIR__, 'approot.inc.php'); } private static function GetFirstDirUpContainingFile(string $sSearchPath, string $sFileToFindGlobPattern): ?string diff --git a/tests/php-unit-tests/unitary-tests/setup/DBBackupDataTest.php b/tests/php-unit-tests/unitary-tests/setup/DBBackupDataTest.php index 03e947f6d9..f21f96e793 100644 --- a/tests/php-unit-tests/unitary-tests/setup/DBBackupDataTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/DBBackupDataTest.php @@ -2,6 +2,7 @@ namespace Combodo\iTop\Test\UnitTest\Core; +use BackupException; use Combodo\iTop\Test\UnitTest\ItopDataTestCase; use DBBackup; use DBRestore; @@ -80,7 +81,7 @@ public function prepareFilesToBackupProvider() */ public function testRestoreListExtraFiles($aFilesToCreate, $aExpectedRelativeExtraFiles) { - require_once(APPROOT.'/env-production/itop-backup/dbrestore.class.inc.php'); + $this->RequireOnceItopFile('/env-production/itop-backup/dbrestore.class.inc.php'); $sTmpDir = sys_get_temp_dir().'/testRestoreListExtraFiles-'.time(); @@ -119,4 +120,19 @@ public function restoreListExtraFilesProvider() ]; } + public function testRestoreFromCompressedBackup() + { + $this->RequireOnceItopFile('env-production/itop-backup/dbrestore.class.inc.php'); + + $oConfig = \utils::GetConfig(); + $oRestore = new DBRestore($oConfig); + + $this->expectException(BackupException::class); + + $this->expectExceptionMessage('Failed to extract archive.'); + $oRestore->RestoreFromCompressedBackup('nonexistent.tar.gz'); + + $this->expectExceptionMessage('Unsupported format for a backup file: very_old_backup.zip'); + $oRestore->RestoreFromCompressedBackup('very_old_backup.zip'); + } } diff --git a/tests/php-unit-tests/unitary-tests/setup/DBBackupTest.php b/tests/php-unit-tests/unitary-tests/setup/DBBackupTest.php index 37a582df10..85c96e53b0 100644 --- a/tests/php-unit-tests/unitary-tests/setup/DBBackupTest.php +++ b/tests/php-unit-tests/unitary-tests/setup/DBBackupTest.php @@ -190,4 +190,44 @@ public function MakeNameProvider(): array ], ]; } + + /** + * @covers DBBackup::GetTmpDir + * @dataProvider GetTmpDirProvider + */ + public function testGetTmpDir(?string $sTmpDir, string $sStartsWith): void + { + $this->RequireOnceItopFile('setup/setuputils.class.inc.php'); + $oConfig = \utils::GetConfig(true); + $oConfig->SetModuleSetting('itop-backup', 'backup_tmpdir', $sTmpDir); + + $sTestedTmpDir = \DBBackup::GetTmpDir($oConfig); + + $this->assertDirectoryExists($sTestedTmpDir); + rmdir($sTestedTmpDir); + + $this->assertStringStartsWith($sStartsWith, $sTestedTmpDir); + } + + public function GetTmpDirProvider(): array + { + return [ + 'Not configured' => [ + 'tmp_dir' => null, + 'starts_with' => static::GetAppRoot() . 'data/itop-backup', + ], + 'Empty conf' => [ + 'tmp_dir' => '', + 'starts_with' => static::GetAppRoot() . 'data/itop-backup', + ], + 'Default settings' => [ + 'tmp_dir' => 'data/', + 'starts_with' => static::GetAppRoot() . 'data/itop-backup', + ], + 'System temporary directory' => [ + 'tmp_dir' => '/tmp', + 'starts_with' => '/tmp/itop-backup', + ], + ]; + } }