<?php
/**
* SeekQuarry/Yioop --
* Open Source Pure PHP Search Engine, Crawler, and Indexer
*
* Copyright (C) 2009 - 2026 Chris Pollett chris@pollett.org
*
* LICENSE:
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
* END LICENSE
*
* @author Chris Pollett chris@pollett.org
* @license https://www.gnu.org/licenses/ GPL3
* @link https://www.seekquarry.com/
* @copyright 2009 - 2026
* @filesource
*/
namespace seekquarry\yioop\tests;
use seekquarry\yioop\configs as C;
use seekquarry\yioop\library as L;
use seekquarry\yioop\controllers\ResourceController;
use seekquarry\atto\WebSite;
use seekquarry\yioop\library\UnitTest;
/**
* ResourceControllerTest checks when the resource controller sends a
* file gzip compressed and that the compressed bytes are the file, and
* which folders requests that name no group may read.
*
* @author Chris Pollett
*/
class ResourceControllerTest extends UnitTest
{
/**
* setUp runs before each case; nothing is needed here.
*/
public function setUp()
{
}
/**
* tearDown runs after each case; nothing is needed here.
*/
public function tearDown()
{
}
/**
* styleSheetIsCompressedWhenAcceptedTestCase checks when a file is
* sent gzip compressed: a style sheet asked for with gzip accepted is,
* and it is not when gzip is not accepted, when a byte range is asked
* for, for a picture type, or for a file too large to buffer. It also
* checks that the compressed bytes the web site gives for the style
* sheet decode to the whole file.
*/
public function styleSheetIsCompressedWhenAcceptedTestCase()
{
$controller = (new \ReflectionClass(ResourceController::class))
->newInstanceWithoutConstructor();
$controller->web_site = new WebSite();
$path = C\BASE_DIR . "/css/search.css";
$size = filesize($path);
$_SERVER['HTTP_ACCEPT_ENCODING'] = "gzip, deflate";
unset($_SERVER['HTTP_RANGE'], $_REQUEST['o'], $_REQUEST['l']);
$this->assertTrue(
$controller->compressWhenSending("text/css", $size),
"a style sheet is compressed when gzip is accepted");
$bytes = $controller->web_site->fileGetContents($path, false, true);
$this->assertTrue(strlen($bytes) < $size / 3,
"the compressed style sheet is under a third of its size");
$this->assertEqual(md5_file($path), md5(gzdecode($bytes)),
"and decodes to the whole file");
$this->assertEqual(false,
$controller->compressWhenSending("image/png", $size),
"a picture type is not compressed");
$this->assertEqual(false, $controller->compressWhenSending(
"text/css", C\MAX_RESOURCE_SERVE_LEN + 1),
"a file too large to buffer is not compressed");
$_SERVER['HTTP_RANGE'] = "bytes=0-10";
$this->assertEqual(false,
$controller->compressWhenSending("text/css", $size),
"a byte range request is not compressed");
unset($_SERVER['HTTP_RANGE'], $_SERVER['HTTP_ACCEPT_ENCODING']);
$this->assertEqual(false,
$controller->compressWhenSending("text/css", $size),
"a request that does not accept gzip is not compressed");
}
/**
* userRequestReadsOnlyTheSafeListTestCase checks that a request with
* s and no g, which reads a user's folder with no group check, is
* refused for any file not in ResourceFolders::USER_FILE_SAFE_LIST,
* and for the safe file asked for in a sub-folder or as a thumbnail.
*/
public function userRequestReadsOnlyTheSafeListTestCase()
{
$controller = $this->bareController();
$answers = [];
foreach ([["n" => "redirect.txt"], ["n" => "secrets.txt"],
["n" => "user_icon.webp", "sf" => "x"],
["n" => "user_icon.webp", "t" => "1"]] as $fields) {
$_REQUEST = ["f" => "resources", "s" => "5"] + $fields;
[, $base_dir] = $controller->getNameAndBaseFolder();
$answers[] = $base_dir;
}
$_REQUEST = [];
$this->assertEqual($answers, [false, false, false, false],
"only the user's icon itself may be read without a group");
}
/**
* requestWithoutGroupReadsOnlySiteFilesTestCase checks that a request
* with neither g nor s reads from the site folder, or with sf naming
* a host from that host's folder, and is refused for an sf that is a
* path to any other folder under app/resources, such as a page's, or
* for a thumbnail.
*/
public function requestWithoutGroupReadsOnlySiteFilesTestCase()
{
$controller = $this->bareController();
$answers = [];
foreach ([["n" => "logo.png"],
["n" => "logo.png", "sf" => "www.example.com"],
["n" => "redirect.txt", "sf" => "F4C/F4C6duyWVQs"],
["n" => "redirect.txt", "sf" => "../users"],
["n" => "logo.png", "t" => "1"]] as $fields) {
$_REQUEST = ["f" => "resources"] + $fields;
[, $base_dir] = $controller->getNameAndBaseFolder();
$answers[] = $base_dir;
}
$_REQUEST = [];
$this->assertEqual($answers, [L\ResourceFolders::sitePath(),
L\ResourceFolders::domainPath("www.example.com"), false, false,
false], "the site's and a host's files only");
}
/**
* bareController makes a ResourceController without running its
* constructor, with a web site object to send through.
*
* @return ResourceController the controller
*/
private function bareController()
{
$controller = (new \ReflectionClass(ResourceController::class))
->newInstanceWithoutConstructor();
$controller->web_site = new WebSite();
return $controller;
}
}