Passed
Push — teampass_3.0 ( fcc9dc...305ec6 )
by Nils
06:57
created

sanitiseString()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 10
rs 10
1
<?php
2
/**
3
 * @author        Nils Laumaillé <[email protected]>
4
 *
5
 * @version       2.1.27
6
 *
7
 * @copyright     2009-2018 Nils Laumaillé
8
 * @license       GNU GPL-3.0
9
 *
10
 * @see          https://www.teampass.net
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
 */
16
use Goodby\CSV\Import\Standard\Lexer;
17
use Goodby\CSV\Import\Standard\Interpreter;
18
use Goodby\CSV\Import\Standard\LexerConfig;
19
20
require_once 'SecureHandler.php';
21
session_name('teampass_session');
22
session_start();
23
if (!isset($_SESSION['CPM']) || $_SESSION['CPM'] === false || !isset($_SESSION['key']) || empty($_SESSION['key'])) {
24
    die('Hacking attempt...');
25
}
26
27
// Load config
28
if (file_exists('../includes/config/tp.config.php')) {
29
    include_once '../includes/config/tp.config.php';
30
} elseif (file_exists('./includes/config/tp.config.php')) {
31
    include_once './includes/config/tp.config.php';
32
} else {
33
    throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
34
}
35
36
// Do checks
37
require_once $SETTINGS['cpassman_dir'].'/includes/config/include.php';
38
require_once $SETTINGS['cpassman_dir'].'/sources/checks.php';
39
if (checkUser($_SESSION['user_id'], $_SESSION['key'], 'items', $SETTINGS) === false) {
40
    // Not allowed page
41
    $_SESSION['error']['code'] = ERR_NOT_ALLOWED;
42
    include $SETTINGS['cpassman_dir'].'/error.php';
43
    exit();
44
}
45
46
/*
47
 * Define Timezone
48
**/
49
if (isset($SETTINGS['timezone']) === true) {
50
    date_default_timezone_set($SETTINGS['timezone']);
51
} else {
52
    date_default_timezone_set('UTC');
53
}
54
55
require_once $SETTINGS['cpassman_dir'].'/includes/language/'.$_SESSION['user_language'].'.php';
56
require_once $SETTINGS['cpassman_dir'].'/includes/config/settings.php';
57
header('Content-type: text/html; charset=utf-8');
58
header('Cache-Control: no-cache, must-revalidate');
59
require_once 'main.functions.php';
60
61
// No time limit
62
set_time_limit(0);
63
64
// Set some constants for program readability
65
define('KP_PATH', 0);
66
define('KP_GROUP', 1);
67
define('KP_TITLE', 2);
68
define('KP_PASSWORD', 3);
69
define('KP_USERNAME', 4);
70
define('KP_URL', 5);
71
define('KP_UUID', 6);
72
define('KP_NOTES', 7);
73
74
// Connect to mysql server
75
require_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
76
if (defined('DB_PASSWD_CLEAR') === false) {
77
    define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
78
}
79
DB::$host = DB_HOST;
80
DB::$user = DB_USER;
81
DB::$password = DB_PASSWD_CLEAR;
82
DB::$dbName = DB_NAME;
83
DB::$port = DB_PORT;
84
DB::$encoding = DB_ENCODING;
85
//$link = mysqli_connect(DB_HOST, DB_USER, DB_PASSWD_CLEAR, DB_NAME, DB_PORT);
86
//$link->set_charset(DB_ENCODING);
87
88
// Class loader
89
require_once $SETTINGS['cpassman_dir'].'/sources/SplClassLoader.php';
90
91
//Load Tree
92
$tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
93
$tree->register();
94
$tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
95
96
//Load AES
97
$aes = new SplClassLoader('Encryption\Crypt', '../includes/libraries');
98
$aes->register();
99
100
//User's language loading
101
require_once $SETTINGS['cpassman_dir'].'/includes/language/'.$_SESSION['user_language'].'.php';
102
103
// POST Varaibles
104
$post_key = filter_input(INPUT_POST, 'key', FILTER_SANITIZE_STRING);
105
$post_data = filter_input(INPUT_POST, 'data', FILTER_SANITIZE_STRING, FILTER_FLAG_NO_ENCODE_QUOTES);
106
107
// Build query
108
switch (filter_input(INPUT_POST, 'type', FILTER_SANITIZE_STRING)) {
109
    //Check if import CSV file format is what expected
110
    case 'import_file_format_csv':
111
        // Check KEY and rights
112
        if ($post_key !== $_SESSION['key']) {
113
            echo prepareExchangedData(
114
                array(
115
                    'error' => true,
116
                    'message' => langHdl('key_is_not_correct'),
117
                ),
118
                'encode'
119
            );
120
            break;
121
        }
122
123
        //load full tree
124
        $tree->rebuild();
125
        $tree = $tree->getDescendants();
126
127
        // Init post variable
128
        $post_operation_id = filter_input(INPUT_POST, 'file', FILTER_SANITIZE_NUMBER_INT);
129
130
        // Get filename from database
131
        $data = DB::queryFirstRow(
132
            'SELECT valeur
133
            FROM '.prefixTable('misc').'
134
            WHERE increment_id = %i AND type = "temp_file"',
135
            $post_operation_id
136
        );
137
138
        // Delete operation id
139
        DB::delete(
140
            prefixTable('misc'),
141
            "increment_id = %i AND type = 'temp_file'",
142
            $post_operation_id
143
        );
144
145
        // do some initializations
146
        $file = $SETTINGS['path_to_files_folder'].'/'.$data['valeur'];
147
        $size = 4096;
148
        $separator = ',';
149
        $enclosure = '"';
150
        $fields_expected = array('Label', 'Login', 'Password', 'URL', 'Comments'); //requiered fields from CSV
151
        $importation_possible = true;
152
        $itemsArray = array();
153
        $line_number = 0;
154
        $account = $text = '';
155
        $continue_on_next_line = false;
156
157
        // Open file
158
        if ($fp = fopen($file, 'r')) {
159
            // data from CSV
160
            $valuesToImport = array();
161
            // load libraries
162
            include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Goodby/CSV/Import/Standard/Lexer.php';
163
            include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Goodby/CSV/Import/Standard/Interpreter.php';
164
            include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Goodby/CSV/Import/Standard/LexerConfig.php';
165
166
            // Lexer configuration
167
            $config = new LexerConfig();
168
            $lexer = new Lexer($config);
169
            $config->setIgnoreHeaderLine('true');
170
            // extract data from CSV file
171
            $interpreter = new Interpreter();
172
            $interpreter->addObserver(function (array $row) use (&$valuesToImport) {
173
                $valuesToImport[] = array(
174
                    'Label' => $row[0],
175
                    'Login' => $row[1],
176
                    'Password' => $row[2],
177
                    'url' => $row[3],
178
                    'Comments' => $row[4],
179
                );
180
            });
181
            $lexer->parse($file, $interpreter);
182
183
            // extract one line
184
            foreach ($valuesToImport as $key => $row) {
185
                //increment number of lines found
186
                ++$line_number;
187
188
                //Check number of fields. MUST be 5. if not stop importation
189
                if (count($row) != 5) {
190
                    $importation_possible = false;
191
                    //Stop if file has not expected structure
192
                    if ($importation_possible === false) {
193
                        echo '[{"error":"bad_structure"}]';
194
                        break;
195
                    }
196
                }
197
198
                //If any comment is on several lines, then replace 'lf' character
199
                $row['Comments'] = str_replace(array("\r\n", "\n", "\r"), '<br>', $row['Comments']);
200
201
                // Check if current line contains a "<br>" character in order to identify an ITEM on several CSV lines
202
                if (substr_count('<br>', $row['Comments']) > 0 || substr_count('<br>', $row['Label']) > 0) {
203
                    $continue_on_next_line = true;
204
                    $comment .= addslashes($row['Label']);
205
                } else {
206
                    // Store in variable values from previous line
207
                    if (empty($account) === false) {
208
                        if ($continue_on_next_line === false) {
209
                            // Prepare listing that will be shown to user
210
                            array_push(
211
                                $itemsArray,
212
                                array(
213
                                    'label' => $account,
214
                                    'login' => $login,
215
                                    'pwd' => $pwd,
216
                                    'url' => $url,
217
                                    'comment' => $comment,
218
                                    'label' => $account,
219
                                    'label' => $account,
220
                                )
221
                            );
222
223
                            // Initialize this variable in order to restart from scratch
224
                            $account = '';
225
                        }
226
                    }
227
                }
228
229
                // Get values of current line
230
                if ($account == '' && $continue_on_next_line === false) {
231
                    $account = trim(htmlspecialchars($row['Label'], ENT_QUOTES, 'UTF-8'));
232
                    $login = trim(htmlspecialchars($row['Login'], ENT_QUOTES, 'UTF-8'));
233
                    $pwd = trim(str_replace('"', '&quot;', $row['Password']));
234
                    $url = trim($row['url']);
235
                    $to_find = array('"', "'");
236
                    $to_ins = array('&quot', '&#39;');
237
                    $comment = htmlentities(
238
                        addslashes(str_replace($to_find, $to_ins, $row['Comments'])),
239
                        ENT_QUOTES,
240
                        'UTF-8'
241
                    );
242
243
                    $continue_on_next_line = false;
244
                }
245
            }
246
            // close file
247
            fclose($fp);
248
        } else {
249
            echo prepareExchangedData(
250
                array(
251
                    'error' => true,
252
                    'message' => langHdl('cannot_open_file'),
253
                ),
254
                'encode'
255
            );
256
257
            //delete file
258
            unlink($file);
259
            break;
260
        }
261
262
        if ($line_number > 0) {
263
            array_push(
264
                $itemsArray,
265
                array(
266
                    'label' => $account,
267
                    'login' => $login,
268
                    'pwd' => $pwd,
269
                    'url' => $url,
270
                    'comment' => $comment,
271
                    'label' => $account,
272
                    'label' => $account,
273
                )
274
            );
275
276
            // Show results to user.
277
            echo prepareExchangedData(
278
                array(
279
                    'error' => false,
280
                    'message' => '',
281
                    'output' => $itemsArray,
282
                    'number' => $line_number++,
283
                ),
284
                'encode'
285
            );
286
        }
287
288
        //delete file
289
        unlink($file);
290
291
        break;
292
293
    //Insert into DB the items the user has selected
294
    case 'import_items':
295
        // Check KEY and rights
296
        if ($post_key !== $_SESSION['key']) {
297
            echo prepareExchangedData(
298
                array(
299
                    'error' => true,
300
                    'message' => langHdl('key_is_not_correct'),
301
                ),
302
                'encode'
303
            );
304
            break;
305
        }
306
307
        // Init
308
        $list = [];
309
310
        // Decrypt and retreive data in JSON format
311
        $post_items = prepareExchangedData($post_data, 'decode');
312
313
        // Init post variable
314
        $post_folder = filter_input(INPUT_POST, 'folder', FILTER_SANITIZE_NUMBER_INT);
315
316
        // Get title for this folder
317
        $data_fld = DB::queryFirstRow(
318
            'SELECT title
319
            FROM '.prefixTable('nested_tree').'
320
            WHERE id = %i',
321
            $post_folder
322
        );
323
324
        //Get some info about personal folder
325
        if (in_array($post_folder, $_SESSION['personal_folders']) === true) {
326
            $personalFolder = 1;
327
        } else {
328
            $personalFolder = 0;
329
        }
330
331
        //Prepare variables
332
        //$listItems = json_decode($post_items, true);
333
334
        // Clean each array entry
335
        array_walk_recursive($post_items, 'cleanOutput');
0 ignored issues
show
Bug introduced by
It seems like $post_items can also be of type string; however, parameter $input of array_walk_recursive() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

335
        array_walk_recursive(/** @scrutinizer ignore-type */ $post_items, 'cleanOutput');
Loading history...
336
337
        // Loop on array
338
        foreach ($post_items as $item) {
339
            //For each item, insert into DB
340
341
            //Encryption key
342
            if ($personalFolder == 1) {
343
                $encrypt = cryption(
344
                    $item['pwd'],
345
                    $_SESSION['user_settings']['session_psk'],
346
                    'encrypt',
347
                    $SETTINGS
348
                );
349
            } else {
350
                $encrypt = cryption(
351
                    $item['pwd'],
352
                    '',
353
                    'encrypt',
354
                    $SETTINGS
355
                );
356
            }
357
358
            // Insert new item in table ITEMS
359
            DB::insert(
360
                prefixTable('items'),
361
                array(
362
                    'label' => substr($item['label'], 0, 500),
363
                    'description' => empty($item['description']) === true ? '' : $item['description'],
364
                    'pw' => $encrypt['string'],
365
                    'pw_iv' => '',
366
                    'url' => empty($item['url']) === true ? '' : substr($item['url'], 0, 500),
367
                    'id_tree' => filter_input(INPUT_POST, 'folder', FILTER_SANITIZE_NUMBER_INT),
368
                    'login' => empty($item['login']) === true ? '' : substr($item['login'], 0, 200),
369
                    'anyone_can_modify' => filter_input(INPUT_POST, 'import_csv_anyone_can_modify', FILTER_SANITIZE_STRING) === 'true' ? 1 : 0,
370
                )
371
            );
372
            $newId = DB::insertId();
373
374
            //if asked, anyone in role can modify
375
            if (null !== filter_input(INPUT_POST, 'import_csv_anyone_can_modify_in_role', FILTER_SANITIZE_STRING)
376
                && filter_input(INPUT_POST, 'import_csv_anyone_can_modify_in_role', FILTER_SANITIZE_STRING) === 'true'
377
            ) {
378
                foreach ($_SESSION['arr_roles'] as $role) {
379
                    DB::insert(
380
                        prefixTable('restriction_to_roles'),
381
                        array(
382
                            'role_id' => $role['id'],
383
                            'item_id' => $newId,
384
                        )
385
                    );
386
                }
387
            }
388
389
            // Insert new item in table LOGS_ITEMS
390
            DB::insert(
391
                prefixTable('log_items'),
392
                array(
393
                    'id_item' => $newId,
394
                    'date' => time(),
395
                    'id_user' => $_SESSION['user_id'],
396
                    'action' => 'at_creation',
397
                )
398
            );
399
400
            array_push($list, $item['row']);
401
402
            //Add entry to cache table
403
            DB::insert(
404
                prefixTable('cache'),
405
                array(
406
                    'id' => $newId,
407
                    'label' => substr($item['label'], 0, 500),
408
                    'description' => empty($item['description']) ? '' : $item['description'],
409
                    'id_tree' => filter_input(INPUT_POST, 'folder', FILTER_SANITIZE_NUMBER_INT),
410
                    'url' => '0',
411
                    'perso' => $personalFolder == 0 ? 0 : 1,
412
                    'login' => empty($item['login']) ? '' : substr($item['login'], 0, 500),
413
                    'folder' => $data_fld['title'],
414
                    'author' => $_SESSION['user_id'],
415
                    'timestamp' => time(),
416
                    'tags' => '',
417
                    'restricted_to' => '0',
418
                    'renewal_period' => '0',
419
                    'timestamp' => time(),
420
                )
421
            );
422
        }
423
424
        echo prepareExchangedData(
425
            array(
426
                'error' => false,
427
                'message' => '',
428
                'items' => $list,
429
            ),
430
            'encode'
431
        );
432
        break;
433
434
    //Check if import KEEPASS file format is what expected
435
    case 'import_file_format_keepass':
436
        // Check KEY and rights
437
        if ($post_key !== $_SESSION['key']) {
438
            echo prepareExchangedData(
439
                array(
440
                    'error' => true,
441
                    'message' => langHdl('key_is_not_correct'),
442
                ),
443
                'encode'
444
            );
445
            break;
446
        }
447
448
        // Decrypt and retreive data in JSON format
449
        $receivedParameters = prepareExchangedData($post_data, 'decode');
450
451
        $post_folder_id = filter_var($receivedParameters['folder-id'], FILTER_SANITIZE_NUMBER_INT);
452
        $post_operation_id = filter_var($receivedParameters['file'], FILTER_SANITIZE_STRING);
453
        $post_edit_all = filter_var($receivedParameters['edit-all'], FILTER_SANITIZE_NUMBER_INT);
454
        $post_edit_role = filter_var($receivedParameters['edit-role'], FILTER_SANITIZE_NUMBER_INT);
455
456
        //Initialization
457
        $root = $meta = $group = $entry = $key = $title = $notes = $pwd = $username = $url = $notKeepassFile = $newItem = $history = $generatorFound = false;
458
        $name = $levelInProgress = $previousLevel = $fullPath = $historyLevel = $path = $display = $keepassVersion = '';
459
        $numGroups = $numItems = 0;
460
        $arrFolders = array();
461
        $temparray = array();
462
        $levelMin = 2;
463
        $foldersSeparator = '@&##&@';
464
        $itemsSeparator = '<=|#|=>';
465
        $lineEndSeparator = '@*1|#9*|@';
466
467
        //prepare CACHE files
468
        $cacheFileName = $SETTINGS['path_to_files_folder'].'/cpassman_cache_'.md5(time().mt_rand());
469
        $cacheFileNameFolder = $cacheFileName.'_folders';
470
        $cacheFile = fopen($cacheFileName, 'w');
471
        $cacheFileF = fopen($cacheFileNameFolder, 'w');
472
        $logFileName = '/keepassImport_'.date('YmdHis').'.log';
473
        $cacheLogFile = fopen($SETTINGS['path_to_files_folder'].$logFileName, 'w');
474
475
        // Get filename from database
476
        $data = DB::queryFirstRow(
477
            'SELECT valeur
478
            FROM '.prefixTable('misc').'
479
            WHERE increment_id = %i AND type = "temp_file"',
480
            $post_operation_id
481
        );
482
483
        // Delete operation id
484
        DB::delete(
485
            prefixTable('misc'),
486
            'increment_id = %i AND type = "temp_file"',
487
            $post_operation_id
488
        );
489
490
        // do some initializations
491
        $file = $data['valeur'];
492
493
        //read xml file
494
        if (file_exists($SETTINGS['path_to_files_folder'].'/'.$file)) {
495
            $xml = simplexml_load_file(
496
                $SETTINGS['path_to_files_folder'].'/'.$file
497
            );
498
        }
499
500
        /**
501
         ** Recursive function that will permit to read each level of XML nodes.
502
         */
503
        function recursiveKeepassXML($xmlRoot, $xmlLevel = 0)
504
        {
505
            global $meta, $root, $group, $name, $entry, $levelMin, $title, $notes, $pwd, $username, $url,
506
                $newItem, $history, $levelInProgress, $historyLevel, $temparray,
507
                $path, $previousLevel, $generatorFound, $cacheFile, $cacheFileF, $numGroups,
508
                $numItems, $foldersSeparator, $itemsSeparator, $keepassVersion, $arrFolders;
509
510
            $groupsArray = array();
511
            if (count($temparray) === 0) {
512
                $temparray = initTempArray();
513
            }
514
515
            // For each node, get the name and SimpleXML balise
516
            foreach ($xmlRoot as $nom => $elem) {
517
                /*
518
                * check if file is generated by keepass 1
519
                * key "pwentry" is only used in KP1.xx XML files
520
                */
521
                if ($nom == 'pwentry') {
522
                    if (empty($keepassVersion)) {
523
                        $keepassVersion = 1;
524
                        $generatorFound = true;
525
                        $entry = true;
526
                    } else {
527
                        $entry = true;
528
                    }
529
530
                    //get children
531
                    $xmlChildren = $elem->children();
532
533
                    //recursive call
534
                    recursiveKeepassXML($xmlChildren, $xmlLevel + 1);
535
                }
536
                //IMPORTING KEEPASS 1 XML FILE
537
                if ($keepassVersion == 1) {
538
                    if ($entry === true && $nom == 'expiretime') {
539
                        //save previous keepass entry
540
                        $tree = preg_replace('/\\\\/', $foldersSeparator, $temparray['tree']);
541
                        fputs(
542
                            $cacheFile,
543
                            $tree.$itemsSeparator.$temparray[KP_GROUP].$itemsSeparator.$temparray[KP_TITLE].
544
                            $itemsSeparator.$temparray[KP_PASSWORD].$itemsSeparator.$temparray[KP_USERNAME].
545
                            $itemsSeparator.$temparray[KP_URL].$itemsSeparator.$temparray[KP_UUID].$itemsSeparator.$temparray[KP_NOTES]."\n"
546
                        );
547
548
                        if (!in_array($temparray['tree'], $arrFolders)) {
549
                            fwrite($cacheFileF, $tree."\n");
550
                            array_push($arrFolders, $temparray['tree']);
551
                        }
552
553
                        $temparray = initTempArray();
554
                        ++$newItem;
555
                    }
556
557
                    if ($entry === true && $nom == 'group') {
558
                        $temparray[KP_GROUP] = addslashes(preg_replace('#[\r\n]#', '', $elem));
559
                        foreach ($elem->attributes() as $attributeskey0 => $attributesvalue1) {
560
                            if ($attributeskey0 == 'tree') {
561
                                $path = explode('\\', $attributesvalue1);
562
                                if (count($path) > 1) {
563
                                    unset($path[0]);
564
                                    $temparray['tree'] = implode('\\', $path).'\\'.$temparray[KP_GROUP];
565
                                } else {
566
                                    $temparray['tree'] = $temparray[KP_GROUP];
567
                                }
568
                            }
569
                        }
570
                        ++$numGroups;
571
                    } elseif ($entry === true && $nom == 'title') {
572
                        $temparray[KP_TITLE] = sanitiseString($elem, '');
573
                    } elseif ($entry === true && $nom == 'username') {
574
                        $temparray[KP_USERNAME] = sanitiseString($elem, '');
575
                    } elseif ($entry === true && $nom == 'url') {
576
                        $temparray[KP_URL] = sanitiseString($elem, '');
577
                    } elseif ($entry === true && $nom == 'uuid') {
578
                        $temparray[KP_UUID] = addslashes(preg_replace('#[\r\n]#', '', $elem));
579
                    } elseif ($entry === true && $nom == 'password') {
580
                        $temparray[KP_PASSWORD] = sanitiseString($elem, '');
581
                    } elseif ($entry === true && $nom == 'notes') {
582
                        $temparray[KP_NOTES] = sanitiseString($elem, '');
583
                    }
584
                }
585
586
                /*
587
                   * check if file is generated by keepass 2
588
                */
589
                if (trim($elem) == '' && $keepassVersion != 1) {
590
                    //check if file is generated by keepass 2
591
                    if ($nom == 'Meta') {
592
                        $meta = true;
593
                    }
594
                    if ($nom == 'Root') {
595
                        $root = true;
596
                    }
597
598
                    if ($nom == 'Group') {
599
                        $group = true;
600
                        $entry = false;
601
                        $name = '';
602
603
                        // recap previous info
604
                        if (!empty($temparray[KP_TITLE])) {
605
                            //store data
606
                            fputs(
607
                                $cacheFile,
608
                                $temparray[KP_PATH].$itemsSeparator.$temparray[KP_GROUP].$itemsSeparator
609
                                .$temparray[KP_TITLE].$itemsSeparator.$temparray[KP_PASSWORD].$itemsSeparator
610
                                .$temparray[KP_USERNAME].$itemsSeparator.$temparray[KP_URL].$itemsSeparator.
611
                                $temparray[KP_UUID].$itemsSeparator.$temparray[KP_NOTES]."\n"
612
                            );
613
614
                            //Clean temp array
615
                            $temparray = initTempArray();
616
617
                            //increment number
618
                            ++$numItems;
619
                        }
620
                        $historyLevel = 0;
621
                    }
622
623
                    //History node needs to be managed in order to not polluate final list
624
                    if ($nom == 'History') {
625
                        $history = true;
626
                        $entry = false;
627
                        $historyLevel = $xmlLevel;
628
                    }
629
630
                    if ($nom == 'Entry' && ($xmlLevel < $historyLevel || empty($historyLevel))) {
631
                        $entry = true;
632
                        $group = false;
633
                        $history = false;
634
635
                        // recap previous info
636
                        if (!empty($temparray[KP_TITLE])) {
637
                            //store data
638
                            fputs(
639
                                $cacheFile,
640
                                $temparray[KP_PATH].$itemsSeparator.$temparray[KP_GROUP].$itemsSeparator
641
                                .$temparray[KP_TITLE].$itemsSeparator.$temparray[KP_PASSWORD].$itemsSeparator
642
                                .$temparray[KP_USERNAME].$itemsSeparator.$temparray[KP_URL].$itemsSeparator
643
                                .$temparray[KP_UUID].$itemsSeparator.$temparray[KP_NOTES]."\n"
644
                            );
645
646
                            //Clean temp array
647
                            $temparray = initTempArray();
648
649
                            //increment number
650
                            ++$numItems;
651
                        }
652
                        $historyLevel = 0;
653
                    }
654
655
                    //get children
656
                    $xmlChildren = $elem->children();
657
658
                    //recursive call
659
                    if ($history !== true) {
660
                        recursiveKeepassXML($xmlChildren, $xmlLevel + 1);
661
                    }
662
663
                    // Force History to false
664
                    $history = false;
665
666
                //IMPORTING KEEPASS 2 XML FILE
667
                } elseif ($keepassVersion != 1) {
668
                    // exit if XML file not generated by KeePass
669
                    if ($meta === true && $nom == 'Generator' && $elem == 'KeePass') {
670
                        $generatorFound = true;
671
                        $keepassVersion = 2;
672
                        break;
673
                    } elseif ($root === true && $xmlLevel > $levelMin) {
674
                        //echo $elem.' - '.$nom.' - ';
675
                        //Check each node name and get data from some of them
676
                        if ($entry === true && $nom == 'Key' && $elem == 'Title') {
677
                            $title = true;
678
                            $notes = $pwd = $url = $username = false;
679
                        } elseif ($entry === true && $nom == 'Key' && $elem == 'Notes') {
680
                            $notes = true;
681
                            $title = $pwd = $url = $username = false;
682
                        } elseif ($entry === true && $nom == 'UUID') {
683
                            $temparray[KP_UUID] = $elem;
684
                        } elseif ($entry === true && $nom == 'Key' && $elem == 'Password') {
685
                            $pwd = true;
686
                            $notes = $title = $url = $username = false;
687
                        } elseif ($entry === true && $nom == 'Key' && $elem == 'URL') {
688
                            $url = true;
689
                            $notes = $pwd = $title = $username = false;
690
                        } elseif ($entry === true && $nom == 'Key' && $elem == 'UserName') {
691
                            $username = true;
692
                            $notes = $pwd = $url = $title = false;
693
                        } elseif ($group === true && $nom == 'Name') {
694
                            $temparray[KP_GROUP] = addslashes(preg_replace('#[\r\n]#', '', $elem));
695
                            $temparray['level'] = $xmlLevel;
696
                            //build current path
697
                            if ($xmlLevel > $levelInProgress) {
698
                                if (!empty($temparray[KP_PATH])) {
699
                                    $temparray[KP_PATH] .= $foldersSeparator.$temparray[KP_GROUP];
700
                                } else {
701
                                    $temparray[KP_PATH] = $temparray[KP_GROUP];
702
                                }
703
                            } elseif ($xmlLevel == $levelInProgress) {
704
                                if ($levelInProgress == 3) {
705
                                    $temparray[KP_PATH] = $temparray[KP_GROUP];
706
                                } else {
707
                                    $temparray[KP_PATH] = substr($temparray[KP_PATH], 0, strrpos($temparray[KP_PATH], $foldersSeparator) + strlen($foldersSeparator)).$temparray[KP_GROUP];
708
                                }
709
                            } else {
710
                                $diff = abs($xmlLevel - $levelInProgress) + 1;
711
                                $tmp = explode($foldersSeparator, $temparray[KP_PATH]);
712
                                $temparray[KP_PATH] = '';
713
                                for ($x = 0; $x < (count($tmp) - $diff); ++$x) {
714
                                    if (!empty($temparray[KP_PATH])) {
715
                                        $temparray[KP_PATH] = $temparray[KP_PATH].$foldersSeparator.$tmp[$x];
716
                                    } else {
717
                                        $temparray[KP_PATH] = $tmp[$x];
718
                                    }
719
                                }
720
                                if (!empty($temparray[KP_PATH])) {
721
                                    $temparray[KP_PATH] .= $foldersSeparator.$temparray[KP_GROUP];
722
                                } else {
723
                                    $temparray[KP_PATH] = $temparray[KP_GROUP];
724
                                }
725
                            }
726
727
                            //store folders
728
                            if (!in_array($temparray[KP_PATH], $groupsArray)) {
729
                                fwrite($cacheFileF, $temparray[KP_PATH]."\n");
730
                                array_push($groupsArray, $temparray[KP_PATH]);
731
                                //increment number
732
                                ++$numGroups;
733
                            }
734
735
                            //Store actual level
736
                            $levelInProgress = $xmlLevel;
737
                            $previousLevel = $temparray[KP_GROUP];
738
                        } elseif ($title === true && $nom == 'Value') {
739
                            $title = false;
740
                            $temparray[KP_TITLE] = sanitiseString($elem, '');
741
                        } elseif ($notes === true && $nom == 'Value') {
742
                            $notes = false;
743
                            $temparray[KP_NOTES] = sanitiseString($elem, '');
744
                        } elseif ($pwd === true && $nom == 'Value') {
745
                            $pwd = false;
746
                            $temparray[KP_PASSWORD] = sanitiseString($elem, '');
747
                        } elseif ($url === true && $nom == 'Value') {
748
                            $url = false;
749
                            $temparray[KP_URL] = sanitiseString($elem, '');
750
                        } elseif ($username === true && $nom == 'Value') {
751
                            $username = false;
752
                            $temparray[KP_USERNAME] = sanitiseString($elem, '');
753
                        }
754
                    }
755
                }
756
            }
757
        }
758
759
        function initTempArray()
760
        {
761
            $temparray = array();
762
            $temparray[KP_PATH] = '';
763
            $temparray[KP_GROUP] = '';
764
            $temparray[KP_TITLE] = '';
765
            $temparray[KP_PASSWORD] = '';
766
            $temparray[KP_USERNAME] = '';
767
            $temparray[KP_URL] = '';
768
            $temparray[KP_UUID] = '';
769
            $temparray[KP_NOTES] = '';
770
771
            return $temparray;
772
        }
773
774
        fputs($cacheLogFile, date('H:i:s ').'Writing XML File '.filter_input(INPUT_POST, 'file', FILTER_SANITIZE_STRING)."\n");
0 ignored issues
show
Bug introduced by
It seems like $cacheLogFile can also be of type false; however, parameter $handle of fputs() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

774
        fputs(/** @scrutinizer ignore-type */ $cacheLogFile, date('H:i:s ').'Writing XML File '.filter_input(INPUT_POST, 'file', FILTER_SANITIZE_STRING)."\n");
Loading history...
775
776
        // Go through each node of XML file
777
        recursiveKeepassXML($xml);
778
779
        //Stop if not a keepass file
780
        if ($generatorFound === false) {
1 ignored issue
show
introduced by
The condition $generatorFound === false is always true.
Loading history...
781
            //Close file & delete it
782
            fclose($cacheFileF);
0 ignored issues
show
Bug introduced by
It seems like $cacheFileF can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

782
            fclose(/** @scrutinizer ignore-type */ $cacheFileF);
Loading history...
783
            fclose($cacheFile);
784
            unlink($cacheFileName);
785
            unlink($cacheFileNameFolder);
786
            unlink($SETTINGS['path_to_files_folder'].'/'.filter_input(INPUT_POST, 'file', FILTER_SANITIZE_STRING));
787
788
            fputs($cacheLogFile, date('H:i').langHdl('import_error_no_read_possible_kp')."\n");
789
790
            echo prepareExchangedData(
791
                array(
792
                    'error' => true,
793
                    'message' => langHdl('import_error_no_read_possible_kp'),
794
                ),
795
                'encode'
796
            );
797
            break;
798
        }
799
800
        //save last item
801
        if (!empty($temparray[KP_TITLE])) {
802
            //store data
803
            fputs(
804
                $cacheFile,
805
                $temparray[KP_PATH].$itemsSeparator.$temparray[KP_GROUP].$itemsSeparator.
806
                $temparray[KP_TITLE].$itemsSeparator.$temparray[KP_PASSWORD].$itemsSeparator
807
                .$temparray[KP_USERNAME].$itemsSeparator.$temparray[KP_URL].$itemsSeparator
808
                .$temparray[KP_UUID].$itemsSeparator.$temparray[KP_NOTES]."\n"
809
            );
810
811
            //increment number
812
            ++$numItems;
813
        }
814
815
        //#################
816
        //# STARTING IMPORTING IF NO ERRORS OR NOT EMPTY
817
        //#################
818
        if ($numItems > 0 || $numGroups > 0) {
819
            // Write in file
820
            fputs($cacheLogFile, date('H:i:s ').langHdl('nb_folders').' '.$numGroups."\n");
821
            fputs($cacheLogFile, date('H:i:s ').langHdl('nb_items').' '.$numItems."\n");
822
823
            $import_perso = false;
824
            $itemsArray = array();
825
            $text = '<span class="fas fa-folder-open"></span>&nbsp;'.langHdl('nb_folders').': '.
826
                $numGroups.'<br><span class="fas fa-tag"></span>>&nbsp;'.langHdl('nb_items').': '.
827
                $numItems.'<br><br>';
828
829
            // If destination is not ROOT then get the complexity level
830
            if (in_array($post_folder_id, $_SESSION['personal_folders']) === true) {
831
                $levelPwComplexity = 50;
832
                $startPathLevel = 1;
833
                $import_perso = true;
834
            } elseif ($post_folder_id > 0) {
835
                $data = DB::queryFirstRow(
836
                    'SELECT m.valeur as value, t.nlevel as nlevel
837
                    FROM '.prefixTable('misc').' as m
838
                    INNER JOIN '.prefixTable('nested_tree').' as t ON (m.intitule = t.id)
839
                    WHERE m.type = %s AND m.intitule = %s',
840
                    'complex',
841
                    $post_folder_id
842
                );
843
                $levelPwComplexity = $data['value'];
844
                $startPathLevel = $data['nlevel'];
845
            } else {
846
                $levelPwComplexity = 50;
847
                $startPathLevel = 0;
848
            }
849
850
            //Get all folders from file
851
            fclose($cacheFileF);
852
            $cacheFileF = fopen($cacheFileNameFolder, 'r');
853
854
            //Create folders
855
            $i = 1;
856
            $level = 0;
857
            $foldersArray = array();
858
            $nbFoldersImported = 0;
859
860
            fputs($cacheLogFile, date('H:i:s ')."Creating Folders\n");
861
            $results = "Folders\n\n";
862
863
            while (!feof($cacheFileF)) {
864
                $folder = fgets($cacheFileF, 4096);
865
                if (!empty($folder)) {
866
                    $folder = str_replace(array("\r\n", "\n", "\r"), '', $folder);
867
                    //get number of levels in path
868
                    $path = explode($foldersSeparator, $folder);
869
                    $folderLevel = count($path);
870
871
                    //get folder name
872
                    if (strrpos($folder, $foldersSeparator) > 0) {
873
                        $fold = substr($folder, strrpos($folder, $foldersSeparator) + strlen($foldersSeparator));
874
                        $parent = implode($foldersSeparator, array_slice($path, 0, -1));
875
                        $parent_id = $foldersArray[$parent]['id'];
876
                    } else {
877
                        $fold = $folder;
878
                        $parent_id = $post_folder_id; //permits to select the folder destination
879
                    }
880
881
                    $fold = stripslashes($fold);
882
                    //create folder - if not exists at the same level
883
                    DB::query(
884
                        'SELECT * FROM '.prefixTable('nested_tree').'
885
                        WHERE nlevel = %i AND title = %s AND parent_id = %i LIMIT 1',
886
                        intval($folderLevel + $startPathLevel),
887
                        $fold,
888
                        $parent_id
889
                    );
890
                    $results .= str_replace($foldersSeparator, '\\', $folder);
891
                    $counter = DB::count();
892
                    if ($counter == 0) {
893
                        $results .= " - Inserting\n";
894
                        //do query
895
                        DB::insert(
896
                            prefixTable('nested_tree'),
897
                            array(
898
                                'parent_id' => $parent_id,
899
                                'title' => stripslashes($fold),
900
                                'nlevel' => $folderLevel,
901
                            )
902
                        );
903
                        $id = DB::insertId();
904
                        //Add complexity level => level is set to "medium" by default.
905
                        DB::insert(
906
                            prefixTable('misc'),
907
                            array(
908
                                'type' => 'complex',
909
                                'intitule' => $id,
910
                                'valeur' => $levelPwComplexity,
911
                            )
912
                        );
913
914
                        //For each role to which the user depends on, add the folder just created.
915
                        foreach ($_SESSION['arr_roles'] as $role) {
916
                            DB::insert(
917
                                prefixTable('roles_values'),
918
                                array(
919
                                    'role_id' => $role['id'],
920
                                    'folder_id' => $id,
921
                                    'type' => 'W',
922
                                )
923
                            );
924
                        }
925
926
                        //Add this new folder to the list of visible folders for the user.
927
                        array_push($_SESSION['groupes_visibles'], $id);
928
929
                        //increment number of imported folders
930
                        ++$nbFoldersImported;
931
                    } else {
932
                        $results .= " - Skipped\n";
933
                        //get folder actual ID
934
                        $data = DB::queryFirstRow(
935
                            'SELECT id FROM '.prefixTable('nested_tree').'
936
                            WHERE nlevel = %i AND title = %s AND parent_id = %i',
937
                            intval($folderLevel + $startPathLevel),
938
                            $fold,
939
                            $parent_id
940
                        );
941
                        $id = $data['id'];
942
                    }
943
944
                    //store in array
945
                    $foldersArray[$folder] = array(
946
                        'folder' => $fold,
947
                        'nlevel' => $folderLevel,
948
                        'id' => $id,
949
                    );
950
951
                    ++$_SESSION['nb_folders'];
952
                    ++$i;
953
                }
954
            }
955
956
            $results .= "\n\nItems\n\n";
957
            //if no new folders them inform
958
            if ($nbFoldersImported > 0) {
959
                fputs($cacheLogFile, date('H:i:s ')."Setting User Rights\n");
960
                //Refresh the rights of actual user
961
                identifyUserRights(
962
                    array_push($_SESSION['groupes_visibles'], $id),
963
                    $_SESSION['no_access_folders'],
964
                    $_SESSION['is_admin'],
965
                    $_SESSION['fonction_id'],
966
                    $SETTINGS
967
                );
968
969
                fputs($cacheLogFile, date('H:i:s ')."Rebuilding Tree\n");
970
                //rebuild full tree
971
                $tree->rebuild();
972
            }
973
974
            fputs($cacheLogFile, date('H:i:s ')."Importing Items\n");
975
976
            // Now import ITEMS
977
            $nbItemsImported = 0;
978
            $count = 0;
979
980
            //Get some info about personal folder
981
            if (in_array($post_folder_id, $_SESSION['personal_folders']) === true) {
982
                $personalFolder = 1;
983
            } else {
984
                $personalFolder = 0;
985
            }
986
987
            //prepare file to be read
988
            fclose($cacheFile);
989
            $cacheFile = fopen($cacheFileName, 'r');
990
991
            while (!feof($cacheFile)) {
992
                //prepare an array with item to import
993
                $full_item = fgets($cacheFile, 8192);
994
                $full_item = str_replace(array("\r\n", "\n", "\r"), '', $full_item);
995
                $item = explode($itemsSeparator, $full_item);
996
997
                ++$count;
998
                if (!($count % 10)) {
999
                    fputs($cacheLogFile, date('H:i:s ')."  Imported $count items (".number_format(($count / $numItems) * 100, 1).")\n");
1000
                }
1001
1002
                if (!empty($item[KP_TITLE])) {
1003
                    //$count++;
1004
                    //check if not exists
1005
                    $results .= str_replace($foldersSeparator, '\\', $item[KP_PATH]).'\\'.$item[KP_TITLE];
1006
1007
                    $pwd = $item[KP_PASSWORD];
1008
1009
                    //Get folder label
1010
                    if (count($foldersArray) == 0 || empty($item[KP_PATH])) {
1011
                        $folderId = $post_folder_id;
1012
                    } else {
1013
                        $folderId = $foldersArray[$item[KP_PATH]]['id'];
1014
                    }
1015
                    $data = DB::queryFirstRow(
1016
                        'SELECT title FROM '.prefixTable('nested_tree').' WHERE id = %i',
1017
                        intval($folderId)
1018
                    );
1019
1020
                    // escape if folderId is empty
1021
                    if (!empty($folderId)) {
1022
                        $results .= " - Inserting\n";
1023
1024
                        // prepare PW
1025
                        if ($import_perso === true) {
1026
                            $encrypt = cryption(
1027
                                $pwd,
1028
                                $_SESSION['user_settings']['session_psk'],
1029
                                'encrypt',
1030
                                $SETTINGS
1031
                            );
1032
                        } else {
1033
                            $encrypt = cryption(
1034
                                $pwd,
1035
                                '',
1036
                                'encrypt',
1037
                                $SETTINGS
1038
                            );
1039
                        }
1040
1041
                        //ADD item
1042
                        DB::insert(
1043
                            prefixTable('items'),
1044
                            array(
1045
                                'label' => substr(stripslashes($item[KP_TITLE]), 0, 500),
1046
                                'description' => stripslashes(str_replace($lineEndSeparator, '<br>', $item[KP_NOTES])),
1047
                                'pw' => $encrypt['string'],
1048
                                'pw_iv' => '',
1049
                                'url' => substr(stripslashes($item[KP_URL]), 0, 500),
1050
                                'id_tree' => $folderId,
1051
                                'login' => substr(stripslashes($item[KP_USERNAME]), 0, 500),
1052
                                'anyone_can_modify' => $post_edit_all,
1053
                            )
1054
                        );
1055
                        $newId = DB::insertId();
1056
1057
                        //if asked, anyone in role can modify
1058
                        if ($post_edit_role === 1) {
1059
                            foreach ($_SESSION['arr_roles'] as $role) {
1060
                                DB::insert(
1061
                                    prefixTable('restriction_to_roles'),
1062
                                    array(
1063
                                        'role_id' => $role['id'],
1064
                                        'item_id' => $newId,
1065
                                    )
1066
                                );
1067
                            }
1068
                        }
1069
1070
                        //Add log
1071
                        DB::insert(
1072
                            prefixTable('log_items'),
1073
                            array(
1074
                                'id_item' => $newId,
1075
                                'date' => time(),
1076
                                'id_user' => $_SESSION['user_id'],
1077
                                'action' => 'at_creation',
1078
                                'raison' => 'at_import',
1079
                            )
1080
                        );
1081
1082
                        //Add entry to cache table
1083
                        DB::insert(
1084
                            prefixTable('cache'),
1085
                            array(
1086
                                'id' => $newId,
1087
                                'label' => substr(stripslashes($item[KP_TITLE]), 0, 500),
1088
                                'description' => stripslashes(str_replace($lineEndSeparator, '<br>', $item[KP_NOTES])),
1089
                                'url' => substr(stripslashes($item[KP_URL]), 0, 500),
1090
                                'tags' => '',
1091
                                'id_tree' => $folderId,
1092
                                'perso' => $personalFolder == 0 ? 0 : 1,
1093
                                'login' => substr(stripslashes($item[KP_USERNAME]), 0, 500),
1094
                                'restricted_to' => '0',
1095
                                'folder' => $data['title'],
1096
                                'author' => $_SESSION['user_id'],
1097
                                'renewal_period' => '0',
1098
                                'timestamp' => time(),
1099
                            )
1100
                        );
1101
1102
                        //increment number of imported items
1103
                        ++$nbItemsImported;
1104
                    } else {
1105
                        $results .= ' - '.$item[KP_TITLE]." was not imported\n";
1106
                    }
1107
                    fputs($cacheLogFile, date('H:i:s ').' '.$results."\n");
1108
                }
1109
            }
1110
1111
            //SHow finished
1112
            $text = '<div class="row ml-3">'.
1113
                '<div class="col-12"><i class="far fa-hand-point-right fa-sm mr-2"></i>'.langHdl('number_of_folders_imported').': <b>'.$nbFoldersImported.'</b></div>'.
1114
                '<div class="col-12"><i class="far fa-hand-point-right fa-sm mr-2"></i>'.langHdl('number_of_items_imported').': <b>'.$nbItemsImported.'</b></div>'.
1115
                '</div>'.
1116
                '<div class="row col-12 ml-3 mt-3 font-weight-bold"><i class="fas fa-check fa-lg mr-2"></i>'.langHdl('import_kp_finished').'</div>';
1117
1118
            //Delete cache file
1119
            fclose($cacheFileF);
1120
            fclose($cacheFile);
1121
            fclose($cacheLogFile);
1122
            unlink($cacheFileName);
1123
            unlink($cacheFileNameFolder);
1124
            unlink($SETTINGS['path_to_files_folder'].'/'.$file);
1125
            unlink($SETTINGS['path_to_files_folder'].$logFileName);
1126
1127
            //Display all messages to user
1128
            echo prepareExchangedData(
1129
                array(
1130
                    'error' => false,
1131
                    'message' => '',
1132
                    'info' => $text,
1133
                ),
1134
                'encode'
1135
            );
1136
        } else {
1137
            echo prepareExchangedData(
1138
                array(
1139
                    'error' => true,
1140
                    'message' => langHdl('no_item_found'),
1141
                ),
1142
                'encode'
1143
            );
1144
        }
1145
        break;
1146
}
1147
1148
spl_autoload_register(function ($class) {
1149
    $prefix = 'League\\Csv\\';
1150
    $base_dir = __DIR__.'/src/';
1151
    $len = strlen($prefix);
1152
    if (strncmp($prefix, $class, $len) !== 0) {
1153
        // no, move to the next registered autoloader
1154
        return;
1155
    }
1156
    $relative_class = substr($class, $len);
1157
    $file = $base_dir.str_replace('\\', '/', $relative_class).'.php';
1158
    if (file_exists($file)) {
1159
        require $file;
1160
    }
1161
});
1162
1163
/**
1164
 * Used to format the string ready for insertion in to the database.
1165
 *
1166
 * @param string $str             String to clean
1167
 * @param string $crLFReplacement Replacement
1168
 *
1169
 * @return string
1170
 */
1171
function sanitiseString($str, $crLFReplacement)
1172
{
1173
    $str = preg_replace('#[\r\n]#', $crLFReplacement, $str);
1174
    $str = str_replace('\\', '&#92;', $str);
1175
    $str = str_replace('"', '&quot;', $str);
1176
    if (!empty($str)) {
1177
        addslashes($str);
1178
    }
1179
1180
    return $str;
1181
}
1182
1183
/**
1184
 * Clean array values.
1185
 *
1186
 * @param string $value String to clean
1187
 *
1188
 * @return string
1189
 */
1190
function cleanOutput(&$value)
1191
{
1192
    return htmlspecialchars_decode($value);
1193
}
1194