]>
Commit | Line | Data |
---|---|---|
e480c0e5 | 1 | <?php |
e480c0e5 | 2 | /** |
cec6349e | 3 | * Uguu |
8f7f8840 | 4 | * |
cec6349e | 5 | * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se> |
8f7f8840 | 6 | * |
cec6349e GJ |
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. | |
8f7f8840 | 11 | * |
cec6349e GJ |
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. | |
8f7f8840 | 16 | * |
cec6349e GJ |
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/>. | |
8f7f8840 | 19 | */ |
8f7f8840 | 20 | |
f059e2cf | 21 | namespace Pomf\Uguu\Classes; |
cec6349e GJ |
22 | |
23 | use Exception; | |
24 | ||
25 | class Upload extends Response | |
e480c0e5 | 26 | { |
cec6349e GJ |
27 | public array $FILE_INFO; |
28 | public array $fingerPrintInfo; | |
29 | private mixed $Connector; | |
30 | ||
31 | /** | |
32 | * 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 | |
33 | * | |
34 | * @param $files array The files array from the $_FILES superglobal. | |
35 | * | |
36 | * @return array An array of arrays. | |
37 | * @throws \Exception | |
38 | */ | |
39 | public function reFiles(array $files):array | |
40 | { | |
41 | $this->Connector = new Connector(); | |
42 | $this->Connector->setDB($this->Connector->DB); | |
43 | $result = []; | |
44 | $files = $this->diverseArray($files); | |
45 | foreach ($files as $file) { | |
46 | $hash = sha1_file($file['tmp_name']); | |
47 | $this->FILE_INFO = [ | |
48 | 'TEMP_NAME' => $file['tmp_name'], | |
49 | 'NAME' => strip_tags($file['name']), | |
50 | 'SIZE' => $file['size'], | |
51 | 'SHA1' => $hash, | |
52 | 'EXTENSION' => $this->fileExtension($file), | |
53 | 'MIME' => $this->fileMIME($file), | |
54 | 'NEW_NAME' => $this->generateName($this->fileExtension($file), $hash), | |
55 | ]; | |
56 | $result[] = [ | |
57 | $this->FILE_INFO['TEMP_NAME'], | |
58 | $this->FILE_INFO['NAME'], | |
59 | $this->FILE_INFO['SIZE'], | |
60 | $this->FILE_INFO['SHA1'], | |
61 | $this->FILE_INFO['EXTENSION'], | |
62 | $this->FILE_INFO['MIME'], | |
63 | ]; | |
64 | } | |
65 | return $result; | |
e480c0e5 | 66 | } |
cec6349e GJ |
67 | |
68 | /** | |
69 | * Takes an array of arrays and returns an array of arrays with the keys and values swapped | |
70 | * | |
71 | * @param $files array an array of arrays | |
72 | * | |
73 | * @return array ``` | |
74 | * array:2 [▼ | |
75 | * 0 => array:2 [▼ | |
76 | * 'TEMP_NAME' => 'example' | |
77 | * 'NAME' => 'example' | |
78 | * 'SIZE' => 'example' | |
79 | * 'SHA1' => 'example' | |
80 | * 'EXTENSION' => 'example' | |
81 | * 'MIME' => 'example' | |
82 | * | |
83 | * ] | |
84 | * 1 => array:2 [▼ | |
85 | * 'TEMP_NAME' => 'example' | |
86 | * 'NAME' => 'example' | |
87 | * 'SIZE' => 'example' | |
88 | * 'SHA1' => 'example' | |
89 | * 'EXTENSION' => 'example' | |
90 | * 'MIME' => 'example' | |
91 | * ] | |
92 | * ] | |
93 | * ``` | |
94 | */ | |
95 | public function diverseArray(array $files):array | |
96 | { | |
97 | $result = []; | |
98 | foreach ($files as $key1 => $value1) { | |
99 | foreach ($value1 as $key2 => $value2) { | |
100 | $result[$key2][$key1] = $value2; | |
101 | } | |
102 | } | |
103 | return $result; | |
e480c0e5 | 104 | } |
cec6349e GJ |
105 | |
106 | /** | |
107 | * Takes a file, checks if it's blacklisted, moves it to the file storage, and then logs it to the database | |
108 | * | |
109 | * @return array An array containing the hash, name, url, and size of the file. | |
110 | * @throws \Exception | |
111 | */ | |
112 | public function uploadFile():array | |
113 | { | |
114 | if ($this->Connector->CONFIG['RATE_LIMIT']) { | |
115 | $this->Connector->checkRateLimit($this->fingerPrintInfo); | |
116 | } | |
117 | if ($this->Connector->CONFIG['BLACKLIST_DB']) { | |
118 | $this->Connector->checkFileBlacklist($this->FILE_INFO); | |
119 | } | |
120 | if ($this->Connector->CONFIG['FILTER_MODE'] && empty($this->FILE_INFO['EXTENSION'])) { | |
121 | $this->checkMimeBlacklist(); | |
122 | } | |
123 | if ($this->Connector->CONFIG['FILTER_MODE'] && !empty($this->FILE_INFO['EXTENSION'])) { | |
124 | $this->checkMimeBlacklist(); | |
125 | $this->checkExtensionBlacklist(); | |
126 | } | |
127 | if (!is_dir($this->Connector->CONFIG['FILES_ROOT'])) { | |
128 | throw new Exception('File storage path not accessible.', 500); | |
129 | } | |
130 | if ( | |
131 | !move_uploaded_file( | |
132 | $this->FILE_INFO['TEMP_NAME'], | |
133 | $this->Connector->CONFIG['FILES_ROOT'] . | |
134 | $this->FILE_INFO['NEW_NAME'], | |
135 | ) | |
136 | ) { | |
137 | throw new Exception('Failed to move file to destination', 500); | |
138 | } | |
139 | if (!chmod($this->Connector->CONFIG['FILES_ROOT'] . $this->FILE_INFO['NEW_NAME'], 0644)) { | |
140 | throw new Exception('Failed to change file permissions', 500); | |
141 | } | |
142 | if (!$this->Connector->CONFIG['LOG_IP']) { | |
143 | $this->fingerPrintInfo['ip'] = null; | |
144 | } | |
145 | $this->Connector->newIntoDB($this->FILE_INFO, $this->fingerPrintInfo); | |
146 | return [ | |
147 | 'hash' => $this->FILE_INFO['SHA1'], | |
148 | 'name' => $this->FILE_INFO['NAME'], | |
149 | 'url' => $this->Connector->CONFIG['FILES_URL'] . '/' . $this->FILE_INFO['NEW_NAME'], | |
150 | 'size' => $this->FILE_INFO['SIZE'], | |
151 | ]; | |
e480c0e5 | 152 | } |
cec6349e GJ |
153 | |
154 | /** | |
155 | * 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 | |
156 | * uploaded | |
157 | * | |
158 | * @param $files_amount int The amount of files that are being uploaded. | |
159 | * | |
160 | * @throws \Exception | |
161 | */ | |
162 | public function fingerPrint(int $files_amount):void | |
163 | { | |
164 | if (!empty($_SERVER['HTTP_USER_AGENT'])) { | |
165 | $USER_AGENT = filter_var($_SERVER['HTTP_USER_AGENT'], FILTER_SANITIZE_ENCODED); | |
166 | $this->fingerPrintInfo = [ | |
167 | 'timestamp' => time(), | |
168 | 'useragent' => $USER_AGENT, | |
169 | 'ip' => $_SERVER['REMOTE_ADDR'], | |
170 | 'ip_hash' => hash('sha1', $_SERVER['REMOTE_ADDR'] . $USER_AGENT), | |
171 | 'files_amount' => $files_amount, | |
172 | ]; | |
173 | } else { | |
174 | throw new Exception('Invalid user agent.', 500); | |
175 | } | |
e480c0e5 | 176 | } |
cec6349e GJ |
177 | |
178 | /** | |
179 | * Returns the MIME type of a file | |
180 | * | |
181 | * @param $file array The file to be checked. | |
182 | * | |
183 | * @return string The MIME type of the file. | |
184 | */ | |
185 | public function fileMIME(array $file):string | |
186 | { | |
187 | $FILE_INFO = finfo_open(FILEINFO_MIME_TYPE); | |
188 | return finfo_file($FILE_INFO, $file['tmp_name']); | |
e480c0e5 | 189 | } |
cec6349e GJ |
190 | |
191 | /** | |
192 | * Takes a file and returns the file extension | |
193 | * | |
194 | * @param $file array The file you want to get the extension from. | |
195 | * | |
196 | * @return ?string The file extension of the file. | |
197 | */ | |
198 | public function fileExtension(array $file):?string | |
199 | { | |
200 | $extension = explode('.', $file['name']); | |
201 | if (substr_count($file['name'], '.') > 0) { | |
202 | return end($extension); | |
203 | } else { | |
204 | return null; | |
205 | } | |
e480c0e5 | 206 | } |
cec6349e GJ |
207 | |
208 | /** | |
209 | * > Check if the file's MIME type is in the blacklist | |
210 | * | |
211 | * @throws \Exception | |
212 | */ | |
213 | public function checkMimeBlacklist():void | |
214 | { | |
215 | if (in_array($this->FILE_INFO['MIME'], $this->Connector->CONFIG['BLOCKED_MIME'])) { | |
216 | throw new Exception('Filetype not allowed.', 415); | |
217 | } | |
e480c0e5 | 218 | } |
cec6349e GJ |
219 | |
220 | /** | |
221 | * > Check if the file extension is in the blacklist | |
222 | * | |
223 | * @throws \Exception | |
224 | */ | |
225 | public function checkExtensionBlacklist():void | |
226 | { | |
227 | if (in_array($this->FILE_INFO['EXTENSION'], $this->Connector->CONFIG['BLOCKED_EXTENSIONS'])) { | |
228 | throw new Exception('Filetype not allowed.', 415); | |
229 | } | |
e480c0e5 | 230 | } |
cec6349e GJ |
231 | |
232 | /** | |
233 | * Generates a random string of characters, checks if it exists in the database, and if it does, it generates another one | |
234 | * | |
235 | * @param $extension string The file extension. | |
236 | * @param $hash string The hash of the file. | |
237 | * | |
238 | * @return string A string | |
239 | * @throws \Exception | |
240 | */ | |
241 | public function generateName(string $extension, string $hash):string | |
242 | { | |
243 | if ($this->Connector->antiDupe($hash)) { | |
244 | do { | |
245 | if ($this->Connector->CONFIG['FILES_RETRIES'] === 0) { | |
246 | throw new Exception('Gave up trying to find an unused name!', 500); | |
247 | } | |
248 | $NEW_NAME = ''; | |
8be20956 GJ |
249 | $count = strlen($this->Connector->CONFIG['ID_CHARSET']); |
250 | while ($this->Connector->CONFIG['NAME_LENGTH']--) { | |
251 | $NEW_NAME .= $this->Connector->CONFIG['ID_CHARSET'][mt_rand(0, $count - 1)]; | |
cec6349e GJ |
252 | } |
253 | if (!empty($extension)) { | |
254 | $NEW_NAME .= '.' . $extension; | |
255 | } | |
256 | } while ($this->Connector->dbCheckNameExists($NEW_NAME) > 0); | |
257 | return $NEW_NAME; | |
258 | } else { | |
259 | return $this->Connector->antiDupe($hash); | |
260 | } | |
e480c0e5 GJ |
261 | } |
262 | } |