]> jfr.im git - uguu.git/blob - src/Classes/Upload.php
17e79f8301f53e42d0eea7d5a16eca071f3fa22c
[uguu.git] / src / Classes / Upload.php
1 <?php
2
3 /**
4 * Uguu
5 *
6 * @copyright Copyright (c) 2022 Go Johansson (nokonoko) <neku@pomf.se>
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>.
20 */
21
22 namespace Pomf\Uguu\Classes;
23
24 use Exception;
25
26 class Upload extends Response
27 {
28 public array $FILE_INFO;
29 public array $fingerPrintInfo;
30 private mixed $Connector;
31
32
33 /**
34 * @throws Exception
35 */
36 public function reFiles($files): array
37 {
38 $this->Connector = new Connector();
39 $this->Connector->setDB($this->Connector->DB);
40 $result = [];
41 $files = $this->diverseArray($files);
42 foreach ($files as $file) {
43 $hash = sha1_file($file['tmp_name']);
44 $this->FILE_INFO = [
45 'TEMP_NAME' => $file['tmp_name'],
46 'NAME' => strip_tags($file['name']),
47 'SIZE' => $file['size'],
48 'SHA1' => $hash,
49 'EXTENSION' => $this->fileExtension($file),
50 'MIME' => $this->fileMIME($file),
51 'NEW_NAME' => $this->generateName($this->fileExtension($file), $hash)
52 ];
53 $result[] = [
54 $this->FILE_INFO['TEMP_NAME'],
55 $this->FILE_INFO['NAME'],
56 $this->FILE_INFO['SIZE'],
57 $this->FILE_INFO['SHA1'],
58 $this->FILE_INFO['EXTENSION'],
59 $this->FILE_INFO['MIME']
60 ];
61 }
62 return $result;
63 }
64 public function diverseArray($files): array
65 {
66 $result = [];
67 foreach ($files as $key1 => $value1) {
68 foreach ($value1 as $key2 => $value2) {
69 $result[$key2][$key1] = $value2;
70 }
71 }
72 return $result;
73 }
74
75 /**
76 * @throws Exception
77 */
78 public function uploadFile(): array
79 {
80
81 if ($this->Connector->CONFIG['RATE_LIMIT']) {
82 $this->Connector->checkRateLimit($this->fingerPrintInfo);
83 }
84
85 if ($this->Connector->CONFIG['BLACKLIST_DB']) {
86 $this->Connector->checkFileBlacklist($this->FILE_INFO);
87 }
88
89 if ($this->Connector->CONFIG['FILTER_MODE'] && empty($this->FILE_INFO['EXTENSION'])) {
90 $this->checkMimeBlacklist();
91 }
92
93 if ($this->Connector->CONFIG['FILTER_MODE'] && !empty($this->FILE_INFO['EXTENSION'])) {
94 $this->checkMimeBlacklist();
95 $this->checkExtensionBlacklist();
96 }
97
98 if (!is_dir($this->Connector->CONFIG['FILES_ROOT'])) {
99 throw new Exception('File storage path not accessible.', 500);
100 }
101
102 if (
103 !move_uploaded_file($this->FILE_INFO['TEMP_NAME'], $this->Connector->CONFIG['FILES_ROOT'] .
104 $this->FILE_INFO['NEW_NAME'])
105 ) {
106 throw new Exception('Failed to move file to destination', 500);
107 }
108
109 if (!chmod($this->Connector->CONFIG['FILES_ROOT'] . $this->FILE_INFO['NEW_NAME'], 0644)) {
110 throw new Exception('Failed to change file permissions', 500);
111 }
112
113 if (!$this->Connector->CONFIG['LOG_IP']) {
114 $this->fingerPrintInfo['ip'] = null;
115 }
116
117 $this->Connector->newIntoDB($this->FILE_INFO, $this->fingerPrintInfo);
118
119 return [
120 'hash' => $this->FILE_INFO['SHA1'],
121 'name' => $this->FILE_INFO['NAME'],
122 'url' => $this->Connector->CONFIG['FILES_URL'] . '/' . $this->FILE_INFO['NEW_NAME'],
123 'size' => $this->FILE_INFO['SIZE']
124 ];
125 }
126
127 public function fingerPrint($files_amount): void
128 {
129 $this->fingerPrintInfo = [
130 'timestamp' => time(),
131 'useragent' => $_SERVER['HTTP_USER_AGENT'],
132 'ip' => $_SERVER['REMOTE_ADDR'],
133 'ip_hash' => hash('sha1', $_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT']),
134 'files_amount' => $files_amount
135 ];
136 }
137
138
139 public function fileMIME($file): string
140 {
141 $finfo = finfo_open(FILEINFO_MIME_TYPE);
142 return finfo_file($finfo, $file['tmp_name']);
143 }
144
145 public function fileExtension($file): ?string
146 {
147 $extension = explode('.', $file['name']);
148 if (substr_count($file['name'], '.') > 0) {
149 return end($extension);
150 } else {
151 return null;
152 }
153 }
154
155
156 /**
157 * @throws Exception
158 */
159 public function checkMimeBlacklist(): void
160 {
161 if (in_array($this->FILE_INFO['MIME'], $this->Connector->CONFIG['BLOCKED_MIME'])) {
162 throw new Exception('Filetype not allowed.', 415);
163 }
164 }
165
166 /**
167 * Check if file extension is blacklisted
168 * if it does throw an exception.
169 *
170 * @throws Exception
171 */
172 public function checkExtensionBlacklist(): void
173 {
174 if (in_array($this->FILE_INFO['EXTENSION'], $this->Connector->CONFIG['BLOCKED_EXTENSIONS'])) {
175 throw new Exception('Filetype not allowed.', 415);
176 }
177 }
178
179 /**
180 * @throws Exception
181 */
182 public function generateName($extension, $hash): string
183 {
184 $a = $this->Connector->antiDupe($hash);
185 if ($a === true) {
186 do {
187 if ($this->Connector->CONFIG['FILES_RETRIES'] === 0) {
188 throw new Exception('Gave up trying to find an unused name!', 500);
189 }
190
191 $NEW_NAME = '';
192 for ($i = 0; $i < $this->Connector->CONFIG['NAME_LENGTH']; ++$i) {
193 $NEW_NAME .= $this->Connector->CONFIG['ID_CHARSET']
194 [mt_rand(0, strlen($this->Connector->CONFIG['ID_CHARSET']))];
195 }
196
197 if (!is_null($extension)) {
198 $NEW_NAME .= '.' . $extension;
199 }
200 } while ($this->Connector->dbCheckNameExists($NEW_NAME) > 0);
201 return $NEW_NAME;
202 } else {
203 return $a;
204 }
205 }
206 }