From e802eae2a2690a80f217bcc2952a1adb100a7a77 Mon Sep 17 00:00:00 2001
From: mattab <matthieu.aubry@gmail.com>
Date: Sat, 14 Sep 2013 12:38:59 +1200
Subject: [PATCH] Adding Filesystem class which contains file operations And
 factored out code in ServerFilesGenerator.php

---
 core/AssetManager.php                         |   2 +-
 core/CacheFile.php                            |   8 +-
 core/Common.php                               |  95 +-----
 core/Filesystem.php                           | 266 ++++++++++++++++
 core/Http.php                                 |   7 +-
 core/Log.php                                  |   8 +-
 core/Piwik.php                                | 291 +-----------------
 core/PluginsManager.php                       |  12 +-
 core/ReportRenderer/Pdf.php                   |   7 +-
 core/Session.php                              |   4 +-
 core/Translate.php                            |   4 +-
 core/Translate/Writer.php                     |   6 +-
 core/Updates/0.2.10.php                       |   4 +-
 core/Updates/0.6.2.php                        |   3 +-
 js/index.php                                  |   1 +
 plugins/API/API.php                           |  26 +-
 plugins/CorePluginsAdmin/Controller.php       |   7 +-
 plugins/CorePluginsAdmin/PluginInstaller.php  |   6 +-
 plugins/CoreUpdater/Controller.php            |  32 +-
 plugins/ImageGraph/API.php                    |   7 +-
 plugins/Installation/Controller.php           |  20 +-
 plugins/Installation/FormDatabaseSetup.php    |  12 +-
 plugins/Installation/ServerFilesGenerator.php | 134 ++++++++
 plugins/LanguagesManager/API.php              |   5 +-
 tests/PHPUnit/Core/CommonTest.php             |   5 +-
 tests/PHPUnit/Core/PiwikTest.php              |   5 +-
 tests/PHPUnit/Core/ReleaseCheckListTest.php   |   6 +-
 27 files changed, 504 insertions(+), 479 deletions(-)
 create mode 100644 core/Filesystem.php
 create mode 100644 plugins/Installation/ServerFilesGenerator.php

diff --git a/core/AssetManager.php b/core/AssetManager.php
index af8e8a4bd4..1f4a190475 100644
--- a/core/AssetManager.php
+++ b/core/AssetManager.php
@@ -540,7 +540,7 @@ class AssetManager
         $mergedFileDirectory = PIWIK_USER_PATH . '/' . self::MERGED_FILE_DIR;
 
         if (!is_dir($mergedFileDirectory)) {
-            Common::mkdir($mergedFileDirectory);
+            Filesystem::mkdir($mergedFileDirectory);
         }
 
         if (!is_writable($mergedFileDirectory)) {
diff --git a/core/CacheFile.php b/core/CacheFile.php
index addb926d6c..4666ee69a1 100644
--- a/core/CacheFile.php
+++ b/core/CacheFile.php
@@ -11,8 +11,6 @@
 namespace Piwik;
 
 use Exception;
-use Piwik\Piwik;
-use Piwik\Common;
 
 /**
  * Code originally inspired from OpenX
@@ -94,7 +92,7 @@ class CacheFile
 
     protected function cleanupId($id)
     {
-        if (!Common::isValidFilename($id)) {
+        if (!Filesystem::isValidFilename($id)) {
             throw new Exception("Invalid cache ID request $id");
         }
         return $id;
@@ -113,7 +111,7 @@ class CacheFile
             return false;
         }
         if (!is_dir($this->cachePath)) {
-            Common::mkdir($this->cachePath);
+            Filesystem::mkdir($this->cachePath);
         }
         if (!is_writable($this->cachePath)) {
             return false;
@@ -176,6 +174,6 @@ class CacheFile
      */
     public function deleteAll()
     {
-        Piwik::unlinkRecursive($this->cachePath, $deleteRootToo = false);
+        Filesystem::unlinkRecursive($this->cachePath, $deleteRootToo = false);
     }
 }
diff --git a/core/Common.php b/core/Common.php
index c9116c8cc1..311e3fdb84 100644
--- a/core/Common.php
+++ b/core/Common.php
@@ -11,11 +11,9 @@
 namespace Piwik;
 
 use Exception;
-use Piwik\IP;
+use Piwik\Plugins\UserCountry\LocationProvider\DefaultProvider;
 use Piwik\Tracker;
 use Piwik\Tracker\Cache;
-use Piwik\PluginsManager;
-use Piwik\Plugins\UserCountry\LocationProvider\DefaultProvider;
 
 /**
  * Static class providing functions used by both the CORE of Piwik and the visitor Tracking engine.
@@ -268,97 +266,6 @@ class Common
         && strlen($matches[2]) > 0;
     }
 
-    /*
-     * File operations
-     */
-
-    /**
-     * ending WITHOUT slash
-     *
-     * @return string
-     */
-    public static function getPathToPiwikRoot()
-    {
-        return realpath(dirname(__FILE__) . "/..");
-    }
-
-    /**
-     * Create directory if permitted
-     *
-     * @param string $path
-     * @param bool $denyAccess
-     */
-    public static function mkdir($path, $denyAccess = true)
-    {
-        if (!is_dir($path)) {
-            // the mode in mkdir is modified by the current umask
-            @mkdir($path, $mode = 0755, $recursive = true);
-        }
-
-        // try to overcome restrictive umask (mis-)configuration
-        if (!is_writable($path)) {
-            @chmod($path, 0755);
-            if (!is_writable($path)) {
-                @chmod($path, 0775);
-
-                // enough! we're not going to make the directory world-writeable
-            }
-        }
-
-        if ($denyAccess) {
-            self::createHtAccess($path, $overwrite = false);
-        }
-    }
-
-    /**
-     * Create .htaccess file in specified directory
-     *
-     * Apache-specific; for IIS @see web.config
-     *
-     * @param string $path     without trailing slash
-     * @param bool $overwrite whether to overwrite an existing file or not
-     * @param string $content
-     */
-    public static function createHtAccess($path, $overwrite = true, $content = "<Files \"*\">\n<IfModule mod_access.c>\nDeny from all\n</IfModule>\n<IfModule !mod_access_compat>\n<IfModule mod_authz_host.c>\nDeny from all\n</IfModule>\n</IfModule>\n<IfModule mod_access_compat>\nDeny from all\n</IfModule>\n</Files>\n")
-    {
-        if (self::isApache()) {
-            $file = $path . '/.htaccess';
-            if ($overwrite || !file_exists($file)) {
-                @file_put_contents($file, $content);
-            }
-        }
-    }
-
-    /**
-     * Get canonicalized absolute path
-     * See http://php.net/realpath
-     *
-     * @param string $path
-     * @return string  canonicalized absolute path
-     */
-    public static function realpath($path)
-    {
-        if (file_exists($path)) {
-            return realpath($path);
-        }
-        return $path;
-    }
-
-    /**
-     * Returns true if the string is a valid filename
-     * File names that start with a-Z or 0-9 and contain a-Z, 0-9, underscore(_), dash(-), and dot(.) will be accepted.
-     * File names beginning with anything but a-Z or 0-9 will be rejected (including .htaccess for example).
-     * File names containing anything other than above mentioned will also be rejected (file names with spaces won't be accepted).
-     *
-     * @param string $filename
-     * @return bool
-     *
-     */
-    public static function isValidFilename($filename)
-    {
-        return (0 !== preg_match('/(^[a-zA-Z0-9]+([a-zA-Z_0-9.-]*))$/D', $filename));
-    }
-
     /*
      * String operations
      */
