scratch – Blame information for rev 125

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