scratch – Blame information for rev 126

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)) {
126 office 99 ## Connect or create the scratch database.
100 $db = new PDO('sqlite:db/scratch.sqlite3');
101 ## Set the error mode to exceptions.
102 $db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
125 office 103 try {
126 office 104  
125 office 105 $db->beginTransaction();
126 office 106  
107 ## Create tags table if it does not exist.
125 office 108 $db->query('CREATE TABLE IF NOT EXISTS "tags" ("hash" text NOT NULL COLLATE NOCASE, "tag" text COLLATE NOCASE)');
126 office 109  
110 ## Now add all the tags.
125 office 111 foreach($tags as $tag) {
126 office 112 $q = $db->prepare('REPLACE INTO "tags" ("hash", "tag") VALUES(:hash, :tag)');
113 $q->bindParam(':hash', $file);
114 $q->bindParam(':tag', $tag);
115 $q->execute();
125 office 116 }
117 $db->commit();
118 } catch (Exception $e) {
126 office 119 error_log($e);
125 office 120 ## Rollback.
121 $db->rollback();
122 }
123 }
124 }
14 office 125  
90 office 126 ### Hook for various file extensions.
127 $opengraph = FALSE;
128 switch(strtoupper($fileExtension)) {
103 office 129 case 'MP4':
90 office 130 case 'GIF':
131 $opengraph = TRUE;
132 break;
133 }
125 office 134  
52 office 135 ### Return the URL to the file.
136 header('Content-Type: text/plain; charset=utf-8');
81 office 137 echo json_encode(
138 array(
139 "hash" => $file,
90 office 140 "timestamp" => $timestamp,
141 "opengraph" => $opengraph
81 office 142 )
143 );
52 office 144 break;
145 case 'GET':
83 office 146 ### Tell browser not to cache files.
147 header("Cache-Control: no-store, no-cache, must-revalidate, max-age=0");
148 header("Cache-Control: post-check=0, pre-check=0", false);
149 header("Pragma: no-cache");
150  
52 office 151 ### If no file has been specified for download then return.
81 office 152 if (!isset($_GET['hash']) or empty($_GET['hash'])) {
153 http_response_code(404);
154 die('File not found.');
52 office 155 }
156  
53 office 157 ### Find the requested file.
52 office 158 $file = array_shift(
159 preg_grep(
81 office 160 '/'.$_GET['hash'].'/',
67 office 161 scandir($config['STORE_FOLDER'])
52 office 162 )
163 );
164  
81 office 165 if (!isset($file) or empty($file)) {
166 http_response_code(404);
167 die('File not found.');
168 }
53 office 169  
170 ### Check the path for path traversals.
171 $fileExtension = pathinfo($file, PATHINFO_EXTENSION);
52 office 172  
53 office 173 #### If the extension is not allowed then return.
174 if (!isset($fileExtension) ||
175 !in_array(strtoupper($fileExtension),
67 office 176 array_map('strtoupper', $config['ALLOWED_FILE_EXTENSIONS']))) {
81 office 177 http_response_code(403);
178 die('File extension not allowed.');
52 office 179 }
53 office 180  
181 #### Build the user path.
182 $userPath = join(
183 DIRECTORY_SEPARATOR,
184 array(
67 office 185 $config['STORE_FOLDER'],
53 office 186 $file
187 )
188 );
52 office 189  
53 office 190 #### Check for path traversals
191 $pathPart = pathinfo($userPath);
192 if (strcasecmp(
67 office 193 realpath($pathPart['dirname']), realpath($config['STORE_FOLDER'])) != 0) {
81 office 194 http_response_code(500);
195 die('Internal server error.');
53 office 196 }
197  
83 office 198 ### Hook for various file extensions.
53 office 199 switch(strtoupper($fileExtension)) {
200 case "HTML":
201 case "HTM":
202 header('Content-type: text/html');
203 break;
204 break;
83 office 205 case "URL":
91 office 206 if(preg_match(
207 "/URL=(https?:\/\/[\-_\.\+!\*'\(\),a-zA-Z0-9]+:?[0-9]{0,5}\/.*?)\n/",
208 file_get_contents($userPath), $matches)) {
83 office 209 header('Location: '.$matches[1]);
210 return;
211 }
212 break;
53 office 213 default:
214 ### Open MIME info database and send the content type.
215 $finfo = finfo_open(FILEINFO_MIME_TYPE);
216 if (!$finfo) {
81 office 217 http_response_code(500);
218 die('Internal server error.');
53 office 219 }
220 header('Content-type: '.finfo_file($finfo, $userPath));
221 finfo_close($finfo);
222 break;
223 }
224  
52 office 225 ### Send the file along with the inline content disposition.
53 office 226 header('Content-length: '.(int)get_file_size($userPath));
227 header('Content-Disposition: inline; filename="' . basename($userPath) . '"');
228 header('Content-Transfer-Encoding: binary');
229 header('X-Sendfile: '.$userPath);
52 office 230 break;
231 }