diff --git a/core/Filesystem.php b/core/Filesystem.php
new file mode 100644
index 0000000000..ab5be2bca0
--- /dev/null
+++ b/core/Filesystem.php
@@ -0,0 +1,266 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik
+ * @package Piwik
+ */
+namespace Piwik;
+
+use Exception;
+
+class Filesystem
+{
+    /**
+     * ending WITHOUT slash
+     *
+     * @return string
+     */
+    public static function getPathToPiwikRoot()
+    {
+        return realpath(dirname(__FILE__) . "/..");
+    }
+
+    /**
+     * Create .htaccess file in specified directory
+     *
+     * Apache-specific; for IIS @see web.config
+     *
+     * @param string $path     without trailing slash
+     * @param bool $overwrite whether to overwrite an existing file or not
+     * @param string $content
+     */
+    public static function createHtAccess($path, $overwrite = true, $content = "<Files \"*\">\n<IfModule mod_access.c>\nDeny from all\n</IfModule>\n<IfModule !mod_access_compat>\n<IfModule mod_authz_host.c>\nDeny from all\n</IfModule>\n</IfModule>\n<IfModule mod_access_compat>\nDeny from all\n</IfModule>\n</Files>\n")
+    {
+        if (Common::isApache()) {
+            $file = $path . '/.htaccess';
+            if ($overwrite || !file_exists($file)) {
+                @file_put_contents($file, $content);
+            }
+        }
+    }
+
+    /**
+     * Returns true if the string is a valid filename
+     * File names that start with a-Z or 0-9 and contain a-Z, 0-9, underscore(_), dash(-), and dot(.) will be accepted.
+     * File names beginning with anything but a-Z or 0-9 will be rejected (including .htaccess for example).
+     * File names containing anything other than above mentioned will also be rejected (file names with spaces won't be accepted).
+     *
+     * @param string $filename
+     * @return bool
+     *
+     */
+    public static function isValidFilename($filename)
+    {
+        return (0 !== preg_match('/(^[a-zA-Z0-9]+([a-zA-Z_0-9.-]*))$/D', $filename));
+    }
+
+    /**
+     * Get canonicalized absolute path
+     * See http://php.net/realpath
+     *
+     * @param string $path
+     * @return string  canonicalized absolute path
+     */
+    public static function realpath($path)
+    {
+        if (file_exists($path)) {
+            return realpath($path);
+        }
+        return $path;
+    }
+
+    /**
+     * Create directory if permitted
+     *
+     * @param string $path
+     * @param bool $denyAccess
+     */
+    public static function mkdir($path, $denyAccess = true)
+    {
+        if (!is_dir($path)) {
+            // the mode in mkdir is modified by the current umask
+            @mkdir($path, $mode = 0755, $recursive = true);
+        }
+
+        // try to overcome restrictive umask (mis-)configuration
+        if (!is_writable($path)) {
+            @chmod($path, 0755);
+            if (!is_writable($path)) {
+                @chmod($path, 0775);
+                // enough! we're not going to make the directory world-writeable
+            }
+        }
+
+        if ($denyAccess) {
+            self::createHtAccess($path, $overwrite = false);
+        }
+    }
+
+    /**
+     * Checks if the filesystem Piwik stores sessions in is NFS or not. This
+     * check is done in order to avoid using file based sessions on NFS system,
+     * since on such a filesystem file locking can make file based sessions
+     * incredibly slow.
+     *
+     * Note: In order to figure this out, we try to run the 'df' program. If
+     * the 'exec' or 'shell_exec' functions are not available, we can't do
+     * the check.
+     *
+     * @return bool True if on an NFS filesystem, false if otherwise or if we
+     *              can't use shell_exec or exec.
+     */
+    public static function checkIfFileSystemIsNFS()
+    {
+        $sessionsPath = Session::getSessionsDirectory();
+
+        // this command will display details for the filesystem that holds the $sessionsPath
+        // path, but only if its type is NFS. if not NFS, df will return one or less lines
+        // and the return code 1. if NFS, it will return 0 and at least 2 lines of text.
+        $command = "df -T -t nfs \"$sessionsPath\" 2>&1";
+
+        if (function_exists('exec')) // use exec
+        {
+            $output = $returnCode = null;
+            @exec($command, $output, $returnCode);
+
+            // check if filesystem is NFS
+            if ($returnCode == 0
+                && count($output) > 1
+            ) {
+                return true;
+            }
+        } else if (function_exists('shell_exec')) // use shell_exec
+        {
+            $output = @shell_exec($command);
+            if ($output) {
+                $output = explode("\n", $output);
+                if (count($output) > 1) // check if filesystem is NFS
+                {
+                    return true;
+                }
+            }
+        }
+
+        return false; // not NFS, or we can't run a program to find out
+    }
+
+    /**
+     * Recursively find pathnames that match a pattern
+     * @see glob()
+     *
+     * @param string $sDir      directory
+     * @param string $sPattern  pattern
+     * @param int $nFlags    glob() flags
+     * @return array
+     */
+    public static function globr($sDir, $sPattern, $nFlags = null)
+    {
+        if (($aFiles = \_glob("$sDir/$sPattern", $nFlags)) == false) {
+            $aFiles = array();
+        }
+        if (($aDirs = \_glob("$sDir/*", GLOB_ONLYDIR)) != false) {
+            foreach ($aDirs as $sSubDir) {
+                if (is_link($sSubDir)) {
+                    continue;
+                }
+
+                $aSubFiles = self::globr($sSubDir, $sPattern, $nFlags);
+                $aFiles = array_merge($aFiles, $aSubFiles);
+            }
+        }
+        return $aFiles;
+    }
+
+    /**
+     * Recursively delete a directory
+     *
+     * @param string $dir            Directory name
+     * @param boolean $deleteRootToo  Delete specified top-level directory as well
+     */
+    public static function unlinkRecursive($dir, $deleteRootToo)
+    {
+        if (!$dh = @opendir($dir)) {
+            return;
+        }
+        while (false !== ($obj = readdir($dh))) {
+            if ($obj == '.' || $obj == '..') {
+                continue;
+            }
+
+            if (!@unlink($dir . '/' . $obj)) {
+                self::unlinkRecursive($dir . '/' . $obj, true);
+            }
+        }
+        closedir($dh);
+        if ($deleteRootToo) {
+            @rmdir($dir);
+        }
+        return;
+    }
+
+    /**
+     * Copy individual file from $source to $target.
+     *
+     * @param string $source      eg. './tmp/latest/index.php'
+     * @param string $dest        eg. './index.php'
+     * @param bool $excludePhp
+     * @throws Exception
+     * @return bool
+     */
+    public static function copy($source, $dest, $excludePhp = false)
+    {
+        static $phpExtensions = array('php', 'tpl', 'twig');
+
+        if ($excludePhp) {
+            $path_parts = pathinfo($source);
+            if (in_array($path_parts['extension'], $phpExtensions)) {
+                return true;
+            }
+        }
+
+        if (!@copy($source, $dest)) {
+            @chmod($dest, 0755);
+            if (!@copy($source, $dest)) {
+                $message = "Error while creating/copying file to <code>$dest</code>. <br />"
+                    . Piwik::getErrorMessageMissingPermissions(self::getPathToPiwikRoot());
+                throw new Exception($message);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Copy recursively from $source to $target.
+     *
+     * @param string $source      eg. './tmp/latest'
+     * @param string $target      eg. '.'
+     * @param bool $excludePhp
+     */
+    public static function copyRecursive($source, $target, $excludePhp = false)
+    {
+        if (is_dir($source)) {
+            self::mkdir($target, false);
+            $d = dir($source);
+            while (false !== ($entry = $d->read())) {
+                if ($entry == '.' || $entry == '..') {
+                    continue;
+                }
+
+                $sourcePath = $source . '/' . $entry;
+                if (is_dir($sourcePath)) {
+                    self::copyRecursive($sourcePath, $target . '/' . $entry, $excludePhp);
+                    continue;
+                }
+                $destPath = $target . '/' . $entry;
+                self::copy($sourcePath, $destPath, $excludePhp);
+            }
+            $d->close();
+        } else {
+            self::copy($source, $target, $excludePhp);
+        }
+    }
+}
diff --git a/core/Http.php b/core/Http.php
index 9f3d52045d..0da5b04503 100644
--- a/core/Http.php
+++ b/core/Http.php
@@ -11,11 +11,6 @@
 namespace Piwik;
 
 use Exception;
-use Piwik\Config;
-use Piwik\Piwik;
-use Piwik\Common;
-use Piwik\IP;
-use Piwik\Version;
 
 /**
  * Server-side http client to retrieve content from remote servers, and optionally save to a local file.
@@ -80,7 +75,7 @@ class Http
         $file = null;
         if ($destinationPath) {
             // Ensure destination directory exists
-            Common::mkdir(dirname($destinationPath));
+            Filesystem::mkdir(dirname($destinationPath));
             if (($file = @fopen($destinationPath, 'wb')) === false || !is_resource($file)) {
                 throw new Exception('Error while creating the file: ' . $destinationPath);
             }
diff --git a/core/Log.php b/core/Log.php
index 48a9f6c429..a2720098dd 100644
--- a/core/Log.php
+++ b/core/Log.php
@@ -9,12 +9,10 @@
  * @package Piwik
  */
 namespace Piwik;
-use Piwik\Config;
-use Piwik\Common;
 
-use Piwik\Log\Exception;
-use Piwik\Log\Error;
 use Piwik\Log\APICall;
+use Piwik\Log\Error;
+use Piwik\Log\Exception;
 use Piwik\Log\Message;
 
 /**
@@ -64,7 +62,7 @@ abstract class Log extends \Zend_Log
 
     function addWriteToFile()
     {
-        Common::mkdir(dirname($this->logToFileFilename));
+        Filesystem::mkdir(dirname($this->logToFileFilename));
         $writerFile = new \Zend_Log_Writer_Stream($this->logToFileFilename);
         $writerFile->setFormatter($this->fileFormatter);
         $this->addWriter($writerFile);
diff --git a/core/Piwik.php b/core/Piwik.php
index 469b1f80e6..d2f760ed7f 100644
--- a/core/Piwik.php
+++ b/core/Piwik.php
@@ -182,68 +182,6 @@ class Piwik
      * File and directory operations
      */
 
-    /**
-     * Copy recursively from $source to $target.
-     *
-     * @param string $source      eg. './tmp/latest'
-     * @param string $target      eg. '.'
-     * @param bool $excludePhp
-     */
-    static public function copyRecursive($source, $target, $excludePhp = false)
-    {
-        if (is_dir($source)) {
-            Common::mkdir($target, false);
-            $d = dir($source);
-            while (false !== ($entry = $d->read())) {
-                if ($entry == '.' || $entry == '..') {
-                    continue;
-                }
-
-                $sourcePath = $source . '/' . $entry;
-                if (is_dir($sourcePath)) {
-                    self::copyRecursive($sourcePath, $target . '/' . $entry, $excludePhp);
-                    continue;
-                }
-                $destPath = $target . '/' . $entry;
-                self::copy($sourcePath, $destPath, $excludePhp);
-            }
-            $d->close();
-        } else {
-            self::copy($source, $target, $excludePhp);
-        }
-    }
-
-    /**
-     * Copy individual file from $source to $target.
-     *
-     * @param string $source      eg. './tmp/latest/index.php'
-     * @param string $dest        eg. './index.php'
-     * @param bool $excludePhp
-     * @throws Exception
-     * @return bool
-     */
-    static public function copy($source, $dest, $excludePhp = false)
-    {
-        static $phpExtensions = array('php', 'tpl', 'twig');
-
-        if ($excludePhp) {
-            $path_parts = pathinfo($source);
-            if (in_array($path_parts['extension'], $phpExtensions)) {
-                return true;
-            }
-        }
-
-        if (!@copy($source, $dest)) {
-            @chmod($dest, 0755);
-            if (!@copy($source, $dest)) {
-                $message = "Error while creating/copying file to <code>$dest</code>. <br />"
-                    . self::getErrorMessageMissingPermissions(Common::getPathToPiwikRoot());
-                throw new Exception($message);
-            }
-        }
-        return true;
-    }
-
     /**
      * Returns friendly error message explaining how to fix permissions
      *
@@ -268,60 +206,6 @@ class Piwik
         return $message;
     }
 
-    /**
-     * Recursively delete a directory
-     *
-     * @param string $dir            Directory name
-     * @param boolean $deleteRootToo  Delete specified top-level directory as well
-     */
-    static public function unlinkRecursive($dir, $deleteRootToo)
-    {
-        if (!$dh = @opendir($dir)) {
-            return;
-        }
-        while (false !== ($obj = readdir($dh))) {
-            if ($obj == '.' || $obj == '..') {
-                continue;
-            }
-
-            if (!@unlink($dir . '/' . $obj)) {
-                self::unlinkRecursive($dir . '/' . $obj, true);
-            }
-        }
-        closedir($dh);
-        if ($deleteRootToo) {
-            @rmdir($dir);
-        }
-        return;
-    }
-
-    /**
-     * Recursively find pathnames that match a pattern
-     * @see glob()
-     *
-     * @param string $sDir      directory
-     * @param string $sPattern  pattern
-     * @param int $nFlags    glob() flags
-     * @return array
-     */
-    public static function globr($sDir, $sPattern, $nFlags = null)
-    {
-        if (($aFiles = \_glob("$sDir/$sPattern", $nFlags)) == false) {
-            $aFiles = array();
-        }
-        if (($aDirs = \_glob("$sDir/*", GLOB_ONLYDIR)) != false) {
-            foreach ($aDirs as $sSubDir) {
-                if (is_link($sSubDir)) {
-                    continue;
-                }
-
-                $aSubFiles = self::globr($sSubDir, $sPattern, $nFlags);
-                $aFiles = array_merge($aFiles, $aSubFiles);
-            }
-        }
-        return $aFiles;
-    }
-
     /**
      * Returns the help text displayed to suggest which command to run to give writable access to a file or directory
      *
@@ -351,7 +235,7 @@ class Piwik
 
         $directoryList = '';
         foreach ($resultCheck as $dir => $bool) {
-            $realpath = Common::realpath($dir);
+            $realpath = Filesystem::realpath($dir);
             if (!empty($realpath) && $bool === false) {
                 $directoryList .= self::getMakeWritableCommand($realpath);
             }
@@ -359,7 +243,7 @@ class Piwik
 
         // Also give the chown since the chmod is only 755
         if (!Common::isWindows()) {
-            $realpath = Common::realpath(PIWIK_INCLUDE_PATH . '/');
+            $realpath = Filesystem::realpath(PIWIK_INCLUDE_PATH . '/');
             $directoryList = "<code>chown -R www-data:www-data " . $realpath . "</code><br/>" . $directoryList;
         }
 
@@ -391,10 +275,10 @@ class Piwik
             // Create an empty directory
             $isFile = strpos($directoryToCheck, '.') !== false;
             if (!$isFile && !file_exists($directoryToCheck)) {
-                Common::mkdir($directoryToCheck);
+                Filesystem::mkdir($directoryToCheck);
             }
 
-            $directory = Common::realpath($directoryToCheck);
+            $directory = Filesystem::realpath($directoryToCheck);
             $resultCheck[$directory] = false;
             if ($directory !== false // realpath() returns FALSE on failure
                 && is_writable($directoryToCheck)
@@ -430,7 +314,7 @@ class Piwik
      */
     static public function getAutoUpdateMakeWritableMessage()
     {
-        $realpath = Common::realpath(PIWIK_INCLUDE_PATH . '/');
+        $realpath = Filesystem::realpath(PIWIK_INCLUDE_PATH . '/');
         $message = '';
         $message .= "<code>chown -R www-data:www-data " . $realpath . "</code><br />";
         $message .= "<code>chmod -R 0755 " . $realpath . "</code><br />";
@@ -438,123 +322,6 @@ class Piwik
         return $message;
     }
 
-    /**
-     * Generate default robots.txt, favicon.ico, etc to suppress
-     * 404 (Not Found) errors in the web server logs, if Piwik
-     * is installed in the web root (or top level of subdomain).
-     *
-     * @see misc/crossdomain.xml
-     */
-    static public function createWebRootFiles()
-    {
-        $filesToCreate = array(
-            '/robots.txt',
-            '/favicon.ico',
-        );
-        foreach ($filesToCreate as $file) {
-            @file_put_contents(PIWIK_DOCUMENT_ROOT . $file, '');
-        }
-    }
-
-    /**
-     * Generate Apache .htaccess files to restrict access
-     */
-    static public function createHtAccessFiles()
-    {
-        // deny access to these folders
-        $directoriesToProtect = array(
-            '/config',
-            '/core',
-            '/lang',
-            '/tmp',
-        );
-        foreach ($directoriesToProtect as $directoryToProtect) {
-            Common::createHtAccess(PIWIK_INCLUDE_PATH . $directoryToProtect, $overwrite = true);
-        }
-
-        // Allow/Deny lives in different modules depending on the Apache version
-        $allow = "<IfModule mod_access.c>\nAllow from all\n</IfModule>\n<IfModule !mod_access_compat>\n<IfModule mod_authz_host.c>\nAllow from all\n</IfModule>\n</IfModule>\n<IfModule mod_access_compat>\nAllow from all\n</IfModule>\n";
-        $deny = "<IfModule mod_access.c>\nDeny from all\n</IfModule>\n<IfModule !mod_access_compat>\n<IfModule mod_authz_host.c>\nDeny from all\n</IfModule>\n</IfModule>\n<IfModule mod_access_compat>\nDeny from all\n</IfModule>\n";
-
-        // more selective allow/deny filters
-        $allowAny = "<Files \"*\">\n" . $allow . "Satisfy any\n</Files>\n";
-        $allowStaticAssets = "<Files ~ \"\\.(test\.php|gif|ico|jpg|png|svg|js|css|swf)$\">\n" . $allow . "Satisfy any\n</Files>\n";
-        $denyDirectPhp = "<Files ~ \"\\.(php|php4|php5|inc|tpl|in|twig)$\">\n" . $deny . "</Files>\n";
-
-        $directoriesToProtect = array(
-            '/js' => $allowAny,
-            '/libs' => $denyDirectPhp . $allowStaticAssets,
-            '/vendor' => $denyDirectPhp . $allowStaticAssets,
-            '/plugins' => $denyDirectPhp . $allowStaticAssets,
-            '/misc/user' => $denyDirectPhp . $allowStaticAssets,
-        );
-        foreach ($directoriesToProtect as $directoryToProtect => $content) {
-            Common::createHtAccess(PIWIK_INCLUDE_PATH . $directoryToProtect, $overwrite = true, $content);
-        }
-    }
-
-    /**
-     * Generate IIS web.config files to restrict access
-     *
-     * Note: for IIS 7 and above
-     */
-    static public function createWebConfigFiles()
-    {
-        @file_put_contents(PIWIK_INCLUDE_PATH . '/web.config',
-            '<?xml version="1.0" encoding="UTF-8"?>
-<configuration>
-  <system.webServer>
-    <security>
-      <requestFiltering>
-        <hiddenSegments>
-          <add segment="config" />
-          <add segment="core" />
-          <add segment="lang" />
-          <add segment="tmp" />
-        </hiddenSegments>
-        <fileExtensions>
-          <add fileExtension=".tpl" allowed="false" />
-          <add fileExtension=".twig" allowed="false" />
-          <add fileExtension=".php4" allowed="false" />
-          <add fileExtension=".php5" allowed="false" />
-          <add fileExtension=".inc" allowed="false" />
-          <add fileExtension=".in" allowed="false" />
-        </fileExtensions>
-      </requestFiltering>
-    </security>
-    <directoryBrowse enabled="false" />
-    <defaultDocument>
-      <files>
-        <remove value="index.php" />
-        <add value="index.php" />
-      </files>
-    </defaultDocument>
-  </system.webServer>
-</configuration>');
-
-        // deny direct access to .php files
-        $directoriesToProtect = array(
-            '/libs',
-            '/vendor',
-            '/plugins',
-        );
-        foreach ($directoriesToProtect as $directoryToProtect) {
-            @file_put_contents(PIWIK_INCLUDE_PATH . $directoryToProtect . '/web.config',
-                '<?xml version="1.0" encoding="UTF-8"?>
-<configuration>
-  <system.webServer>
-    <security>
-      <requestFiltering>
-        <denyUrlSequences>
-          <add sequence=".php" />
-        </denyUrlSequences>
-      </requestFiltering>
-    </security>
-  </system.webServer>
-</configuration>');
-        }
-    }
-
     /**
      * Get file integrity information (in PIWIK_INCLUDE_PATH).
      *
@@ -2011,54 +1778,6 @@ class Piwik
         return false;
     }
 
-    /**
-     * Checks if the filesystem Piwik stores sessions in is NFS or not. This
-     * check is done in order to avoid using file based sessions on NFS system,
-     * since on such a filesystem file locking can make file based sessions
-     * incredibly slow.
-     *
-     * Note: In order to figure this out, we try to run the 'df' program. If
-     * the 'exec' or 'shell_exec' functions are not available, we can't do
-     * the check.
-     *
-     * @return bool True if on an NFS filesystem, false if otherwise or if we
-     *              can't use shell_exec or exec.
-     */
-    public static function checkIfFileSystemIsNFS()
-    {
-        $sessionsPath = Session::getSessionsDirectory();
-
-        // this command will display details for the filesystem that holds the $sessionsPath
-        // path, but only if its type is NFS. if not NFS, df will return one or less lines
-        // and the return code 1. if NFS, it will return 0 and at least 2 lines of text.
-        $command = "df -T -t nfs \"$sessionsPath\" 2>&1";
-
-        if (function_exists('exec')) // use exec
-        {
-            $output = $returnCode = null;
-            @exec($command, $output, $returnCode);
-
-            // check if filesystem is NFS
-            if ($returnCode == 0
-                && count($output) > 1
-            ) {
-                return true;
-            }
-        } else if (function_exists('shell_exec')) // use shell_exec
-        {
-            $output = @shell_exec($command);
-            if ($output) {
-                $output = explode("\n", $output);
-                if (count($output) > 1) // check if filesystem is NFS
-                {
-                    return true;
-                }
-            }
-        }
-
-        return false; // not NFS, or we can't run a program to find out
-    }
-
     /**
      * Returns the option name of the option that stores the time the archive.php
      * script was last run.
diff --git a/core/PluginsManager.php b/core/PluginsManager.php
index b12ab0cd45..2df4707994 100644
--- a/core/PluginsManager.php
+++ b/core/PluginsManager.php
@@ -11,12 +11,8 @@
 
 namespace Piwik;
 
-use Piwik\Config;
-use Piwik\Piwik;
-use Piwik\Common;
-use Piwik\EventDispatcher;
-use Piwik\Translate;
 use Piwik\Plugin\MetadataLoader;
+use Piwik\Translate;
 
 require_once PIWIK_INCLUDE_PATH . '/core/EventDispatcher.php';
 
@@ -210,7 +206,7 @@ class PluginsManager
 
     public static function deletePluginFromFilesystem($plugin)
     {
-        Piwik::unlinkRecursive(PIWIK_INCLUDE_PATH . '/plugins/' . $plugin, $deleteRootToo = true);
+        Filesystem::unlinkRecursive(PIWIK_INCLUDE_PATH . '/plugins/' . $plugin, $deleteRootToo = true);
     }
 
     /**
@@ -321,7 +317,7 @@ class PluginsManager
     {
         $existingPlugins = $this->readPluginsDirectory();
         $isPluginInFilesystem = array_search($pluginName, $existingPlugins) !== false;
-        return Common::isValidFilename($pluginName)
+        return Filesystem::isValidFilename($pluginName)
                 && $isPluginInFilesystem;
     }
 
@@ -497,7 +493,7 @@ class PluginsManager
         $pluginFileName = sprintf("%s/%s.php", $pluginName, $pluginName);
         $pluginClassName = $pluginName;
 
-        if (!Common::isValidFilename($pluginName)) {
+        if (!Filesystem::isValidFilename($pluginName)) {
             throw new \Exception(sprintf("The plugin filename '%s' is not a valid filename", $pluginFileName));
         }
 
diff --git a/core/ReportRenderer/Pdf.php b/core/ReportRenderer/Pdf.php
index 842908de72..d548523eae 100644
--- a/core/ReportRenderer/Pdf.php
+++ b/core/ReportRenderer/Pdf.php
@@ -11,9 +11,10 @@
 namespace Piwik\ReportRenderer;
 
 use Piwik\Common;
-use Piwik\TCPDF;
-use Piwik\ReportRenderer;
+use Piwik\Filesystem;
 use Piwik\Plugins\API\API;
+use Piwik\ReportRenderer;
+use Piwik\TCPDF;
 
 /**
  * @see libs/tcpdf
@@ -378,7 +379,7 @@ class Pdf extends ReportRenderer
                         if ($logoHeight < 16) {
                             $topMargin = 2;
                         }
-                        $path = Common::getPathToPiwikRoot() . "/" . $rowMetadata['logo'];
+                        $path = Filesystem::getPathToPiwikRoot() . "/" . $rowMetadata['logo'];
                         if (file_exists($path)) {
                             $this->TCPDF->Image($path, $posX + ($leftMargin = 2), $posY + $topMargin, $logoWidth / 4);
                         }
diff --git a/core/Session.php b/core/Session.php
index 59e02a89a2..d89df96ea1 100644
--- a/core/Session.php
+++ b/core/Session.php
@@ -84,7 +84,7 @@ class Session extends Zend_Session
             // for "files", use our own folder to prevent local session file hijacking
             $sessionPath = self::getSessionsDirectory();
             // We always call mkdir since it also chmods the directory which might help when permissions were reverted for some reasons
-            Common::mkdir($sessionPath);
+            Filesystem::mkdir($sessionPath);
 
             @ini_set('session.save_handler', 'files');
             @ini_set('session.save_path', $sessionPath);
@@ -131,7 +131,7 @@ class Session extends Zend_Session
 
             $message = sprintf("Error: %s %s %s\n<pre>Debug: the original error was \n%s</pre>",
                 Piwik_Translate('General_ExceptionUnableToStartSession'),
-                Piwik::getErrorMessageMissingPermissions(Common::getPathToPiwikRoot() . '/tmp/sessions/'),
+                Piwik::getErrorMessageMissingPermissions(Filesystem::getPathToPiwikRoot() . '/tmp/sessions/'),
                 $enableDbSessions,
                 $e->getMessage()
             );
diff --git a/core/Translate.php b/core/Translate.php
index dbb6c6d3f6..c36410f505 100644
--- a/core/Translate.php
+++ b/core/Translate.php
@@ -10,8 +10,6 @@
  */
 namespace Piwik;
 use Exception;
-use Piwik\Config;
-use Piwik\Common;
 
 /**
  * @package Piwik
@@ -104,7 +102,7 @@ class Translate
     private function loadCoreTranslationFile($language)
     {
         $path = PIWIK_INCLUDE_PATH . '/lang/' . $language . '.json';
-        if (!Common::isValidFilename($language) || !is_readable($path)) {
+        if (!Filesystem::isValidFilename($language) || !is_readable($path)) {
             throw new Exception(Piwik_TranslateException('General_ExceptionLanguageFileNotFound', array($language)));
         }
         $data = file_get_contents($path);
diff --git a/core/Translate/Writer.php b/core/Translate/Writer.php
index 914914955e..4360b1ecbf 100644
--- a/core/Translate/Writer.php
+++ b/core/Translate/Writer.php
@@ -12,7 +12,7 @@
 namespace Piwik\Translate;
 
 use Exception;
-use Piwik\Common;
+use Piwik\Filesystem;
 use Piwik\PluginsManager;
 use Piwik\Translate\Filter\FilterAbstract;
 use Piwik\Translate\Validate\ValidateAbstract;
@@ -249,7 +249,7 @@ class Writer
 
         $path = $this->getTranslationPath();
 
-        Common::mkdir(dirname($path));
+        Filesystem::mkdir(dirname($path));
 
         return file_put_contents($path, $this->__toString());
     }
@@ -270,7 +270,7 @@ class Writer
 
         $path = $this->getTemporaryTranslationPath();
 
-        Common::mkdir(dirname($path));
+        Filesystem::mkdir(dirname($path));
 
         return file_put_contents($path, $this->__toString());
     }
diff --git a/core/Updates/0.2.10.php b/core/Updates/0.2.10.php
index 588c3f98ff..a378dccd51 100644
--- a/core/Updates/0.2.10.php
+++ b/core/Updates/0.2.10.php
@@ -8,8 +8,8 @@
  * @category Piwik
  * @package Updates
  */
-use Piwik\Piwik;
 use Piwik\Common;
+use Piwik\Filesystem;
 use Piwik\Updater;
 use Piwik\Updates;
 
@@ -66,7 +66,7 @@ class Piwik_Updates_0_2_10 extends Updates
         );
         foreach ($obsoleteDirectories as $dir) {
             if (file_exists(PIWIK_INCLUDE_PATH . $dir)) {
-                Piwik::unlinkRecursive(PIWIK_INCLUDE_PATH . $dir, true);
+                Filesystem::unlinkRecursive(PIWIK_INCLUDE_PATH . $dir, true);
             }
         }
     }
diff --git a/core/Updates/0.6.2.php b/core/Updates/0.6.2.php
index af541713a2..0651f2f676 100644
--- a/core/Updates/0.6.2.php
+++ b/core/Updates/0.6.2.php
@@ -8,6 +8,7 @@
  * @category Piwik
  * @package Updates
  */
+use Piwik\Filesystem;
 use Piwik\Piwik;
 use Piwik\Plugins\SitesManager\API;
 use Piwik\Tracker\Cache;
@@ -34,7 +35,7 @@ class Piwik_Updates_0_6_2 extends Updates
         );
         foreach ($obsoleteDirectories as $dir) {
             if (file_exists($dir)) {
-                Piwik::unlinkRecursive($dir, true);
+                Filesystem::unlinkRecursive($dir, true);
             }
         }
 
diff --git a/js/index.php b/js/index.php
index 721e3353ef..5d8ec0a41b 100644
--- a/js/index.php
+++ b/js/index.php
@@ -26,6 +26,7 @@ define('PIWIK_USER_PATH', '..');
 
 require_once PIWIK_INCLUDE_PATH . '/libs/upgradephp/upgrade.php';
 require_once PIWIK_INCLUDE_PATH . '/core/Piwik.php';
+require_once PIWIK_INCLUDE_PATH . '/core/ProxyHttp.php';
 
 $file = '../piwik.js';
 
diff --git a/plugins/API/API.php b/plugins/API/API.php
index d26b46db47..19cade72f1 100644
--- a/plugins/API/API.php
+++ b/plugins/API/API.php
@@ -10,19 +10,19 @@
  */
 namespace Piwik\Plugins\API;
 
-use Piwik\API\Request;
 use Piwik\API\Proxy;
+use Piwik\API\Request;
+use Piwik\Config;
 use Piwik\DataTable\Filter\ColumnDelete;
 use Piwik\DataTable\Row;
+use Piwik\DataTable;
+use Piwik\Date;
+use Piwik\Filesystem;
 use Piwik\Metrics;
 use Piwik\Piwik;
-use Piwik\Common;
-use Piwik\Config;
-use Piwik\Date;
-use Piwik\DataTable;
 use Piwik\Tracker\GoalManager;
-use Piwik\Version;
 use Piwik\Translate;
+use Piwik\Version;
 
 require_once PIWIK_INCLUDE_PATH . '/core/Config.php';
 
@@ -284,14 +284,14 @@ class API
     {
         $logo = 'plugins/Zeitgeist/images/logo.png';
         if (Config::getInstance()->branding['use_custom_logo'] == 1
-            && file_exists(Common::getPathToPiwikRoot() . '/misc/user/logo.png')
+            && file_exists(Filesystem::getPathToPiwikRoot() . '/misc/user/logo.png')
         ) {
             $logo = 'misc/user/logo.png';
         }
         if (!$pathOnly) {
             return Piwik::getPiwikUrl() . $logo;
         }
-        return Common::getPathToPiwikRoot() . '/' . $logo;
+        return Filesystem::getPathToPiwikRoot() . '/' . $logo;
     }
 
     /**
@@ -304,14 +304,14 @@ class API
     {
         $logo = 'plugins/Zeitgeist/images/logo-header.png';
         if (Config::getInstance()->branding['use_custom_logo'] == 1
-            && file_exists(Common::getPathToPiwikRoot() . '/misc/user/logo-header.png')
+            && file_exists(Filesystem::getPathToPiwikRoot() . '/misc/user/logo-header.png')
         ) {
             $logo = 'misc/user/logo-header.png';
         }
         if (!$pathOnly) {
             return Piwik::getPiwikUrl() . $logo;
         }
-        return Common::getPathToPiwikRoot() . '/' . $logo;
+        return Filesystem::getPathToPiwikRoot() . '/' . $logo;
     }
 
     /**
@@ -325,14 +325,14 @@ class API
     {
         $logo = 'plugins/Zeitgeist/images/logo.svg';
         if (Config::getInstance()->branding['use_custom_logo'] == 1
-            && file_exists(Common::getPathToPiwikRoot() . '/misc/user/logo.svg')
+            && file_exists(Filesystem::getPathToPiwikRoot() . '/misc/user/logo.svg')
         ) {
             $logo = 'misc/user/logo.svg';
         }
         if (!$pathOnly) {
             return Piwik::getPiwikUrl() . $logo;
         }
-        return Common::getPathToPiwikRoot() . '/' . $logo;
+        return Filesystem::getPathToPiwikRoot() . '/' . $logo;
     }
 
     /**
@@ -346,7 +346,7 @@ class API
             /* We always have our application logo */
             return true;
         } else if (Config::getInstance()->branding['use_custom_logo'] == 1
-            && file_exists(Common::getPathToPiwikRoot() . '/misc/user/logo.svg')
+            && file_exists(Filesystem::getPathToPiwikRoot() . '/misc/user/logo.svg')
         ) {
             return true;
         }
diff --git a/plugins/CorePluginsAdmin/Controller.php b/plugins/CorePluginsAdmin/Controller.php
index d5ae70cf41..867a645953 100644
--- a/plugins/CorePluginsAdmin/Controller.php
+++ b/plugins/CorePluginsAdmin/Controller.php
@@ -10,12 +10,13 @@
  */
 namespace Piwik\Plugins\CorePluginsAdmin;
 
-use Piwik\Piwik;
 use Piwik\Common;
 use Piwik\Config;
+use Piwik\Filesystem;
+use Piwik\Piwik;
 use Piwik\Plugin;
-use Piwik\View;
 use Piwik\Url;
+use Piwik\View;
 
 /**
  *
@@ -195,7 +196,7 @@ class Controller extends \Piwik\Controller\Admin
         $pluginName = $this->initPluginModification();
         $uninstalled = \Piwik\PluginsManager::getInstance()->uninstallPlugin($pluginName);
         if (!$uninstalled) {
-            $path = Common::getPathToPiwikRoot() . '/plugins/' . $pluginName . '/';
+            $path = Filesystem::getPathToPiwikRoot() . '/plugins/' . $pluginName . '/';
             $messagePermissions = Piwik::getErrorMessageMissingPermissions($path);
 
             $messageIntro = Piwik_Translate("Warning: \"%s\" could not be uninstalled. Piwik did not have enough permission to delete the files in $path. ",
diff --git a/plugins/CorePluginsAdmin/PluginInstaller.php b/plugins/CorePluginsAdmin/PluginInstaller.php
index 0fa791c795..eb7cf7095c 100644
--- a/plugins/CorePluginsAdmin/PluginInstaller.php
+++ b/plugins/CorePluginsAdmin/PluginInstaller.php
@@ -9,7 +9,7 @@
  * @package CorePluginsAdmin
  */
 namespace Piwik\Plugins\CorePluginsAdmin;
-use Piwik\Http;
+use Piwik\Filesystem;
 use Piwik\Piwik;
 use Piwik\Unzip;
 
@@ -93,7 +93,7 @@ class PluginInstaller
         $pluginTargetPath = PIWIK_USER_PATH . self::PATH_TO_EXTRACT . $this->pluginName;
 
         $this->removeFolderIfExists($pluginTargetPath);
-        Piwik::copyRecursive($tmpPluginFolder, $pluginTargetPath);
+        Filesystem::copyRecursive($tmpPluginFolder, $pluginTargetPath);
     }
 
     /**
@@ -102,7 +102,7 @@ class PluginInstaller
     private function removeFolderIfExists($pathExtracted)
     {
         if (file_exists($pathExtracted)) {
-            Piwik::unlinkRecursive($pathExtracted, true);
+            Filesystem::unlinkRecursive($pathExtracted, true);
         }
     }
 
diff --git a/plugins/CoreUpdater/Controller.php b/plugins/CoreUpdater/Controller.php
index 6853dc3cc9..ae48b8e8ce 100644
--- a/plugins/CoreUpdater/Controller.php
+++ b/plugins/CoreUpdater/Controller.php
@@ -13,19 +13,19 @@ namespace Piwik\Plugins\CoreUpdater;
 use Exception;
 use Piwik\API\Request;
 use Piwik\ArchiveProcessor\Rules;
-use Piwik\Config;
-use Piwik\Piwik;
 use Piwik\Common;
+use Piwik\Config;
+use Piwik\Filesystem;
 use Piwik\Http;
+use Piwik\Piwik;
+use Piwik\Plugins\LanguagesManager\LanguagesManager;
+use Piwik\Unzip;
+use Piwik\UpdateCheck;
 use Piwik\Updater;
-use Piwik\View;
+use Piwik\Updater_UpdateErrorException;
 use Piwik\Version;
-use Piwik\UpdateCheck;
-use Piwik\Unzip;
+use Piwik\View;
 use Piwik\View\OneClickDone;
-use Piwik\Plugins\CoreUpdater\CoreUpdater;
-use Piwik\Plugins\LanguagesManager\LanguagesManager;
-use Piwik\Updater_UpdateErrorException;
 
 /**
  *
@@ -138,7 +138,7 @@ class Controller extends \Piwik\Controller
         $this->pathRootExtractedPiwik = $pathExtracted . 'piwik';
 
         if (file_exists($this->pathRootExtractedPiwik)) {
-            Piwik::unlinkRecursive($this->pathRootExtractedPiwik, true);
+            Filesystem::unlinkRecursive($this->pathRootExtractedPiwik, true);
         }
 
         $archive = Unzip::factory('PclZip', $this->pathPiwikZip);
@@ -173,7 +173,7 @@ class Controller extends \Piwik\Controller
     {
         $configFileBefore = PIWIK_USER_PATH . '/config/global.ini.php';
         $configFileAfter = PIWIK_USER_PATH . self::CONFIG_FILE_BACKUP;
-        Piwik::copy($configFileBefore, $configFileAfter);
+        Filesystem::copy($configFileBefore, $configFileAfter);
     }
 
     private function oneClick_Copy()
@@ -189,7 +189,7 @@ class Controller extends \Piwik\Controller
          * Copy all files to PIWIK_INCLUDE_PATH.
          * These files are accessed through the dispatcher.
          */
