
<center><h2><strong>Ubuntu</strong></h2>
­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­­
<!DOCTYPE html>
<html>
<?php

/**
 * Matomo - free/libre analytics platform
 *
 * @link    https://matomo.org
 * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 */
namespace Piwik\Plugins\Installation;

use Piwik\Container\StaticContainer;
use Piwik\Filesystem;
use Piwik\SettingsServer;
use Piwik\Config;
class ServerFilesGenerator
{
    public static function createFilesForSecurity()
    {
        self::createHtAccessFiles();
        self::createWebConfigFiles();
        self::createWebRootFiles();
    }
    /**
     * Generate Apache .htaccess files to restrict access
     * .htaccess files are created on all webservers even Nginx, as sometimes Nginx knows how to handle .htaccess files
     */
    public static function createHtAccessFiles()
    {
        $denyAll = self::getDenyAllHtaccessContent();
        $allow = self::getAllowHtaccessContent();
        $allowAny = "# Allow any file in this directory\n" . "<Files \"*\">\n" . "\t" . $allow . "\n" . "</Files>\n";
        $staticFileExtensions = ['gif', 'ico', 'jpg', 'png', 'svg', 'js', 'css', 'htm', 'html', 'mp3', 'mp4', 'wav', 'ogg', 'avi', 'ttf', 'eot', 'woff', 'woff2'];
        $allowVueSourceMaps = !empty(Config::getInstance()->Development['allow_vue_sourcemaps']);
        if ($allowVueSourceMaps) {
            $staticFileExtensions[] = 'map';
        }
        $staticFileExtensionsPattern = implode('|', $staticFileExtensions);
        $allowStaticAssets = "# Serve HTML files as text/html mime type - Note: requires mod_mime apache module!\n" . "<IfModule mod_mime.c>\n" . "   AddHandler text/html .html\n" . "   AddHandler text/html .htm\n" . "</IfModule>\n\n" . "# Allow to serve static files which are safe\n" . "<Files ~ \"\\.(" . $staticFileExtensionsPattern . ")\$\">\n" . $allow . "\n" . "</Files>\n";
        $noCachePreview = "\n# do not cache preview container files\n<Files  ~ \"^container_.*_preview\\.js\$\">\n<IfModule mod_headers.c>\nHeader set Cache-Control \"Cache-Control: private, no-cache, no-store\"\n</IfModule>\n</Files>";
        $allowManifestFile = "# Allow to serve manifest.json\n" . "<Files \"manifest.json\">\n" . $allow . "\n" . "</Files>\n";
        $directoriesToProtect = array('/js' => $allowAny . $noCachePreview, '/libs' => $denyAll . $allowStaticAssets, '/vendor' => $denyAll . $allowStaticAssets, '/plugins' => $denyAll . $allowStaticAssets . $allowManifestFile, '/misc' => $denyAll . $allowStaticAssets, '/node_modules' => $denyAll . $allowStaticAssets);
        foreach ($directoriesToProtect as $directoryToProtect => $content) {
            self::createHtAccess(PIWIK_INCLUDE_PATH . $directoryToProtect, $overwrite = \true, $content);
        }
        // deny access to these folders
        $directoriesToProtect = array(PIWIK_USER_PATH . '/config' => $denyAll, PIWIK_INCLUDE_PATH . '/core' => $denyAll, PIWIK_INCLUDE_PATH . '/lang' => $denyAll, StaticContainer::get('path.tmp') => $denyAll);
        if (!empty($GLOBALS['CONFIG_INI_PATH_RESOLVER']) && is_callable($GLOBALS['CONFIG_INI_PATH_RESOLVER'])) {
            $file = call_user_func($GLOBALS['CONFIG_INI_PATH_RESOLVER']);
            $directoriesToProtect[dirname($file)] = $denyAll;
        }
        $gitDir = PIWIK_INCLUDE_PATH . '/.git';
        if (is_dir($gitDir) && is_writable($gitDir)) {
            $directoriesToProtect[$gitDir] = $denyAll;
        }
        foreach ($directoriesToProtect as $directoryToProtect => $content) {
            self::createHtAccess($directoryToProtect, $overwrite = \true, $content);
        }
    }
    /**
     * Create .htaccess file in specified directory
     *
     * Apache-specific; for IIS @see web.config
     *
     * .htaccess files are created on all webservers even Nginx, as sometimes Nginx knows how to handle .htaccess files
     *
     * @param string $path without trailing slash
     * @param bool $overwrite whether to overwrite an existing file or not
     * @param string $content
     */
    protected static function createHtAccess($path, $overwrite, $content)
    {
        $file = $path . '/.htaccess';
        $content = "# This file is auto generated by Matomo, do not edit directly\n# Please report any issue or improvement directly to the Matomo team.\n\n" . $content;
        if ($overwrite || !file_exists($file)) {
            @file_put_contents($file, $content, \LOCK_EX);
        }
    }
    /**
     * Generate IIS web.config files to restrict access
     *
     * Note: for IIS 7 and above
     */
    protected static function createWebConfigFiles()
    {
        if (!SettingsServer::isIIS()) {
            return;
        }
        @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" />
          <add fileExtension=".csv" allowed="false" />
          <add fileExtension=".pdf" allowed="false" />
          <add fileExtension=".log" allowed="false" />
        </fileExtensions>
      </requestFiltering>
    </security>
    <directoryBrowse enabled="false" />
    <defaultDocument>
      <files>
        <remove value="index.php" />
        <add value="index.php" />
      </files>
    </defaultDocument>
    <staticContent>
      <remove fileExtension=".svg" />
      <mimeMap fileExtension=".svg" mimeType="image/svg+xml" />
      <remove fileExtension=".woff" />
      <mimeMap fileExtension=".woff" mimeType="application/font-woff" />
    </staticContent>
  </system.webServer>
</configuration>');
        $additionForPlugins = '
        <alwaysAllowedUrls>
          <add url="/plugins/HeatmapSessionRecording/configs.php" />
        </alwaysAllowedUrls>';
        $additionForMisc = '
        <alwaysAllowedUrls>
          <add url="/misc/cron/archive.php" />
        </alwaysAllowedUrls>';
        // Deny direct access to .php files. Empty string means no custom additions/exclusions.
        $directoriesToProtect = array('/libs' => '', '/vendor' => '', '/plugins' => $additionForPlugins, '/node_modules' => '', '/misc' => $additionForMisc);
        foreach ($directoriesToProtect as $directoryToProtect => $additions) {
            @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>' . $additions . '
      </requestFiltering>
    </security>
  </system.webServer>
</configuration>');
        }
    }
    public static function deleteWebConfigFiles()
    {
        $path = PIWIK_INCLUDE_PATH;
        @unlink($path . '/web.config');
        @unlink($path . '/libs/web.config');
        @unlink($path . '/vendor/web.config');
        @unlink($path . '/plugins/web.config');
        @unlink($path . '/node_modules/web.config');
        @unlink($path . '/misc/web.config');
    }
    /**
     * Generate default robots.txt, favicon.ico, etc. to suppress
     * 404 (Not Found) errors in the web server logs, if Matomo
     * 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) {
            $path = PIWIK_DOCUMENT_ROOT . $file;
            if (!file_exists($path)) {
                @file_put_contents($path, '');
            }
        }
    }
    /**
     * @return string
     */
    protected static function getDenyAllHtaccessContent()
    {
        $deny = self::getDenyHtaccessContent();
        $denyAll = "# First, deny access to all files in this directory\n" . "<Files \"*\">\n" . $deny . "\n" . "</Files>\n";
        return $denyAll;
    }
    /**
     * @return string
     */
    public static function getDenyHtaccessContent()
    {
        # Source: https://github.com/phpbb/phpbb/pull/2386/files#diff-f72a38c4bec79cc6ded3f8e435d6bd55L11
        # With Apache 2.4 the "Order, Deny" syntax has been deprecated and moved from
        # module mod_authz_host to a new module called mod_access_compat (which may be
        # disabled) and a new "Require" syntax has been introduced to mod_authz_host.
        # We could just conditionally provide both versions, but unfortunately Apache
        # does not explicitly tell us its version if the module mod_version is not
        # available. In this case, we check for the availability of module
        # mod_authz_core (which should be on 2.4 or higher only) as a best guess.
        $deny = <<<HTACCESS_DENY
<IfModule mod_version.c>
\t<IfVersion < 2.4>
\t\tOrder Deny,Allow
\t\tDeny from All
\t</IfVersion>
\t<IfVersion >= 2.4>
\t\tRequire all denied
\t</IfVersion>
</IfModule>
<IfModule !mod_version.c>
\t<IfModule !mod_authz_core.c>
\t\tOrder Deny,Allow
\t\tDeny from All
\t</IfModule>
\t<IfModule mod_authz_core.c>
\t\tRequire all denied
\t</IfModule>
</IfModule>
HTACCESS_DENY;
        return $deny;
    }
    /**
     * @return string
     */
    public static function getAllowHtaccessContent()
    {
        $allow = <<<HTACCESS_ALLOW
<IfModule mod_version.c>
\t<IfVersion < 2.4>
\t\tOrder Allow,Deny
\t\tAllow from All
\t</IfVersion>
\t<IfVersion >= 2.4>
\t\tRequire all granted
\t</IfVersion>
</IfModule>
<IfModule !mod_version.c>
\t<IfModule !mod_authz_core.c>
\t\tOrder Allow,Deny
\t\tAllow from All
\t</IfModule>
\t<IfModule mod_authz_core.c>
\t\tRequire all granted
\t</IfModule>
</IfModule>
HTACCESS_ALLOW;
        return $allow;
    }
    /**
     * Deletes all existing .htaccess files that Matomo may have created
     */
    public static function deleteHtAccessFiles()
    {
        $files = Filesystem::globr(PIWIK_INCLUDE_PATH, ".htaccess");
        // only delete files that match the list of directories we create htaccess files in
        // (i.e. not the root /.htaccess)
        $directoriesWithAutoHtaccess = array('/js', '/libs', '/vendor', '/plugins', '/misc', '/node_modules', '/config', '/core', '/lang', '/tmp');
        foreach ($files as $file) {
            foreach ($directoriesWithAutoHtaccess as $dirToDelete) {
                // only delete the first .htaccess and not the ones in subdirectories
                $pathToDelete = $dirToDelete . '/.htaccess';
                if (strpos($file, $pathToDelete) !== \false) {
                    @unlink($file);
                }
            }
        }
    }
}
