]>
jfr.im git - uguu.git/blob - src/Classes/Upload.php
5 * @copyright Copyright (c) 2022-2023 Go Johansson (nokonoko) <neku@pomf.se>
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21 namespace Pomf\Uguu\Classes
;
23 class Upload
extends Response
25 public array $FILE_INFO;
26 public array $fingerPrintInfo;
27 private mixed $Connector;
30 * Takes an array of files, and returns an array of arrays containing the file's temporary name,
31 * name, size, SHA1 hash, extension, and MIME type
33 * @param $files array The files array from the $_FILES superglobal.
35 * @return array An array of arrays.
37 public function reFiles(array $files):array
39 $this->Connector
= new Connector();
41 $files = $this->diverseArray($files);
42 foreach ($files as $file) {
44 'TEMP_NAME' => $file['tmp_name'],
45 'NAME' => strip_tags($this->checkNameLength($file['name'])),
46 'SIZE' => $file['size'],
47 'SHA1' => sha1_file($file['tmp_name']),
48 'EXTENSION' => $this->fileExtension($file),
49 'MIME' => $this->fileMIME($file),
53 // Check if anti dupe is enabled
54 if ($this->Connector
->CONFIG
['ANTI_DUPE']) {
55 // Check if hash exists in DB, if it does return the name of the file
56 $dupeResult = $this->Connector
->antiDupe($this->FILE_INFO
['SHA1']);
57 if ($dupeResult['result']) {
58 $this->FILE_INFO
['FILENAME'] = $dupeResult['name'];
59 $this->FILE_INFO
['DUPE'] = true;
62 // If its not a dupe then generate a new name
63 if (!$this->FILE_INFO
['DUPE']) {
64 $this->FILE_INFO
['FILENAME'] = $this->generateName($this->FILE_INFO
['EXTENSION']);
67 $this->FILE_INFO
['TEMP_NAME'],
68 $this->FILE_INFO
['NAME'],
69 $this->FILE_INFO
['SIZE'],
70 $this->FILE_INFO
['SHA1'],
71 $this->FILE_INFO
['EXTENSION'],
72 $this->FILE_INFO
['MIME'],
73 $this->FILE_INFO
['DUPE'],
74 $this->FILE_INFO
['FILENAME'],
81 * Takes an array of arrays and returns an array of arrays with the keys and values swapped
83 * @param $files array an array of arrays
88 * 'TEMP_NAME' => 'example'
92 * 'EXTENSION' => 'example'
97 * 'TEMP_NAME' => 'example'
100 * 'SHA1' => 'example'
101 * 'EXTENSION' => 'example'
102 * 'MIME' => 'example'
107 public function diverseArray(array $files):array
110 foreach ($files as $key1 => $value1) {
111 foreach ($value1 as $key2 => $value2) {
112 $result[$key2][$key1] = $value2;
119 * Takes a file, checks if it's blacklisted, moves it to the file storage, and then logs it to the database
121 * @return array An array containing the hash, name, url, and size of the file.
123 public function uploadFile():array
126 case $this->Connector
->CONFIG
['RATE_LIMIT']:
128 $this->Connector
->checkRateLimit(
129 $this->fingerPrintInfo
,
130 $this->Connector
->CONFIG
['RATE_LIMIT_TIMEOUT'],
131 $this->Connector
->CONFIG
['RATE_LIMIT_FILES'],
134 $this->Connector
->response
->error(
136 'Rate limit, please wait ' . $this->Connector
->CONFIG
['RATE_LIMIT_TIMEOUT'] .
137 ' seconds before uploading again.',
141 case $this->Connector
->CONFIG
['BLACKLIST_DB']:
142 $this->Connector
->checkFileBlacklist($this->FILE_INFO
['SHA1']);
144 case $this->Connector
->CONFIG
['FILTER_MODE'] && empty($this->FILE_INFO
['EXTENSION']):
145 $this->checkMimeBlacklist();
147 case $this->Connector
->CONFIG
['FILTER_MODE'] && !empty($this->FILE_INFO
['EXTENSION']):
148 $this->checkMimeBlacklist();
149 $this->checkExtensionBlacklist();
152 // If its not a dupe then skip checking if file can be written and
153 // skip inserting it into the DB.
154 if (!$this->FILE_INFO
['DUPE']) {
155 if (!is_dir($this->Connector
->CONFIG
['FILES_ROOT'])) {
156 $this->Connector
->response
->error(500, 'File storage path not accessible.');
160 $this->FILE_INFO
['TEMP_NAME'],
161 $this->Connector
->CONFIG
['FILES_ROOT'] .
162 $this->FILE_INFO
['FILENAME'],
165 $this->Connector
->response
->error(500, 'Failed to move file to destination.');
167 if (!chmod($this->Connector
->CONFIG
['FILES_ROOT'] . $this->FILE_INFO
['FILENAME'], 0644)) {
168 $this->Connector
->response
->error(500, 'Failed to change file permissions.');
170 $this->Connector
->newIntoDB($this->FILE_INFO
, $this->fingerPrintInfo
);
173 'hash' => $this->FILE_INFO
['SHA1'],
174 'name' => $this->FILE_INFO
['NAME'],
175 'filename' => $this->FILE_INFO
['FILENAME'],
176 'url' => 'https://' . $this->Connector
->CONFIG
['FILE_DOMAIN'] . '/' . $this->FILE_INFO
['FILENAME'],
177 'size' => $this->FILE_INFO
['SIZE'],
178 'dupe' => $this->FILE_INFO
['DUPE'],
183 * Takes the amount of files that are being uploaded, and creates a fingerprint of the user's IP address,
184 * user agent, and the amount of files being
187 * @param $files_amount int The amount of files that are being uploaded.
190 public function fingerPrint(int $files_amount):void
192 if (!empty($_SERVER['HTTP_USER_AGENT'])) {
193 $USER_AGENT = filter_var($_SERVER['HTTP_USER_AGENT'], FILTER_SANITIZE_ENCODED
);
195 if ($this->Connector
->CONFIG
['LOG_IP']) {
196 $ip = $_SERVER['REMOTE_ADDR'];
198 $this->fingerPrintInfo
= [
199 'timestamp' => time(),
200 'useragent' => $USER_AGENT,
202 'ip_hash' => hash('sha1', $_SERVER['REMOTE_ADDR'] . $USER_AGENT),
203 'files_amount' => $files_amount,
206 $this->Connector
->response
->error(500, 'Invalid user agent.');
211 * Returns the MIME type of a file
213 * @param $file array The file to be checked.
215 * @return string The MIME type of the file.
217 public function fileMIME(array $file):string
219 $FILE_INFO = finfo_open(FILEINFO_MIME_TYPE
);
220 return finfo_file($FILE_INFO, $file['tmp_name']);
224 * It takes an array of strings, and returns the last two strings joined by a dot,
225 * unless the last two strings are in the array of strings in the
226 * `DOUBLE_DOTS_EXTENSIONS` config variable, in which case it returns the last string
228 * @param $extension array The extension of the file.
230 * @return string The last two elements of the array are joined together and returned.
232 public function doubleDotExtension(array $extension):string
234 $doubleDotArray = array_slice($extension, -2, 2);
235 $doubleDot = strtolower(preg_replace('/[^a-zA-Z.]/', '', join('.', $doubleDotArray)));
236 if (in_array($doubleDot, $this->Connector
->CONFIG
['DOUBLE_DOTS_EXTENSIONS'])) {
239 return end($extension);
244 * Takes a file and returns the file extension
246 * @param $file array The file you want to get the extension from.
248 * @return string The file extension of the file.
250 public function fileExtension(array $file):?string
252 if(str_contains($file['name'], '.')){
253 $extension = explode('.', $file['name']);
254 $dotCount = substr_count($file['name'], '.');
255 return match ($dotCount) {
256 1 => end($extension),
257 2 => $this->doubleDotExtension($extension),
258 default => end($extension)
265 * > Check if the file's MIME type is in the blacklist
268 public function checkMimeBlacklist():void
270 if (in_array($this->FILE_INFO
['MIME'], $this->Connector
->CONFIG
['BLOCKED_MIME'])) {
271 $this->Connector
->response
->error(415, 'Filetype not allowed');
276 * > Check if the file extension is in the blacklist
279 public function checkExtensionBlacklist():void
281 if (in_array($this->FILE_INFO
['EXTENSION'], $this->Connector
->CONFIG
['BLOCKED_EXTENSIONS'])) {
282 $this->Connector
->response
->error(415, 'Filetype not allowed');
286 public function checkNameLength(string $fileName):string
288 if (strlen($fileName) > 250) {
289 return substr($fileName, 0, 250);
296 * Generates a random string of characters, checks if it exists in the database,
297 * and if it does, it generates another one
299 * @param $extension string The file extension.
301 * @return string A string
303 public function generateName(string $extension):string
306 if ($this->Connector
->CONFIG
['FILES_RETRIES'] === 0) {
307 $this->Connector
->response
->error(500, 'Gave up trying to find an unused name!');
310 for ($i = 0; $i < $this->Connector
->CONFIG
['NAME_LENGTH']; $i++
) {
311 $index = rand(0, strlen($this->Connector
->CONFIG
['ID_CHARSET']) - 1);
312 $NEW_NAME .= $this->Connector
->CONFIG
['ID_CHARSET'][$index];
314 if ($extension != "NOEXT") {
315 $NEW_NAME .= '.' . $extension;
317 } while ($this->Connector
->dbCheckNameExists($NEW_NAME));