-        Piwik::copyRecursive($this->pathRootExtractedPiwik, PIWIK_INCLUDE_PATH);
+        Filesystem::copyRecursive($this->pathRootExtractedPiwik, PIWIK_INCLUDE_PATH);
 
         /*
          * These files are visible in the web root and are generally
@@ -206,23 +206,23 @@ class Controller extends \Piwik\Controller
             );
 
             foreach ($specialCases as $file) {
-                Piwik::copy($this->pathRootExtractedPiwik . $file, PIWIK_DOCUMENT_ROOT . $file);
+                Filesystem::copy($this->pathRootExtractedPiwik . $file, PIWIK_DOCUMENT_ROOT . $file);
             }
 
             /*
              * Copy the non-PHP files (e.g., images, css, javascript)
              */
-            Piwik::copyRecursive($this->pathRootExtractedPiwik, PIWIK_DOCUMENT_ROOT, true);
+            Filesystem::copyRecursive($this->pathRootExtractedPiwik, PIWIK_DOCUMENT_ROOT, true);
         }
 
         /*
          * Config files may be user (account) specific
          */
         if (PIWIK_INCLUDE_PATH !== PIWIK_USER_PATH) {
-            Piwik::copyRecursive($this->pathRootExtractedPiwik . '/config', PIWIK_USER_PATH . '/config');
+            Filesystem::copyRecursive($this->pathRootExtractedPiwik . '/config', PIWIK_USER_PATH . '/config');
         }
 
