Passed
Push — development ( 6d01aa...59b91b )
by Nils
04:34
created

sanitaze_upload_file()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 8
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 10
rs 10
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 163 and the first side effect is on line 15.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * @package       upload.attachments.php
4
 * @author        Nils Laumaillé <[email protected]>
5
 * @version       2.1.27
6
 * @copyright     2009-2018 Nils Laumaillé
7
 * @license       GNU GPL-3.0
8
 * @link          https://www.teampass.net
9
 *
10
 * This library is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
 */
14
15
require_once('../SecureHandler.php');
16
session_start();
17
if (!isset($_SESSION['CPM']) || $_SESSION['CPM'] != 1 ||
18
    !isset($_SESSION['user_id']) || empty($_SESSION['user_id']) ||
19
    !isset($_SESSION['key']) || empty($_SESSION['key'])
20
) {
21
    die('Hacking attempt...');
22
}
23
24
// Load config
25
if (file_exists('../../includes/config/tp.config.php')) {
26
    require_once '../../includes/config/tp.config.php';
27
} else {
28
    throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
29
}
30
31
/* do checks */
32
require_once '../checks.php';
33
if (!checkUser($_SESSION['user_id'], $_SESSION['key'], "items")) {
34
    $_SESSION['error']['code'] = ERR_NOT_ALLOWED; //not allowed page
35
    handleAttachmentError('Not allowed to ...', 110);
36
    exit();
37
}
38
39
//check for session
40
if (null !== filter_input(INPUT_POST, 'PHPSESSID', FILTER_SANITIZE_STRING)) {
41
    session_id(filter_input(INPUT_POST, 'PHPSESSID', FILTER_SANITIZE_STRING));
42
} elseif (isset($_GET['PHPSESSID'])) {
43
    session_id($_GET['PHPSESSID']);
44
} else {
45
    handleAttachmentError('No Session was found.', 110);
46
}
47
48
// load functions
49
require_once $SETTINGS['cpassman_dir'].'/sources/main.functions.php';
50
51
52
// Get parameters
53
$chunk = isset($_REQUEST["chunk"]) ? (int) $_REQUEST["chunk"] : 0;
54
$chunks = isset($_REQUEST["chunks"]) ? (int) $_REQUEST["chunks"] : 0;
55
$fileName = isset($_REQUEST["name"]) ? $_REQUEST["name"] : '';
56
57
58
// token check
59
if (null === filter_input(INPUT_POST, 'user_token', FILTER_SANITIZE_STRING)) {
60
    handleAttachmentError('No user token found.', 110);
61
    exit();
62
} else {
63
    //Connect to mysql server
64
    require_once '../../includes/config/settings.php';
65
    require_once '../../includes/libraries/Database/Meekrodb/db.class.php';
66
    $pass = defuse_return_decrypted($pass);
67
    DB::$host = $server;
68
    DB::$user = $user;
69
    DB::$password = $pass;
70
    DB::$dbName = $database;
71
    DB::$port = $port;
72
    DB::$encoding = $encoding;
73
    DB::$error_handler = true;
74
    $link = mysqli_connect($server, $user, $pass, $database, $port);
75
    $link->set_charset($encoding);
76
77
    // delete expired tokens
78
    DB::delete(prefix_table("tokens"), "end_timestamp < %i", time());
79
80
    // Prepare POST variables
81
    $post_user_token = filter_input(INPUT_POST, 'user_token', FILTER_SANITIZE_STRING);
82
    $post_type_upload = filter_input(INPUT_POST, 'type_upload', FILTER_SANITIZE_STRING);
83
    $post_itemId = filter_input(INPUT_POST, 'itemId', FILTER_SANITIZE_NUMBER_INT);
84
    $post_files_number = filter_input(INPUT_POST, 'files_number', FILTER_SANITIZE_NUMBER_INT);
85
    $post_timezone = filter_input(INPUT_POST, 'timezone', FILTER_SANITIZE_STRING);
86
87
    if (isset($_SESSION[$post_user_token])
88
        && ($chunk < $chunks - 1)
89
        && $_SESSION[$post_user_token] >= 0
90
    ) {
91
        // increase end_timestamp for token
92
        DB::update(
93
            prefix_table('tokens'),
94
            array(
95
                'end_timestamp' => time() + 10
96
                ),
97
            "user_id = %i AND token = %s",
98
            $_SESSION['user_id'],
99
            $post_user_token
100
        );
101
    } else {
102
        // create a session if several files to upload
103
        if (isset($_SESSION[$post_user_token]) === false
104
            || empty($_SESSION[$post_user_token])
105
            || $_SESSION[$post_user_token] === "0"
106
        ) {
107
            $_SESSION[$post_user_token] = $post_files_number;
108
        } elseif ($_SESSION[$post_user_token] > 0) {
109
            // increase end_timestamp for token
110
            DB::update(
111
                prefix_table('tokens'),
112
                array(
113
                    'end_timestamp' => time() + 30
114
                    ),
115
                "user_id = %i AND token = %s",
116
                $_SESSION['user_id'],
117
                $post_user_token
118
            );
119
            // decrease counter of files to upload
120
            $_SESSION[$post_user_token]--;
121
        } else {
122
            // no more files to upload, kill session
123
            unset($_SESSION[$post_user_token]);
124
            handleAttachmentError('No user token found.', 110);
125
            die();
126
        }
127
128
        // check if token is expired
129
        $data = DB::queryFirstRow(
130
            "SELECT end_timestamp FROM ".prefix_table("tokens")." WHERE user_id = %i AND token = %s",
131
            $_SESSION['user_id'],
132
            $post_user_token
133
        );
134
        // clear user token
135
        if ($_SESSION[$post_user_token] === 0) {
136
            DB::delete(
137
                prefix_table("tokens"),
138
                "user_id = %i AND token = %s",
139
                $_SESSION['user_id'],
140
                $post_user_token
141
            );
142
            unset($_SESSION[$post_user_token]);
143
        }
144
145
        if (time() > $data['end_timestamp']) {
146
            // too old
147
            unset($_SESSION[$post_user_token]);
148
            handleAttachmentError('User token expired.', 110);
149
            die();
150
        }
151
    }
152
153
    // Load Settings
154
    require_once $SETTINGS['cpassman_dir'].'/includes/config/tp.config.php';
155
}
156
157
// HTTP headers for no cache etc
158
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
159
header("Last-Modified: ".gmdate("D, d M Y H:i:s")." GMT");
160
header("Cache-Control: no-store, no-cache, must-revalidate");
161
header("Cache-Control: post-check=0, pre-check=0", false);
162
163
function sanitaze_upload_file($data)
164
{
165
    $imgName   = $data;
166
    $indexOFF  = strrpos($imgName, '.');
167
    $nameFile  = substr($imgName, 0,$indexOFF);
168
    $extension = substr($imgName, $indexOFF);
169
    $clean     = preg_replace("([^\w\s\d\-_~,;\[\]\(\)])", "", 
170
    $nameFile);
171
    $NAMEFILE  = str_replace(' ', '', $clean).$extension;
172
    return $NAMEFILE;
173
}
174
175
$targetDir = $SETTINGS['path_to_upload_folder'];
176
177
$cleanupTargetDir = true; // Remove old files
178
$maxFileAge = 5 * 3600; // Temp file age in seconds
179
$valid_chars_regex = 'A-Za-z0-9_@./#&+-'; //accept only those characters
180
$MAX_FILENAME_LENGTH = 260;
181
$max_file_size_in_bytes = 2147483647; //2Go
182
183
if (null !== $post_timezone) {
184
    date_default_timezone_set($post_timezone);
185
}
186
187
// Check post_max_size
188
$POST_MAX_SIZE = ini_get('post_max_size');
189
$unit = strtoupper(substr($POST_MAX_SIZE, -1));
190
$multiplier = ($unit == 'M' ? 1048576 : ($unit == 'K' ? 1024 : ($unit == 'G' ? 1073741824 : 1)));
191
if ((int) $_SERVER['CONTENT_LENGTH'] > $multiplier * (int) $POST_MAX_SIZE && $POST_MAX_SIZE) {
192
    handleAttachmentError('POST exceeded maximum allowed size.', 111);
193
}
194
195
// Validate the file size (Warning: the largest files supported by this code is 2GB)
196
$file_size = @filesize($_FILES['file']['tmp_name']);
197
if (!$file_size || $file_size > $max_file_size_in_bytes) {
198
    handleAttachmentError('File exceeds the maximum allowed size', 120);
199
}
200
if ($file_size <= 0) {
201
    handleAttachmentError('File size outside allowed lower bound', 112);
202
}
203
204
// Validate the upload
205
if (!isset($_FILES['file'])) {
206
    handleAttachmentError('No upload found in $_FILES for Filedata', 121);
207
} elseif (isset($_FILES['file']['error']) && $_FILES['file']['error'] != 0) {
208
    handleAttachmentError($uploadErrors[$_FILES['Filedata']['error']], 122);
209
} elseif (!isset($_FILES['file']['tmp_name']) || !@is_uploaded_file($_FILES['file']['tmp_name'])) {
210
    handleAttachmentError('Upload failed is_uploaded_file test.', 123);
211
} elseif (!isset($_FILES['file']['name'])) {
212
    handleAttachmentError('File has no name.', 113);
213
}
214
215
// Validate file name (for our purposes we'll just remove invalid characters)
216
$file_name = strtolower(basename($_FILES['file']['name']));
217
if (strlen($file_name) == 0 || strlen($file_name) > $MAX_FILENAME_LENGTH) {
218
    handleAttachmentError('Invalid file name: '.$file_name.'.', 114);
219
}
220
221
// Validate file extension
222
$ext = strtolower(getFileExtension($_REQUEST["name"]));
223
if (!in_array(
224
    $ext,
225
    explode(
226
        ',',
227
        $SETTINGS['upload_docext'].','.$SETTINGS['upload_imagesext'].
228
        ','.$SETTINGS['upload_pkgext'].','.$SETTINGS['upload_otherext']
229
    )
230
)) {
231
    handleAttachmentError('Invalid file extension.', 115);
232
}
233
234
// 5 minutes execution time
235
set_time_limit(5 * 60);
236
237
// Clean the fileName for security reasons
238
$fileName = sanitaze_upload_file(basename($fileName));
239
240
// Make sure the fileName is unique but only if chunking is disabled
241
if ($chunks < 2 && file_exists($targetDir.DIRECTORY_SEPARATOR.$fileName)) {
242
    $ext = strrpos($fileName, '.');
243
    $fileNameA = substr($fileName, 0, $ext);
244
    $fileNameB = substr($fileName, $ext);
245
246
    $count = 1;
247
    while (file_exists($targetDir.DIRECTORY_SEPARATOR.$fileNameA.'_'.$count.$fileNameB)) {
248
        $count++;
249
    }
250
251
    $fileName = $fileNameA.'_'.$count.$fileNameB;
252
}
253
254
$filePath = $targetDir.DIRECTORY_SEPARATOR.$fileName;
255
256
// Create target dir
257
if (!file_exists($targetDir)) {
258
    try {
259
        mkdir($targetDir, 0777, true);
260
    } catch (Exception $e) {
261
        print_r($e);
262
    }
263
}
264
265
// Remove old temp files
266
if ($cleanupTargetDir && is_dir($targetDir) && ($dir = opendir($targetDir))) {
267
    while (($file = readdir($dir)) !== false) {
268
        $tmpfilePath = $targetDir.DIRECTORY_SEPARATOR.$file;
269
270
        // Remove temp file if it is older than the max age and is not the current file
271
        if (preg_match('/\.part$/', $file)
272
            && (filemtime($tmpfilePath) < time() - $maxFileAge)
273
            && ($tmpfilePath != "{$filePath}.part")
274
        ) {
275
            fileDelete($tmpfilePath);
276
        }
277
    }
278
279
    closedir($dir);
280
} else {
281
    die('{"jsonrpc" : "2.0", "error" : {"code": 100, "message": "Failed to open temp directory."}, "id" : "id"}');
282
}
283
284
// Look for the content type header
285
if (isset($_SERVER["HTTP_CONTENT_TYPE"])) {
286
    $contentType = $_SERVER["HTTP_CONTENT_TYPE"];
287
}
288
289
if (isset($_SERVER["CONTENT_TYPE"])) {
290
    $contentType = $_SERVER["CONTENT_TYPE"];
291
}
292
293
// Handle non multipart uploads older WebKit versions didn't support multipart in HTML5
294
if (strpos($contentType, "multipart") !== false) {
295
    if (isset($_FILES['file']['tmp_name']) && is_uploaded_file($_FILES['file']['tmp_name'])) {
296
        // Open temp file
297
        $out = fopen("{$filePath}.part", $chunk == 0 ? "wb" : "ab");
298
299
        if ($out) {
0 ignored issues
show
introduced by
$out is of type resource|false, thus it always evaluated to false.
Loading history...
300
            // Read binary input stream and append it to temp file
301
            $in = fopen($_FILES['file']['tmp_name'], "rb");
302
303
            if ($in) {
304
                while ($buff = fread($in, 4096)) {
305
                    fwrite($out, $buff);
306
                }
307
            } else {
308
                die(
309
                    '{"jsonrpc" : "2.0",
310
                    "error" : {"code": 101, "message": "Failed to open input stream."},
311
                    "id" : "id"}'
312
                );
313
            }
314
            fclose($in);
315
            fclose($out);
316
317
            fileDelete($_FILES['file']['tmp_name']);
318
        } else {
319
            die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
320
        }
321
    } else {
322
        die('{"jsonrpc" : "2.0", "error" : {"code": 103, "message": "Failed to move uploaded file."}, "id" : "id"}');
323
    }
