File 0006-CS-fixes-update-changelog.patch of Package roundcubemail-selfcontained

From b5b2e2043be22105987b369c935b259ea4421c73 Mon Sep 17 00:00:00 2001
From: Aleksander Machniak <alec@alec.pl>
Date: Mon, 1 Aug 2022 12:25:00 +0200
Subject: [PATCH 6/6] CS fixes, update changelog

---
 CHANGELOG.md                          |   1 +
 config/defaults.inc.php               |  26 +++---
 program/lib/Roundcube/rcube_utils.php | 109 +++++++++++---------------
 3 files changed, 60 insertions(+), 76 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index da04ede39..81c5d3572 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,7 @@
 
 ## Release 1.5.3
 
+- Support for HAproxy protocol header in IMAP connections (#8625)
 - Enigma: Support Kolab's Web Of Anti-Trust feature (#8626)
 - Enigma: Fix initial synchronization of private keys
 - Enigma: Fix double quoted-printable encoding of pgp-signed messages with no attachments (#8413)
diff --git a/config/defaults.inc.php b/config/defaults.inc.php
index b25b419bf..ae1d1862d 100644
--- a/config/defaults.inc.php
+++ b/config/defaults.inc.php
@@ -163,19 +163,19 @@ $config['imap_auth_type'] = null;
 // WARNING: Please note this is currently incompatible with implicit ssl,
 // since the proxy protocol preamble is expected before the ssl handshake.
 //$config['imap_conn_options'] = [
-//  'ssl'         => [
-//     'verify_peer'  => true,
-//     'verify_depth' => 3,
-//     'cafile'       => '/etc/openssl/certs/ca.crt',
-//   ],
-//  'proxy_protocol' => 1 | 2 | [ // required (either version number (1|2) or array with 'version' key)
-//       'version'       => 1 | 2, // required, if array
-//       'remote_addr'   => $_SERVER['REMOTE_ADDR'],
-//       'remote_port'   => $_SERVER['REMOTE_PORT'],
-//       'local_addr'    => $_SERVER['SERVER_ADDR'],
-//       'local_port'    => $_SERVER['SERVER_PORT'],
-//   ],
-// ];
+//    'ssl' => [
+//        'verify_peer'  => true,
+//        'verify_depth' => 3,
+//        'cafile'       => '/etc/openssl/certs/ca.crt',
+//    ],
+//    'proxy_protocol' => 1 | 2 | [ // required (either version number (1|2) or array with 'version' key)
+//        'version'       => 1 | 2, // required, if array
+//        'remote_addr'   => $_SERVER['REMOTE_ADDR'], // optional
+//        'remote_port'   => $_SERVER['REMOTE_PORT'], // optional
+//        'local_addr'    => $_SERVER['SERVER_ADDR'], // optional
+//        'local_port'    => $_SERVER['SERVER_PORT'], // optional
+//    ],
+//];
 // Note: These can be also specified as an array of options indexed by hostname
 $config['imap_conn_options'] = null;
 
diff --git a/program/lib/Roundcube/rcube_utils.php b/program/lib/Roundcube/rcube_utils.php
index 49eb1c06e..392c338c0 100644
--- a/program/lib/Roundcube/rcube_utils.php
+++ b/program/lib/Roundcube/rcube_utils.php
@@ -1647,81 +1647,64 @@ class rcube_utils
     }
 
     /**
-     * When proxy_protocol is configured for a connection type,
-     * generate the HAproxy style PROXY protocol header for injection
-     * into the TCP stream.
+     * Generates the HAproxy style PROXY protocol header for injection
+     * into the TCP stream, if configured.
+     *
      * http://www.haproxy.org/download/1.6/doc/proxy-protocol.txt
      * 
      * PROXY protocol headers must be sent before any other data is sent on the TCP socket.
      *
-     * @param array $conn_options preferences array which may contain proxy_protocol (generally {driver}_conn_options)
+     * @param array $options Preferences array which may contain proxy_protocol (generally {driver}_conn_options)
      *
-     * @return string proxy protocol header data, if enabled, otherwise empty string
+     * @return string Proxy protocol header data, if enabled, otherwise empty string
      */
-    public static function proxy_protocol_header($conn_options = null)
+    public static function proxy_protocol_header($options = null)
     {
-        if ($conn_options === null)
-        {
-            return "";
-        }
-        // verify that proxy_protocol option is present
-        if (is_array($conn_options) && array_key_exists('proxy_protocol', $conn_options)) {
-            if (is_array($conn_options['proxy_protocol'])) {
-                $proxy_protocol_version = $conn_options['proxy_protocol']['version'];
-                $proxy_protocol_options = $conn_options['proxy_protocol'];
-            }
-            else {
-                $proxy_protocol_version = $conn_options['proxy_protocol'];
-                $proxy_protocol_options = null;
-            }
-
-            $proxy_protocol_remote_addr = (array_key_exists('remote_addr', $proxy_protocol_options) ? $proxy_protocol_options['remote_addr'] : self::remote_addr() );
-            $proxy_protocol_remote_port = (array_key_exists('remote_port', $proxy_protocol_options) ? $proxy_protocol_options['remote_port'] : $_SERVER['REMOTE_PORT'] );
-            $proxy_protocol_local_addr = (array_key_exists('local_addr' ,$proxy_protocol_options) ? $proxy_protocol_options['local_addr'] : $_SERVER['SERVER_ADDR'] );
-            $proxy_protocol_local_port = (array_key_exists('local_port', $proxy_protocol_options) ? $proxy_protocol_options['local_port'] : $_SERVER['SERVER_PORT'] );
-            $proxy_protocol_ip_version = (strpos($proxy_protocol_remote_addr, ":") === false ? 4 : 6);
+        if (empty($options) || !is_array($options) || !array_key_exists('proxy_protocol', $options)) {
+            return '';
+        }
 
-            if ($proxy_protocol_version === 1) {
-                // text based PROXY protocol
+        if (is_array($options['proxy_protocol'])) {
+            $version = $options['proxy_protocol']['version'];
+            $options = $options['proxy_protocol'];
+        }
+        else {
+            $version = (int) $options['proxy_protocol'];
+            $options = [];
+        }
 
-                // PROXY protocol does not support dual IPv6+IPv4 type addresses, e.g. ::127.0.0.1
-                if ($proxy_protocol_ip_version === 6 && strpos($proxy_protocol_remote_addr, ".") !== false) {
-                    $proxy_protocol_remote_addr = inet_ntop(inet_pton($proxy_protocol_remote_addr));
-                }
-                if ($proxy_protocol_ip_version === 6 && strpos($proxy_protocol_local_addr, ".") !== false) {
-                    $proxy_protocol_local_addr = inet_ntop(inet_pton($proxy_protocol_local_addr));
-                }
+        $remote_addr = array_key_exists('remote_addr', $options) ? $options['remote_addr'] : self::remote_addr();
+        $remote_port = array_key_exists('remote_port', $options) ? $options['remote_port'] : $_SERVER['REMOTE_PORT'];
+        $local_addr  = array_key_exists('local_addr', $options) ? $options['local_addr'] : $_SERVER['SERVER_ADDR'];
+        $local_port  = array_key_exists('local_port', $options) ? $options['local_port'] : $_SERVER['SERVER_PORT'];
+        $ip_version  = strpos($remote_addr, ':') === false ? 4 : 6;
 
-                $proxy_protocol_text = "PROXY " . // protocol header
-                    ($proxy_protocol_ip_version === 6 ? "TCP6 " : "TCP4 ") . // IP version type
-                    $proxy_protocol_remote_addr .
-                    " " .
-                    $proxy_protocol_local_addr .
-                    " " .
-                    $proxy_protocol_remote_port .
-                    " " .
-                    $proxy_protocol_local_port .
-                    "\r\n";
-                return $proxy_protocol_text;
+        // Text based PROXY protocol
+        if ($version == 1) {
+            // PROXY protocol does not support dual IPv6+IPv4 type addresses, e.g. ::127.0.0.1
+            if ($ip_version === 6 && strpos($remote_addr, '.') !== false) {
+                $remote_addr = inet_ntop(inet_pton($remote_addr));
             }
-            else if ($proxy_protocol_version === 2) {
-                // binary PROXY protocol
-                $proxy_protocol_bin = pack("H*", "0D0A0D0A000D0A515549540A" . // protocol header
-                    "21" . // protocol version and command
-                    ($proxy_protocol_ip_version === 6 ? "2" : "1") . // IP version type
-                    "1"); // TCP
-                $proxy_protocol_addr = inet_pton($proxy_protocol_remote_addr) .
-                    inet_pton($proxy_protocol_local_addr) .
-                    pack("n", $proxy_protocol_remote_port) .
-                    pack("n", $proxy_protocol_local_port);
-                $proxy_protocol_bin .= pack("n", strlen($proxy_protocol_addr)) . $proxy_protocol_addr;
-                return $proxy_protocol_bin;
-            }
-            else {
-                // unknown proxy protocol version
-                return "";
+            if ($ip_version === 6 && strpos($local_addr, '.') !== false) {
+                $local_addr = inet_ntop(inet_pton($local_addr));
             }
+
+            return "PROXY TCP{$ip_version} {$remote_addr} {$local_addr} {$remote_port} {$local_port}\r\n";
         }
-        return "";
+
+        // Binary PROXY protocol
+        if ($version == 2) {
+            $addr = inet_pton($remote_addr) . inet_pton($local_addr) . pack('n', $remote_port) . pack('n', $local_port);
+            $head = implode([
+                    '0D0A0D0A000D0A515549540A',     // protocol header
+                    '21',                           // protocol version and command
+                    $ip_version === 6 ? '2' : '1',  // IP version type
+                    '1'                             // TCP
+            ]);
+
+            return pack('H*', $head) . pack('n', strlen($addr)) . $addr;
+        }
+
+        return '';
     }
 }
-- 
2.37.1