-        Piwik::unlinkRecursive($this->pathRootExtractedPiwik, true);
+        Filesystem::unlinkRecursive($this->pathRootExtractedPiwik, true);
 
         if (function_exists('apc_clear_cache')) {
             apc_clear_cache(); // clear the system (aka 'opcode') cache
@@ -289,7 +289,7 @@ class Controller extends \Piwik\Controller
     private function doWelcomeUpdates($view, $componentsWithUpdateFile)
     {
         $view->new_piwik_version = Version::VERSION;
-        $view->commandUpgradePiwik = "<br /><code>php " . Common::getPathToPiwikRoot() . "/index.php  -- \"module=CoreUpdater\" </code>";
+        $view->commandUpgradePiwik = "<br /><code>php " . Filesystem::getPathToPiwikRoot() . "/index.php  -- \"module=CoreUpdater\" </code>";
         $pluginNamesToUpdate = array();
         $coreToUpdate = false;
 
diff --git a/plugins/ImageGraph/API.php b/plugins/ImageGraph/API.php
index 6266bf9ed2..8cf9850ce1 100644
--- a/plugins/ImageGraph/API.php
+++ b/plugins/ImageGraph/API.php
@@ -11,12 +11,13 @@
 namespace Piwik\Plugins\ImageGraph;
 
 use Exception;
+use Piwik\Common;
+use Piwik\Filesystem;
 use Piwik\Period;
 use Piwik\Piwik;
-use Piwik\Common;
-use Piwik\Translate;
 use Piwik\Plugins\API\API as MetaAPI;
 use Piwik\Plugins\ImageGraph\StaticGraph;
+use Piwik\Translate;
 
 /**
  * The ImageGraph.get API call lets you generate beautiful static PNG Graphs for any existing Piwik report.
@@ -510,7 +511,7 @@ class API
                 $fileName = self::$DEFAULT_PARAMETERS[$graphType][self::FILENAME_KEY] . '_' . $apiModule . '_' . $apiAction . $idGoal . ' ' . str_replace(',', '-', $date) . ' ' . $idSite . '.png';
                 $fileName = str_replace(array(' ', '/'), '_', $fileName);
 
-                if (!Common::isValidFilename($fileName)) {
+                if (!Filesystem::isValidFilename($fileName)) {
                     throw new Exception('Error: Image graph filename ' . $fileName . ' is not valid.');
                 }
 
diff --git a/plugins/Installation/Controller.php b/plugins/Installation/Controller.php
index 5df88f94f4..106023195d 100644
--- a/plugins/Installation/Controller.php
+++ b/plugins/Installation/Controller.php
@@ -18,6 +18,7 @@ use Piwik\Config;
 use Piwik\DataAccess\ArchiveTableCreator;
 use Piwik\Db\Adapter;
 use Piwik\Db;
+use Piwik\Filesystem;
 use Piwik\Http;
 use Piwik\Piwik;
 use Piwik\Plugins\LanguagesManager\LanguagesManager;
@@ -63,6 +64,16 @@ class Controller extends \Piwik\Controller\Admin
         Piwik_PostEvent('InstallationController.construct', array($this));
     }
 
+    protected static function initServerFilesForSecurity()
+    {
+        if (Common::isIIS()) {
+            ServerFilesGenerator::createWebConfigFiles();
+        } else {
+            ServerFilesGenerator::createHtAccessFiles();
+        }
+        ServerFilesGenerator::createWebRootFiles();
+    }
+
     /**
      * Get installation steps
      *
@@ -720,12 +731,7 @@ class Controller extends \Piwik\Controller\Admin
 
         $infos['can_auto_update'] = Piwik::canAutoUpdate();
 
-        if (Common::isIIS()) {
-            Piwik::createWebConfigFiles();
-        } else {
-            Piwik::createHtAccessFiles();
-        }
-        Piwik::createWebRootFiles();
+        self::initServerFilesForSecurity();
 
         $infos['phpVersion_minimum'] = $piwik_minimumPHPVersion;
         $infos['phpVersion'] = PHP_VERSION;
@@ -858,7 +864,7 @@ class Controller extends \Piwik\Controller\Admin
         }
 
         // check if filesystem is NFS, if it is file based sessions won't work properly
-        $infos['is_nfs'] = Piwik::checkIfFileSystemIsNFS();
+        $infos['is_nfs'] = Filesystem::checkIfFileSystemIsNFS();
 
         // determine whether there are any errors/warnings from the checks done above
         $infos['has_errors'] = false;
diff --git a/plugins/Installation/FormDatabaseSetup.php b/plugins/Installation/FormDatabaseSetup.php
index 71f4643c8a..84efb6e698 100644
--- a/plugins/Installation/FormDatabaseSetup.php
+++ b/plugins/Installation/FormDatabaseSetup.php
@@ -10,14 +10,14 @@
  */
 namespace Piwik\Plugins\Installation;
 
-use Piwik\Db\Adapter;
-use Piwik\Piwik;
-use Piwik\Common;
-use Piwik\QuickForm2;
 use Exception;
-use HTML_QuickForm2_Rule;
 use HTML_QuickForm2_DataSource_Array;
 use HTML_QuickForm2_Factory;
+use HTML_QuickForm2_Rule;
+use Piwik\Db\Adapter;
+use Piwik\Filesystem;
+use Piwik\Piwik;
+use Piwik\QuickForm2;
 use Zend_Db_Adapter_Exception;
 
 /**
@@ -309,7 +309,7 @@ class FormDatabaseSetup_Rule_checkValidFilename extends HTML_QuickForm2_Rule
     {
         $prefix = $this->owner->getValue();
         return empty($prefix)
-            || Common::isValidFilename($prefix);
+            || Filesystem::isValidFilename($prefix);
     }
 }
 
diff --git a/plugins/Installation/ServerFilesGenerator.php b/plugins/Installation/ServerFilesGenerator.php
new file mode 100644
index 0000000000..2fbf591a21
--- /dev/null
+++ b/plugins/Installation/ServerFilesGenerator.php
@@ -0,0 +1,134 @@
+<?php
+/**
+ * Piwik - Open source web analytics
+ *
+ * @link http://piwik.org
+ * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
+ *
+ * @category Piwik_Plugins
+ * @package Installation
+ */
+namespace Piwik\Plugins\Installation;
+
+use Piwik\Filesystem;
+
+class ServerFilesGenerator
+{
+
+    /**
+     * Generate Apache .htaccess files to restrict access
+     */
+    public static function createHtAccessFiles()
+    {
+        // deny access to these folders
+        $directoriesToProtect = array(
+            '/config',
+            '/core',
+            '/lang',
+            '/tmp',
+        );
+        foreach ($directoriesToProtect as $directoryToProtect) {
+            Filesystem::createHtAccess(PIWIK_INCLUDE_PATH . $directoryToProtect, $overwrite = true);
+        }
+
+        // Allow/Deny lives in different modules depending on the Apache version
+        $allow = "<IfModule mod_access.c>\nAllow from all\n</IfModule>\n<IfModule !mod_access_compat>\n<IfModule mod_authz_host.c>\nAllow from all\n</IfModule>\n</IfModule>\n<IfModule mod_access_compat>\nAllow from all\n</IfModule>\n";
+        $deny = "<IfModule mod_access.c>\nDeny from all\n</IfModule>\n<IfModule !mod_access_compat>\n<IfModule mod_authz_host.c>\nDeny from all\n</IfModule>\n</IfModule>\n<IfModule mod_access_compat>\nDeny from all\n</IfModule>\n";
+
+        // more selective allow/deny filters
+        $allowAny = "<Files \"*\">\n" . $allow . "Satisfy any\n</Files>\n";
+        $allowStaticAssets = "<Files ~ \"\\.(test\.php|gif|ico|jpg|png|svg|js|css|swf)$\">\n" . $allow . "Satisfy any\n</Files>\n";
+        $denyDirectPhp = "<Files ~ \"\\.(php|php4|php5|inc|tpl|in|twig)$\">\n" . $deny . "</Files>\n";
+
+        $directoriesToProtect = array(
+            '/js'        => $allowAny,
+            '/libs'      => $denyDirectPhp . $allowStaticAssets,
+            '/vendor'    => $denyDirectPhp . $allowStaticAssets,
+            '/plugins'   => $denyDirectPhp . $allowStaticAssets,
+            '/misc/user' => $denyDirectPhp . $allowStaticAssets,
+        );
+        foreach ($directoriesToProtect as $directoryToProtect => $content) {
+            Filesystem::createHtAccess(PIWIK_INCLUDE_PATH . $directoryToProtect, $overwrite = true, $content);
+        }
+    }
+
+    /**
+     * Generate IIS web.config files to restrict access
+     *
+     * Note: for IIS 7 and above
+     */
+    public static function createWebConfigFiles()
+    {
+        @file_put_contents(PIWIK_INCLUDE_PATH . '/web.config',
+            '<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+  <system.webServer>
+    <security>
+      <requestFiltering>
+        <hiddenSegments>
+          <add segment="config" />
+          <add segment="core" />
+          <add segment="lang" />
+          <add segment="tmp" />
+        </hiddenSegments>
+        <fileExtensions>
+          <add fileExtension=".tpl" allowed="false" />
+          <add fileExtension=".twig" allowed="false" />
+          <add fileExtension=".php4" allowed="false" />
+          <add fileExtension=".php5" allowed="false" />
+          <add fileExtension=".inc" allowed="false" />
+          <add fileExtension=".in" allowed="false" />
+        </fileExtensions>
+      </requestFiltering>
+    </security>
+    <directoryBrowse enabled="false" />
+    <defaultDocument>
+      <files>
+        <remove value="index.php" />
+        <add value="index.php" />
+      </files>
+    </defaultDocument>
+  </system.webServer>
+</configuration>');
+
+        // deny direct access to .php files
+        $directoriesToProtect = array(
+            '/libs',
+            '/vendor',
+            '/plugins',
+        );
+        foreach ($directoriesToProtect as $directoryToProtect) {
+            @file_put_contents(PIWIK_INCLUDE_PATH . $directoryToProtect . '/web.config',
+                '<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+  <system.webServer>
+    <security>
+      <requestFiltering>
+        <denyUrlSequences>
+          <add sequence=".php" />
+        </denyUrlSequences>
+      </requestFiltering>
+    </security>
+  </system.webServer>
+</configuration>');
+        }
+    }
+
+    /**
+     * Generate default robots.txt, favicon.ico, etc to suppress
+     * 404 (Not Found) errors in the web server logs, if Piwik
+     * is installed in the web root (or top level of subdomain).
+     *
+     * @see misc/crossdomain.xml
+     */
+    public static function createWebRootFiles()
+    {
+        $filesToCreate = array(
+            '/robots.txt',
+            '/favicon.ico',
+        );
+        foreach ($filesToCreate as $file) {
+            @file_put_contents(PIWIK_DOCUMENT_ROOT . $file, '');
+        }
+    }
+}
\ No newline at end of file
diff --git a/plugins/LanguagesManager/API.php b/plugins/LanguagesManager/API.php
index eccb98050a..c7bee7603a 100644
--- a/plugins/LanguagesManager/API.php
+++ b/plugins/LanguagesManager/API.php
@@ -11,9 +11,10 @@
  */
 namespace Piwik\Plugins\LanguagesManager;
 
-use Piwik\Piwik;
 use Piwik\Common;
 use Piwik\Db;
+use Piwik\Filesystem;
+use Piwik\Piwik;
 
 /**
  * The LanguagesManager API lets you access existing Piwik translations, and change Users languages preferences.
@@ -54,7 +55,7 @@ class API
     public function isLanguageAvailable($languageCode)
     {
         return $languageCode !== false
-            && Common::isValidFilename($languageCode)
+            && Filesystem::isValidFilename($languageCode)
             && in_array($languageCode, $this->getAvailableLanguages());
     }
 
diff --git a/tests/PHPUnit/Core/CommonTest.php b/tests/PHPUnit/Core/CommonTest.php
index b0f69a69c4..e666ad9b5d 100644
--- a/tests/PHPUnit/Core/CommonTest.php
+++ b/tests/PHPUnit/Core/CommonTest.php
@@ -1,5 +1,6 @@
 <?php
 use Piwik\Common;
+use Piwik\Filesystem;
 
 /**
  * Piwik - Open source web analytics
@@ -383,7 +384,7 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
             "test", "test.txt", "test.......", "en-ZHsimplified",
         );
         foreach ($valid as $toTest) {
-            $this->assertTrue(Common::isValidFilename($toTest), $toTest . " not valid!");
+            $this->assertTrue(Filesystem::isValidFilename($toTest), $toTest . " not valid!");
         }
     }
 
@@ -398,7 +399,7 @@ class Core_CommonTest extends PHPUnit_Framework_TestCase
             "../test", "/etc/htpasswd", '$var', ';test', '[bizarre]', '', ".htaccess", "very long long eogaioge ageja geau ghaeihieg heiagie aiughaeui hfilename",
         );
         foreach ($notvalid as $toTest) {
-            $this->assertFalse(Common::isValidFilename($toTest), $toTest . " valid but shouldn't!");
+            $this->assertFalse(Filesystem::isValidFilename($toTest), $toTest . " valid but shouldn't!");
         }
     }
 
diff --git a/tests/PHPUnit/Core/PiwikTest.php b/tests/PHPUnit/Core/PiwikTest.php
index d9ae053b25..0da18e4fbf 100644
--- a/tests/PHPUnit/Core/PiwikTest.php
+++ b/tests/PHPUnit/Core/PiwikTest.php
@@ -5,8 +5,9 @@
  * @link http://piwik.org
  * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
  */
-use Piwik\Piwik;
 use Piwik\Access;
+use Piwik\Filesystem;
+use Piwik\Piwik;
 use Piwik\Plugins\SitesManager\API;
 use Piwik\Translate;
 
@@ -259,6 +260,6 @@ class PiwikTest extends DatabaseTestCase
      */
     public function testCheckIfFileSystemIsNFSOnNonNFS()
     {
-        $this->assertFalse(Piwik::checkIfFileSystemIsNFS());
+        $this->assertFalse(Filesystem::checkIfFileSystemIsNFS());
     }
 }
diff --git a/tests/PHPUnit/Core/ReleaseCheckListTest.php b/tests/PHPUnit/Core/ReleaseCheckListTest.php
index a70cae6777..cdb12c3ca2 100644
--- a/tests/PHPUnit/Core/ReleaseCheckListTest.php
+++ b/tests/PHPUnit/Core/ReleaseCheckListTest.php
@@ -1,6 +1,6 @@
 <?php
-use Piwik\Piwik;
 use Piwik\Common;
+use Piwik\Filesystem;
 use Piwik\Tracker\Db;
 
 /**
@@ -57,7 +57,7 @@ class ReleaseCheckListTest extends PHPUnit_Framework_TestCase
     public function testTemplatesDontContainDebug()
     {
         $patternFailIfFound = 'dump(';
-        $files = Piwik::globr(PIWIK_INCLUDE_PATH . '/plugins', '*.twig');
+        $files = Filesystem::globr(PIWIK_INCLUDE_PATH . '/plugins', '*.twig');
         foreach ($files as $file) {
             $content = file_get_contents($file);
             $this->assertFalse(strpos($content, $patternFailIfFound), 'found in ' . $file);
@@ -124,7 +124,7 @@ class ReleaseCheckListTest extends PHPUnit_Framework_TestCase
             // SVN native does not make this work on windows
             return;
         }
-        foreach (Piwik::globr(PIWIK_DOCUMENT_ROOT, '*') as $file) {
+        foreach (Filesystem::globr(PIWIK_DOCUMENT_ROOT, '*') as $file) {
             // skip files in these folders
             if (strpos($file, '/.git/') !== false ||
                 strpos($file, '/documentation/') !== false ||
-- 
GitLab