]> jfr.im git - uguu.git/commitdiff
1.7.0 v.1.7.0
authorGo Johansson (neku) <redacted>
Sat, 18 Feb 2023 14:22:37 +0000 (15:22 +0100)
committerGo Johansson (neku) <redacted>
Sat, 18 Feb 2023 14:22:37 +0000 (15:22 +0100)
16 files changed:
README.md
docker/Dockerfile
docker/nginx/nginx.conf
docker/nginx/uguu.conf
docker/php/php.ini
package.json
src/Classes/Connector.php
src/Classes/CuteGrills.php
src/Classes/Database.php
src/Classes/GrillLoader.php
src/Classes/Response.php
src/Classes/Upload.php
src/config.json
src/static/dbSchemas/postgres_schema.sql [new file with mode: 0644]
src/static/php/grill.php
src/static/php/upload.php

index c6d7fcba0ed73ce7e4c4487e77014abc4f65c30a..e3144f0cfa2c69334d5d8bc21ee9493e8a323abf 100644 (file)
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ See the real world site at [uguu.se](https://uguu.se).
 
 ## Requirements
 
-Tested and working with Nginx + PHP-8.1 + SQLite/MySQL.
+Tested and working with Nginx + PHP-8.1 + SQLite/MySQL/PostgreSQL.
 
 Node is used to compile Uguu, after that it runs on PHP.
 
@@ -57,7 +57,6 @@ tests.
 
 ## Upcoming Features
 
-* PostgreSQL Support
 * S3 Bucket Support
 * Azure File Storage Support
 * Temporal/RR Support
index 76786321677db4a887c804c140a22db348d55f98..c0358975d97e4d7f6e10319f774eefa9b4774083 100644 (file)
@@ -12,7 +12,7 @@ RUN chmod a+x nodejssetup.sh
 RUN ./nodejssetup.sh
 RUN apt-get install -y nodejs gcc g++ make
 RUN apt-get install -y build-essential nginx-full php8.1-fpm php8.1 sqlite3 php8.1-sqlite3 \
-                       php8.1-curl php8.1-cli php8.1-lz4 \
+                       php8.1-curl php8.1-cli php8.1-lz4 php8.1-pgsql \
                        php8.1-mcrypt php8.1-mysql php8.1-xdebug php8.1-zip \
                        php8.1-common php8.1-readline php8.1-bcmath php8.1-common php8.1-xml
 
index 22ca8510ab21a80fecfec13f0974e6673e4c4608..56543da3c258870e485c26e5df8c1f49e59514da 100644 (file)
@@ -4,7 +4,7 @@ pid /run/nginx.pid;
 include /etc/nginx/modules-enabled/*.conf;
 
 events {
-        worker_connections 1024;
+        worker_connections 4096;
         multi_accept on;
 }
 
index 45b3d35508ef4c9cc6812a65cbd4c79dabacaca1..e5fc914b2305be42e03c6bb127b5c7982e5f89ea 100644 (file)
@@ -3,8 +3,8 @@ server {
     server_name       XMAINDOMAINX;
 
     ssl on;
-    ssl_certificate   /root/.acme.sh/XMAINDOMAINX/fullchain.cer;
-    ssl_certificate_key /root/.acme.sh/XMAINDOMAINX/XMAINDOMAINX.key;
+    ssl_certificate   /root/.acme.sh/XMAINDOMAINX_ecc/fullchain.cer;
+    ssl_certificate_key /root/.acme.sh/XMAINDOMAINX_ecc/XMAINDOMAINX.key;
     ssl_protocols     TLSv1.2 TLSv1.3;
     ssl_ciphers       'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
     ssl_ecdh_curve    secp384r1;
@@ -39,8 +39,8 @@ server {
     server_name      XFILESDOMAINX;
 
     ssl              on;
-    ssl_certificate   /root/.acme.sh/XMAINDOMAINX/fullchain.cer;
-    ssl_certificate_key /root/.acme.sh/XMAINDOMAINX/XMAINDOMAINX.key;
+    ssl_certificate   /root/.acme.sh/XMAINDOMAINX_ecc/fullchain.cer;
+    ssl_certificate_key /root/.acme.sh/XMAINDOMAINX_ecc/XMAINDOMAINX.key;
     ssl_protocols    TLSv1.2 TLSv1.3;
     ssl_ciphers      'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
     ssl_ecdh_curve   secp384r1;
index 1488a117dd14b5606e41fd233863d16b9d8121a5..892b816f5b85110512bdb8037139e1ae50e3a838 100644 (file)
@@ -427,7 +427,7 @@ max_input_time = 60
 
 ; Maximum amount of memory a script may consume
 ; http://php.net/memory-limit
-memory_limit = 128M
+memory_limit = 256M
 
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ; Error handling and logging ;
@@ -700,7 +700,7 @@ auto_globals_jit = On
 ; Its value may be 0 to disable the limit. It is ignored if POST data reading
 ; is disabled through enable_post_data_reading.
 ; http://php.net/post-max-size
-post_max_size = 128M
+post_max_size = 256M
 
 ; Automatically add files before PHP document.
 ; http://php.net/auto-prepend-file
@@ -852,10 +852,10 @@ file_uploads = On
 
 ; Maximum allowed size for uploaded files.
 ; http://php.net/upload-max-filesize
-upload_max_filesize = 128M
+upload_max_filesize = 256M
 
 ; Maximum number of files that can be uploaded via a single request
-max_file_uploads = 20
+max_file_uploads = 100
 
 ;;;;;;;;;;;;;;;;;;
 ; Fopen wrappers ;
index 25459dbd387535959f2b8eddf5a57ac2f31d04f1..1d71bff0ce4c1353529fdc4acf44d49fc10aa991 100644 (file)
@@ -1,6 +1,6 @@
 {
   "name": "uguu",
-  "version": "1.6.7",
+  "version": "1.7.0",
   "description": "Uguu is a simple lightweight temporary file host with support for drop, paste, click and API uploading.",
   "homepage": "https://uguu.se",
   "repository": {
index 4c82c1743ee855557580886126cd444eefd343fe..71fe0cdc0d1981a795b07f3bdd273a661921a1ec 100644 (file)
@@ -2,7 +2,7 @@
     /**
      * Uguu
      *
-     * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
+     * @copyright Copyright (c) 2022-2023 Go Johansson (nokonoko) <neku@pomf.se>
      *
      * 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
     
     namespace Pomf\Uguu\Classes;
     
-    use Exception;
     use PDO;
     
     class Connector extends Database
     {
         public PDO $DB;
+        public string $dbType;
         public array $CONFIG;
+        public Response $response;
         
-        /**
-         * Reads the config.json file and populates the CONFIG property with the settings
-         *
-         * @throws \Exception
-         */
-        public function __construct()
+        public function errorHandler(int $errno, string $errstr):void
         {
-            if (!file_exists(__DIR__ . '/../config.json')) {
-                throw new Exception('Cant read settings file.', 500);
-            }
-            try {
-                $this->CONFIG = json_decode(
-                   file_get_contents(__DIR__ . '/../config.json'),
-                   true,
-                );
-                $this->assemble();
+            if ($this->CONFIG['DEBUG']) {
+                $this->response->error(500, 'Server error: ' . $errstr);
+            } else {
+                $this->response->error(500, 'Server error.');
             }
-            catch (Exception $e) {
-                throw new Exception($e->getMessage(), 500);
+        }
+        
+        public function fatalErrorHandler():void
+        {
+            if (!is_null($e = error_get_last())) {
+                if ($this->CONFIG['DEBUG']) {
+                    $this->response->error(500, 'FATAL Server error: ' . print_r($e, true));
+                } else {
+                    $this->response->error(500, 'Server error.');
+                }
             }
         }
         
         /**
-         * > Tries to connect to the database
+         * Reads the config.json file and populates the CONFIG property with the settings
+         * Also assembles the PDO DB connection and registers error handlers.
          *
-         * @throws \Exception
          */
-        public function assemble()
+        public function __construct()
         {
-            try {
-                $this->DB = new PDO(
-                   $this->CONFIG['DB_MODE'] . ':' . $this->CONFIG['DB_PATH'],
-                   $this->CONFIG['DB_USER'],
-                   $this->CONFIG['DB_PASS']
-                );
-            }
-            catch (Exception) {
-                throw new Exception('Cant connect to DB.', 500);
+            $this->response = new Response('json');
+            if (!file_exists(__DIR__ . '/../config.json')) {
+                $this->response->error(500, 'Cant read settings file.');
             }
+            $this->CONFIG = json_decode(
+               file_get_contents(__DIR__ . '/../config.json'),
+               true,
+            );
+            ini_set('display_errors', 0);
+            set_error_handler([$this, "errorHandler"]);
+            register_shutdown_function([$this, "fatalErrorHandler"]);
+            $this->dbType = $this->CONFIG['DB_MODE'];
+            $this->DB = new PDO(
+               $this->CONFIG['DB_MODE'] . ':' . $this->CONFIG['DB_PATH'],
+               $this->CONFIG['DB_USER'],
+               $this->CONFIG['DB_PASS']
+            );
         }
-    }
+    }
\ No newline at end of file
index af10590f9323e76260dc05803fc82331ee44bc66..75ec018449d078163cba4163d9088753c88a2382 100644 (file)
@@ -2,7 +2,7 @@
     /**
      * Uguu
      *
-     * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
+     * @copyright Copyright (c) 2022-2023 Go Johansson (nokonoko) <neku@pomf.se>
      *
      * 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
index 1b8880ff0a2fe66848347d65e8ca8e833491ab61..da295ded4146216f987654253354e52fc3537735 100644 (file)
@@ -1,9 +1,8 @@
 <?php
-
     /**
      * Uguu
      *
-     * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
+     * @copyright Copyright (c) 2022-2023 Go Johansson (nokonoko) <neku@pomf.se>
      *
      * 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
      * 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 DateTimeZone;
-    use Exception;
+    
     use PDO;
-    use DateTime;
-
-class Database
-{
-    private PDO $DB;
-
-    /**
-     * Sets the value of the DB variable.
-     *
-     * @param $DB PDO The database connection.
-     */
-    public function setDB(PDO $DB): void
-    {
-        $this->DB = $DB;
-    }
-
-    /**
-     * Checks if a file name exists in the database
-     *
-     * @param $name string The name of the file.
-     *
-     * @return bool The number of rows that match the query.
-     * @throws \Exception
-     */
-    public function dbCheckNameExists(string $name): bool
+    
+    class Database
     {
-        try {
-            $q = $this->DB->prepare('SELECT * FROM files WHERE EXISTS
-                                            (SELECT filename FROM files WHERE filename = (:name)) LIMIT 1');
+        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',
+                default => 'SELECT filename FROM files WHERE filename = (:name) AND EXISTS (SELECT id FROM files WHERE filename = (:name)) LIMIT 1'
+            };
+            $q = $this->DB->prepare($query);
             $q->bindValue(':name', $name);
             $q->execute();
             $result = $q->fetch();
-            if ($result) {
+            $q->closeCursor();
+            if (isset($result['exists']) and $result['exists']) {
+                return true;
+            } elseif ($result) {
                 return true;
             }
             return false;
-        } catch (Exception) {
-            throw new Exception('Cant check if name exists in DB.', 500);
         }
-    }
-
-    /**
-     * Checks if the file is blacklisted
-     *
-     * @param $FILE_INFO array An array containing the following:
-     *
-     * @throws \Exception
-     */
-    public function checkFileBlacklist(array $FILE_INFO): void
-    {
-        try {
-            $q = $this->DB->prepare('SELECT * FROM blacklist WHERE EXISTS
-                                            (SELECT hash FROM blacklist WHERE hash = (:hash)) LIMIT 1');
-            $q->bindValue(':hash', $FILE_INFO['SHA1']);
+        
+        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',
+                default => 'SELECT id FROM blacklist WHERE EXISTS(SELECT id FROM blacklist WHERE hash = (:hash)) LIMIT 1'
+            };
+            $q = $this->DB->prepare($query);
+            $q->bindValue(':hash', $hash);
             $q->execute();
             $result = $q->fetch();
-            if ($result) {
-                throw new Exception('File blacklisted!', 415);
+            $q->closeCursor();
+            if (isset($result['exists']) and $result['exists']) {
+                $this->response->error(415, 'File blacklisted.');
+            } elseif ($result) {
+                $this->response->error(415, 'File blacklisted.');
             }
-        } catch (Exception) {
-            throw new Exception('Cant check blacklist DB.', 500);
         }
-    }
-
-    /**
-     * Checks if the file already exists in the database
-     *
-     * @param $hash string The hash of the file you want to check for.
-     *
-     * @throws \Exception
-     */
-    public function antiDupe(string $hash): array
-    {
-        try {
-            $q = $this->DB->prepare(
-                'SELECT * FROM files WHERE EXISTS
-                        (SELECT filename FROM files WHERE hash = (:hash)) LIMIT 1',
-            );
+        
+        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',
+                default => 'SELECT filename FROM files WHERE hash = (:hash) AND EXISTS (SELECT id FROM files WHERE hash = (:hash)) LIMIT 1'
+            };
+            $q = $this->DB->prepare($query);
             $q->bindValue(':hash', $hash);
             $q->execute();
             $result = $q->fetch();
-            if ($result) {
+            $q->closeCursor();
+            if (!$result) {
                 return [
-                'result' => true,
-                'name' => $result['filename'],
+                   'result' => false,
                 ];
             } else {
                 return [
-                   'result' => false
+                   'result' => true,
+                   'name'   => $result['filename'],
                 ];
             }
-        } catch (Exception) {
-            throw new Exception('Cant check for dupes in DB.', 500);
         }
-    }
-
-    /**
-     * Inserts a new file into the database
-     *
-     * @param $FILE_INFO       array
-     * @param $fingerPrintInfo array
-     *
-     * @throws \Exception
-     */
-    public function newIntoDB(array $FILE_INFO, array $fingerPrintInfo): void
-    {
-        try {
+        
+        public function newIntoDB(array $FILE_INFO, array $fingerPrintInfo):void
+        {
             $q = $this->DB->prepare(
-                'INSERT INTO files (hash, originalname, filename, size, date, ip)' .
-                'VALUES (:hash, :orig, :name, :size, :date, :ip)',
+               'INSERT INTO files (hash, originalname, filename, size, date, ip)' .
+               'VALUES (:hash, :orig, :name, :size, :date, :ip)',
             );
             $q->bindValue(':hash', $FILE_INFO['SHA1']);
             $q->bindValue(':orig', $FILE_INFO['NAME']);
-            $q->bindValue(':name', $FILE_INFO['NEW_NAME']);
+            $q->bindValue(':name', $FILE_INFO['FILENAME']);
             $q->bindValue(':size', $FILE_INFO['SIZE'], PDO::PARAM_INT);
             $q->bindValue(':date', $fingerPrintInfo['timestamp']);
             $q->bindValue(':ip', $fingerPrintInfo['ip']);
             $q->execute();
-        } catch (Exception) {
-            throw new Exception('Cant insert into DB.', 500);
+            $q->closeCursor();
         }
-    }
-
-    /**
-     * Creates a new row in the database with the information provided
-     *
-     * @param $fingerPrintInfo array
-     *
-     * @throws \Exception
-     */
-    public function createRateLimit(array $fingerPrintInfo): void
-    {
-        try {
+        
+        public function createRateLimit(array $fingerPrintInfo):void
+        {
             $q = $this->DB->prepare(
-                'INSERT INTO ratelimit (iphash, files, time)' .
-                'VALUES (:iphash, :files, :time)',
+               'INSERT INTO ratelimit (iphash, files, time)' .
+               'VALUES (:iphash, :files, :time)',
             );
             $q->bindValue(':iphash', $fingerPrintInfo['ip_hash']);
             $q->bindValue(':files', $fingerPrintInfo['files_amount']);
             $q->bindValue(':time', $fingerPrintInfo['timestamp']);
             $q->execute();
-        } catch (Exception $e) {
-            throw new Exception(500, $e->getMessage());
+            $q->closeCursor();
         }
-    }
-
-    /**
-     * Update the rate limit table with the new file count and timestamp
-     *
-     * @param $fCount          int The number of files uploaded by the user.
-     * @param $iStamp          bool A boolean value that determines whether or not to update the timestamp.
-     * @param $fingerPrintInfo array An array containing the following keys:
-     *
-     * @throws \Exception
-     */
-    public function updateRateLimit(int $fCount, bool $iStamp, array $fingerPrintInfo): void
-    {
-        try {
+        
+        public function updateRateLimit(int $fCount, bool $iStamp, array $fingerPrintInfo):void
+        {
             if ($iStamp) {
                 $q = $this->DB->prepare(
-                    'UPDATE ratelimit SET files = (:files), time = (:time) WHERE iphash = (:iphash)',
+                   'UPDATE ratelimit SET files = (:files), time = (:time) WHERE iphash = (:iphash)',
                 );
                 $q->bindValue(':time', $fingerPrintInfo['timestamp']);
             } else {
                 $q = $this->DB->prepare(
-                    'UPDATE ratelimit SET files = (:files) WHERE iphash = (:iphash)',
+                   'UPDATE ratelimit SET files = (:files) WHERE iphash = (:iphash)',
                 );
             }
             $q->bindValue(':files', $fCount);
             $q->bindValue(':iphash', $fingerPrintInfo['ip_hash']);
             $q->execute();
-        } catch (Exception $e) {
-            throw new Exception(500, $e->getMessage());
-        }
-    }
-
-    /**
-     * @throws \Exception
-     */
-    public function compareTime(int $timestamp, int $seconds_d): bool
-    {
-        $dateTime_end = new DateTime('now', new DateTimeZone('Europe/Stockholm'));
-        $dateTime_start = new DateTime();
-        $dateTime_start->setTimestamp($timestamp);
-        $diff = strtotime($dateTime_end->format('Y-m-d H:i:s')) - strtotime($dateTime_start->format('Y-m-d H:i:s'));
-        if ($diff > $seconds_d) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Checks if the user has uploaded more than 100 files in the last minute, if so it returns true,
-     * if not it updates the database with the new file
-     * count and timestamp
-     *
-     * @param $fingerPrintInfo array An array containing the following:
-     *
-     * @return bool A boolean value.
-     * @throws \Exception
-     */
-    public function checkRateLimit(array $fingerPrintInfo, int $rateTimeout, int $fileLimit): bool
-    {
-        $q = $this->DB->prepare(
-            'SELECT files, time, iphash, COUNT(*) AS count FROM ratelimit WHERE iphash = (:iphash)',
-        );
-        $q->bindValue(':iphash', $fingerPrintInfo['ip_hash']);
-        $q->execute();
-        $result = $q->fetch();
-
-        //If there is no other match a record does not exist, create one.
-        if (!$result['count'] > 0) {
-            $this->createRateLimit($fingerPrintInfo);
-            return false;
+            $q->closeCursor();
         }
-
-        // Apply rate-limit when file count reached and timeout not reached.
-        if ($result['files'] === $fileLimit and !$this->compareTime($result['time'], $rateTimeout)) {
-            return true;
-        }
-
-        // Update timestamp if timeout reached.
-        if ($this->compareTime($result['time'], $rateTimeout)) {
-            $this->updateRateLimit($fingerPrintInfo['files_amount'], true, $fingerPrintInfo);
+        
+        public function compareTime(int $timestamp, int $seconds_d):bool
+        {
+            $diff = time() - $timestamp;
+            if ($diff > $seconds_d) {
+                return true;
+            }
             return false;
         }
-
-        // Add filecount, timeout not reached.
-        if ($result['files'] < $fileLimit and !$this->compareTime($result['time'], $rateTimeout)) {
-            $this->updateRateLimit($result['files'] + $fingerPrintInfo['files_amount'], false, $fingerPrintInfo);
+        
+        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',
+                default => 'SELECT * FROM ratelimit WHERE iphash = (:iphash) AND EXISTS (SELECT id FROM ratelimit WHERE iphash = (:iphash)) LIMIT 1'
+            };
+            $q = $this->DB->prepare($query);
+            $q->bindValue(':iphash', $fingerPrintInfo['ip_hash']);
+            $q->execute();
+            $result = $q->fetch();
+            $q->closeCursor();
+            //If there is no other match a record does not exist, create one.
+            if (!$result) {
+                $this->createRateLimit($fingerPrintInfo);
+                return false;
+            }
+            // Apply rate-limit when file count reached and timeout not reached.
+            if ($result['files'] === $fileLimit and !$this->compareTime($result['time'], $rateTimeout)) {
+                return true;
+            }
+            // Update timestamp if timeout reached, reset file count and add the incoming file count.
+            if ($this->compareTime($result['time'], $rateTimeout)) {
+                $this->updateRateLimit($fingerPrintInfo['files_amount'], true, $fingerPrintInfo);
+                return false;
+            }
+            // Add filecount, timeout not reached.
+            if ($result['files'] < $fileLimit and !$this->compareTime($result['time'], $rateTimeout)) {
+                $this->updateRateLimit($result['files'] + $fingerPrintInfo['files_amount'], false, $fingerPrintInfo);
+                return false;
+            }
             return false;
         }
-
-        return false;
-    }
-}
+    }
\ No newline at end of file
index 7fbbfa6000745c7c33938891bc3bc9a892294052..18d6f888e5ba4b1c430e36b1d5ee0d065de3af13 100644 (file)
@@ -2,7 +2,7 @@
     /**
      * Uguu
      *
-     * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
+     * @copyright Copyright (c) 2022-2023 Go Johansson (nokonoko) <neku@pomf.se>
      *
      * 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
index 6d7a9f7a31707ffb9a47f46dffaf5813cfa4d425..18c743d04a52d1009be3f62fe2ae359086337de2 100644 (file)
@@ -1,9 +1,8 @@
 <?php
-
     /**
      * Uguu
      *
-     * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
+     * @copyright Copyright (c) 2022-2023 Go Johansson (nokonoko) <neku@pomf.se>
      *
      * 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
      * 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
-     *
-     * @param $response_type string The type of response you want to return.
-     *                       Valid options are: csv, html, json, text.
-     */
-    public function __construct(string $response_type)
+    
+    class Response
     {
-        switch ($response_type) {
-            case 'csv':
-                header('Content-Type: text/csv; charset=UTF-8');
-                $this->type = $response_type;
-                break;
-            case 'html':
-                header('Content-Type: text/html; charset=UTF-8');
-                $this->type = $response_type;
-                break;
-            case 'json':
-                header('Content-Type: application/json; charset=UTF-8');
-                $this->type = $response_type;
-                break;
-            case 'gyazo':
-                header('Content-Type: text/plain; charset=UTF-8');
-                $this->type = 'text';
-                break;
-            case 'text':
-                header('Content-Type: text/plain; charset=UTF-8');
-                $this->type = $response_type;
-                break;
-            default:
-                header('Content-Type: application/json; charset=UTF-8');
-                $this->type = 'json';
-                break;
+        public string $type;
+        
+        /**
+         * Takes a string as an argument and sets the header to the appropriate content type
+         *
+         * @param $response_type string The type of response you want to return.
+         *                       Valid options are: csv, html, json, text.
+         */
+        public function __construct(string $response_type)
+        {
+            switch ($response_type) {
+                case 'csv':
+                    header('Content-Type: text/csv; charset=UTF-8');
+                    $this->type = $response_type;
+                    break;
+                case 'html':
+                    header('Content-Type: text/html; charset=UTF-8');
+                    $this->type = $response_type;
+                    break;
+                case 'json':
+                    header('Content-Type: application/json; charset=UTF-8');
+                    $this->type = $response_type;
+                    break;
+                case 'gyazo':
+                    header('Content-Type: text/plain; charset=UTF-8');
+                    $this->type = 'text';
+                    break;
+                case 'text':
+                    header('Content-Type: text/plain; charset=UTF-8');
+                    $this->type = $response_type;
+                    break;
+                default:
+                    header('Content-Type: application/json; charset=UTF-8');
+                    $this->type = 'json';
+                    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): void
-    {
-        $response = match ($this->type) {
-            'csv' => $this->csvError($desc),
-            'html' => $this->htmlError($code, $desc),
-            'json' => $this->jsonError($code, $desc),
-            'text' => $this->textError($code, $desc),
-        };
+        
+        /**
+         * 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
+        {
+            $response = match ($this->type) {
+                'csv' => $this->csvError($desc),
+                'html' => $this->htmlError($code, $desc),
+                'json' => $this->jsonError($code, $desc),
+                'text' => $this->textError($code, $desc),
+            };
             http_response_code($code);
             echo $response;
-    }
-
-    /* Returning a string that contains the error message. */
-    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
-     *
-     * @param $code        int|string The error code.
-     * @param $description string The description of the error.
-     *
-     * @return string A 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
-     *
-     * @param $code        int|string The error code.
-     * @param $description string The description of the error.
-     *
-     * @return bool|string A JSON string
-     */
-    private static function jsonError(int|string $code, string $description): bool|string
-    {
-        return json_encode([
-           'success'     => false,
-           'errorcode'   => $code,
-           'description' => $description,
-        ], JSON_PRETTY_PRINT);
-    }
-
-    /**
-     * Returns a string that contains the error code and description
-     *
-     * @param $code        int|string The error code.
-     * @param $description string The description of the error.
-     *
-     * @return string A string with the error code and description.
-     */
-    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."
-     *
-     * The `match` keyword is a new feature in PHP 8. It's a lot like a switch statement, but it's more powerful
-     *
-     * @param $files array An array of file objects.
-     */
-    public function send(array $files): void
-    {
-        $response = match ($this->type) {
-            'csv' => $this->csvSuccess($files),
-            'html' => $this->htmlSuccess($files),
-            'json' => $this->jsonSuccess($files),
-            'text' => $this->textSuccess($files),
-        };
+            exit(1);
+        }
+        
+        /* Returning a string that contains the error message. */
+        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
+         *
+         * @param $code        int|string The error code.
+         * @param $description string The description of the error.
+         *
+         * @return string A 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
+         *
+         * @param $code        int|string The error code.
+         * @param $description string The description of the error.
+         *
+         * @return bool|string A JSON string
+         */
+        private static function jsonError(int|string $code, string $description):bool|string
+        {
+            return json_encode([
+               'success'     => false,
+               'errorcode'   => $code,
+               'description' => $description,
+            ], JSON_PRETTY_PRINT);
+        }
+        
+        /**
+         * Returns a string that contains the error code and description
+         *
+         * @param $code        int|string The error code.
+         * @param $description string The description of the error.
+         *
+         * @return string A string with the error code and description.
+         */
+        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."
+         *
+         * The `match` keyword is a new feature in PHP 8. It's a lot like a switch statement, but it's more powerful
+         *
+         * @param $files array An array of file objects.
+         */
+        public function send(array $files):void
+        {
+            $response = match ($this->type) {
+                'csv' => $this->csvSuccess($files),
+                'html' => $this->htmlSuccess($files),
+                'json' => $this->jsonSuccess($files),
+                'text' => $this->textSuccess($files),
+            };
             http_response_code(200); // "200 OK". Success.
             echo $response;
-    }
-
-    /**
-     * Takes an array of files and returns a CSV string
-     *
-     * @param $files array An array of files that have been uploaded.
-     *
-     * @return string A string of the files in the array.
-     */
-    private static function csvSuccess(array $files): string
-    {
-        $result = '"name","url","hash","size"' . "\r\n";
-        foreach ($files as $file) {
-            $result .= '"' . $file['name'] . '"' . ',' .
-               '"' . $file['url'] . '"' . ',' .
-               '"' . $file['hash'] . '"' . ',' .
-               '"' . $file['size'] . '"' . "\r\n";
         }
-        return $result;
-    }
-
-    /**
-     * Takes an array of files and returns a string of HTML links
-     *
-     * @param $files array An array of files to be uploaded.
-     *
-     * @return string the result of the foreach loop.
-     */
-    private static function htmlSuccess(array $files): string
-    {
-        $result = '';
-        foreach ($files as $file) {
-            $result .= '<a href="' . $file['url'] . '">' . $file['url'] . '</a><br>';
+        
+        /**
+         * Takes an array of files and returns a CSV string
+         *
+         * @param $files array An array of files that have been uploaded.
+         *
+         * @return string A string of the files in the array.
+         */
+        private static function csvSuccess(array $files):string
+        {
+            $result = '"name","url","hash","size"' . "\r\n";
+            foreach ($files as $file) {
+                $result .= '"' . $file['name'] . '"' . ',' .
+                   '"' . $file['url'] . '"' . ',' .
+                   '"' . $file['hash'] . '"' . ',' .
+                   '"' . $file['size'] . '"' . "\r\n";
+            }
+            return $result;
         }
-        return $result;
-    }
-
-    /**
-     * Returns a JSON string that contains a success message and the files that were uploaded
-     *
-     * @param $files array The files to be uploaded.
-     *
-     * @return bool|string A JSON 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
-     *
-     * @param $files array The files to be uploaded.
-     *
-     * @return string the url of the file.
-     */
-    private static function textSuccess(array $files): string
-    {
-        $result = '';
-        foreach ($files as $file) {
-            $result .= $file['url'] . "\n";
+        
+        /**
+         * Takes an array of files and returns a string of HTML links
+         *
+         * @param $files array An array of files to be uploaded.
+         *
+         * @return string the result of the foreach loop.
+         */
+        private static function htmlSuccess(array $files):string
+        {
+            $result = '';
+            foreach ($files as $file) {
+                $result .= '<a href="' . $file['url'] . '">' . $file['url'] . '</a><br>';
+            }
+            return $result;
+        }
+        
+        /**
+         * Returns a JSON string that contains a success message and the files that were uploaded
+         *
+         * @param $files array The files to be uploaded.
+         *
+         * @return bool|string A JSON 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
+         *
+         * @param $files array The files to be uploaded.
+         *
+         * @return string the url of the file.
+         */
+        private static function textSuccess(array $files):string
+        {
+            $result = '';
+            foreach ($files as $file) {
+                $result .= $file['url'] . "\n";
+            }
+            return $result;
         }
-        return $result;
-    }
-}
+    }
\ No newline at end of file
index e72f0df9d1a8f41d32d4189c27e1af4b8a832330..e7becc081b3af7afed0cd0fc3036df0315461239 100644 (file)
@@ -1,9 +1,8 @@
 <?php
-
     /**
      * Uguu
      *
-     * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
+     * @copyright Copyright (c) 2022-2023 Go Johansson (nokonoko) <neku@pomf.se>
      *
      * 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
      * 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 Exception;
-
-class Upload extends Response
-{
-    public array $FILE_INFO;
-    public array $fingerPrintInfo;
-    private mixed $Connector;
-
-    /**
-     * Takes an array of files, and returns an array of arrays containing the file's temporary name,
-     * name, size, SHA1 hash, extension, and MIME type
-     *
-     * @param $files array The files array from the $_FILES superglobal.
-     *
-     * @return array An array of arrays.
-     * @throws \Exception
-     */
-    public function reFiles(array $files): array
+    
+    class Upload extends Response
     {
-        $this->Connector = new Connector();
-        $this->Connector->setDB($this->Connector->DB);
-        $result = [];
-        $files = $this->diverseArray($files);
-        foreach ($files as $file) {
-            $hash = sha1_file($file['tmp_name']);
-            $this->FILE_INFO = [
-               'TEMP_NAME' => $file['tmp_name'],
-               'NAME'      => strip_tags($this->checkNameLength($file['name'])),
-               'SIZE'      => $file['size'],
-               'SHA1'      => $hash,
-               'EXTENSION' => $this->fileExtension($file),
-               'MIME'      => $this->fileMIME($file),
-            ];
-
-            if ($this->Connector->CONFIG['ANTI_DUPE']) {
-                $dupeResult = $this->Connector->antiDupe($hash);
-                if ($dupeResult['result']) {
-                    $this->FILE_INFO['NEW_NAME'] = $dupeResult['name'];
+        public array $FILE_INFO;
+        public array $fingerPrintInfo;
+        private mixed $Connector;
+        
+        /**
+         * Takes an array of files, and returns an array of arrays containing the file's temporary name,
+         * name, size, SHA1 hash, extension, and MIME type
+         *
+         * @param $files array The files array from the $_FILES superglobal.
+         *
+         * @return array An array of arrays.
+         */
+        public function reFiles(array $files):array
+        {
+            $this->Connector = new Connector();
+            $result = [];
+            $files = $this->diverseArray($files);
+            foreach ($files as $file) {
+                $this->FILE_INFO = [
+                   'TEMP_NAME' => $file['tmp_name'],
+                   'NAME'      => strip_tags($this->checkNameLength($file['name'])),
+                   'SIZE'      => $file['size'],
+                   'SHA1'      => sha1_file($file['tmp_name']),
+                   'EXTENSION' => $this->fileExtension($file),
+                   'MIME'      => $this->fileMIME($file),
+                   'DUPE'      => false,
+                   'FILENAME'  => null,
+                ];
+                // Check if anti dupe is enabled
+                if ($this->Connector->CONFIG['ANTI_DUPE']) {
+                    // Check if hash exists in DB, if it does return the name of the file
+                    $dupeResult = $this->Connector->antiDupe($this->FILE_INFO['SHA1']);
+                    if ($dupeResult['result']) {
+                        $this->FILE_INFO['FILENAME'] = $dupeResult['name'];
+                        $this->FILE_INFO['DUPE'] = true;
+                    }
                 }
+                // If its not a dupe then generate a new name
+                if (!$this->FILE_INFO['DUPE']) {
+                    $this->FILE_INFO['FILENAME'] = $this->generateName($this->FILE_INFO['EXTENSION']);
+                }
+                $result[] = [
+                   $this->FILE_INFO['TEMP_NAME'],
+                   $this->FILE_INFO['NAME'],
+                   $this->FILE_INFO['SIZE'],
+                   $this->FILE_INFO['SHA1'],
+                   $this->FILE_INFO['EXTENSION'],
+                   $this->FILE_INFO['MIME'],
+                   $this->FILE_INFO['DUPE'],
+                   $this->FILE_INFO['FILENAME'],
+                ];
             }
-
-            if (!isset($this->FILE_INFO['NEW_NAME'])) {
-                $this->FILE_INFO['NEW_NAME'] = $this->generateName($this->FILE_INFO['EXTENSION']);
-            }
-
-            $result[] = [
-               $this->FILE_INFO['TEMP_NAME'],
-               $this->FILE_INFO['NAME'],
-               $this->FILE_INFO['SIZE'],
-               $this->FILE_INFO['SHA1'],
-               $this->FILE_INFO['EXTENSION'],
-               $this->FILE_INFO['MIME'],
-            ];
+            return $result;
         }
-        return $result;
-    }
-
-    /**
-     * Takes an array of arrays and returns an array of arrays with the keys and values swapped
-     *
-     * @param $files array an array of arrays
-     *
-     * @return array ```
-     * array:2 [â–¼
-     *   0 => array:2 [â–¼
-     *     'TEMP_NAME' => 'example'
-     *     'NAME' => 'example'
-     *     'SIZE' => 'example'
-     *     'SHA1' => 'example'
-     *     'EXTENSION' => 'example'
-     *     'MIME' => 'example'
-     *
-     *   ]
-     *   1 => array:2 [â–¼
-     *     'TEMP_NAME' => 'example'
-     *     'NAME' => 'example'
-     *     'SIZE' => 'example'
-     *     'SHA1' => 'example'
-     *     'EXTENSION' => 'example'
-     *     'MIME' => 'example'
-     *   ]
-     * ]
-     * ```
-     */
-    public function diverseArray(array $files): array
-    {
-        $result = [];
-        foreach ($files as $key1 => $value1) {
-            foreach ($value1 as $key2 => $value2) {
-                $result[$key2][$key1] = $value2;
+        
+        /**
+         * Takes an array of arrays and returns an array of arrays with the keys and values swapped
+         *
+         * @param $files array an array of arrays
+         *
+         * @return array ```
+         * array:2 [â–¼
+         *   0 => array:2 [â–¼
+         *     'TEMP_NAME' => 'example'
+         *     'NAME' => 'example'
+         *     'SIZE' => 'example'
+         *     'SHA1' => 'example'
+         *     'EXTENSION' => 'example'
+         *     'MIME' => 'example'
+         *
+         *   ]
+         *   1 => array:2 [â–¼
+         *     'TEMP_NAME' => 'example'
+         *     'NAME' => 'example'
+         *     'SIZE' => 'example'
+         *     'SHA1' => 'example'
+         *     'EXTENSION' => 'example'
+         *     'MIME' => 'example'
+         *   ]
+         * ]
+         * ```
+         */
+        public function diverseArray(array $files):array
+        {
+            $result = [];
+            foreach ($files as $key1 => $value1) {
+                foreach ($value1 as $key2 => $value2) {
+                    $result[$key2][$key1] = $value2;
+                }
             }
+            return $result;
         }
-        return $result;
-    }
-
-    /**
-     * Takes a file, checks if it's blacklisted, moves it to the file storage, and then logs it to the database
-     *
-     * @return array An array containing the hash, name, url, and size of the file.
-     * @throws \Exception
-     */
-    public function uploadFile(): array
-    {
-        switch (true) {
-            case $this->Connector->CONFIG['RATE_LIMIT']:
-                if (
-                    $this->Connector->checkRateLimit(
-                        $this->fingerPrintInfo,
-                        (int) $this->Connector->CONFIG['RATE_LIMIT_TIMEOUT'],
-                        (int) $this->Connector->CONFIG['RATE_LIMIT_FILES']
-                    )
-                ) {
-                    throw new Exception('Rate limit, please wait ' . $this->Connector->CONFIG['RATE_LIMIT_TIMEOUT'] .
-                       ' seconds before uploading again.', 500);
-                }
+        
+        /**
+         * Takes a file, checks if it's blacklisted, moves it to the file storage, and then logs it to the database
+         *
+         * @return array An array containing the hash, name, url, and size of the file.
+         */
+        public function uploadFile():array
+        {
+            switch (true) {
+                case $this->Connector->CONFIG['RATE_LIMIT']:
+                    if (
+                       $this->Connector->checkRateLimit(
+                          $this->fingerPrintInfo,
+                          $this->Connector->CONFIG['RATE_LIMIT_TIMEOUT'],
+                          $this->Connector->CONFIG['RATE_LIMIT_FILES'],
+                       )
+                    ) {
+                        $this->Connector->response->error(
+                           500,
+                           'Rate limit, please wait ' . $this->Connector->CONFIG['RATE_LIMIT_TIMEOUT'] .
+                           ' seconds before uploading again.',
+                        );
+                    }
                 // Continue
-            case $this->Connector->CONFIG['BLACKLIST_DB']:
-                $this->Connector->checkFileBlacklist($this->FILE_INFO);
+                case $this->Connector->CONFIG['BLACKLIST_DB']:
+                    $this->Connector->checkFileBlacklist($this->FILE_INFO['SHA1']);
                 // Continue
-            case $this->Connector->CONFIG['FILTER_MODE'] && empty($this->FILE_INFO['EXTENSION']):
-                $this->checkMimeBlacklist();
+                case $this->Connector->CONFIG['FILTER_MODE'] && empty($this->FILE_INFO['EXTENSION']):
+                    $this->checkMimeBlacklist();
                 // Continue
-            case $this->Connector->CONFIG['FILTER_MODE'] && !empty($this->FILE_INFO['EXTENSION']):
-                $this->checkMimeBlacklist();
-                $this->checkExtensionBlacklist();
+                case $this->Connector->CONFIG['FILTER_MODE'] && !empty($this->FILE_INFO['EXTENSION']):
+                    $this->checkMimeBlacklist();
+                    $this->checkExtensionBlacklist();
                 // Continue
-        }
-        if (!is_dir($this->Connector->CONFIG['FILES_ROOT'])) {
-            throw new Exception('File storage path not accessible.', 500);
-        }
-        if (
-            !move_uploaded_file(
-                $this->FILE_INFO['TEMP_NAME'],
-                $this->Connector->CONFIG['FILES_ROOT'] .
-                $this->FILE_INFO['NEW_NAME'],
-            )
-        ) {
-            throw new Exception('Failed to move file to destination', 500);
-        }
-        if (!chmod($this->Connector->CONFIG['FILES_ROOT'] . $this->FILE_INFO['NEW_NAME'], 0644)) {
-            throw new Exception('Failed to change file permissions', 500);
-        }
-
-
-
-        $this->Connector->newIntoDB($this->FILE_INFO, $this->fingerPrintInfo);
-        return [
-           'hash' => $this->FILE_INFO['SHA1'],
-           'name' => $this->FILE_INFO['NAME'],
-           'url'  => 'https://' . $this->Connector->CONFIG['FILE_DOMAIN'] . '/' . $this->FILE_INFO['NEW_NAME'],
-           'size' => $this->FILE_INFO['SIZE'],
-        ];
-    }
-
-    /**
-     * 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.
-     *
-     * @throws \Exception
-     */
-    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 = null;
-            if ($this->Connector->CONFIG['LOG_IP']) {
-                $ip = $_SERVER['REMOTE_ADDR'];
             }
-            $this->fingerPrintInfo = [
-               'timestamp'    => time(),
-               'useragent'    => $USER_AGENT,
-               'ip'           => $ip,
-               'ip_hash'      => hash('sha1', $_SERVER['REMOTE_ADDR'] . $USER_AGENT),
-               'files_amount' => $files_amount,
+            // If its not a dupe then skip checking if file can be written and
+            // skip inserting it into the DB.
+            if (!$this->FILE_INFO['DUPE']) {
+                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 (!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);
+            }
+            return [
+               'hash'     => $this->FILE_INFO['SHA1'],
+               'name'     => $this->FILE_INFO['NAME'],
+               'filename' => $this->FILE_INFO['FILENAME'],
+               'url'      => 'https://' . $this->Connector->CONFIG['FILE_DOMAIN'] . '/' . $this->FILE_INFO['FILENAME'],
+               'size'     => $this->FILE_INFO['SIZE'],
+               'dupe'     => $this->FILE_INFO['DUPE'],
             ];
-        } else {
-            throw new Exception('Invalid user agent.', 500);
         }
-    }
-
-    /**
-     * Returns the MIME type of a file
-     *
-     * @param $file array The file to be checked.
-     *
-     * @return string The MIME type of the file.
-     */
-    public function fileMIME(array $file): string
-    {
-        $FILE_INFO = finfo_open(FILEINFO_MIME_TYPE);
-        return finfo_file($FILE_INFO, $file['tmp_name']);
-    }
-
-    /**
-     * It takes an array of strings, and returns the last two strings joined by a dot,
-     * unless the last two strings are in the array of strings in the
-     * `DOUBLE_DOTS_EXTENSIONS` config variable, in which case it returns the last string
-     *
-     * @param $extension array The extension of the file.
-     *
-     * @return string The last two elements of the array are joined together and returned.
-     */
-    public function doubleDotExtension(array $extension): string
-    {
-        $doubleDotArray = array_slice($extension, -2, 2);
-        $doubleDot = strtolower(preg_replace('/[^a-zA-Z.]/', '', join('.', $doubleDotArray)));
-        if (in_array($doubleDot, $this->Connector->CONFIG['DOUBLE_DOTS_EXTENSIONS'])) {
-            return $doubleDot;
-        } else {
-            return end($extension);
+        
+        /**
+         * 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
+        {
+            if (!empty($_SERVER['HTTP_USER_AGENT'])) {
+                $USER_AGENT = filter_var($_SERVER['HTTP_USER_AGENT'], FILTER_SANITIZE_ENCODED);
+                $ip = null;
+                if ($this->Connector->CONFIG['LOG_IP']) {
+                    $ip = $_SERVER['REMOTE_ADDR'];
+                }
+                $this->fingerPrintInfo = [
+                   'timestamp'    => time(),
+                   'useragent'    => $USER_AGENT,
+                   'ip'           => $ip,
+                   'ip_hash'      => hash('sha1', $_SERVER['REMOTE_ADDR'] . $USER_AGENT),
+                   'files_amount' => $files_amount,
+                ];
+            } else {
+                $this->Connector->response->error(500, 'Invalid user agent.');
+            }
         }
-    }
-
-    /**
-     * Takes a file and returns the file extension
-     *
-     * @param $file array The file you want to get the extension from.
-     *
-     * @return string The file extension of the file.
-     */
-    public function fileExtension(array $file): string
-    {
-        $extension = explode('.', $file['name']);
-        $dotCount = substr_count($file['name'], '.');
-        return match ($dotCount) {
-            0 => null,
-            1 => end($extension),
-            2 => $this->doubleDotExtension($extension),
-            default => end($extension)
-        };
-    }
-
-    /**
-     * > Check if the file's MIME type is in the blacklist
-     *
-     * @throws \Exception
-     */
-    public function checkMimeBlacklist(): void
-    {
-        if (in_array($this->FILE_INFO['MIME'], $this->Connector->CONFIG['BLOCKED_MIME'])) {
-            throw new Exception('Filetype not allowed.', 415);
+        
+        /**
+         * Returns the MIME type of a file
+         *
+         * @param $file array The file to be checked.
+         *
+         * @return string The MIME type of the file.
+         */
+        public function fileMIME(array $file):string
+        {
+            $FILE_INFO = finfo_open(FILEINFO_MIME_TYPE);
+            return finfo_file($FILE_INFO, $file['tmp_name']);
         }
-    }
-
-    /**
-     * > Check if the file extension is in the blacklist
-     *
-     * @throws \Exception
-     */
-    public function checkExtensionBlacklist(): void
-    {
-        if (in_array($this->FILE_INFO['EXTENSION'], $this->Connector->CONFIG['BLOCKED_EXTENSIONS'])) {
-            throw new Exception('Filetype not allowed.', 415);
+        
+        /**
+         * It takes an array of strings, and returns the last two strings joined by a dot,
+         * unless the last two strings are in the array of strings in the
+         * `DOUBLE_DOTS_EXTENSIONS` config variable, in which case it returns the last string
+         *
+         * @param $extension array The extension of the file.
+         *
+         * @return string The last two elements of the array are joined together and returned.
+         */
+        public function doubleDotExtension(array $extension):string
+        {
+            $doubleDotArray = array_slice($extension, -2, 2);
+            $doubleDot = strtolower(preg_replace('/[^a-zA-Z.]/', '', join('.', $doubleDotArray)));
+            if (in_array($doubleDot, $this->Connector->CONFIG['DOUBLE_DOTS_EXTENSIONS'])) {
+                return $doubleDot;
+            } else {
+                return end($extension);
+            }
         }
-    }
-    
-    public function checkNameLength(string $fileName): string {
-        if (strlen($fileName) > 250) {
-            return substr($fileName, 0, 250);
-        } else {
-            return $fileName;
+        
+        /**
+         * Takes a file and returns the file extension
+         *
+         * @param $file array The file you want to get the extension from.
+         *
+         * @return string The file extension of the file.
+         */
+        public function fileExtension(array $file):string
+        {
+            $extension = explode('.', $file['name']);
+            $dotCount = substr_count($file['name'], '.');
+            return match ($dotCount) {
+                0 => null,
+                1 => end($extension),
+                2 => $this->doubleDotExtension($extension),
+                default => end($extension)
+            };
         }
-    }
-
-    /**
-     * Generates a random string of characters, checks if it exists in the database,
-     * and if it does, it generates another one
-     *
-     * @param $extension string The file extension.
-     *
-     * @return string A string
-     * @throws \Exception
-     */
-    public function generateName(string $extension): string
-    {
-        do {
-            if ($this->Connector->CONFIG['FILES_RETRIES'] === 0) {
-                throw new Exception('Gave up trying to find an unused name!', 500);
+        
+        /**
+         * > Check if the file's MIME type is in the blacklist
+         *
+         */
+        public function checkMimeBlacklist():void
+        {
+            if (in_array($this->FILE_INFO['MIME'], $this->Connector->CONFIG['BLOCKED_MIME'])) {
+                $this->Connector->response->error(415, 'Filetype not allowed');
             }
-            $NEW_NAME = '';
-            $count = strlen($this->Connector->CONFIG['ID_CHARSET']);
-            while ($this->Connector->CONFIG['NAME_LENGTH']--) {
-                $NEW_NAME .= $this->Connector->CONFIG['ID_CHARSET'][mt_rand(0, $count - 1)];
+        }
+        
+        /**
+         * > Check if the file extension is in the blacklist
+         *
+         */
+        public function checkExtensionBlacklist():void
+        {
+            if (in_array($this->FILE_INFO['EXTENSION'], $this->Connector->CONFIG['BLOCKED_EXTENSIONS'])) {
+                $this->Connector->response->error(415, 'Filetype not allowed');
             }
-            if (!empty($extension)) {
-                $NEW_NAME .= '.' . $extension;
+        }
+        
+        public function checkNameLength(string $fileName):string
+        {
+            if (strlen($fileName) > 250) {
+                return substr($fileName, 0, 250);
+            } else {
+                return $fileName;
             }
-        } while ($this->Connector->dbCheckNameExists($NEW_NAME));
+        }
+        
+        /**
+         * Generates a random string of characters, checks if it exists in the database,
+         * and if it does, it generates another one
+         *
+         * @param $extension string The file extension.
+         *
+         * @return string A string
+         */
+        public function generateName(string $extension):string
+        {
+            do {
+                if ($this->Connector->CONFIG['FILES_RETRIES'] === 0) {
+                    $this->Connector->response->error(500, 'Gave up trying to find an unused name!');
+                }
+                $NEW_NAME = '';
+                for ($i = 0; $i < $this->Connector->CONFIG['NAME_LENGTH']; $i++) {
+                    $index = rand(0, strlen($this->Connector->CONFIG['ID_CHARSET']) - 1);
+                    $NEW_NAME .= $this->Connector->CONFIG['ID_CHARSET'][$index];
+                }
+                if (!empty($extension)) {
+                    $NEW_NAME .= '.' . $extension;
+                }
+            } while ($this->Connector->dbCheckNameExists($NEW_NAME));
             return $NEW_NAME;
-    }
-}
+        }
+    }
\ No newline at end of file
index b58a96ea200380b405f8ec7b2af8fcafdf23c076..bc49250c06882703218ffef595bacaa06296e009 100755 (executable)
@@ -3,12 +3,13 @@
     "allowErrors": false
   },
   "dest": "dist",
-  "pkgVersion": "1.6.7",
+  "pkgVersion": "1.7.0",
   "pages": [
     "index.ejs",
     "faq.ejs",
     "tools.ejs"
   ],
+  "DEBUG": false,
   "max_upload_size": 128,
   "expireTime": "48",
   "siteName": "Uguu",
diff --git a/src/static/dbSchemas/postgres_schema.sql b/src/static/dbSchemas/postgres_schema.sql
new file mode 100644 (file)
index 0000000..1748259
--- /dev/null
@@ -0,0 +1,27 @@
+CREATE TABLE files
+(
+    id           serial PRIMARY KEY,
+    hash         text    NOT NULL,
+    originalname text    NOT NULL,
+    filename     text    NOT NULL,
+    size         integer not null,
+    date         integer not null,
+    ip           text    null
+);
+
+CREATE TABLE blacklist
+(
+    id           serial PRIMARY KEY,
+    hash         text    NOT NULL,
+    originalname text    NOT NULL,
+    time         integer not null,
+    ip           text    null
+);
+
+CREATE TABLE ratelimit
+(
+    id     serial PRIMARY KEY,
+    iphash text    NOT NULL,
+    files  integer not null,
+    time   integer not null
+);
\ No newline at end of file
index 6472d53a96eeb3ea479c17bb07c525dc5aacdceb..eb6acd08115ef6af7897ac10f41fea44da1ceeb9 100644 (file)
@@ -3,7 +3,7 @@
     /**
      * Uguu
      *
-     * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
+     * @copyright Copyright (c) 2022-2023 Go Johansson (nokonoko) <neku@pomf.se>
      *
      * 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
index 698cf1ed37e7b212844093fc6a67945822cac53b..7d73373b05899d1bdb96f964b337d5da63f6f7b1 100644 (file)
@@ -1,9 +1,9 @@
 <?php
-
+    
     /**
      * Uguu
      *
-     * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
+     * @copyright Copyright (c) 2022-2023 Go Johansson (nokonoko) <neku@pomf.se>
      *
      * 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
      * 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\Upload;
     use Pomf\Uguu\Classes\Response;
-
-    /**
-     * It takes a string and an array as arguments, creates a new Upload object,
-     * calls the reFiles method on the Upload object, calls the fingerPrint method on
-     * the Upload object, calls the uploadFile method on the Upload object,
-     * calls the send method on the Upload object, and calls the error method on the
-     * Upload object
-     *
-     * @param $outputFormat string The format of the output, json or xml
-     * @param $files        array The file to be uploaded, which is an array.
-     *
-     * @throws \Exception
-     */
-function handleFile(string $outputFormat, array $files): void
-{
-    $fCount = count($files['size']);
-    $upload = new Upload($outputFormat);
-    $files = $upload->reFiles($files);
-    try {
+    
+    function handleFiles(string $outputFormat, array $files):void
+    {
+        $upload = new Upload($outputFormat);
+        $files = $upload->reFiles($files);
+        $fCount = count($files);
         $upload->fingerPrint($fCount);
         $res = [];
-        foreach ($files as $ignored) {
+        $i = 0;
+        while ($i < $fCount) {
             $res[] = $upload->uploadFile();
+            $i++;
         }
         if (!empty($res)) {
             $upload->send($res);
         }
-    } catch (Exception $e) {
-        $upload->error(500, $e->getMessage());
     }
-}
-
+    
     $response = new Response('json');
-
-if (!isset($_FILES['files']) or empty($_FILES['files'])) {
-    $response->error(400, 'No input file(s)');
-}
-if (isset($_GET['output']) and !empty($_GET['output'])) {
-    $resType = strtolower(preg_replace('/[^a-zA-Z]/', '', $_GET['output']));
-} else {
-    $resType = 'json';
-}
-
-try {
-    handleFile($resType, $_FILES['files']);
-} catch (Exception $e) {
-    $response->error($e->getCode(), $e->getMessage());
-}
+    $resType = (isset($_GET['output']) and !empty($_GET['output'])) ? strtolower(preg_replace('/[^a-zA-Z]/', '', $_GET['output'])) : 'json';
+    if (!isset($_FILES['files']) or empty($_FILES['files'])) {
+        $response->error(400, 'No input file(s)');
+    }
+    handleFiles($resType, $_FILES['files']);
\ No newline at end of file