324
} else {
325
    // Open temp file
326
    $out = fopen("{$filePath}.part", $chunk == 0 ? "wb" : "ab");
327
328
    if ($out) {
0 ignored issues
show
introduced by
$out is of type resource|false, thus it always evaluated to false.
Loading history...
329
        // Read binary input stream and append it to temp file
330
        $in = fopen("php://input", "rb");
331
332
        if ($in) {
333
            while ($buff = fread($in, 4096)) {
334
                fwrite($out, $buff);
335
            }
336
        } else {
337
            die('{"jsonrpc" : "2.0", "error" : {"code": 101, "message": "Failed to open input stream."}, "id" : "id"}');
338
        }
339
        fclose($in);
340
        fclose($out);
341
    } else {
342
        die('{"jsonrpc" : "2.0", "error" : {"code": 102, "message": "Failed to open output stream."}, "id" : "id"}');
343
    }
344
}
345
346
// Check if file has been uploaded
347
if (!$chunks || $chunk == $chunks - 1) {
348
    // Strip the temp .part suffix off
349
    rename("{$filePath}.part", $filePath);
350
} else {
351
    // continue uploading other chunks
352
    die();
353
}
354
355
// Get some variables
356
$fileRandomId = md5($fileName.time());
357
rename($filePath, $targetDir.DIRECTORY_SEPARATOR.$fileRandomId);
358
359
// Encrypt the file if requested
360
if (isset($SETTINGS['enable_attachment_encryption']) && $SETTINGS['enable_attachment_encryption'] === '1') {
361
    // Do encryption
362
    prepareFileWithDefuse(
363
        'encrypt',
364
        $targetDir.DIRECTORY_SEPARATOR.$fileRandomId,
365
        $targetDir.DIRECTORY_SEPARATOR.$fileRandomId."_encrypted"
366
    );
367
368
    // Do cleanup of files
369
    unlink($targetDir.DIRECTORY_SEPARATOR.$fileRandomId);
370
    rename(
371
        $targetDir.DIRECTORY_SEPARATOR.$fileRandomId."_encrypted",
372
        $targetDir.DIRECTORY_SEPARATOR.$fileRandomId
373
    );
374
375
    $file_status = "encrypted";
376
} else {
377
    $file_status = "clear";
378
}
379
380
// Case ITEM ATTACHMENTS - Store to database
381
if (null !== $post_type_upload && $post_type_upload === "item_attachments") {
382
    DB::insert(
383
        $pre.'files',
384
        array(
385
            'id_item' => $post_itemId,
386
            'name' => $fileName,
387
            'size' => $_FILES['file']['size'],
388
            'extension' => getFileExtension($fileName),
389
            'type' => $_FILES['file']['type'],
390
            'file' => $fileRandomId,
391
            'status' => $file_status
392
        )
393
    );
394
395
    // Log upload into databse
396
    DB::insert(
397
        $pre.'log_items',
398
        array(
399
            'id_item' => $post_itemId,
400
            'date' => time(),
401
            'id_user' => $_SESSION['user_id'],
402
            'action' => 'at_modification',
403
            'raison' => 'at_add_file : '.addslashes($fileName)
404
        )
405
    );
406
}
407
408
// Return JSON-RPC response
409
die('{"jsonrpc" : "2.0", "result" : null, "id" : "id"}');
410
411
412
/* Handles the error output. */
413
function handleAttachmentError($message, $code)
414
{
415
    echo '{"jsonrpc" : "2.0", "error" : {"code": '.htmlentities($code, ENT_QUOTES).', "message": "'.htmlentities($message, ENT_QUOTES).'"}, "id" : "id"}';
416
}
417