From e30e4c0d766d316f51d41e9ca95599c328b61083 Mon Sep 17 00:00:00 2001
From: Ludovic <54670129+lbr38@users.noreply.github.com>
Date: Tue, 21 Jan 2025 17:44:19 +0100
Subject: [PATCH] Squashed commit of the following:

commit 271a10b288bb3896ea53a9103c0d98fce92c022c
Merge: fb1149a 7e575bb
Author: Ludo <54670129+lbr38@users.noreply.github.com>
Date:   Tue Jan 21 11:30:03 2025 -0500

    Merge pull request #229 from alryaz/patch-2

    feat: add PVE repositories

commit fb1149a382c8f29ff213a803cd11d83d41ac3a33
Author: Ludovic <54670129+lbr38@users.noreply.github.com>
Date:   Tue Jan 21 17:22:13 2025 +0100

    patch

commit 132dbc66fa67a804f8a52df4596a2b91eea4f95f
Author: Ludovic <54670129+lbr38@users.noreply.github.com>
Date:   Tue Jan 21 17:12:27 2025 +0100

    patch

commit f86c8902eeebd0ce7887a26d8f9528f666033a59
Merge: 982e65d e3b248e
Author: Ludo <54670129+lbr38@users.noreply.github.com>
Date:   Tue Jan 21 11:12:14 2025 -0500

    Merge pull request #228 from alryaz/patch-1

    feat: import binary GPG keys

commit 982e65d9ab27f4c29f38d1dac8341cd89446e187
Author: Ludovic <54670129+lbr38@users.noreply.github.com>
Date:   Tue Jan 21 13:58:04 2025 +0100

    4.16.2

commit 7e575bb4dbe599ac5e3542f4a2c5325b5028bf6c
Author: Ryazanov Alexander Mihailovich <alryaz@alryaz.com>
Date:   Tue Jan 21 11:40:11 2025 +0300

    feat: add PVE repositories

commit e3b248e5314af0f9af249ec47d8c4254d085859f
Author: Ryazanov Alexander Mihailovich <alryaz@alryaz.com>
Date:   Tue Jan 21 11:30:11 2025 +0300

    fix: curl printing to stdout

commit 70b6d48f12d823b208aa5db79a0a40f3cb831b8d
Author: Ryazanov Alexander Mihailovich <alryaz@alryaz.com>
Date:   Tue Jan 21 11:17:20 2025 +0300

    refactor: prase binary GPG keys
---
 docker/Dockerfile                             |   2 +-
 www/controllers/Common.php                    |  17 ++
 www/controllers/Gpg.php                       | 185 +++++++++---------
 www/controllers/Repo/Mirror/Deb.php           |   4 +-
 www/controllers/Repo/Mirror/Rpm.php           |   7 +-
 www/controllers/Repo/Source/Deb.php           |   9 +-
 www/controllers/Repo/Source/Rpm.php           |  17 +-
 www/public/resources/styles/common.css        |   5 +
 .../source-repositories/deb/proxmox-ve.yml    | 111 +++++++++++
 www/update/database/4.12.1.php                |   1 +
 www/version                                   |   2 +-
 11 files changed, 251 insertions(+), 109 deletions(-)
 create mode 100644 www/templates/source-repositories/deb/proxmox-ve.yml

diff --git a/docker/Dockerfile b/docker/Dockerfile
index a26a6ebc..92c7c1d6 100644
--- a/docker/Dockerfile
+++ b/docker/Dockerfile
@@ -19,7 +19,7 @@ ARG max_upload_size
 
 # Install dependencies
 RUN apt-get update -y
-RUN apt-get install findutils iputils-ping git gnupg2 rpm librpmsign9 createrepo-c apt-utils curl ca-certificates apt-transport-https dnsutils xz-utils bzip2 vim -y
+RUN apt-get install findutils iputils-ping git gnupg2 rpm librpmsign9 createrepo-c apt-utils curl ca-certificates apt-transport-https dnsutils xz-utils bzip2 zstd vim -y
 
 # Install postfix
 RUN apt-get install postfix -y
