From 892bd13d900680c30e3a9ab5bc37ae43593078eb Mon Sep 17 00:00:00 2001
From: Johannes Meyer <johannes.meyer@icinga.com>
Date: Wed, 17 Apr 2019 10:23:10 +0200
Subject: [PATCH] Merge pull request #3751 from
 Icinga/fix/php-does-not-strip-trailing-whitespace-3733

Fix php ini parser does not strip trailing whitespace

(cherry picked from commit f027bc50e8e13d2cf048b2a5758dbce664be6171)
Signed-off-by: Johannes Meyer <johannes.meyer@icinga.com>
---
 library/Icinga/File/Ini/IniParser.php         |  3 +-
 .../library/Icinga/File/Ini/IniParserTest.php | 65 +++++++++++++++++++
 2 files changed, 67 insertions(+), 1 deletion(-)

diff --git a/library/Icinga/File/Ini/IniParser.php b/library/Icinga/File/Ini/IniParser.php
index 6be0dec15..5ec03a2e9 100644
--- a/library/Icinga/File/Ini/IniParser.php
+++ b/library/Icinga/File/Ini/IniParser.php
@@ -304,6 +304,7 @@ class IniParser
         $str = str_replace('\"', '"', $str);
         $str = str_replace('\\\\', '\\', $str);
 
-        return $str;
+        // This replacement is a work-around for PHP bug #76965. Fixed with versions 7.1.24, 7.2.12 and 7.3.0.
+        return preg_replace('~^([\'"])(.*?)\1\s+$~', '$2', $str);
     }
 }
diff --git a/test/php/library/Icinga/File/Ini/IniParserTest.php b/test/php/library/Icinga/File/Ini/IniParserTest.php
index 7de2e1abe..5a1d7df90 100644
--- a/test/php/library/Icinga/File/Ini/IniParserTest.php
+++ b/test/php/library/Icinga/File/Ini/IniParserTest.php
@@ -170,4 +170,69 @@ EOD;
             'IniParser::parseIniFile does resolve environment variables'
         );
     }
+
+    public function testPhpBug76965()
+    {
+        $config = <<<'EOD'
+[section]
+a = "foobar" 
+b = "foo"bar ""     
+c =   "foobar"  ; comment
+d =' foo ' 
+e =foo
+f = foo
+g = ""foo" "; Edge case, see below for more details
+EOD;
+
+        $parsedConfig = parse_ini_string($config, true, INI_SCANNER_RAW)['section'];
+        if ($parsedConfig['a'] === 'foobar') {
+            $this->markTestSkipped('This version of PHP is not affected by bug #76965');
+        }
+
+        $this->assertEquals('"foobar" ', $parsedConfig['a'], 'PHP version affected but bug #76965 not in effect?');
+        $this->assertEquals('"foo"bar ""     ', $parsedConfig['b'], 'PHP version affected but bug #76965 not in effect?');
+        $this->assertEquals('"foobar"  ', $parsedConfig['c'], 'PHP version affected but bug #76965 not in effect?');
+        $this->assertEquals("' foo ' ", $parsedConfig['d'], 'PHP version affected but bug #76965 not in effect?');
+
+        file_put_contents($this->tempFile, $config);
+        $configObject = IniParser::parseIniFile($this->tempFile);
+
+        $this->assertEquals(
+            'foobar',
+            $configObject->get('section', 'a'),
+            'Workaround for PHP bug #76965 missing'
+        );
+        $this->assertEquals(
+            'foo"bar "',
+            $configObject->get('section', 'b'),
+            'Workaround for PHP bug #76965 missing'
+        );
+        $this->assertEquals(
+            'foobar',
+            $configObject->get('section', 'c'),
+            'Workaround for PHP bug #76965 missing'
+        );
+        $this->assertEquals(
+            ' foo ',
+            $configObject->get('section', 'd'),
+            'Workaround for PHP bug #76965 missing'
+        );
+        $this->assertEquals(
+            'foo',
+            $configObject->get('section', 'e'),
+            'Workaround for PHP bug #76965 missing'
+        );
+        $this->assertEquals(
+            'foo',
+            $configObject->get('section', 'f'),
+            'Workaround for PHP bug #76965 missing'
+        );
+        // This is an edge case which will fail with the current work-around implementation.
+        // Though, it's considered a really rare case and as such deliberately ignored.
+        /*$this->assertEquals(
+            '"foo" ',
+            $configObject->get('section', 'g'),
+            'Workaround for PHP bug #76965 too greedy'
+        );*/
+    }
 }