scratch – Diff between revs 140 and 141

Subversion Repositories:
Rev:
Only display areas with differencesIgnore whitespace
Rev 140 Rev 141
1 <?php 1 <?php
2   2  
3 ########################################################################### 3 ###########################################################################
4 ## Copyright (C) Wizardry and Steamworks 2017 - License: GNU GPLv3 ## 4 ## Copyright (C) Wizardry and Steamworks 2017 - License: GNU GPLv3 ##
5 ########################################################################### 5 ###########################################################################
6   6  
7 require_once('php/pseudocrypt.php'); 7 require_once('php/pseudocrypt.php');
8 require_once('php/functions.php'); 8 require_once('php/functions.php');
-   9 require_once('php/ip.php');
9 require_once('vendor/autoload.php'); 10 require_once('vendor/autoload.php');
10   11  
11 ### Load configuration. 12 ### Load configuration.
12 $config = spyc_load_file('config.yaml'); 13 $config = spyc_load_file('config.yaml');
13   14  
14 #### POST -> upload / GET -> download 15 #### POST -> upload / GET -> download
15 switch ($_SERVER['REQUEST_METHOD']) { 16 switch ($_SERVER['REQUEST_METHOD']) {
16 case 'POST': 17 case 'POST':
17 #### Script restrictions. 18 #### Script restrictions.
18 session_start(); 19 session_start();
19 if (empty($_POST['token']) || !hash_equals($_SESSION['token'], $_POST['token'])) { 20 if (empty($_POST['token']) || !hash_equals($_SESSION['token'], $_POST['token'])) {
20 http_response_code(403); 21 http_response_code(403);
21 die('Forbidden.'); 22 die('Forbidden.');
22 } 23 }
23 #### Retrieve uploaded file. 24 #### Retrieve uploaded file.
24 if (!empty($_FILES['file']) and 25 if (!empty($_FILES['file']) and
25 is_uploaded_file($_FILES['file']['tmp_name'])) { 26 is_uploaded_file($_FILES['file']['tmp_name'])) {
26 if($_FILES['file']['size'] > $config['ALLOWED_ASSET_SIZE'] * 1048576) { 27 if($_FILES['file']['size'] > $config['ALLOWED_ASSET_SIZE'] * 1048576) {
27 http_response_code(403); 28 http_response_code(403);
28 die('File size exceeds '.$config['ALLOWED_ASSET_SIZE'].'MiB.'); 29 die('File size exceeds '.$config['ALLOWED_ASSET_SIZE'].'MiB.');
29 } 30 }
30 # Regular multipart/form-data upload. 31 # Regular multipart/form-data upload.
31 $name = $_FILES['file']['name']; 32 $name = $_FILES['file']['name'];
32 $data = atomized_get_contents($_FILES['file']['tmp_name']); 33 $data = atomized_get_contents($_FILES['file']['tmp_name']);
33 } else { 34 } else {
34 if((int)get_file_size("php://input") > $config['ALLOWED_ASSET_SIZE'] * 1048576) { 35 if((int)get_file_size("php://input") > $config['ALLOWED_ASSET_SIZE'] * 1048576) {
35 http_response_code(403); 36 http_response_code(403);
36 die('File size exceeds '.$config['ALLOWED_ASSET_SIZE'].'MiB.'); 37 die('File size exceeds '.$config['ALLOWED_ASSET_SIZE'].'MiB.');
37 } 38 }
38 # Raw POST data. 39 # Raw POST data.
39 $name = urldecode(@$_SERVER['HTTP_X_FILE_NAME']); 40 $name = urldecode(@$_SERVER['HTTP_X_FILE_NAME']);
40 $data = atomized_get_contents("php://input"); 41 $data = atomized_get_contents("php://input");
41 } 42 }
42   43  
43 #### Grab the file extension. 44 #### Grab the file extension.
44 $fileExtension = pathinfo($name, PATHINFO_EXTENSION); 45 $fileExtension = pathinfo($name, PATHINFO_EXTENSION);
45   46  
46 #### If the extension is not allowed then change it to a text extension. 47 #### If the extension is not allowed then change it to a text extension.
47 if (!isset($fileExtension) || 48 if (!isset($fileExtension) ||
48 !in_array(strtoupper($fileExtension), 49 !in_array(strtoupper($fileExtension),
49 array_map('strtoupper', $config['ALLOWED_FILE_EXTENSIONS']))) { 50 array_map('strtoupper', $config['ALLOWED_FILE_EXTENSIONS']))) {
50 http_response_code(403); 51 http_response_code(403);
51 die('File extension not allowed.'); 52 die('File extension not allowed.');
52 } 53 }
53 54
54 #### Hash filename. 55 #### Hash filename.
55 $file = strtolower( 56 $file = strtolower(
56 PseudoCrypt::hash( 57 PseudoCrypt::hash(
57 preg_replace( 58 preg_replace(
58 '/\D/', 59 '/\D/',
59 '', 60 '',
60 hash( 61 hash(
61 'sha512', 62 'sha512',
62 $data 63 $data
63 ) 64 )
64 ), 65 ),
65 $config['ASSET_HASH_SIZE'] 66 $config['ASSET_HASH_SIZE']
66 ) 67 )
67 ); 68 );
68   69  
69 #### Build the user path. 70 #### Build the user path.
70 $userPath = join( 71 $userPath = join(
71 DIRECTORY_SEPARATOR, 72 DIRECTORY_SEPARATOR,
72 array( 73 array(
73 $config['STORE_FOLDER'], 74 $config['STORE_FOLDER'],
74 $file 75 $file
75 ) 76 )
76 ); 77 );
77   78  
78 #### Check for path traversals. 79 #### Check for path traversals.
79 $pathPart = pathinfo($userPath.'.'.$fileExtension); 80 $pathPart = pathinfo($userPath.'.'.$fileExtension);
80 if (strcasecmp( 81 if (strcasecmp(
81 realpath($pathPart['dirname']), realpath($config['STORE_FOLDER'])) != 0) { 82 realpath($pathPart['dirname']), realpath($config['STORE_FOLDER'])) != 0) {
82 http_response_code(500); 83 http_response_code(500);
83 die('Internal server error.'); 84 die('Internal server error.');
84 } 85 }
85   86  
86 #### Store the file. 87 #### Store the file.
87 $timestamp = atomized_put_contents($userPath.'.'.$fileExtension, $data); 88 $timestamp = atomized_put_contents($userPath.'.'.$fileExtension, $data);
88 89
89 ### Log IP address. 90 ### Log IP address.
90 $db = new PDO('sqlite:db/scratch.sqlite3'); 91 $db = new PDO('sqlite:db/scratch.sqlite3');
91 $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 92 $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
92 try { 93 try {
93 $db->beginTransaction(); 94 $db->beginTransaction();
94 95
95 ## Create tags table if it does not exist. 96 ## Create tags table if it does not exist.
96 $db->query('CREATE TABLE IF NOT EXISTS "uploaders" ("hash" text NOT NULL COLLATE NOCASE, "ip" text COLLATE NOCASE, UNIQUE("hash") ON CONFLICT REPLACE)'); 97 $db->query('CREATE TABLE IF NOT EXISTS "uploaders" ("hash" text NOT NULL COLLATE NOCASE, "ip" text COLLATE NOCASE, UNIQUE("hash") ON CONFLICT REPLACE)');
97 98
98 $q = $db->prepare('REPLACE INTO "uploaders" ("hash", "ip") VALUES(:hash, :ip)'); 99 $q = $db->prepare('REPLACE INTO "uploaders" ("hash", "ip") VALUES(:hash, :ip)');
99 $q->bindParam(':hash', $file); 100 $q->bindParam(':hash', $file);
100 $q->bindParam(':ip', $_SERVER['REMOTE_ADDR']); 101 $q->bindParam(':ip', get_ip_address());
101 $q->execute(); 102 $q->execute();
102 103
103 $db->commit(); 104 $db->commit();
104 } catch (Exception $e) { 105 } catch (Exception $e) {
105 error_log($e); 106 error_log($e);
106 ## Rollback. 107 ## Rollback.
107 $db->rollback(); 108 $db->rollback();
108 } 109 }
109 110
110 ### Process any sent tags. 111 ### Process any sent tags.
111 if(isset($_POST['tags'])) { 112 if(isset($_POST['tags'])) {
112 $tags = json_decode( 113 $tags = json_decode(
113 stripslashes( 114 stripslashes(
114 $_POST['tags'] 115 $_POST['tags']
115 ) 116 )
116 ); 117 );
117 118
118 ## If we have any tags then insert them into the database. 119 ## If we have any tags then insert them into the database.
119 if(!empty($tags)) { 120 if(!empty($tags)) {
120 ## Connect or create the scratch database. 121 ## Connect or create the scratch database.
121 $db = new PDO('sqlite:db/scratch.sqlite3'); 122 $db = new PDO('sqlite:db/scratch.sqlite3');
122 ## Set the error mode to exceptions. 123 ## Set the error mode to exceptions.
123 $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 124 $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
124 try { 125 try {
125 126
126 $db->beginTransaction(); 127 $db->beginTransaction();
127 128
128 ## Create tags table if it does not exist. 129 ## Create tags table if it does not exist.
129 $db->query('CREATE TABLE IF NOT EXISTS "tags" ("hash" text NOT NULL COLLATE NOCASE, "tag" text COLLATE NOCASE, UNIQUE("hash", "tag") ON CONFLICT REPLACE)'); 130 $db->query('CREATE TABLE IF NOT EXISTS "tags" ("hash" text NOT NULL COLLATE NOCASE, "tag" text COLLATE NOCASE, UNIQUE("hash", "tag") ON CONFLICT REPLACE)');
130 131
131 ## Now add all the tags. 132 ## Now add all the tags.
132 foreach($tags as $tag) { 133 foreach($tags as $tag) {
133 $q = $db->prepare('REPLACE INTO "tags" ("hash", "tag") VALUES(:hash, :tag)'); 134 $q = $db->prepare('REPLACE INTO "tags" ("hash", "tag") VALUES(:hash, :tag)');
134 $q->bindParam(':hash', $file); 135 $q->bindParam(':hash', $file);
135 $q->bindParam(':tag', $tag); 136 $q->bindParam(':tag', $tag);
136 $q->execute(); 137 $q->execute();
137 } 138 }
138 $db->commit(); 139 $db->commit();
139 } catch (Exception $e) { 140 } catch (Exception $e) {
140 error_log($e); 141 error_log($e);
141 ## Rollback. 142 ## Rollback.
142 $db->rollback(); 143 $db->rollback();
143 } 144 }
144 } 145 }
145 } 146 }
146   147  
147 ### Hook for various file extensions. 148 ### Hook for various file extensions.
148 $opengraph = FALSE; 149 $opengraph = FALSE;
149 switch(strtoupper($fileExtension)) { 150 switch(strtoupper($fileExtension)) {
150 case 'MP4': 151 case 'MP4':
151 case 'GIF': 152 case 'GIF':
152 $opengraph = TRUE; 153 $opengraph = TRUE;
153 break; 154 break;
154 } 155 }
155 156
156 ### Return the URL to the file. 157 ### Return the URL to the file.
157 header('Content-Type: text/plain; charset=utf-8'); 158 header('Content-Type: text/plain; charset=utf-8');
158 echo json_encode( 159 echo json_encode(
159 array( 160 array(
160 "hash" => $file, 161 "hash" => $file,
161 "timestamp" => $timestamp, 162 "timestamp" => $timestamp,
162 "opengraph" => $opengraph 163 "opengraph" => $opengraph
163 ) 164 )
164 ); 165 );
165 break; 166 break;
166 case 'GET': 167 case 'GET':
167 ### Tell browser not to cache files. 168 ### Tell browser not to cache files.
168 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0"); 169 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
169 header("Cache-Control: post-check=0, pre-check=0", false); 170 header("Cache-Control: post-check=0, pre-check=0", false);
170 header("Pragma: no-cache"); 171 header("Pragma: no-cache");
171 172
172 ### If no file has been specified for download then return. 173 ### If no file has been specified for download then return.
173 if (!isset($_GET['hash']) or empty($_GET['hash'])) { 174 if (!isset($_GET['hash']) or empty($_GET['hash'])) {
174 http_response_code(404); 175 http_response_code(404);
175 die('File not found.'); 176 die('File not found.');
176 } 177 }
177   178  
178 ### Find the requested file. 179 ### Find the requested file.
179 $file = array_shift( 180 $file = array_shift(
180 preg_grep( 181 preg_grep(
181 '/'.$_GET['hash'].'/', 182 '/'.$_GET['hash'].'/',
182 scandir($config['STORE_FOLDER']) 183 scandir($config['STORE_FOLDER'])
183 ) 184 )
184 ); 185 );
185   186  
186 if (!isset($file) or empty($file)) { 187 if (!isset($file) or empty($file)) {
187 http_response_code(404); 188 http_response_code(404);
188 die('File not found.'); 189 die('File not found.');
189 } 190 }
190 191
191 ### Check the path for path traversals. 192 ### Check the path for path traversals.
192 $fileExtension = pathinfo($file, PATHINFO_EXTENSION); 193 $fileExtension = pathinfo($file, PATHINFO_EXTENSION);
193   194  
194 #### If the extension is not allowed then return. 195 #### If the extension is not allowed then return.
195 if (!isset($fileExtension) || 196 if (!isset($fileExtension) ||
196 !in_array(strtoupper($fileExtension), 197 !in_array(strtoupper($fileExtension),
197 array_map('strtoupper', $config['ALLOWED_FILE_EXTENSIONS']))) { 198 array_map('strtoupper', $config['ALLOWED_FILE_EXTENSIONS']))) {
198 http_response_code(403); 199 http_response_code(403);
199 die('File extension not allowed.'); 200 die('File extension not allowed.');
200 } 201 }
201 202
202 #### Build the user path. 203 #### Build the user path.
203 $userPath = join( 204 $userPath = join(
204 DIRECTORY_SEPARATOR, 205 DIRECTORY_SEPARATOR,
205 array( 206 array(
206 $config['STORE_FOLDER'], 207 $config['STORE_FOLDER'],
207 $file 208 $file
208 ) 209 )
209 ); 210 );
210   211  
211 #### Check for path traversals 212 #### Check for path traversals
212 $pathPart = pathinfo($userPath); 213 $pathPart = pathinfo($userPath);
213 if (strcasecmp( 214 if (strcasecmp(
214 realpath($pathPart['dirname']), realpath($config['STORE_FOLDER'])) != 0) { 215 realpath($pathPart['dirname']), realpath($config['STORE_FOLDER'])) != 0) {
215 http_response_code(500); 216 http_response_code(500);
216 die('Internal server error.'); 217 die('Internal server error.');
217 } 218 }
218   219  
219 ### Hook for various file extensions. 220 ### Hook for various file extensions.
220 switch(strtoupper($fileExtension)) { 221 switch(strtoupper($fileExtension)) {
221 case "HTML": 222 case "HTML":
222 case "HTM": 223 case "HTM":
223 header('Content-type: text/html'); 224 header('Content-type: text/html');
224 break; 225 break;
225 break; 226 break;
226 case "URL": 227 case "URL":
227 if(preg_match( 228 if(preg_match(
228 "/URL=(https?:\/\/[\-_\.\+!\*'\(\),a-zA-Z0-9]+:?[0-9]{0,5}\/.*?)\n/", 229 "/URL=(https?:\/\/[\-_\.\+!\*'\(\),a-zA-Z0-9]+:?[0-9]{0,5}\/.*?)\n/",
229 file_get_contents($userPath), $matches)) { 230 file_get_contents($userPath), $matches)) {
230 header('Location: '.$matches[1]); 231 header('Location: '.$matches[1]);
231 return; 232 return;
232 } 233 }
233 break; 234 break;
234 default: 235 default:
235 ### Open MIME info database and send the content type. 236 ### Open MIME info database and send the content type.
236 $finfo = finfo_open(FILEINFO_MIME_TYPE); 237 $finfo = finfo_open(FILEINFO_MIME_TYPE);
237 if (!$finfo) { 238 if (!$finfo) {
238 http_response_code(500); 239 http_response_code(500);
239 die('Internal server error.'); 240 die('Internal server error.');
240 } 241 }
241 header('Content-type: '.finfo_file($finfo, $userPath)); 242 header('Content-type: '.finfo_file($finfo, $userPath));
242 finfo_close($finfo); 243 finfo_close($finfo);
243 break; 244 break;
244 } 245 }
245 246
246 ### Send the file along with the inline content disposition. 247 ### Send the file along with the inline content disposition.
247 header('Content-length: '.(int)get_file_size($userPath)); 248 header('Content-length: '.(int)get_file_size($userPath));
248 header('Content-Disposition: inline; filename="' . basename($userPath) . '"'); 249 header('Content-Disposition: inline; filename="' . basename($userPath) . '"');
249 header('Content-Transfer-Encoding: binary'); 250 header('Content-Transfer-Encoding: binary');
250 header('X-Sendfile: '.$userPath); 251 header('X-Sendfile: '.$userPath);
251 break; 252 break;
252 } 253 }
253   254