diff --git a/www/controllers/Repo/Operation/Mirror.php b/www/controllers/Repo/Operation/Mirror.php
deleted file mode 100644
index ceed234fd..000000000
--- a/www/controllers/Repo/Operation/Mirror.php
+++ /dev/null
@@ -1,1469 +0,0 @@
-type = $type;
- }
-
- public function setUrl(string $url)
- {
- $this->url = $url;
- }
-
- public function setDist(string $dist)
- {
- $this->dist = $dist;
- }
-
- public function setSection(string $section)
- {
- $this->section = $section;
- }
-
- public function setReleasever(string $releasever)
- {
- $this->releasever = $releasever;
- }
-
- public function setArch(array $arch)
- {
- $this->arch = $arch;
- }
-
- public function setCheckSignature(string $checkSignature)
- {
- $this->checkSignature = $checkSignature;
- }
-
- public function setGpgKeyUrl(string $url)
- {
- $this->gpgKeyUrl = $url;
- }
-
- public function setTranslation(array $translation)
- {
- $this->translation = $translation;
- }
-
- public function setWorkingDir(string $dir)
- {
- $this->workingDir = $dir;
- }
-
- public function setSyncSource(string $syncSource)
- {
- $this->syncSource = $syncSource;
- }
-
- public function setSslCustomCertificate(string $path)
- {
- $this->sslCustomCertificate = $path;
- }
-
- public function setSslCustomPrivateKey(string $path)
- {
- $this->sslCustomPrivateKey = $path;
- }
-
- /**
- * Get distant repomd.xml file
- * (RPM mirror)
- */
- private function getRepoMd(string $url)
- {
- $this->logOutput(PHP_EOL . '- Getting repomd.xml from ' . $url . '/repodata/repomd.xml ... ');
-
- if (!$this->download($url . '/repodata/repomd.xml', $this->workingDir . '/repomd.xml')) {
- $this->logError('error', 'Could not download repomd.xml');
- }
-
- $this->logOK();
- }
-
- /**
- * Get primary packages list file
- * (RPM mirror)
- */
- private function getPackagesList(string $url, string $checksum)
- {
- $this->logOutput(PHP_EOL . '- Getting primary.xml.gz from ' . $url . ' ... ');
-
- if (!$this->download($url, $this->workingDir . '/primary.xml.gz')) {
- throw new Exception('Could not download primary.xml.gz');
- }
-
- /**
- * Check that downloaded file checksum is the same as the provided checksum from repomd.xml
- * Try with sha512, sha256 then sha1
- */
- if (hash_file('sha512', $this->workingDir . '/primary.xml.gz') != $checksum) {
- if (hash_file('sha256', $this->workingDir . '/primary.xml.gz') != $checksum) {
- if (hash_file('sha1', $this->workingDir . '/primary.xml.gz') != $checksum) {
- throw new Exception('Error: primary.xml.gz checksum does not match provided checksum');
- }
- }
- }
-
- $this->logOK();
- }
-
- /**
- * Download Release file
- * (DEB mirror)
- */
- private function getReleaseFile()
- {
- $this->logOutput(PHP_EOL . '- Getting Release file ... ');
-
- /**
- * Check that Release.xx file exists before downloading it to prevent error message displaying for nothing
- */
- if (\Controllers\Common::urlFileExists($this->url . '/dists/' . $this->dist . '/InRelease', $this->sslCustomCertificate, $this->sslCustomPrivateKey)) {
- $this->download($this->url . '/dists/' . $this->dist . '/InRelease', $this->workingDir . '/InRelease');
- }
- if (\Controllers\Common::urlFileExists($this->url . '/dists/' . $this->dist . '/Release', $this->sslCustomCertificate, $this->sslCustomPrivateKey)) {
- $this->download($this->url . '/dists/' . $this->dist . '/Release', $this->workingDir . '/Release');
- }
- if (\Controllers\Common::urlFileExists($this->url . '/dists/' . $this->dist . '/Release.gpg', $this->sslCustomCertificate, $this->sslCustomPrivateKey)) {
- $this->download($this->url . '/dists/' . $this->dist . '/Release.gpg', $this->workingDir . '/Release.gpg');
- }
-
- /**
- * Print an error and quit if no Release file has been found
- */
- if (!file_exists($this->workingDir . '/InRelease') and !file_exists($this->workingDir . '/Release') and !file_exists($this->workingDir . '/Release.gpg')) {
- $this->logError('No Release file has been found in the source repository ' . $this->url . '/dists/' . $this->dist . '/ (looked for InRelease, Release and Release.gpg)', 'Release file not found');
- }
-
- $this->logOK();
- }
-
- /**
- * Parsing repomd.xml to get database location
- * (RPM mirror)
- */
- private function parseRepoMd()
- {
- if (!file_exists($this->workingDir . '/repomd.xml')) {
- $this->logError('Could not parse ' . $this->workingDir . '/repomd.xml: File not found');
- }
-
- /**
- * Convert repomd.xml XML content to JSON to PHP array for a simpler parsing
- */
- $xml = simplexml_load_file($this->workingDir . '/repomd.xml');
- $json = json_encode($xml);
- unset($xml);
- $jsonArray = json_decode($json, true);
- unset($json);
-
- foreach ($jsonArray['data'] as $data) {
- if (isset($data['@attributes'])) {
- /**
- * Find an array with attribute 'type' equals to 'primary'
- * This array will contains location to the primary package list xml file
- * e.g
- *
- * Array
- * (
- * [revision] => 1661873831
- * [data] => Array
- * (
- * [1] => Array
- * (
- * [@attributes] => Array
- * (
- * [type] => primary
- * )
- * [checksum] => 247192a75689c2205dafa6665b4dbf114fc641858dcb446c644aa934858108b2
- * [open-checksum] => 5f114045145909f9243d6687e8267c3cabfd3866b7f48a4a0da3d450895a56a5
- * [location] => Array
- * (
- * [@attributes] => Array
- * (
- * [href] => repodata/247192a75689c2205dafa6665b4dbf114fc641858dcb446c644aa934858108b2-primary.xml.gz
- * )
- * )
- * [timestamp] => 1661873832
- * [size] => 31711
- * [open-size] => 493575
- * )
- *
- */
- if ($data['@attributes']['type'] == 'primary') {
- $this->primaryLocation = $data['location']['@attributes']['href'];
- $this->primaryChecksum = $data['checksum'];
-
- /**
- * If $data['checksum'] is an array with multiple checksums found (sha, sha256, sha512), then just keep the first of them.
- */
- if (is_array($data['checksum'])) {
- $this->primaryChecksum = $data['checksum'][0];
- /**
- * Else if $data['checksum'] is a string
- */
- } else {
- $this->primaryChecksum = $data['checksum'];
- }
- }
- }
- }
-
- /**
- * If location and checksum could not be find, throw an error
- */
- if (empty($this->primaryLocation) or empty($this->primaryChecksum)) {
- $this->logError('Could not find location of the package list file');
- }
- }
-
- /**
- * Parse primary packages list file to find .rpm packages location and their checksum
- * (RPM mirror)
- */
- private function parsePrimaryPackagesList(string $primaryFile)
- {
- $error = 0;
- $this->rpmPackagesLocation = array();
-
- $this->logOutput(PHP_EOL . '- Retrieving packages list from ' . $primaryFile . ' ... ');
-
- /**
- * Gunzip primary.xml.gz
- */
- try {
- \Controllers\Common::gunzip($primaryFile);
- } catch (Exception $e) {
- $this->logError($e, 'Error while uncompressing primary.xml.gz');
- }
-
- /**
- * Read the now gunzipped primary.xml.gz file
- */
- $primaryFile = str_replace('.gz', '', $primaryFile);
-
- /**
- * Convert primary.xml content from XML to JSON to PHP array for a simpler parsing
- */
- $xml = simplexml_load_file($primaryFile);
- $json = json_encode($xml);
- unset($xml);
- $jsonArray = json_decode($json, true);
- unset($json);
-
- /**
- * First count number of packages because retrieving the packages informations is different if there is only one package or multiple packages
- */
- $packageCount = $jsonArray['@attributes']['packages'];
-
- /**
- * Case there is only one package in the target repository
- */
- if ($packageCount == 1) {
- /**
- * Find package location
- */
- if (!empty($jsonArray['package']['location']['@attributes']['href'])) {
- $packageLocation = $jsonArray['package']['location']['@attributes']['href'];
-
- /**
- * If package checksum is not found then it can not be retrieved
- */
- if (empty($jsonArray['package']['checksum'])) {
- $this->logOutput(PHP_EOL . ' Could not find checksum value for package ' . $packageLocation . '' . PHP_EOL);
- $error++;
- } else {
- $packageChecksum = $jsonArray['package']['checksum'];
-
- /**
- * If path and checksum have been parsed, had them to the global rpm packages list array
- */
- $this->rpmPackagesLocation[] = array('location' => $packageLocation, 'checksum' => $packageChecksum);
- }
- }
- }
-
- /**
- * Case there is more than one package in the target repository
- */
- if ($packageCount > 1) {
- foreach ($jsonArray['package'] as $data) {
- /**
- * Find package location
- */
- if (!empty($data['location']['@attributes']['href'])) {
- $packageLocation = $data['location']['@attributes']['href'];
-
- /**
- * If package checksum is not found then it can not be retrieved
- */
- if (empty($data['checksum'])) {
- $this->logOutput(PHP_EOL . ' Could not find checksum value for package ' . $packageLocation . '' . PHP_EOL);
- $error++;
- continue;
- }
-
- $packageChecksum = $data['checksum'];
-
- /**
- * If path and checksum have been parsed, had them to the global rpm packages list array
- */
- $this->rpmPackagesLocation[] = array('location' => $packageLocation, 'checksum' => $packageChecksum);
- }
- }
- }
-
- /**
- * Print error if no package location has been found
- */
- if (empty($this->rpmPackagesLocation)) {
- $this->logError('No package found');
- }
-
- /**
- * Print OK if there was no warning
- */
- if ($error == 0) {
- $this->logOK();
- }
- }
-
- /**
- * Parse Release file to find :
- * - Packages indices file location (Packages / Packages.gz)
- * - Sources packages file location (Sources / Sources.gz)
- * - Translation file location (Translation-en / Translation-en.bz2)
- *
- * (DEB mirror)
- */
- private function parseReleaseFile()
- {
- if (file_exists($this->workingDir . '/InRelease')) {
- $content = file($this->workingDir . '/InRelease');
- } elseif (file_exists($this->workingDir . '/Release')) {
- $content = file($this->workingDir . '/Release');
- }
-
- $this->logOutput(PHP_EOL . '- Searching for Packages indices file location ... ');
-
- /**
- * Process research of Packages indices for each arch
- */
- foreach ($this->arch as $arch) {
- /**
- * Packages pattern to search in the Release file
- * e.g: main/binary-amd64/Packages
- */
- $regex = $this->section . '/binary-' . $arch . '/Packages($|.gz$|.xz$)';
-
- /**
- * Parse the whole file, searching for the desired lines
- */
- foreach ($content as $line) {
- if (preg_match("#$regex#", $line)) {
- /**
- * Explode the line to separate hashes and location
- */
- $splittedLine = explode(' ', trim($line));
-
- /**
- * We only need the location with its SHA256 (64 caracters long)
- * e.g: bd29d2ec28c10fec66a139d8e9a88ca01ff0f2533ca3fab8dc33c13b533059c1 1279885 main/binary-amd64/Packages
- */
- if (strlen($splittedLine[0]) == '64') {
- $location = end($splittedLine);
- $checksum = $splittedLine[0];
-
- /**
- * Include this Package.xx file only if it does really exist on the remote server (sometimes it can be declared in Release but not exists...)
- */
- if (\Controllers\Common::urlFileExists($this->url . '/dists/' . $this->dist . '/' . $location, $this->sslCustomCertificate, $this->sslCustomPrivateKey)) {
- $this->packagesIndicesLocation[] = array('location' => $location, 'checksum' => $checksum);
-
- /**
- * Then ignore all next Package.xx indices file from the same arch as at least one has been found
- */
- break 1;
- }
- }
- }
- }
- }
-
- /**
- * Throw an error if no Packages indices file location has been found
- */
- if (empty($this->packagesIndicesLocation)) {
- $this->logError('No Packages indices file location has been found.', 'Cannot retrieve Packages indices file');
- }
-
- $this->logOK();
-
- /**
- * Process research of Sources files for the current section
- */
- if ($this->syncSource == 'yes') {
- $this->logOutput(PHP_EOL . '- Searching for Sources indices file location ... ');
-
- /**
- * Sources pattern to search in the Release file
- * e.g: main/source/Sources
- */
- $regex = $this->section . '/source/Sources';
-
- /**
- * Parse the whole file, searching for the desired lines
- */
- foreach ($content as $line) {
- if (preg_match("#$regex$#", $line)) {
- /**
- * Explode the line to separate hashes and location
- */
- $splittedLine = explode(' ', trim($line));
-
- /**
- * We only need the location with its md5sum (32 caracters long)
- * e.g: 1440dd54895a24684cdbb39ddc54ea22 40470389 main/source/Sources
- */
- if (strlen($splittedLine[0]) == '32') {
- $this->sourcesIndicesLocation[] = array('location' => end($splittedLine), 'md5sum' => $splittedLine[0]);
- }
- }
- }
-
- /**
- * Throw an error if no Sources indices file location has been found
- */
- if (empty($this->packagesIndicesLocation)) {
- $this->logError('No Sources indices file location has been found. Check that specified distribution and section names are correct.', 'Cannot retrieve Sources indices file');
- }
-
- $this->logOK();
- }
-
- /**
- * Process research of Translation files for each requested translation language
- */
- if (!empty($this->translation)) {
- $this->logOutput(PHP_EOL . '- Searching for Translation file(s) location ... ');
-
- foreach ($this->translation as $translation) {
- /**
- * Translation pattern to search in the Release file
- * e.g: main/i18n/Translation-fr.bz2
- */
- $regex = $this->section . '/i18n/Translation-' . $translation . '.bz2';
-
- /**
- * Parse the whole file, searching for the desired lines
- */
- foreach ($content as $line) {
- if (preg_match("#$regex$#", $line)) {
- /**
- * Explode the line to separate hashes and location
- */
- $splittedLine = explode(' ', trim($line));
-
- /**
- * We only need the location with its md5sum (32 caracters long)
- * e.g: 35e89f49cdfaa179e552aee1d67c5cdb 2478327 main/i18n/Translation-fr.bz2
- */
- if (strlen($splittedLine[0]) == '32') {
- $this->translationsLocation[] = array('location' => end($splittedLine), 'md5sum' => $splittedLine[0]);
- }
- }
- }
- }
-
- /**
- * Throw an error if no Translation file location has been found
- */
- if (empty($this->translationsLocation)) {
- $this->logError('No Translation file location has been found. There may have no translation available for this repository.', 'Cannot retrieve translations files');
- }
-
- $this->logOK();
- }
-
- unset($content);
- }
-
- /**
- * Parse Packages indices file to find .deb packages location
- * (DEB mirror)
- */
- private function parsePackagesIndiceFile()
- {
- $this->logOutput('- Retrieving deb packages list ... ');
-
- /**
- * Process research for each Package file (could have multiple if multiple archs have been specified)
- */
- foreach ($this->packagesIndicesLocation as $packageIndice) {
- $packageIndicesLocation = $packageIndice['location'];
- $packageIndicesChecksum = $packageIndice['checksum'];
- $packageIndicesName = preg_split('#/#', $packageIndicesLocation);
- $packageIndicesName = end($packageIndicesName);
-
- /**
- * Download Packages.xx file using its location
- */
- if (!$this->download($this->url . '/dists/' . $this->dist . '/' . $packageIndicesLocation, $this->workingDir . '/' . $packageIndicesName)) {
- $this->logError('Error while downloading ' . $packageIndicesName . ' indices file: ' . $this->url . '/' . $packageIndicesLocation, 'Could not download ' . $packageIndicesName . ' indices file');
- }
-
- /**
- * Then check that the Packages.xx file's checksum matches the one that what specified in Release file
- */
- if (hash_file('sha256', $this->workingDir . '/' . $packageIndicesName) !== $packageIndicesChecksum) {
- $this->logError($packageIndicesName . ' indices file\'s SHA256 checksum does not match the SHA256 checksum specified in the Release file ' . $packageIndicesChecksum, 'Could not verify Packages indices file');
- }
-
- /**
- * Uncompress Packages.xx if it is compressed (.gz or .xz)
- */
- if (preg_match('/.gz$/i', $packageIndicesName)) {
- try {
- \Controllers\Common::gunzip($this->workingDir . '/' . $packageIndicesName);
- } catch (Exception $e) {
- $this->logError($e, 'Error while uncompressing ' . $packageIndicesName);
- }
- }
- if (preg_match('/.xz$/i', $packageIndicesName)) {
- try {
- \Controllers\Common::xzUncompress($this->workingDir . '/Packages.xz');
- } catch (Exception $e) {
- $this->logError($e, 'Error while uncompressing Packages.xz');
- }
- }
-
- /**
- * Get all .deb packages location from the uncompressed Packages file
- */
- $packageLocation = '';
- $packageChecksum = '';
- $handle = fopen($this->workingDir . '/Packages', 'r');
-
- if ($handle) {
- while (($line = fgets($handle)) !== false) {
- /**
- * Get deb location
- */
- if (preg_match('/^Filename:\s+(.*)/im', $line)) {
- $packageLocation = trim(str_replace('Filename: ', '', $line));
- }
-
- /**
- * Get deb SHA256
- */
- if (preg_match('/^SHA256:\s+(.*)/im', $line)) {
- $packageChecksum = trim(str_replace('SHA256: ', '', $line));
- }
-
- /**
- * If location and checksum have been parsed, had them to the global deb packages list array
- */
- if (!empty($packageLocation) and !empty($packageChecksum)) {
- $this->debPackagesLocation[] = array('location' => $packageLocation, 'checksum' => $packageChecksum);
-
- unset($packageLocation, $packageChecksum);
- }
- }
-
- fclose($handle);
- }
- }
-
- /**
- * Quit if no packages have been found
- */
- if (empty($this->debPackagesLocation)) {
- $this->logError('No packages found in Packages indices file');
- }
-
- $this->logOK();
- }
-
- /**
- * Parse Sources indices file to find .dsc/tar.gz/tar.xz sources packages location
- * (DEB mirror)
- */
- private function parseSourcesIndiceFile()
- {
- if ($this->syncSource != 'yes') {
- return;
- }
-
- $this->logOutput('- Retrieving sources packages list ... ');
-
- /**
- * Process research for each Sources file
- */
- foreach ($this->sourcesIndicesLocation as $sourcesIndice) {
- $sourcesIndicesLocation = $sourcesIndice['location'];
- $sourcesIndexMd5 = $sourcesIndice['md5sum'];
-
- /**
- * Download Source file using its location
- */
- if (!$this->download($this->url . '/dists/' . $this->dist . '/' . $sourcesIndicesLocation, $this->workingDir . '/Sources')) {
- $this->logError('Error while downloading Sources indices file: ' . $this->url . '/' . $sourcesIndicesLocation, 'Could not download Sources indices file');
- }
-
- /**
- * Gunzip Sources.gz
- */
- // try {
- // \Controllers\Common::gunzip($this->workingDir . '/Sources.gz');
- // } catch(Exception $e) {
- // $this->logError($e, 'Error while uncompressing Sources.gz');
- // }
-
- /**
- * Then check that the gunzip Sources file's md5 is the same as the one that what specified in Release file
- */
- if (md5_file($this->workingDir . '/Sources') !== $sourcesIndexMd5) {
- $this->logError('Sources indices file\'s md5 (' . md5_file($this->workingDir . '/Sources') . ') does not match the md5 specified in the Release file ' . $sourcesIndexMd5, 'Could not verify Packages indices file');
- }
-
- /**
- * Get all .dsc/tar.gz/tar.xz sources packages location from the Sources file
- */
- $directory = '';
- $packageLocation = '';
- $packageMd5 = '';
- $linecount = 0;
- $handle = fopen($this->workingDir . '/Sources', 'r');
-
- if ($handle) {
- while (($line = fgets($handle)) !== false) {
- /**
- * Get .dsc/tar.gz/tar.xz directory location
- */
- if (preg_match('/^Directory:\s+(.*)/im', $line)) {
- $directory = trim(str_replace('Directory: ', '', $line));
- }
-
- /**
- * Get .dsc/tar.gz/tar.xz location
- */
- if (preg_match('/^Files:$/im', $line)) {
- /**
- * If line starts with 'Files:' then get the next 3 lines that contain the packages name and md5sum
- * Use current $linecount to get the next 3 lines
- */
- $spl = new \SplFileObject($this->workingDir . '/Sources');
-
- for ($i = 1; $i < 4; $i++) {
- $spl->seek($linecount + $i);
- $packageLine = $spl->current();
- $packageLine = explode(' ', $packageLine);
- $packageMd5 = trim($packageLine[1]);
- $packageLocation = trim($packageLine[3]);
-
- /**
- * Add founded packages to the global array
- */
- if (!empty($directory) and !empty($packageLocation) and !empty($packageMd5)) {
- $this->sourcesPackagesLocation[] = array('location' => $directory . '/' . $packageLocation, 'md5sum' => $packageMd5);
- }
- }
-
- unset($spl, $packageLocation, $packageMd5);
- }
-
- $linecount++;
- }
-
- fclose($handle);
- }
- }
-
- /**
- * Quit if no sources packages have been found
- */
- if (empty($this->sourcesPackagesLocation)) {
- $this->logError('No packages found in Packages indices file');
- }
-
- $this->logOK();
- }
-
- /**
- * Check Release file GPG signature
- * (DEB mirror)
- */
- private function checkReleaseGPGSignature()
- {
- /**
- * Quit if signature check is disabled
- */
- if ($this->checkSignature === 'no') {
- return;
- }
-
- $this->logOutput('- Checking Release GPG signature ... ');
-
- /**
- * Check signature from InRelease file in priority, else from Release.gpg file
- */
- if (file_exists($this->workingDir . '/InRelease')) {
- $this->checkGPGSignature($this->workingDir . '/InRelease');
- } elseif (file_exists($this->workingDir . '/Release.gpg')) {
- $this->checkGPGSignature($this->workingDir . '/Release.gpg');
- }
-
- $this->logOK();
- }
-
- /**
- * Check GPG signature of specified file
- * (DEB mirror)
- */
- private function checkGPGSignature(string $file)
- {
- $myprocess = new \Controllers\Process('gpgv --homedir ' . GPGHOME . ' ' . $file);
- $myprocess->execute();
- $output = $myprocess->getOutput();
- $myprocess->close();
-
- /**
- * If gpgv returned an error then signature is invalid
- */
- if ($myprocess->getExitCode() != 0) {
- $this->logError('No GPG key could verify the signature of downloaded file ' . $file . ': ' . PHP_EOL . $output, 'Error while checking GPG signature');
- }
- }
-
- /**
- * Initialize mirroring task
- */
- private function initialize()
- {
- /**
- * Create working dir if not exist
- */
- if (!is_dir($this->workingDir)) {
- if (!mkdir($this->workingDir, 0770, true)) {
- throw new Exception('Cannot create temporary working directory');
- }
- }
-
- /**
- * Initialize shared curl handle
- */
- $this->curlHandle = curl_init();
- }
-
- /**
- * Download specified distant file
- */
- private function download(string $url, string $savePath)
- {
- $curlError = 0;
- $localFile = fopen($savePath, "w");
-
- /**
- * Use a shared curl handle '$this->curlHandle' and do not reinitialize it every time to speed up downloads
- */
- curl_setopt($this->curlHandle, CURLOPT_URL, $url); // set remote file url
- curl_setopt($this->curlHandle, CURLOPT_FILE, $localFile); // set output file
- curl_setopt($this->curlHandle, CURLOPT_TIMEOUT, 300); // set timeout
- curl_setopt($this->curlHandle, CURLOPT_FOLLOWLOCATION, true); // follow redirect
- curl_setopt($this->curlHandle, CURLOPT_ENCODING, ''); // use compression if any
-
- /**
- * If a custom ssl certificate and private key must be used
- */
- if (!empty($this->sslCustomCertificate)) {
- curl_setopt($this->curlHandle, CURLOPT_SSLCERT, $this->sslCustomCertificate);
- }
- if (!empty($this->sslCustomPrivateKey)) {
- curl_setopt($this->curlHandle, CURLOPT_SSLKEY, $this->sslCustomPrivateKey);
- }
-
- /**
- * Execute curl
- */
- curl_exec($this->curlHandle);
-
- /**
- * If curl has failed (meaning a curl param might be invalid)
- */
- if (curl_errno($this->curlHandle)) {
- $this->logError('Curl error: ' . curl_error($this->curlHandle), 'Download error');
-
- curl_close($this->curlHandle);
- fclose($localFile);
- }
-
- /**
- * Check that the http return code is 200 (the file has been downloaded)
- */
- $status = curl_getinfo($this->curlHandle);
-
- if ($status["http_code"] != 200) {
- /**
- * If return code is 404
- */
- if ($status["http_code"] == '404') {
- $this->logOutput('File not found (404)');
- } else {
- $this->logOutput('File could not be downloaded (http return code is: ' . $status["http_code"] . ')');
- }
-
- curl_close($this->curlHandle);
- fclose($localFile);
-
- return false;
- }
-
- return true;
- }
-
- /**
- * Download rpm packages
- * (RPM mirror)
- */
- private function downloadRpmPackages(string $url)
- {
- /**
- * Target directory in which packages will be downloaded
- */
- $targetDir = $this->workingDir . '/packages/' . $this->currentArch;
-
- /**
- * Create directory in which packages will be downloaded
- */
- if (!is_dir($targetDir)) {
- mkdir($targetDir, 0770, true);
- }
-
- /**
- * If GPG signature check is enabled, either use a distant http:// GPG key or use the repomanager keyring
- */
- if ($this->checkSignature == 'yes') {
- $mygpg = new \Controllers\GPG();
-
- /**
- * If the source repo has a distant http:// gpg signature key, then download it
- */
- if (!empty($this->gpgKeyUrl)) {
- if (!$this->download($this->gpgKeyUrl, TEMP_DIR . '/gpgkey-to-import.gpg')) {
- $this->logError('Could not retrieve distant GPG signature key: ' . $this->gpgKeyUrl, 'Could not retrieve distant GPG signature key');
- }
-
- /**
- * Import key inside trusted keyring
- */
- $myprocess = new \Controllers\Process('/usr/bin/gpg --no-default-keyring --keyring ' . GPGHOME . '/trustedkeys.gpg --import ' . TEMP_DIR . '/gpgkey-to-import.gpg');
- $myprocess->execute();
-
- /**
- * Delete temporary GPG key file
- */
- unlink(TEMP_DIR . '/gpgkey-to-import.gpg');
-
- /**
- * Quits if import has failed
- */
- if ($myprocess->getExitCode() != 0) {
- $this->logError('Error while importing distant GPG signature key', 'Could not import distant GPG signature key');
- }
-
- $myprocess->close();
- }
-
- /**
- * Get all known editors GPG public keys imported into repomanager keyring
- */
- $knownPublicKeys = $mygpg->getTrustedKeys();
-
- /**
- * Filter to retrieve key Id column only
- */
- $knownPublicKeys = array_column($knownPublicKeys, 'id');
- }
-
- /**
- * Print URL from which packages are downloaded
- */
- $this->logOutput(PHP_EOL . '- Downloading packages from: ' . $url . PHP_EOL);
-
- /**
- * Count total packages to print progression during syncing
- */
- $totalPackages = count($this->rpmPackagesLocation);
- $packageCounter = 0;
-
- /**
- * Download each package and check its md5
- */
- foreach ($this->rpmPackagesLocation as $rpmPackage) {
- /**
- * Before downloading each package, check if there is enough disk space left (2GB minimum)
- */
- if (disk_free_space(REPOS_DIR) < 2000000000) {
- $this->logError('Repo storage has reached 2GB (minimum) of free space left. Operation automatically stopped.', 'Low disk space');
- }
-
- $rpmPackageLocation = $rpmPackage['location'];
- $rpmPackageChecksum = $rpmPackage['checksum'];
- $rpmPackageName = preg_split('#/#', $rpmPackageLocation);
- $rpmPackageName = end($rpmPackageName);
- $packageCounter++;
-
- /**
- * Output package to download to log file
- */
- $this->logOutput('(' . $packageCounter . '/' . $totalPackages . ') ➙ ' . $rpmPackageLocation . ' ... ');
-
- /**
- * Check if file does not already exists before downloading it (e.g. copied from a previously snapshot)
- */
- if (file_exists($targetDir . '/' . $rpmPackageName)) {
- $this->logOutput('already exists (ignoring)' . PHP_EOL);
- continue;
- }
-
- /**
- * Download file if it does not already exist
- */
- if (!$this->download($url . '/' . $rpmPackageLocation, $targetDir . '/' . $rpmPackageName)) {
- $this->logError('error', 'Error while retrieving packages');
- }
-
- /**
- * Check that downloaded rpm package's matches the checksum specified by the primary.xml file
- * Try with sha256 then sha1
- */
- if (hash_file('sha256', $targetDir . '/' . $rpmPackageName) != $rpmPackageChecksum) {
- if (hash_file('sha1', $targetDir . '/' . $rpmPackageName) != $rpmPackageChecksum) {
- $this->logError('checksum (sha256) does not match (tried sha256 and sha1)', 'Error while retrieving packages');
- }
- }
-
- /**
- * Check rpm GPG signature if enabled
- *
- * https://blog.remirepo.net/post/2020/03/13/Extension-rpminfo-pour-php
- *
- * using rpm :
- * rpm -q --qf "%|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{(none}|}| %{NVRA}\n" PACKAGE.rpm
- * rpm --checksig PACKAGE.rpm
- */
- if ($this->checkSignature === 'yes') {
- /**
- * Throw an error if there are no known GPG public keys because it is impossible to check for signature then
- */
- if (empty($knownPublicKeys)) {
- $this->logError('Cannot check for signature because there is no GPG public keys imported in Repomanager\'s keyring', 'Cannot check packages signature');
- }
-
- /**
- * Extract package header
- */
- $myprocess = new \Controllers\Process('/usr/bin/rpm -qp --qf "%|DSAHEADER?{%{DSAHEADER:pgpsig}}:{%|RSAHEADER?{%{RSAHEADER:pgpsig}}:{(none}|}| %{NVRA}\n" ' . $targetDir. '/' . $rpmPackageName);
- $myprocess->execute();
- $content = $myprocess->getOutput();
- $myprocess->close();
-
- /**
- * Parse package's GPG signature key Id from header content
- */
- if (!preg_match('/key ID(.*) /i', $content, $matches)) {
- $this->logError('GPG signature key ID is not found in the package header', 'Could not verify GPG signatures');
- }
-
- /**
- * Retrieve GPG signature key Id from $matches
- */
- $keyId = trim($matches[1]);
-
- /**
- * Remove last ':' character
- */
- $keyId = rtrim($keyId, ':');
-
- /**
- * Now check if that key Id appears in known public keys Id
- * If not, throw an error, else, signature is OK
- */
- if (!preg_grep("/$keyId\$/i", $knownPublicKeys)) {
- $this->logError('signature is not OK', 'Package has invalid signature');
- }
- }
-
- /**
- * Print OK if package has been downloaded and verified successfully
- */
- $this->logOK();
- }
-
- unset($this->rpmPackagesLocation, $totalPackages, $packageCounter);
- }
-
- /**
- * Download deb packages
- * (DEB mirror)
- */
- private function downloadDebPackages($url)
- {
- /**
- * Target directory in which packages will be downloaded
- */
- $targetDir = $this->workingDir . '/packages';
-
- /**
- * Create directory in which packages will be downloaded
- */
- if (!is_dir($targetDir)) {
- mkdir($targetDir, 0770, true);
- }
-
- /**
- * Print URL from which packages are downloaded
- */
- $this->logOutput(PHP_EOL . '- Downloading packages from: ' . $url . PHP_EOL);
-
- /**
- * Count total packages to print progression during syncing
- */
- $totalPackages = count($this->debPackagesLocation);
- $packageCounter = 0;
-
- /**
- * Download each package and check its md5
- */
- foreach ($this->debPackagesLocation as $debPackage) {
- /**
- * Before downloading each package, check if there is enough disk space left (2GB minimum)
- */
- if (disk_free_space(REPOS_DIR) < 2000000000) {
- $this->logError('Repo storage has reached 2GB (minimum) of free space left. Operation automatically stopped.', 'Low disk space');
- }
-
- $debPackageLocation = $debPackage['location'];
- $debPackageChecksum = $debPackage['checksum'];
- $debPackageName = preg_split('#/#', $debPackageLocation);
- $debPackageName = end($debPackageName);
- $packageCounter++;
-
- /**
- * Output package to download to log file
- */
- $this->logOutput('(' . $packageCounter . '/' . $totalPackages . ') ➙ ' . $debPackageLocation . ' ... ');
-
- /**
- * Check if file does not already exists before downloading it (e.g. copied from a previously snapshot)
- */
- if (file_exists($targetDir . '/' . $debPackageName)) {
- $this->logOutput('already exists (ignoring)' . PHP_EOL);
- continue;
- }
-
- /**
- * Download
- */
- if (!$this->download($url . '/' . $debPackageLocation, $targetDir . '/' . $debPackageName)) {
- $this->logError('error', 'Error while retrieving packages');
- }
-
- /**
- * Check that downloaded deb package's sha256 matches the sha256 specified by the Packages file
- */
- if (hash_file('sha256', $targetDir . '/' . $debPackageName) != $debPackageChecksum) {
- $this->logError('SHA256 does not match', 'Error while retrieving packages');
- }
-
- /**
- * Print OK if package has been downloaded and verified successfully
- */
- $this->logOK();
- }
-
- unset($this->debPackagesLocation, $totalPackages, $packageCounter);
- }
-
- /**
- * Download deb sources packages
- * (DEB mirror)
- */
- private function downloadDebSourcesPackages($url)
- {
- if ($this->syncSource != 'yes') {
- return;
- }
-
- /**
- * Create directory in which sources packages will be downloaded
- */
- mkdir($this->workingDir . '/sources', 0770, true);
-
- /**
- * Print URL from which sources packages are downloaded
- */
- $this->logOutput(PHP_EOL . '- Downloading sources packages from: ' . $url . PHP_EOL);
-
- /**
- * Download each source package and check its md5
- */
- foreach ($this->sourcesPackagesLocation as $sourcePackage) {
- $sourcePackageLocation = $sourcePackage['location'];
- $sourcePackageMd5 = $sourcePackage['md5sum'];
- $sourcePackageName = preg_split('#/#', $sourcePackageLocation);
- $sourcePackageName = end($sourcePackageName);
-
- /**
- * Output source package to download to log file
- */
- $this->logOutput(' ➙ ' . $sourcePackageLocation . ' ... ');
-
- /**
- * Download
- */
- if (!$this->download($url . '/' . $sourcePackageLocation, $this->workingDir . '/sources/' . $sourcePackageName)) {
- $this->logError('error', 'Error while retrieving sources packages');
- }
-
- /**
- * Check that downloaded source package's md5 matches the md5sum specified by the Sources indices file
- */
- if (md5_file($this->workingDir . '/sources/' . $sourcePackageName) != $sourcePackageMd5) {
- $this->logError('md5 does not match', 'Error while retrieving sources packages');
- }
-
- /**
- * Print OK if source package has been downloaded and verified successfully
- */
- $this->logOK();
- }
-
- unset($this->sourcesPackagesLocation);
- }
-
- /**
- * Download translation packages
- * (DEB mirror)
- */
- private function downloadTranslation()
- {
- if (empty($this->translationsLocation)) {
- return;
- }
-
- /**
- * Create directory in which packages will be downloaded
- */
- mkdir($this->workingDir . '/translations', 0770, true);
-
- /**
- * Download each package and check its md5
- */
- foreach ($this->translationsLocation as $translation) {
- $translationLocation = $translation['location'];
- $translationMd5 = $translation['md5sum'];
- $translationName = preg_split('#/#', $translationLocation);
- $translationName = end($translationName);
- $translationUrl = $this->url . '/dists/' . $this->dist . '/' . $translationLocation;
-
- /**
- * Output package to download to log file
- */
- $this->logOutput('Downloading translation: ' . $translationUrl . ' ... ');
-
- /**
- * Download
- */
- if (!$this->download($translationUrl, $this->workingDir . '/translations/' . $translationName)) {
- $this->logError('error', 'Error while retrieving packages');
- }
-
- /**
- * Check that downloaded deb package's md5 matches the md5sum specified by the Release file
- */
- if (md5_file($this->workingDir . '/translations/' . $translationName) != $translationMd5) {
- $this->logError('md5 does not match', 'Error while retrieving packages');
- }
-
- /**
- * Print OK if package has been downloaded and verified successfully
- */
- $this->logOK();
- }
-
- unset($this->translationsLocation);
- }
-
- /**
- * Set log file to output to
- */
- public function setOutputFile(string $file)
- {
- $this->outputFile = $file;
-
- /**
- * If file does not exist, try to create it to be sure it is writeable
- */
- if (!file_exists($file)) {
- if (!touch($file)) {
- throw new Exception('Cannot create output log file: ' . $file);
- }
- }
- }
-
- /**
- * Enable output to log file
- */
- public function outputToFile(bool $enable = false)
- {
- if ($enable == true) {
- $this->outputToFile = true;
- }
- }
-
- /**
- * Write specified message to log file
- */
- private function logOutput(string $message)
- {
- /**
- * Only write if logging is enabled
- */
- if ($this->outputToFile === true and !empty($this->outputFile)) {
- file_put_contents($this->outputFile, $message, FILE_APPEND);
- }
- }
-
- /**
- * Write a green 'OK' to log file
- */
- private function logOK()
- {
- $this->logOutput('OK' . PHP_EOL);
- }
-
- /**
- * Write a red error message to log file and throw an Exception
- */
- private function logError(string $errorMessage, string $exceptionMessage = null)
- {
- /**
- * If no specific exception message has been specified, then it will be the same as the error message displayed
- */
- if (empty($exceptionMessage)) {
- $exceptionMessage = $errorMessage;
- }
-
- $this->logOutput('' . $errorMessage . '' . PHP_EOL);
- throw new Exception($exceptionMessage);
- }
-
- /**
- * Clean remaining files in working directory
- */
- private function clean()
- {
- if (file_exists($this->workingDir . '/primary.xml')) {
- unlink($this->workingDir . '/primary.xml');
- }
- if (file_exists($this->workingDir . '/primary.xml.gz')) {
- unlink($this->workingDir . '/primary.xml.gz');
- }
- if (file_exists($this->workingDir . '/repomd.xml')) {
- unlink($this->workingDir . '/repomd.xml');
- }
- if (file_exists($this->workingDir . '/InRelease')) {
- unlink($this->workingDir . '/InRelease');
- }
- if (file_exists($this->workingDir . '/Release')) {
- unlink($this->workingDir . '/Release');
- }
- if (file_exists($this->workingDir . '/Release.gpg')) {
- unlink($this->workingDir . '/Release.gpg');
- }
- if (file_exists($this->workingDir . '/Packages.gz')) {
- unlink($this->workingDir . '/Packages.gz');
- }
- if (file_exists($this->workingDir . '/Packages')) {
- unlink($this->workingDir . '/Packages');
- }
- if (file_exists($this->workingDir . '/Sources')) {
- unlink($this->workingDir . '/Sources');
- }
- }
-
- /**
- * Mirror a rpm repository
- */
- private function mirrorRpm()
- {
- /**
- * Quit if rpm is not present on the system and that signature check is enabled
- */
- if ($this->checkSignature == 'yes' and !(file_exists('/usr/bin/rpm'))) {
- throw new Exception('rpm is not present on the system (searched in /usr/bin/rpm)');
- }
-
- /**
- * If source package must be synced, add SRPMS to arch array
- */
- if ($this->syncSource == 'yes') {
- $this->arch[] = 'SRPMS';
- }
-
- /**
- * Retrive packages for each arch that have been specified
- */
- foreach ($this->arch as $this->currentArch) {
- $url = $this->url;
-
- /**
- * Replace $releasever value
- */
- if (preg_match('/\$releasever/i', $url)) {
- $url = str_replace('$releasever', $this->releasever, $url);
- }
-
- /**
- * Replace $basearch value
- */
- if (preg_match('/\$basearch/i', $url)) {
- $url = str_replace('$basearch', $this->currentArch, $url);
- }
-
- /**
- * Delete final slash if exist
- */
- $url = rtrim($url, '/');
-
- /**
- * Get repomd.xml
- */
- $this->getRepoMd($url);
-
- /**
- * Find primary packages list location
- */
- $this->parseRepoMd();
-
- /**
- * Get primary packages list file
- */
- $this->getPackagesList($url . '/' . $this->primaryLocation, $this->primaryChecksum);
-
- /**
- * Parse primary packages list file
- */
- $this->parsePrimaryPackagesList($this->workingDir . '/primary.xml.gz');
-
- /**
- * Download rpm packages
- */
- $this->downloadRpmPackages($url);
-
- /**
- * Clean remaining files
- */
- $this->clean();
- }
- }
-
- /**
- * Mirror a deb repository
- */
- private function mirrorDeb()
- {
- /**
- * Try to download distant Release / InRelease file
- */
- $this->getReleaseFile();
-
- /**
- * Check Release GPG signature if enabled
- */
- $this->checkReleaseGPGSignature();
-
- /**
- * Parse Release file to find Packages source files location
- */
- $this->parseReleaseFile();
-
- /**
- * Parse Packages indices file to find packages location
- */
- $this->parsePackagesIndiceFile();
-
- /**
- * Parse Sources indices file to find sources packages location
- */
- $this->parseSourcesIndiceFile();
-
- /**
- * Download deb packages
- */
- $this->downloadDebPackages($this->url);
-
- /**
- * Download sources packages
- */
- $this->downloadDebSourcesPackages($this->url);
-
- /**
- * Download translations
- */
- $this->downloadTranslation();
-
- /**
- * Clean remaining files
- */
- $this->clean();
- }
-
- /**
- * Mirror a repository
- */
- public function mirror()
- {
- $this->initialize();
-
- if ($this->type == 'rpm') {
- $this->mirrorRpm();
- }
-
- if ($this->type == 'deb') {
- $this->mirrorDeb();
- }
- }
-}
diff --git a/www/controllers/Repo/Package.php b/www/controllers/Repo/Package.php
index ed2cf65f7..742fa9e95 100644
--- a/www/controllers/Repo/Package.php
+++ b/www/controllers/Repo/Package.php
@@ -6,6 +6,11 @@
class Package
{
+ private $validMimeTypes = array(
+ 'application/x-rpm',
+ 'application/vnd.debian.binary-package',
+ );
+
/**
* Upload package to repo
*/
@@ -158,11 +163,12 @@ public function upload(int $snapId, $packages)
}
/**
- * Check that the package is valid
+ * Check that the package as a valid Mime type
*/
- if ($packageType !== 'application/x-rpm' and $packageType !== 'application/vnd.debian.binary-package') {
+ if (!in_array(mime_content_type($packageTmpName), $this->validMimeTypes)) {
$uploadError++;
$packageInvalid[] = $packageName;
+ continue;
}
/**