]>
Commit | Line | Data |
---|---|---|
d8c46ff7 GJ |
1 | <?php |
2 | /** | |
8fa0750d GJ |
3 | * Handles POST uploads, generates filenames, moves files around and commits |
4 | * uploaded metadata to database. | |
d8c46ff7 GJ |
5 | */ |
6 | require_once 'classes/Response.class.php'; | |
7 | require_once 'classes/UploadException.class.php'; | |
8 | require_once 'classes/UploadedFile.class.php'; | |
9 | require_once 'includes/database.inc.php'; | |
10 | ||
11 | /** | |
8fa0750d GJ |
12 | * Generates a random name for the file, retrying until we get an unused one. |
13 | * | |
14 | * @param UploadedFile $file | |
15 | * | |
16 | * @return string | |
d8c46ff7 GJ |
17 | */ |
18 | function generateName($file) | |
19 | { | |
20 | global $db; | |
21 | global $doubledots; | |
22 | ||
23 | // We start at N retries, and --N until we give up | |
b349c51a GJ |
24 | $tries = UGUU_FILES_RETRIES; |
25 | $length = UGUU_FILES_LENGTH; | |
d9744300 | 26 | |
d8c46ff7 GJ |
27 | //Get EXT |
28 | $ext = pathinfo($file->name, PATHINFO_EXTENSION); | |
d9744300 GJ |
29 | |
30 | //Get MIME | |
d8c46ff7 GJ |
31 | $finfo = finfo_open(FILEINFO_MIME_TYPE); |
32 | $type_mime = finfo_file($finfo, $file->tempfile); | |
33 | finfo_close($finfo); | |
34 | ||
35 | // Check if extension is a double-dot extension and, if true, override $ext | |
36 | $revname = strrev($file->name); | |
37 | foreach ($doubledots as $ddot) { | |
38 | if (stripos($revname, $ddot) === 0) { | |
39 | $ext = strrev($ddot); | |
40 | } | |
41 | } | |
42 | ||
43 | do { | |
44 | // Iterate until we reach the maximum number of retries | |
45 | if ($tries-- === 0) { | |
d9744300 GJ |
46 | http_response_code(500); |
47 | throw new Exception( | |
d8c46ff7 GJ |
48 | 'Gave up trying to find an unused name', |
49 | 500 | |
50 | ); // HTTP status code "500 Internal Server Error" | |
51 | } | |
52 | ||
53 | $chars = ID_CHARSET; | |
54 | $name = ''; | |
55 | for ($i = 0; $i < $length; ++$i) { | |
56 | $name .= $chars[mt_rand(0, strlen($chars))]; | |
57 | } | |
58 | ||
59 | // Add the extension to the file name | |
60 | if (isset($ext) && $ext !== '') { | |
61 | $name .= '.'.$ext; | |
62 | } | |
63 | ||
5e56fb98 GJ |
64 | // Check if file is whitelisted or blacklisted |
65 | switch (CONFIG_FILTER_MODE) { | |
66 | ||
67 | case false: | |
68 | //check if MIME is blacklisted | |
69 | if (in_array($type_mime, unserialize(CONFIG_BLOCKED_MIME))) { | |
70 | http_response_code(415); | |
71 | exit(0); | |
72 | } | |
73 | //Check if EXT is blacklisted | |
74 | if (in_array($ext, unserialize(CONFIG_BLOCKED_EXTENSIONS))) { | |
75 | http_response_code(415); | |
76 | exit(0); | |
77 | } | |
78 | break; | |
79 | ||
80 | case true: | |
81 | //Check if MIME is whitelisted | |
82 | if (!in_array($type_mime, unserialize(CONFIG_BLOCKED_MIME))) { | |
83 | http_response_code(415); | |
84 | exit(0); | |
85 | } | |
86 | //Check if EXT is whitelisted | |
87 | if (!in_array($ext, unserialize(CONFIG_BLOCKED_EXTENSIONS))) { | |
88 | http_response_code(415); | |
89 | exit(0); | |
90 | } | |
91 | break; | |
8fa0750d GJ |
92 | } |
93 | ||
d8c46ff7 GJ |
94 | // Check if a file with the same name does already exist in the database |
95 | $q = $db->prepare('SELECT COUNT(filename) FROM files WHERE filename = (:name)'); | |
96 | $q->bindValue(':name', $name, PDO::PARAM_STR); | |
97 | $q->execute(); | |
98 | $result = $q->fetchColumn(); | |
99 | // If it does, generate a new name | |
d9744300 GJ |
100 | } while ($result > 0); |
101 | ||
102 | return $name; | |
103 | } | |
d8c46ff7 GJ |
104 | |
105 | /** | |
106 | * Handles the uploading and db entry for a file. | |
107 | * | |
108 | * @param UploadedFile $file | |
109 | * | |
110 | * @return array | |
111 | */ | |
112 | function uploadFile($file) | |
113 | { | |
114 | global $db; | |
d8c46ff7 GJ |
115 | |
116 | // Handle file errors | |
117 | if ($file->error) { | |
118 | throw new UploadException($file->error); | |
119 | } | |
120 | ||
d9744300 GJ |
121 | //fixes a bug |
122 | $lol = $file->getSha1(); | |
123 | ||
124 | // Check if a file with the same hash and size (a file which is the same) | |
125 | // does already exist in the database; if it does, return the proper link | |
126 | // and data. PHP deletes the temporary file just uploaded automatically. | |
a24bf794 | 127 | if(ANTI_DUPE){ |
d9744300 GJ |
128 | $q = $db->prepare('SELECT filename, COUNT(*) AS count FROM files WHERE hash = (:hash) AND size = (:size)'); |
129 | $q->bindValue(':hash', $file->getSha1(), PDO::PARAM_STR); | |
130 | $q->bindValue(':size', $file->size, PDO::PARAM_INT); | |
131 | $q->execute(); | |
132 | $result = $q->fetch(); | |
133 | if ($result['count'] > 0) { | |
134 | return [ | |
135 | 'hash' => $file->getSha1(), | |
136 | 'name' => $file->name, | |
137 | 'url' => UGUU_URL.rawurlencode($result['filename']), | |
138 | 'size' => $file->size, | |
139 | ]; | |
140 | } | |
141 | } | |
d8c46ff7 | 142 | |
d9744300 GJ |
143 | // Generate a name for the file |
144 | $newname = generateName($file); | |
145 | ||
d8c46ff7 | 146 | // Store the file's full file path in memory |
d9744300 | 147 | $uploadFile = UGUU_FILES_ROOT.$newname; |
d8c46ff7 GJ |
148 | |
149 | // Attempt to move it to the static directory | |
150 | if (!move_uploaded_file($file->tempfile, $uploadFile)) { | |
d9744300 GJ |
151 | http_response_code(500); |
152 | throw new Exception( | |
d8c46ff7 GJ |
153 | 'Failed to move file to destination', |
154 | 500 | |
155 | ); // HTTP status code "500 Internal Server Error" | |
156 | } | |
157 | ||
158 | // Need to change permissions for the new file to make it world readable | |
159 | if (!chmod($uploadFile, 0644)) { | |
d9744300 GJ |
160 | http_response_code(500); |
161 | throw new Exception( | |
d8c46ff7 GJ |
162 | 'Failed to change file permissions', |
163 | 500 | |
164 | ); // HTTP status code "500 Internal Server Error" | |
165 | } | |
166 | ||
a24bf794 GJ |
167 | // Log IP |
168 | if(LOG_IP){ | |
169 | $ip = $_SERVER['REMOTE_ADDR']; | |
170 | } else { | |
171 | $ip = null; | |
172 | } | |
d9744300 | 173 | |
d8c46ff7 | 174 | // Common parameters binding |
a24bf794 | 175 | $q = $db->prepare('INSERT INTO files (hash, originalname, filename, size, date, ip) VALUES (:hash, :orig, :name, :size, :date, :ip)'); |
d8c46ff7 GJ |
176 | $q->bindValue(':hash', $file->getSha1(), PDO::PARAM_STR); |
177 | $q->bindValue(':orig', strip_tags($file->name), PDO::PARAM_STR); | |
178 | $q->bindValue(':name', $newname, PDO::PARAM_STR); | |
179 | $q->bindValue(':size', $file->size, PDO::PARAM_INT); | |
d9744300 | 180 | $q->bindValue(':date', time(), PDO::PARAM_STR); |
8fa0750d | 181 | $q->bindValue(':ip', $ip, PDO::PARAM_STR); |
d8c46ff7 GJ |
182 | $q->execute(); |
183 | ||
d9744300 | 184 | return [ |
d8c46ff7 GJ |
185 | 'hash' => $file->getSha1(), |
186 | 'name' => $file->name, | |
b349c51a | 187 | 'url' => UGUU_URL.rawurlencode($newname), |
d8c46ff7 | 188 | 'size' => $file->size, |
d9744300 | 189 | ]; |
d8c46ff7 GJ |
190 | } |
191 | ||
192 | /** | |
193 | * Reorder files array by file. | |
194 | * | |
195 | * @return array | |
196 | */ | |
197 | function diverseArray($files) | |
198 | { | |
d9744300 | 199 | $result = []; |
d8c46ff7 GJ |
200 | |
201 | foreach ($files as $key1 => $value1) { | |
202 | foreach ($value1 as $key2 => $value2) { | |
203 | $result[$key2][$key1] = $value2; | |
204 | } | |
205 | } | |
206 | ||
207 | return $result; | |
208 | } | |
209 | ||
210 | /** | |
211 | * Reorganize the $_FILES array into something saner. | |
212 | * | |
213 | * @return array | |
214 | */ | |
215 | function refiles($files) | |
216 | { | |
d9744300 | 217 | $result = []; |
d8c46ff7 GJ |
218 | $files = diverseArray($files); |
219 | ||
220 | foreach ($files as $file) { | |
221 | $f = new UploadedFile(); | |
222 | $f->name = $file['name']; | |
223 | $f->mime = $file['type']; | |
224 | $f->size = $file['size']; | |
225 | $f->tempfile = $file['tmp_name']; | |
226 | $f->error = $file['error']; | |
227 | $result[] = $f; | |
228 | } | |
229 | ||
230 | return $result; | |
231 | } | |
232 | ||
233 | $type = isset($_GET['output']) ? $_GET['output'] : 'json'; | |
234 | $response = new Response($type); | |
235 | ||
236 | if (isset($_FILES['files'])) { | |
237 | $uploads = refiles($_FILES['files']); | |
238 | ||
239 | try { | |
240 | foreach ($uploads as $upload) { | |
241 | $res[] = uploadFile($upload); | |
242 | } | |
243 | $response->send($res); | |
244 | } catch (Exception $e) { | |
245 | $response->error($e->getCode(), $e->getMessage()); | |
246 | } | |
247 | } else { | |
248 | $response->error(400, 'No input file(s)'); | |
249 | } |