vendor
package-lock.json
uguu.sq3
-.idea
.phpdoc
.vscode
composer.phar
--- /dev/null
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="WEB_MODULE" version="4">
+ <component name="NewModuleRootManager">
+ <content url="file://$MODULE_DIR$">
+ <sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
+ <sourceFolder url="file://$MODULE_DIR$/src/." isTestSource="false" packagePrefix="Pomf\Uguu\" />
+ <sourceFolder url="file://$MODULE_DIR$/src/./Benchmarks" isTestSource="true" packagePrefix="Pomf\Uguu\Benchmarks\" />
+ <sourceFolder url="file://$MODULE_DIR$/src/./Classes" isTestSource="false" packagePrefix="Pomf\Uguu\Classes\" />
+ <excludeFolder url="file://$MODULE_DIR$/src/vendor/composer" />
+ <excludeFolder url="file://$MODULE_DIR$/build" />
+ <excludeFolder url="file://$MODULE_DIR$/dist" />
+ <excludeFolder url="file://$MODULE_DIR$/node_modules" />
+ </content>
+ <orderEntry type="inheritedJdk" />
+ <orderEntry type="sourceFolder" forTests="false" />
+ </component>
+</module>
\ No newline at end of file
--- /dev/null
+<component name="ProjectCodeStyleConfiguration">
+ <state>
+ <option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
+ </state>
+</component>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="Codeception">
+ <option name="configurations">
+ <list>
+ <Configuration>
+ <option name="path" value="$PROJECT_DIR$/tests" />
+ </Configuration>
+ <Configuration>
+ <option name="path" value="$PROJECT_DIR$/src/tests" />
+ </Configuration>
+ </list>
+ </option>
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<component name="CopyrightManager">
+ <copyright>
+ <option name="notice" value="Uguu @copyright Copyright (c) 2022-2024 Go Johansson (nokonoko) <neku@pomf.se> Note that this was previously distributed under the MIT license 2015-2022. If you are a company that wants to use Uguu I urge you to contact me to solve any potential license issues rather then using pre-2022 code. A special thanks goes out to the open source community around the world for supporting and being the backbone of projects like Uguu. This project can be found at <https://github.com/nokonoko/Uguu>. 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/>." />
+ <option name="myName" value="Uguu GPL-3" />
+ </copyright>
+</component>
\ No newline at end of file
--- /dev/null
+<component name="CopyrightManager">
+ <settings default="Uguu GPL-3" />
+</component>
\ No newline at end of file
--- /dev/null
+<component name="InspectionProjectProfileManager">
+ <profile version="1.0">
+ <option name="myName" value="Project Default" />
+ <inspection_tool class="PhpCSFixerValidationInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
+ <inspection_tool class="PhpCompoundNamespaceDepthInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
+ <inspection_tool class="PhpLongTypeFormInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
+ <inspection_tool class="PhpMissingVisibilityInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
+ <inspection_tool class="PhpModifierOrderInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
+ <inspection_tool class="PhpNewClassMissingParameterListInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
+ <inspection_tool class="PhpSeparateElseIfInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
+ <inspection_tool class="PhpTraitsUseListInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
+ <inspection_tool class="PhpVarUsageInspection" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
+ </profile>
+</component>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="ProjectModuleManager">
+ <modules>
+ <module fileurl="file://$PROJECT_DIR$/.idea/Uguu.iml" filepath="$PROJECT_DIR$/.idea/Uguu.iml" />
+ </modules>
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="MessDetectorOptionsConfiguration">
+ <option name="transferred" value="true" />
+ </component>
+ <component name="PHPCSFixerOptionsConfiguration">
+ <option name="codingStandard" value="PSR12" />
+ <option name="transferred" value="true" />
+ </component>
+ <component name="PHPCodeSnifferOptionsConfiguration">
+ <option name="highlightLevel" value="WARNING" />
+ <option name="transferred" value="true" />
+ </component>
+ <component name="PhpCSFixer">
+ <phpcsfixer_settings>
+ <PhpCSFixerConfiguration standards="DoctrineAnnotation;PER;PER-CS;PER-CS1.0;PER-CS2.0;PHP54Migration;PHP56Migration;PHP70Migration;PHP71Migration;PHP73Migration;PHP74Migration;PHP80Migration;PHP81Migration;PHP82Migration;PHP83Migration;PHP84Migration;PHPUnit100Migration;PHPUnit30Migration;PHPUnit32Migration;PHPUnit35Migration;PHPUnit43Migration;PHPUnit48Migration;PHPUnit50Migration;PHPUnit52Migration;PHPUnit54Migration;PHPUnit55Migration;PHPUnit56Migration;PHPUnit57Migration;PHPUnit60Migration;PHPUnit75Migration;PHPUnit84Migration;PSR1;PSR12;PSR2;PhpCsFixer;Symfony" tool_path="/opt/homebrew/bin/php-cs-fixer" />
+ </phpcsfixer_settings>
+ </component>
+ <component name="PhpExternalFormatter">
+ <option name="externalFormatter" value="PHP_CS_FIXER" />
+ </component>
+ <component name="PhpIncludePathManager">
+ <include_path>
+ <path value="$PROJECT_DIR$/src/vendor/composer" />
+ <path value="$PROJECT_DIR$/dist/vendor/doctrine/lexer" />
+ <path value="$PROJECT_DIR$/dist/vendor/doctrine/annotations" />
+ <path value="$PROJECT_DIR$/dist/vendor/phpbench/phpbench" />
+ <path value="$PROJECT_DIR$/dist/vendor/phpbench/container" />
+ <path value="$PROJECT_DIR$/dist/vendor/phpbench/dom" />
+ <path value="$PROJECT_DIR$/dist/vendor/psr/cache" />
+ <path value="$PROJECT_DIR$/dist/vendor/webmozart/glob" />
+ <path value="$PROJECT_DIR$/dist/vendor/psr/container" />
+ <path value="$PROJECT_DIR$/dist/vendor/psr/log" />
+ <path value="$PROJECT_DIR$/dist/vendor/seld/jsonlint" />
+ <path value="$PROJECT_DIR$/dist/vendor/symfony/polyfill-ctype" />
+ <path value="$PROJECT_DIR$/dist/vendor/symfony/filesystem" />
+ <path value="$PROJECT_DIR$/dist/vendor/symfony/options-resolver" />
+ <path value="$PROJECT_DIR$/dist/vendor/symfony/polyfill-intl-normalizer" />
+ <path value="$PROJECT_DIR$/dist/vendor/composer" />
+ <path value="$PROJECT_DIR$/dist/vendor/symfony/finder" />
+ <path value="$PROJECT_DIR$/dist/vendor/symfony/string" />
+ <path value="$PROJECT_DIR$/dist/vendor/symfony/deprecation-contracts" />
+ <path value="$PROJECT_DIR$/dist/vendor/symfony/service-contracts" />
+ <path value="$PROJECT_DIR$/dist/vendor/symfony/process" />
+ <path value="$PROJECT_DIR$/dist/vendor/symfony/console" />
+ <path value="$PROJECT_DIR$/dist/vendor/symfony/polyfill-mbstring" />
+ <path value="$PROJECT_DIR$/dist/vendor/symfony/polyfill-intl-grapheme" />
+ </include_path>
+ </component>
+ <component name="PhpProjectSharedConfiguration" php_language_level="8.3">
+ <option name="suggestChangeDefaultLanguageLevel" value="false" />
+ </component>
+ <component name="PhpStanOptionsConfiguration">
+ <option name="transferred" value="true" />
+ </component>
+ <component name="PhpUnit">
+ <phpunit_settings>
+ <PhpUnitSettings custom_loader_path="$PROJECT_DIR$/src/vendor/autoload.php" phpunit_phar_path="" />
+ </phpunit_settings>
+ </component>
+ <component name="PsalmOptionsConfiguration">
+ <option name="transferred" value="true" />
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="PHPSpec">
+ <suites>
+ <PhpSpecSuiteConfiguration>
+ <option name="myPath" value="$PROJECT_DIR$" />
+ </PhpSpecSuiteConfiguration>
+ <PhpSpecSuiteConfiguration>
+ <option name="myPath" value="$PROJECT_DIR$/src" />
+ </PhpSpecSuiteConfiguration>
+ </suites>
+ </component>
+</project>
\ No newline at end of file
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+ <component name="VcsDirectoryMappings">
+ <mapping directory="" vcs="Git" />
+ </component>
+</project>
\ No newline at end of file
pageList = $(shell $(CURDIR)/$(NODEJQ) -r ".pages[]" $(CURDIR)/$(CONF))
noExt = $(shell echo $(i) | cut -d '.' -f1)
-all: builddirs npm_dependencies ejs minify copy-img copy-php
+all: builddirs npm_dependencies ejs minify copy-img copy-php copy-benchmarks
ejs:
$(foreach i,$(pageList), \
cp -v $(CURDIR)/src/static/php/*.php $(CURDIR)/build/php/
cp -v $(CURDIR)/src/Classes/*.php $(CURDIR)/build/php/Classes/
+copy-benchmarks:
+ cp -v $(CURDIR)/src/Benchmarks/*.php $(CURDIR)/build/php/Benchmarks/
+ cp -v $(CURDIR)/src/Benchmarks/file.jpg $(CURDIR)/build/php/Benchmarks/
+ cp -v $(CURDIR)/src/Benchmarks/runBenchmark.sh $(CURDIR)/build/php/Benchmarks/
+
install: installdirs
rm -rf $(DESTDIR)/*
cp -rv $(CURDIR)/build/* $(DESTDIR)/
docker rm -f uguu
builddirs:
- mkdir -p $(CURDIR)/build $(CURDIR)/build/img $(CURDIR)/build/html $(CURDIR)/build/html/min $(CURDIR)/build/html/unmin $(CURDIR)/build/js $(CURDIR)/build/css $(CURDIR)/build/php $(CURDIR)/build/php/Classes $(CURDIR)/build/public
+ mkdir -p $(CURDIR)/build $(CURDIR)/build/img $(CURDIR)/build/html $(CURDIR)/build/html/min $(CURDIR)/build/html/unmin $(CURDIR)/build/js $(CURDIR)/build/css $(CURDIR)/build/php $(CURDIR)/build/php/Classes $(CURDIR)/build/php/Benchmarks $(CURDIR)/build/php/Benchmarks/tmp $(CURDIR)/build/public
We'd really like if you can take some time to make sure your coding style is
consistent with the project. Uguu follows [PHP
-PSR-2](https://www.php-fig.org/psr/psr-12/) and [Airbnb JavaScript
-(ES5)](https://github.com/airbnb/javascript/tree/es5-deprecated/es5) (`airbnb/legacy`)
-coding style guides. We use ESLint and PHPCS tools to enforce these standards.
+PSR-12](https://www.php-fig.org/psr/psr-12/)
+coding style guides. We use PHPCS tools to enforce these standards.
You can also help by sending us feature requests or writing documentation and
tests.
--- /dev/null
+## Uguu 1.8.6
+
+### Whats new
+
+* Includes INDEX creation in the dbSchemas files, this greatly improves performance when performing filename generation, antidupe, blacklist or rate-limit checks against the database,
+ especially on big databases. It's recommended you follow the instructions below on how to add INDEX.
+* time() is called once in connector to get a timestamp instead of multiple times.
+* The function `diverseArray` is now called `transposeArray`, the variables within the function are also renamed to make it easier to understand.
+* The function `uploadFile` performs a check if `BENCHMARK_MODE` is set in the configuration, if it is the file will not be uploaded.
+* Benchmarking capbility added.
+* Docs updated with how to use [Benchmarking](https://github.com/nokonoko/Uguu/wiki/Benchmarking) and also a [Optimization Guide](https://github.com/nokonoko/Uguu/wiki/Optimization).
+
+### Breaking changes
+
+* config.json must include the `"BENCHMARK_MODE"` value, should be set to `false` when not benchmarking, otherwise file(s) will not be uploaded.
+
+### Add INDEX to an existing Uguu installation
+
+#### SQLite
+
+```
+CREATE INDEX files_hash_idx ON files (hash);
+CREATE INDEX files_name_idx ON files (filename);
+CREATE INDEX ratelimit_iphash_idx ON ratelimit (iphash);
+CREATE INDEX blacklist_hash_idx ON blacklist (hash);
+```
+
+#### PostgreSQL
+
+```
+CREATE INDEX files_hash_idx ON files (hash);
+CREATE INDEX files_name_idx ON files (filename);
+CREATE INDEX ratelimit_iphash_idx ON ratelimit (iphash);
+CREATE INDEX blacklist_hash_idx ON blacklist (hash);
+```
+
+#### MySQL
+
+```
+CREATE INDEX files_hash_idx ON files (hash);
+CREATE INDEX files_name_idx ON files (filename);
+CREATE INDEX ratelimit_iphash_idx ON ratelimit (iphash);
+CREATE INDEX blacklist_hash_idx ON blacklist (hash);
+```
\ No newline at end of file
{
"name": "uguu",
- "version": "1.8.5",
+ "version": "1.8.6",
"description": "Uguu is a simple lightweight temporary file host with support for drop, paste, click and API uploading.",
"homepage": "https://uguu.se",
"repository": {
--- /dev/null
+<?php
+/*
+ * Uguu
+ *
+ * @copyright Copyright (c) 2022-2024 Go Johansson (nokonoko) <neku@pomf.se>
+ *
+ * Note that this was previously distributed under the MIT license 2015-2022.
+ *
+ * If you are a company that wants to use Uguu I urge you to contact me to
+ * solve any potential license issues rather then using pre-2022 code.
+ *
+ * A special thanks goes out to the open source community around the world
+ * for supporting and being the backbone of projects like Uguu.
+ *
+ * This project can be found at <https://github.com/nokonoko/Uguu>.
+ *
+ * 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/>.
+ */
+
+$finder = (new PhpCsFixer\Finder())
+ ->in(__DIR__)
+ ->exclude([
+ 'dist',
+ 'build',
+ 'node_modules'
+ ])
+;
+
+return (new PhpCsFixer\Config())
+ ->setRules([
+ '@PSR12' => true,
+ 'strict_param' => true,
+ ])
+ ->setFinder($finder)
+;
--- /dev/null
+<?php
+/*
+ * Uguu
+ *
+ * @copyright Copyright (c) 2022-2024 Go Johansson (nokonoko) <neku@pomf.se>
+ *
+ * Note that this was previously distributed under the MIT license 2015-2022.
+ *
+ * If you are a company that wants to use Uguu I urge you to contact me to
+ * solve any potential license issues rather then using pre-2022 code.
+ *
+ * A special thanks goes out to the open source community around the world
+ * for supporting and being the backbone of projects like Uguu.
+ *
+ * This project can be found at <https://github.com/nokonoko/Uguu>.
+ *
+ * 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/>.
+ */
+
+namespace Pomf\Uguu\Benchmarks;
+
+use Pomf\Uguu\Classes\Upload;
+use Pomf\Uguu\Classes\Response;
+
+class UguuBench
+{
+ public function handleFiles(string $outputFormat, array $files): void
+ {
+ $upload = new Upload($outputFormat);
+ $files = $upload->reFiles($files);
+ $fCount = count($files);
+ $upload->fingerPrint($fCount);
+ $res = [];
+ $i = 0;
+ while ($i < $fCount) {
+ $res[] = $upload->uploadFile();
+ $i++;
+ }
+ if (!empty($res)) {
+ $upload->send($res);
+ }
+ }
+
+ public function handleUp()
+ {
+ //$tmp = tempnam(__DIR__ . '/tmp/', 'benchmarkUguu');
+ //file_put_contents($tmp, file_get_contents(__DIR__ . '/file.jpg'));
+ // Mock the $_SERVER array
+ $_SERVER = [
+ 'HTTP_USER_AGENT' => 'curl/8.4.0',
+ 'REMOTE_ADDR' => '1.2.3.4'
+ ];
+ // Mock the $_FILES array
+ $_FILES = [
+ 'files' => [
+ 'name' => [
+ 0 => 'file.jpg',
+ ],
+ 'full_path' => [
+ 0 => 'file.jpg',
+ ],
+ 'type' => [
+ 0 => 'image/jpeg',
+ ],
+ 'tmp_name' => [
+ 0 => __DIR__ . '/file.jpg',
+ ],
+ 'error' => [
+ 0 => 0,
+ ],
+ 'size' => [
+ 0 => 12345,
+ ],
+ ],
+ ];
+ $_GET['output'] = 'benchmark';
+ $resType = (isset($_GET['output']) and !empty($_GET['output'])) ? strtolower(preg_replace('/[^a-zA-Z]/', '', $_GET['output'])) : 'json';
+ $response = new Response($resType);
+ if (!isset($_FILES['files']) or empty($_FILES['files'])) {
+ $response->error(400, 'No input file(s)');
+ }
+ $this->handleFiles($resType, $_FILES['files']);
+ }
+
+ /**
+ * @Revs(500)
+ * @Iterations(15)
+ */
+ public function benchTest()
+ {
+ $this->handleUp();
+ }
+}
--- /dev/null
+#!/bin/bash
+cd ..
+sqlite3 /Users/neku/repos/Uguu/dist/Benchmarks/tmp/uguu.sq3 -init /Users/neku/repos/Uguu/src/static/dbSchemas/sqlite_schema.sql ""
+./vendor/bin/phpbench run Benchmarks --report=default
+rm -rf Benchmarks/tmp/*.jpg
+rm -rf Benchmarks/tmp/uguu.sq3
\ No newline at end of file
class Connector extends Database
{
public PDO $DB;
+ public Redis $keyDB;
public string $dbType;
- public array $CONFIG;
+ public mixed $CONFIG;
public Response $response;
public Randomizer $randomizer;
-
- public function errorHandler(int $errno, string $errstr):void
+ public int $currentTime;
+
+ public function errorHandler(int $errno, string $errstr): void
{
if ($this->CONFIG['DEBUG']) {
$this->response->error(500, 'Server error: ' . $errstr);
$this->response->error(500, 'Server error.');
}
}
-
- public function fatalErrorHandler():void
+
+ public function fatalErrorHandler(): void
{
if (!is_null($e = error_get_last())) {
if ($this->CONFIG['DEBUG']) {
}
}
}
-
+
/**
* Reads the config.json file and populates the CONFIG property with the settings
* Also assembles the PDO DB connection and registers error handlers.
$this->CONFIG['DB_USER'],
$this->CONFIG['DB_PASS'],
);
+
$this->randomizer = new Randomizer();
+ $this->currentTime = time();
}
}
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-
+
namespace Pomf\Uguu\Classes;
class CuteGrills
{
public array $GRILLS;
-
/**
* Loads the list of grills, then redirects to a random grill
*/
- public function showGrills():void
+ public function showGrills(): void
{
$this->loadGrills();
if (!headers_sent()) {
);
}
}
-
+
/**
* Loads the images from the `img/grills/` directory into the `GRILLS` array
*/
- public function loadGrills():void
+ public function loadGrills(): void
{
$this->GRILLS = array_slice(scandir('img/grills/'), 2);
}
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-
+
namespace Pomf\Uguu\Classes;
use PDO;
-
+
class Database
{
- public function dbCheckNameExists(string $name):bool
+ public function dbCheckNameExists(string $name): bool
{
$query = match ($this->dbType) {
'pgsql' => 'SELECT EXISTS(SELECT id FROM files WHERE filename = (:name)), filename FROM files WHERE filename = (:name) LIMIT 1',
}
return false;
}
-
- public function checkFileBlacklist(string $hash):void
+
+ public function checkFileBlacklist(string $hash): void
{
$query = match ($this->dbType) {
'pgsql' => 'SELECT EXISTS(SELECT id FROM blacklist WHERE hash = (:hash)), hash FROM blacklist WHERE hash = (:hash) LIMIT 1',
$this->response->error(415, 'File blacklisted.');
}
}
-
- public function antiDupe(string $hash):array
+
+ public function antiDupe(string $hash): array
{
$query = match ($this->dbType) {
'pgsql' => 'SELECT EXISTS(SELECT id FROM files WHERE hash = (:hash)), filename FROM files WHERE hash = (:hash) LIMIT 1',
];
}
}
-
- public function newIntoDB(array $FILE_INFO, array $fingerPrintInfo):void
+
+ public function newIntoDB(array $FILE_INFO, array $fingerPrintInfo): void
{
$q = $this->DB->prepare(
'INSERT INTO files (hash, originalname, filename, size, date, ip)' .
$q->execute();
$q->closeCursor();
}
-
- public function createRateLimit(array $fingerPrintInfo):void
+
+ public function createRateLimit(array $fingerPrintInfo): void
{
$q = $this->DB->prepare(
'INSERT INTO ratelimit (iphash, files, time)' .
$q->execute();
$q->closeCursor();
}
-
- public function updateRateLimit(int $fCount, bool $iStamp, array $fingerPrintInfo):void
+
+ public function updateRateLimit(int $fCount, bool $iStamp, array $fingerPrintInfo): void
{
if ($iStamp) {
$q = $this->DB->prepare(
$q->execute();
$q->closeCursor();
}
-
- public function compareTime(int $timestamp, int $seconds_d):bool
+
+ public function compareTime(int $timestamp, int $seconds_d): bool
{
- $diff = time() - $timestamp;
+ $diff = $this->currentTime - $timestamp;
if ($diff > $seconds_d) {
return true;
}
return false;
}
-
- public function checkRateLimit(array $fingerPrintInfo, int $rateTimeout, int $fileLimit):bool
+
+ public function checkRateLimit(array $fingerPrintInfo, int $rateTimeout, int $fileLimit): bool
{
$query = match ($this->dbType) {
'pgsql' => 'SELECT EXISTS(SELECT id FROM ratelimit WHERE iphash = (:iphash)), id, iphash, files, time FROM ratelimit WHERE iphash = (:iphash) LIMIT 1',
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-
+
namespace Pomf\Uguu\Classes;
class GrillLoader extends CuteGrills
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-
+
namespace Pomf\Uguu\Classes;
class Response
{
public string $type;
-
+
/**
* Takes a string as an argument and sets the header to the appropriate content type
*
header('Content-Type: application/json; charset=UTF-8');
$this->type = $response_type;
break;
+ case 'benchmark':
+ $this->type = $response_type;
+ break;
case 'gyazo':
header('Content-Type: text/plain; charset=UTF-8');
$this->type = 'text';
break;
}
}
-
+
/**
* Returns a string based on the type of response requested
*
* @param $code mixed The HTTP status code to return.
* @param $desc string The description of the error.
*/
- public function error(int $code, string $desc):string
+ public function error(int $code, string $desc): string
{
$response = match ($this->type) {
'csv' => $this->csvError($desc),
'html' => $this->htmlError($code, $desc),
'json' => $this->jsonError($code, $desc),
+ 'benchmark' => $this->jsonError($code, $desc),
'text' => $this->textError($code, $desc),
};
http_response_code($code);
echo $response;
exit(1);
}
-
+
/* Returning a string that contains the error message. */
- private static function csvError(string $description):string
+ private static function csvError(string $description): string
{
return '"error"' . "\r\n" . "\"$description\"" . "\r\n";
}
-
+
/**
* Returns a string containing an HTML paragraph element with the error code and description
*
*
* @return string A string.
*/
- private static function htmlError(int|string $code, string $description):string
+ private static function htmlError(int|string $code, string $description): string
{
return '<p>ERROR: (' . $code . ') ' . $description . '</p>';
}
-
+
/**
* Returns a JSON string with the error code and description
*
*
* @return bool|string A JSON string
*/
- private static function jsonError(int|string $code, string $description):bool|string
+ private static function jsonError(int|string $code, string $description): bool|string
{
return json_encode([
'success' => false,
'description' => $description,
], JSON_PRETTY_PRINT);
}
-
+
/**
* Returns a string that contains the error code and description
*
*
* @return string A string with the error code and description.
*/
- private static function textError(int|string $code, string $description):string
+ private static function textError(int|string $code, string $description): string
{
return 'ERROR: (' . $code . ') ' . $description;
}
-
+
/**
* "If the type is csv, then call the csvSuccess function,
* if the type is html, then call the htmlSuccess function, etc."
*
* @param $files array An array of file objects.
*/
- public function send(array $files):void
+ public function send(array $files): void
{
$response = match ($this->type) {
'csv' => $this->csvSuccess($files),
'html' => $this->htmlSuccess($files),
'json' => $this->jsonSuccess($files),
+ 'benchmark' => true,
'text' => $this->textSuccess($files),
};
http_response_code(200); // "200 OK". Success.
- echo $response;
+ if($this->type != 'benchmark') {
+ echo $response;
+ }
}
-
+
/**
* Takes an array of files and returns a CSV string
*
*
* @return string A string of the files in the array.
*/
- private static function csvSuccess(array $files):string
+ private static function csvSuccess(array $files): string
{
$result = '"name","url","hash","size"' . "\r\n";
foreach ($files as $file) {
}
return $result;
}
-
+
/**
* Takes an array of files and returns a string of HTML links
*
*
* @return string the result of the foreach loop.
*/
- private static function htmlSuccess(array $files):string
+ private static function htmlSuccess(array $files): string
{
$result = '';
foreach ($files as $file) {
}
return $result;
}
-
+
/**
* Returns a JSON string that contains a success message and the files that were uploaded
*
*
* @return bool|string A JSON string
*/
- private static function jsonSuccess(array $files):bool|string
+ private static function jsonSuccess(array $files): bool|string
{
return json_encode([
'success' => true,
'files' => $files,
], JSON_PRETTY_PRINT);
}
-
+
/**
* Takes an array of files and returns a string of URLs
*
*
* @return string the url of the file.
*/
- private static function textSuccess(array $files):string
+ private static function textSuccess(array $files): string
{
$result = '';
foreach ($files as $file) {
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+/**
+ * Takes the amount of files that are being uploaded, and creates a fingerprint of the user's IP address,
+ * user agent, and the amount of files being uploaded.
+ *
+ * @param int $files_amount The amount of files that are being uploaded.
+ */
+
namespace Pomf\Uguu\Classes;
class Upload extends Response
public array $FILE_INFO;
public array $fingerPrintInfo;
private mixed $Connector;
-
+
/**
* Resolves and processes an array of files, performing various checks and operations on each file.
*
* * - 'dupe' : Indicates if the uploaded file is a duplicate.
* * - 'filename' : The final filename of the uploaded file.
*/
- public function reFiles(array $files):array
+ public function reFiles(array $files): array
{
$this->Connector = new Connector();
$result = [];
- $files = $this->diverseArray($files);
+ $files = $this->transposeArray($files);
foreach ($files as $file) {
$this->FILE_INFO = [
'TEMP_NAME' => $file['tmp_name'],
}
return $result;
}
-
+
/**
- * Rearranges a multidimensional array by exchanging the keys of the first and second level.
+ * Transposes a 2-dimensional array.
+ *
+ * Transposes the given 2-dimensional array, where the rows of the input array become the columns
+ * of the transposed array.
*
- * @param array $files The multidimensional array to be rearranged.
+ * @param array $inputArray The input 2-dimensional array to transpose.
*
- * @return array The rearranged array with exchanged keys of the first and second level.
+ * @return array The transposed array, with exchanged keys of the first and second level.
*/
- public function diverseArray(array $files):array
+ public function transposeArray(array $inputArray): array
{
- $result = [];
- foreach ($files as $key1 => $value1) {
- foreach ($value1 as $key2 => $value2) {
- $result[$key2][$key1] = $value2;
+ $transposedArray = [];
+ foreach ($inputArray as $key1 => $nestedArray) {
+ foreach ($nestedArray as $key2 => $nestedValue) {
+ $transposedArray[$key2][$key1] = $nestedValue;
}
}
- return $result;
+ return $transposedArray;
}
-
+
/**
* Performs various checks (if enabled), insert info into database, moves file to storage
* location, then returns an array of file information.
* - size : The size of the uploaded file
* - dupe : Boolean indicating whether the file is a duplicate
*/
- public function uploadFile():array
+ public function uploadFile(): array
{
if ($this->Connector->CONFIG['RATE_LIMIT']) {
if (
if (!is_dir($this->Connector->CONFIG['FILES_ROOT'])) {
$this->Connector->response->error(500, 'File storage path not accessible.');
}
- if (
- !move_uploaded_file(
- $this->FILE_INFO['TEMP_NAME'],
- $this->Connector->CONFIG['FILES_ROOT'] .
- $this->FILE_INFO['FILENAME'],
- )
- ) {
- $this->Connector->response->error(500, 'Failed to move file to destination.');
+ if (!is_writable($this->Connector->CONFIG['FILES_ROOT'])) {
+ $this->Connector->response->error(500, 'File storage path not writeable.');
}
- if (!chmod($this->Connector->CONFIG['FILES_ROOT'] . $this->FILE_INFO['FILENAME'], 0644)) {
- $this->Connector->response->error(500, 'Failed to change file permissions.');
+ if(!$this->Connector->CONFIG['BENCHMARK_MODE']) {
+ if (
+ !move_uploaded_file(
+ $this->FILE_INFO['TEMP_NAME'],
+ $this->Connector->CONFIG['FILES_ROOT'] .
+ $this->FILE_INFO['FILENAME'],
+ )
+ ) {
+ $this->Connector->response->error(500, 'Failed to move file to destination.');
+ }
+ if (!chmod($this->Connector->CONFIG['FILES_ROOT'] . $this->FILE_INFO['FILENAME'], 0644)) {
+ $this->Connector->response->error(500, 'Failed to change file permissions.');
+ }
}
$this->Connector->newIntoDB($this->FILE_INFO, $this->fingerPrintInfo);
}
'dupe' => $this->FILE_INFO['DUPE'],
];
}
-
+
/**
* Takes the amount of files that are being uploaded, and creates a fingerprint of the user's IP address,
* user agent, and the amount of files being uploaded.
* @param $files_amount int The amount of files that are being uploaded.
*
*/
- public function fingerPrint(int $files_amount):void
+ public function fingerPrint(int $files_amount): void
{
if (!empty($_SERVER['HTTP_USER_AGENT'])) {
$USER_AGENT = filter_var($_SERVER['HTTP_USER_AGENT'], FILTER_SANITIZE_ENCODED);
$ip = $_SERVER['REMOTE_ADDR'];
}
$this->fingerPrintInfo = [
- 'timestamp' => time(),
+ 'timestamp' => $this->Connector->currentTime,
'useragent' => $USER_AGENT,
'ip' => $ip,
'ip_hash' => hash('xxh3', $_SERVER['REMOTE_ADDR'] . $USER_AGENT),
$this->Connector->response->error(500, 'Invalid user agent.');
}
}
-
+
/**
* Returns the MIME type of a file
*
*
* @return string The MIME type of the file.
*/
- public function fileMIME(array $file):string
+ public function fileMIME(array $file): string
{
$FILE_INFO = finfo_open(FILEINFO_MIME_TYPE);
return finfo_file($FILE_INFO, $file['tmp_name']);
}
-
+
/**
* Determines the double dot file extension from the given file.
*
*
* @return string The extracted extension.
*/
- public function doubleDotExtension(array $extension):string
+ public function doubleDotExtension(array $extension): string
{
$doubleDotArray = array_slice($extension, -2, 2);
$doubleDot = strtolower(preg_replace('/[^a-zA-Z.]/', '', join('.', $doubleDotArray)));
}
return end($extension);
}
-
+
/**
* Determines the file extension from the given file.
*
*
* @return string|bool The file extension if it exists, or false if the file name does not contain a dot.
*/
- public function fileExtension(array $file):string|bool
+ public function fileExtension(array $file): string|bool
{
if (str_contains($file['name'], '.')) {
$extension = explode('.', $file['name']);
}
return false;
}
-
+
/**
* Checks if the MIME type of the uploaded file is in the blacklist.
*
* is not allowed.
*
*/
- public function checkMimeBlacklist():void
+ public function checkMimeBlacklist(): void
{
if (in_array($this->FILE_INFO['MIME'], $this->Connector->CONFIG['FILTER_MIME'])) {
$this->Connector->response->error(415, 'Filetype not allowed');
}
}
-
+
/**
* Checks if the MIME type of the uploaded file is in the whitelist.
*
* is not allowed.
*
*/
- public function checkMimeWhitelist():void
+ public function checkMimeWhitelist(): void
{
if (!in_array($this->FILE_INFO['MIME'], $this->Connector->CONFIG['FILTER_MIME'])) {
$this->Connector->response->error(415, 'Filetype not allowed');
}
}
-
+
/**
* Checks if the extension of the uploaded file is in the blacklist.
*
* is not allowed.
*
*/
- public function checkExtensionBlacklist():void
+ public function checkExtensionBlacklist(): void
{
if (in_array($this->FILE_INFO['EXTENSION'], $this->Connector->CONFIG['FILTER_EXTENSIONS'])) {
$this->Connector->response->error(415, 'Filetype not allowed');
}
}
-
+
/**
* Checks if the extension of the uploaded file is in the whitelist.
*
* is not allowed.
*
*/
- public function checkExtensionWhitelist():void
+ public function checkExtensionWhitelist(): void
{
if (!in_array($this->FILE_INFO['EXTENSION'], $this->Connector->CONFIG['FILTER_EXTENSIONS'])) {
$this->Connector->response->error(415, 'Filetype not allowed');
}
}
-
+
/**
* Checks if the length of the given filename exceeds 250 characters.
*
*
* @return string The filename, either unchanged or truncated if its length exceeds 250 characters.
*/
- public function checkNameLength(string $fileName):string
+ public function checkNameLength(string $fileName): string
{
if (strlen($fileName) > 250) {
return substr($fileName, 0, 250);
}
return $fileName;
}
-
+
/**
* Generates a unique name for a file.
*
*
* @return string The generated unique name for the file.
*/
- public function generateName(string $extension):string
+ public function generateName(string $extension): string
{
do {
if ($this->Connector->CONFIG['FILES_RETRIES'] === 0) {
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-
+
namespace Pomf\Uguu\Classes;
use PDO;
-
+
class expireChecker
{
public PDO $DB;
* @var \Pomf\Uguu\Classes\Response
*/
private Response $response;
-
- public function checkDB():bool|array
+ public function checkDB(): bool|array
{
if (is_int($this->CONFIG['expireTime'])) {
$this->timeUnit = strtoupper($this->CONFIG['expireTimeUnit']);
return false;
}
}
-
- public function cleanRateLimitDB():void
+
+ public function cleanRateLimitDB(): void
{
$query = match ($this->dbType) {
'pgsql' => 'DELETE FROM ratelimit WHERE time < EXTRACT(epoch from NOW() - INTERVAL \'24 HOURS\')',
$q->execute();
$q->closeCursor();
}
-
- public function deleteFiles(array $filenames):void
+
+ public function deleteFiles(array $filenames): void
{
foreach ($filenames as $filename) {
unlink($this->CONFIG['FILES_ROOT'] . $filename);
}
}
-
- public function deleteFromDB(array $ids):void
+
+ public function deleteFromDB(array $ids): void
{
foreach ($ids as $id) {
$query = match ($this->dbType) {
$q->closeCursor();
}
}
-
+
/**
* Reads the config.json file and populates the CONFIG property with the settings
* Also assembles the PDO DB connection and registers error handlers.
"Pomf\\Uguu\\Classes\\": "./Classes"
}
},
+ "autoload-dev": {
+ "psr-4": {
+ "Pomf\\Uguu\\Benchmarks\\": "./Benchmarks"
+ }
+ },
"authors": [
{
"name": "Go Johansson (neku)",
"ext-fileinfo": "*",
"ext-pdo": "*"
},
+ "require-dev": {
+ "phpbench/phpbench": "^1.0"
+ },
"config": {
"optimize-autoloader": true,
"classmap-authoritative": true
{
"dest": "dist",
- "pkgVersion": "1.8.5",
+ "pkgVersion": "1.8.6",
"pages": [
"index.ejs",
"faq.ejs",
"api.ejs"
],
"DEBUG": false,
+ "BENCHMARK_MODE": true,
"max_upload_size": 128,
"expireTime": 8,
"expireTimeUnit": "hours",
"kofiUrl": "",
"malwareBanner": false,
"DB_MODE": "sqlite",
- "DB_PATH": "/var/www/db/uguu.sq3",
+ "DB_PATH": "/var/www/uguu-dev/uguu.sq3",
"DB_USER": "NULL",
"DB_PASS": "NULL",
"LOG_IP": false,
"ANTI_DUPE": false,
"BLACKLIST_DB": false,
"FILTER_MODE": true,
- "RATE_LIMIT": true,
+ "RATE_LIMIT": false,
"RATE_LIMIT_TIMEOUT": 60,
"RATE_LIMIT_FILES": 100,
- "FILES_ROOT": "/var/www/files/",
+ "FILES_ROOT": "/var/www/dev-files/",
"FILES_RETRIES": 15,
"NAME_LENGTH": 8,
"ID_CHARSET": "abcdefghijklmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ",
--- /dev/null
+{
+ "$schema": "./vendor/phpbench/phpbench/phpbench.schema.json",
+ "runner.bootstrap": "vendor/autoload.php"
+}
\ No newline at end of file
`files` int(15) default NULL,
`time` int(15) DEFAULT NULL,
PRIMARY KEY (`id`)
-);
\ No newline at end of file
+);
+
+CREATE INDEX files_hash_idx ON files (hash);
+CREATE INDEX files_name_idx ON files (filename);
+CREATE INDEX ratelimit_iphash_idx ON ratelimit (iphash);
+CREATE INDEX blacklist_hash_idx ON blacklist (hash);
\ No newline at end of file
email text NOT NULL,
pass text not null,
level integer not null
-);
\ No newline at end of file
+);
+CREATE INDEX files_hash_idx ON files (hash);
+CREATE INDEX files_name_idx ON files (filename);
+CREATE INDEX ratelimit_iphash_idx ON ratelimit (iphash);
+CREATE INDEX blacklist_hash_idx ON blacklist (hash);
\ No newline at end of file
, `time` integer default NULL
, `files` integer default NULL
);
+CREATE INDEX files_hash_idx ON files (hash);
+CREATE INDEX files_name_idx ON files (filename);
+CREATE INDEX ratelimit_iphash_idx ON ratelimit (iphash);
+CREATE INDEX blacklist_hash_idx ON blacklist (hash);
END TRANSACTION;
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
-
+
require_once __DIR__ . '/../vendor/autoload.php';
-
+
use Pomf\Uguu\Classes\GrillLoader;
-
+
new GrillLoader();
use Pomf\Uguu\Classes\Upload;
use Pomf\Uguu\Classes\Response;
-function handleFiles(string $outputFormat, array $files):void
+function handleFiles(string $outputFormat, array $files): void
{
$upload = new Upload($outputFormat);
$files = $upload->reFiles($files);