scratch – Diff between revs 128 and 140

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