From 0a57f27ea5c1a6d54627f6af15c516f18f44b229 Mon Sep 17 00:00:00 2001
From: Andreas Gohr <andi@splitbrain.org>
Date: Sat, 3 Aug 2013 17:15:13 +0200
Subject: [PATCH] fixed cleanPath bug in tar library FS#2802

This time the test case was correct and actually showed a bug in the tar
library. The error occured only on the first build (directory build/0/)
where the zero was stripped from the path name.

I added unit tests to the cleanPath function and discovered another bug
with handling relative directories. I rewrote the cleanPath() function
and now it should finally work.

Unit tests FTW!
---
 _test/tests/inc/tar.test.php | 23 ++++++++++++++++++++++-
 inc/Tar.class.php            | 28 +++++++++++-----------------
 2 files changed, 33 insertions(+), 18 deletions(-)

diff --git a/_test/tests/inc/tar.test.php b/_test/tests/inc/tar.test.php
index 9801ca1e0..417f1a853 100644
--- a/_test/tests/inc/tar.test.php
+++ b/_test/tests/inc/tar.test.php
@@ -58,6 +58,8 @@ class Tar_TestCase extends DokuWikiTest {
         $tar->addData('another/testdata3.txt', 'testcontent3');
         $tar->close();
 
+copy ($tmp, '/tmp/test.tar');
+
         $this->assertTrue(filesize($tmp) > 30); //arbitrary non-zero number
         $data = file_get_contents($tmp);
 
@@ -66,7 +68,7 @@ class Tar_TestCase extends DokuWikiTest {
         $this->assertTrue(strpos($data, 'testcontent3') !== false, 'Content in TAR');
 
         // fullpath might be too long to be stored as full path FS#2802
-        $this->assertTrue(strpos($data, "$tdir") !== false, 'Path in TAR');
+        $this->assertTrue(strpos($data, "$tdir") !== false, "Path in TAR '$tdir'");
         $this->assertTrue(strpos($data, "testdata1.txt") !== false, 'File in TAR');
 
         $this->assertTrue(strpos($data, 'noway/testdata2.txt') !== false, 'Path in TAR');
@@ -396,4 +398,23 @@ class Tar_TestCase extends DokuWikiTest {
 
         $this->assertEquals(512*4, strlen($file)); // 1 header block + data block + 2 footer blocks
     }
+
+
+    public function test_cleanPath(){
+        $tar = new Tar();
+        $tests = array (
+            '/foo/bar' => 'foo/bar',
+            '/foo/bar/' => 'foo/bar',
+            'foo//bar' => 'foo/bar',
+            'foo/0/bar' => 'foo/0/bar',
+            'foo/../bar' => 'bar',
+            'foo/bang/bang/../../bar' => 'foo/bar',
+            'foo/../../bar' => 'bar',
+            'foo/.././../bar' => 'bar',
+        );
+
+        foreach($tests as $in => $out){
+            $this->assertEquals($out, $tar->cleanPath($in), "Input: $in");
+        }
+    }
 }
diff --git a/inc/Tar.class.php b/inc/Tar.class.php
index d1a38ea0e..bc87d7d29 100644
--- a/inc/Tar.class.php
+++ b/inc/Tar.class.php
@@ -568,29 +568,23 @@ class Tar {
     }
 
     /**
-     * Cleans up a path and removes relative parts
+     * Cleans up a path and removes relative parts, also strips leading slashes
      *
      * @param string $p_dir
      * @return string
      */
-    protected function cleanPath($p_dir) {
-        $r = '';
-        if($p_dir) {
-            $subf = explode("/", $p_dir);
-
-            for($i = count($subf) - 1; $i >= 0; $i--) {
-                if($subf[$i] == ".") {
-                    # do nothing
-                } elseif($subf[$i] == "..") {
-                    $i--;
-                } elseif(!$subf[$i] && $i != count($subf) - 1 && $i) {
-                    # do nothing
-                } else {
-                    $r = $subf[$i].($i != (count($subf) - 1) ? "/".$r : "");
-                }
+    public function cleanPath($path) {
+        $path=explode('/', $path);
+        $newpath=array();
+        foreach($path as $p) {
+            if ($p === '' || $p === '.') continue;
+            if ($p==='..') {
+                array_pop($newpath);
+                continue;
             }
+            array_push($newpath, $p);
         }
-        return $r;
+        return trim(implode('/', $newpath), '/');
     }
 
     /**
-- 
GitLab