diff --git a/www/controllers/Common.php b/www/controllers/Common.php
index 90b94b61..128a41c0 100644
--- a/www/controllers/Common.php
+++ b/www/controllers/Common.php
@@ -535,6 +535,23 @@ public static function xzUncompress(string $filename, string $outputFilename = n
         unset($myprocess, $content);
     }
 
+    /**
+     *  Uncompress specified zstd file 'file.zst' to 'file'
+     */
+    public static function zstdUncompress(string $filename)
+    {
+        $myprocess = new \Controllers\Process('/usr/bin/unzstd ' . $filename);
+        $myprocess->execute();
+        $content = $myprocess->getOutput();
+        $myprocess->close();
+
+        if ($myprocess->getExitCode() != 0) {
+            throw new Exception('Error while uncompressing zstd file ' . $filename . ': ' . $content);
+        }
+
+        unset($myprocess, $content);
+    }
+
     /**
      *  Return true if distant URL is reachable
      *  The target URL can be a file or a directory
diff --git a/www/controllers/Gpg.php b/www/controllers/Gpg.php
index 18e447db..834315ff 100644
--- a/www/controllers/Gpg.php
+++ b/www/controllers/Gpg.php
@@ -306,6 +306,13 @@ public function import(string $gpgKeyUrl, string $gpgKeyFingerprint, string $gpg
             throw new Exception('Invalid URL');
         }
 
+        /**
+         *  If the user specified a URL in the fingerprint field, quit
+         */
+        if (!empty($gpgKeyFingerprint) and preg_match('#^http(s)?://#', $gpgKeyFingerprint)) {
+            throw new Exception('Invalid fingerprint');
+        }
+
         try {
             /**
              *  Import GPG key from URL
@@ -339,76 +346,88 @@ public function import(string $gpgKeyUrl, string $gpgKeyFingerprint, string $gpg
     }
 
     /**
-     *  Import a plain text GPG key
+     *  Import a file-based GPG key
      */
-    public function importPlainText(string $gpgKey)
+    private function importRawContent(string $fileContent) : array
     {
-        $gpgKey = \Controllers\Common::validateData($gpgKey);
-        $gpgTempFile = TEMP_DIR . '/.repomanager-newgpgkey.tmp';
-
-        /**
-         *  Check if the ASCII text contains invalid characters
-         */
-        if (!\Controllers\Common::isAlphanum($gpgKey, array('-', '=', '+', '/', ' ', ':', '.', '(', ')', "\n", "\r"))) {
-            throw new Exception('ASCII GPG key contains invalid characters');
-        }
-
         /**
          *  Quit if user tries to import a GPG from url
          */
-        if (preg_match('#http(s)?://#', $gpgKey)) {
+        if (preg_match('#http(s)?://#', $fileContent)) {
             throw new Exception('GPG key must be specified in ASCII text format');
         }
 
         /**
          *  Quit if the user tries to import a file on the system
          */
-        if (file_exists($gpgKey)) {
+        if (file_exists($fileContent)) {
             throw new Exception('GPG key must be specified in ASCII text format');
         }
 
         /**
          *  Create a temporary file with the ASCII text
          */
-        if (!file_put_contents($gpgTempFile, $gpgKey)) {
+        $gpgTempFile = TEMP_DIR . '/.repomanager-newgpgkey.tmp';
+        if (!file_put_contents($gpgTempFile, $fileContent)) {
             throw new Exception('could not initialize GPG import');
         }
 
-        /**
-         *  First, extract the fingerprints from the GPG key (there could be one or multiple)
-         */
-        $myprocess = new Process("/usr/bin/gpg --homedir " . GPGHOME . " --no-default-keyring --keyring " . GPGHOME . "/trustedkeys.gpg --show-keys --with-fingerprint --with-colons " . $gpgTempFile . " | grep '^fpr' | cut -d: -f10");
-        $myprocess->execute();
-        $content = $myprocess->getOutput();
-        $myprocess->close();
+        try {
+            /**
+             *  First, extract the fingerprints from the GPG key (there could be one or multiple)
+             */
+            $myprocess = new Process("/usr/bin/gpg --homedir " . GPGHOME . " --no-default-keyring --keyring " . GPGHOME . "/trustedkeys.gpg --show-keys --with-fingerprint --with-colons " . $gpgTempFile . " | grep '^fpr' | cut -d: -f10");
+            $myprocess->execute();
+            $content = $myprocess->getOutput();
+            $myprocess->close();
 
-        if ($myprocess->getExitCode() != 0) {
-            throw new Exception('could not retrieve fingerprint(s) from GPG key: <br><br><pre class="codeblock">"' . $content . '</pre>');
-        }
+            if ($myprocess->getExitCode() != 0) {
+                throw new Exception('could not retrieve fingerprint(s) from GPG key: <br><br><pre class="codeblock">"' . $content . '</pre>');
+            }
 
-        /**
-         *  Output will print all fingerprints on multiple lines (one per fingerprint)
-         */
-        $fingerprints = explode(PHP_EOL, $content);
+            /**
+             *  Output will print all fingerprints on multiple lines (one per fingerprint)
+             */
+            $fingerprints = explode(PHP_EOL, $content);
 
-        /**
-         *  Import file into the repomanager trusted keyring
-         */
-        $myprocess = new \Controllers\Process('/usr/bin/gpg --no-default-keyring --keyring ' . GPGHOME . '/trustedkeys.gpg --import ' . $gpgTempFile);
-        $myprocess->execute();
-        $content = $myprocess->getOutput();
-        $myprocess->close();
+            /**
+             *  Import file into the repomanager trusted keyring
+             */
+            $myprocess = new \Controllers\Process('/usr/bin/gpg --no-default-keyring --keyring ' . GPGHOME . '/trustedkeys.gpg --import ' . $gpgTempFile);
+            $myprocess->execute();
+            $content = $myprocess->getOutput();
+            $myprocess->close();
+
+            if ($myprocess->getExitCode() != 0) {
+                throw new Exception('<pre class="codeblock margin-top-5">' . $content . '</pre>');
+            }
+
+            return $fingerprints;
+        } finally {
+            /**
+             *  Delete temp file
+             */
+            if (!unlink($gpgTempFile)) {
+                throw new Exception('cannot delete temporary file: ' . $tempFile);
+            }
+        }
+    }
+
+    /**
+     *  Import a plain text GPG key
+     */
+    public function importPlainText(string $gpgKey) : array
+    {
+        $gpgKey = \Controllers\Common::validateData($gpgKey);
 
         /**
-         *  Delete temp file
+         *  Check if the ASCII text contains invalid characters
          */
-        unlink($gpgTempFile);
-
-        if ($myprocess->getExitCode() != 0) {
-            throw new Exception('<pre class="codeblock margin-top-5">' . $content . '</pre>');
+        if (!\Controllers\Common::isAlphanum($gpgKey, array('-', '=', '+', '/', ' ', ':', '.', '(', ')', "\n", "\r"))) {
+            throw new Exception('ASCII GPG key contains invalid characters');
         }
 
-        return $fingerprints;
+        return $this->importRawContent($gpgKey);
     }
 
     /**
@@ -417,9 +436,6 @@ public function importPlainText(string $gpgKey)
      */
     public function importFromUrl(string $url) : array
     {
-        $fingerprints = [];
-        $tempFile = TEMP_DIR . '/import-gpgkey.asc';
-
         /**
          *  Quit if the URL is not valid
          */
@@ -436,16 +452,15 @@ public function importFromUrl(string $url) : array
          *  Init curl
          */
         $ch = curl_init();
-        $targetFile = fopen($tempFile, 'w');
 
         /**
          *  Download GPG key from URL
          */
         curl_setopt($ch, CURLOPT_URL, $url);
-        curl_setopt($ch, CURLOPT_FILE, $targetFile);    // set output file
         curl_setopt($ch, CURLOPT_TIMEOUT, 5);           // set timeout
         curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true); // follow redirect
         curl_setopt($ch, CURLOPT_ENCODING, '');         // use compression if any
+        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); // output content to return
 
         /**
          *  If a proxy has been specified
@@ -454,66 +469,46 @@ public function importFromUrl(string $url) : array
             curl_setopt($ch, CURLOPT_PROXY, PROXY);
         }
 
-        if (curl_exec($ch) === false) {
-            /**
-             *  If curl has failed (meaning a curl param might be invalid)
-             */
-            throw new Exception('curl error: ' . curl_error($ch));
+        $result = curl_exec($ch);
 
-            curl_close($ch);
-            fclose($targetFile);
-        }
+        try {
+            if ($result === false) {
+                /**
+                 *  If curl has failed (meaning a curl param might be invalid)
+                 */
+                throw new Exception('curl error: ' . curl_error($ch));
+            }
 
-        /**
-         *  Check that the http return code is 200 (the file has been downloaded)
-         */
-        $status = curl_getinfo($ch);
+            if (empty($result)) {
+                /**
+                 *  If key is empty, meaning bad key
+                 */
+                throw new Exception('empty gpg key response (downloaded file is empty)');
+            }
 
-        if ($status["http_code"] != 200) {
             /**
-             *  If return code is 404
+             *  Check that the http return code is 200 (the file has been downloaded)
              */
-            if ($status["http_code"] == '404') {
-                throw new Exception('404 file not found');
-            } else {
-                throw new Exception('file could not be downloaded (http return code is: ' . $status["http_code"] . ')');
+            $status = curl_getinfo($ch);
+
+            if ($status["http_code"] != 200) {
+                /**
+                 *  If return code is 404
+                 */
+                if ($status["http_code"] == '404') {
+                    throw new Exception('404 file not found');
+                } else {
+                    throw new Exception('file could not be downloaded (http return code is: ' . $status["http_code"] . ')');
+                }
             }
-
+        } finally {
             curl_close($ch);
-            fclose($targetFile);
-
-            return false;
-        }
-
-        fclose($targetFile);
-        curl_close($ch);
-
-        /**
-         *  Get GPG key content
-         */
-        $gpgKey = file_get_contents($tempFile);
-
-        if ($gpgKey === false) {
-            throw new Exception('error while reading temporary file: ' . $tempFile);
-        }
-
-        if (empty($gpgKey)) {
-            throw new Exception('empty while reading temporary file: ' . $tempFile . ' (file is empty)');
         }
 
         /**
          *  Import GPG key
          */
-        $fingerprints = $this->importPlainText($gpgKey);
-
-        /**
-         *  Delete temp file
-         */
-        if (!unlink($tempFile)) {
-            throw new Exception('cannot delete temporary file: ' . $tempFile);
-        }
-
-        return $fingerprints;
+        return $this->importRawContent($result);
     }
 
     /**
diff --git a/www/controllers/Repo/Mirror/Deb.php b/www/controllers/Repo/Mirror/Deb.php
index e305e557..7be8716c 100644
--- a/www/controllers/Repo/Mirror/Deb.php
+++ b/www/controllers/Repo/Mirror/Deb.php
@@ -99,7 +99,7 @@ private function parseReleaseFile()
                     $splittedLine = explode(' ', trim($line));
 
                     /**
-                     *  We only need the location with its SHA256 (64 caracters long)
+                     *  We only need the location with its SHA256 (64 characters long)
                      *  e.g: bd29d2ec28c10fec66a139d8e9a88ca01ff0f2533ca3fab8dc33c13b533059c1  1279885 main/binary-amd64/Packages
                      */
                     if (strlen($splittedLine[0]) == '64') {
@@ -181,7 +181,7 @@ private function parseReleaseFile()
                         $splittedLine = explode(' ', trim($line));
 
                         /**
-                         *  We only need the location with its md5sum (32 caracters long)
+                         *  We only need the location with its md5sum (32 characters long)
                          *  e.g: 35e89f49cdfaa179e552aee1d67c5cdb  2478327 main/i18n/Translation-fr.bz2
                          */
                         if (strlen($splittedLine[0]) == '32') {
diff --git a/www/controllers/Repo/Mirror/Rpm.php b/www/controllers/Repo/Mirror/Rpm.php
index 857a6c7c..9af0828b 100644
--- a/www/controllers/Repo/Mirror/Rpm.php
+++ b/www/controllers/Repo/Mirror/Rpm.php
@@ -451,6 +451,11 @@ private function parsePrimaryPackagesList(string $primaryFile)
              */
             } elseif ($mime == 'application/gzip') {
                 \Controllers\Common::gunzip($primaryFile);
+            /**
+             *  Case mime type is application/zstd (.gz file)
+             */
+            } elseif ($mime == 'application/zstd') {
+                \Controllers\Common::zstdUncompress($primaryFile);
             /**
              *  Case it's another mime type, throw an error
              */
@@ -458,7 +463,7 @@ private function parsePrimaryPackagesList(string $primaryFile)
                 throw new Exception('MIME type not supported: ' . $mime . '. Please contact the developer to add support for this MIME type.');
             }
         } catch (Exception $e) {
-            throw new Exception($e, 'Error while uncompressing <code>'. end(explode('/', $primaryFile)) . '</code>');
+            throw new Exception('Error while uncompressing <code>'. end(explode('/', $primaryFile)) . '</code>: <pre class="codeblock">' . $e->getMessage() . '</pre>');
         }
 
         /**
diff --git a/www/controllers/Repo/Source/Deb.php b/www/controllers/Repo/Source/Deb.php
index 4a4d1470..40e12fd0 100644
--- a/www/controllers/Repo/Source/Deb.php
+++ b/www/controllers/Repo/Source/Deb.php
@@ -240,12 +240,15 @@ public function addGpgKey(int $id, int $distributionId, string $gpgKeyUrl, strin
          */
         foreach ($fingerprints as $fingerprint) {
             // Ignore fingerprint if already exists
-            foreach ($currentParams['distributions'][$distributionId]['gpgkeys'] as $gpgKeyDefinition) {
-                if (isset($gpgKeyDefinition['fingerprint']) and $gpgKeyDefinition['fingerprint'] == $fingerprint) {
-                    continue 2;
+            if (!empty($currentParams['distributions'][$distributionId]['gpgkeys'])) {
+                foreach ($currentParams['distributions'][$distributionId]['gpgkeys'] as $gpgKeyDefinition) {
+                    if (isset($gpgKeyDefinition['fingerprint']) and $gpgKeyDefinition['fingerprint'] == $fingerprint) {
+                        continue 2;
+                    }
                 }
             }
 
+            // Otherwise add the fingerprint
             $currentParams['distributions'][$distributionId]['gpgkeys'][] = array(
                 'fingerprint' => $fingerprint
             );
diff --git a/www/controllers/Repo/Source/Rpm.php b/www/controllers/Repo/Source/Rpm.php
index 0314ad37..743dbcc7 100644
--- a/www/controllers/Repo/Source/Rpm.php
+++ b/www/controllers/Repo/Source/Rpm.php
@@ -28,9 +28,11 @@ public function addReleasever(int $id, string $name)
         /**
          *  Check that a release version with the same name does not already exist
          */
-        foreach ($currentParams['releasever'] as $releasever) {
-            if ($releasever['name'] == $name) {
-                throw new Exception('Release version ' . $name . ' already exists');
+        if (!empty($currentParams['releasever'])) {
+            foreach ($currentParams['releasever'] as $releasever) {
+                if ($releasever['name'] == $name) {
+                    throw new Exception('Release version ' . $name . ' already exists');
+                }
             }
         }
 
@@ -156,12 +158,15 @@ public function addGpgKey(int $id, int $releaseverId, string $gpgKeyUrl, string
          */
         foreach ($fingerprints as $fingerprint) {
             // Ignore fingerprint if already exists
-            foreach ($currentParams['releasever'][$releaseverId]['gpgkeys'] as $gpgKeyDefinition) {
-                if (isset($gpgKeyDefinition['fingerprint']) and $gpgKeyDefinition['fingerprint'] == $fingerprint) {
-                    continue 2;
+            if (!empty($currentParams['releasever'][$releaseverId]['gpgkeys'])) {
+                foreach ($currentParams['releasever'][$releaseverId]['gpgkeys'] as $gpgKeyDefinition) {
+                    if (isset($gpgKeyDefinition['fingerprint']) and $gpgKeyDefinition['fingerprint'] == $fingerprint) {
+                        continue 2;
+                    }
                 }
             }
 
+            // Otherwise add the fingerprint
             $currentParams['releasever'][$releaseverId]['gpgkeys'][] = array(
                 'fingerprint' => $fingerprint
             );
diff --git a/www/public/resources/styles/common.css b/www/public/resources/styles/common.css
index 2dbb7666..75795dc0 100644
--- a/www/public/resources/styles/common.css
+++ b/www/public/resources/styles/common.css
@@ -213,6 +213,7 @@ pre.codeblock {
 .min-height-100 {min-height: 100px}.min-height-200 {min-height: 200px}.min-height-300 {min-height: 300px}.min-height-400 {min-height: 400px}.min-height-500 {min-height: 500px}.min-height-600 {min-height: 600px}.min-height-700 {min-height: 700px}.min-height-800 {min-height: 800px}.min-height-900 {min-height: 900px}.min-height-1000 {min-height: 1000px}
 .min-width-100 {min-width: 100px}.min-width-200 {min-width: 200px}.min-width-300 {min-width: 300px}.min-width-400 {min-width: 400px}.min-width-500 {min-width: 500px}.min-width-600 {min-width: 600px}.min-width-700 {min-width: 700px}.min-width-800 {min-width: 800px}.min-width-900 {min-width: 900px}.min-width-1000 {min-width: 1000px}
 .width-100{width:100%}
+.height-100{height:100%}
 .max-width-fit{max-width:fit-content}
 .resize-disabled{resize:none;}
 
@@ -344,6 +345,10 @@ td {
 /**
  *  Forms, button, input, textarea
  */
+input {
+    accent-color: #15bf7f;
+}
+
 input[type=text], input[type=date], input[type=time], input[type=number], input[type=email], input[type=password], input[type=color], input[type=file], select {
     width: 100%;
     height: 35px;
diff --git a/www/templates/source-repositories/deb/proxmox-ve.yml b/www/templates/source-repositories/deb/proxmox-ve.yml
new file mode 100644
index 00000000..2c0efd42
--- /dev/null
+++ b/www/templates/source-repositories/deb/proxmox-ve.yml
@@ -0,0 +1,111 @@
+---
+description: Proxmox Virtual Environment
+type: deb
+repositories:
+  - name: pve-public
+    type: deb
+    description: Proxmox Virtual Environment Public Repositories
+    url: http://download.proxmox.com/debian/pve
+    distributions:
+      - name: bookworm
+        description: PVE 8 for Debian Bookworm
+        components:
+          - name: pve-no-subscription
+          - name: pvetest
+        gpgkeys:
+          - fingerprint: F4E136C67CDCE41AE6DE6FC81140AF8F639E0C39
+          - link: https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg
+      - name: bullseye
+        description: PVE 7 for Debian Bullseye
+        components:
+          - name: pve-no-subscription
+          - name: pvetest
+        gpgkeys:
+          - fingerprint: 28139A2F830BD68478A1A01FDD4BA3917E23BF59
+          - link: https://enterprise.proxmox.com/debian/proxmox-release-bullseye.gpg
+      - name: buster
+        description: PVE 6 for Debian Buster
+        components:
+          - name: pve-no-subscription
+          - name: pvetest
+        gpgkeys:
+          - fingerprint: 353479F83781D7F8ED5F5AC57BF2812E8A6E88E0
+          - link: https://enterprise.proxmox.com/debian/proxmox-ve-release-6.x.gpg
+      - name: stretch
+        description: PVE 5 for Debian Stretch
+        components:
+          - name: pve-no-subscription
+          - name: pvetest
+        gpgkeys:
+          - fingerprint: 359E95965E2C3D643159CD300D9A1950E2EF0603
+          - link: https://enterprise.proxmox.com/debian/proxmox-ve-release-5.x.gpg
+      - name: stretch
+        description: PVE 4 for Debian Jessie
+        components:
+          - name: pve-no-subscription
+          - name: pvetest
+        gpgkeys:
+          - fingerprint: BE257BAA5D406D01157D323EC23AC7F49887F95A
+          - link: https://enterprise.proxmox.com/debian/proxmox-ve-release-4.x.gpg
+      - name: stretch
+        description: PVE 3 for Debian Wheezy
+        components:
+          - name: pve-no-subscription
+          - name: pvetest
+        gpgkeys:
+          - fingerprint: BE257BAA5D406D01157D323EC23AC7F49887F95A
+          - link: https://enterprise.proxmox.com/debian/key.asc
+      - name: squeeze
+        description: PVE 2 for Debian Squeeze
+        components:
+          - name: pve
+        gpgkeys:
+          - fingerprint: BE257BAA5D406D01157D323EC23AC7F49887F95A
+          - link: https://enterprise.proxmox.com/debian/key.asc
+  - name: pve-enterprise
+    type: deb
+    description: Proxmox Virtual Environment Enterprise Repositories
+    url: https://enterprise.proxmox.com/debian/pve
+    distributions:
+      - name: bookworm
+        description: PVE 8 for Debian Bookworm
+        components:
+          - name: pve-enterprise
+        gpgkeys:
+          - fingerprint: F4E136C67CDCE41AE6DE6FC81140AF8F639E0C39
+          - link: https://enterprise.proxmox.com/debian/proxmox-release-bookworm.gpg
+      - name: bullseye
+        description: PVE 7 for Debian Bullseye
+        components:
+          - name: pve-enterprise
+        gpgkeys:
+          - fingerprint: 28139A2F830BD68478A1A01FDD4BA3917E23BF59
+          - link: https://enterprise.proxmox.com/debian/proxmox-release-bullseye.gpg
+      - name: buster
+        description: PVE 6 for Debian Buster
+        components:
+          - name: pve-enterprise
+        gpgkeys:
+          - fingerprint: 353479F83781D7F8ED5F5AC57BF2812E8A6E88E0
+          - link: https://enterprise.proxmox.com/debian/proxmox-ve-release-6.x.gpg
+      - name: stretch
+        description: PVE 5 for Debian Stretch
+        components:
+          - name: pve-enterprise
+        gpgkeys:
+          - fingerprint: 359E95965E2C3D643159CD300D9A1950E2EF0603
+          - link: https://enterprise.proxmox.com/debian/proxmox-ve-release-5.x.gpg
+      - name: stretch
+        description: PVE 4 for Debian Jessie
+        components:
+          - name: pve-enterprise
+        gpgkeys:
+          - fingerprint: BE257BAA5D406D01157D323EC23AC7F49887F95A
+          - link: https://enterprise.proxmox.com/debian/proxmox-ve-release-4.x.gpg
+      - name: stretch
+        description: PVE 3 for Debian Wheezy
+        components:
+          - name: pve-enterprise
+        gpgkeys:
+          - fingerprint: BE257BAA5D406D01157D323EC23AC7F49887F95A
+          - link: https://enterprise.proxmox.com/debian/key.asc
diff --git a/www/update/database/4.12.1.php b/www/update/database/4.12.1.php
index 212e9358..798b50cb 100644
--- a/www/update/database/4.12.1.php
+++ b/www/update/database/4.12.1.php
@@ -21,6 +21,7 @@
     $lists = [
         // deb
         'github/deb/debian',
+        'github/deb/debian', // TODO temporary fix: import the same source twice to avoid a bug with gpg initialization, to fix
         'github/deb/debian-archive',
         'github/deb/ubuntu',
         'github/deb/ubuntu-archive',
diff --git a/www/version b/www/version
index 99463916..418b826b 100644
--- a/www/version
+++ b/www/version
@@ -1 +1 @@
-4.16.1
\ No newline at end of file
+4.16.2
\ No newline at end of file