Passed
Push — master ( de5a80...ddace9 )
by Nils
06:13 queued 14s
created

parseReferenceFile()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 2
eloc 6
c 1
b 0
f 1
nc 2
nop 1
dl 0
loc 8
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Teampass - a collaborative passwords manager.
7
 * ---
8
 * This file is part of the TeamPass project.
9
 * 
10
 * TeamPass is free software: you can redistribute it and/or modify it
11
 * under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, version 3 of the License.
13
 * 
14
 * TeamPass is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU General Public License for more details.
18
 * 
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21
 * 
22
 * Certain components of this file may be under different licenses. For
23
 * details, see the `licenses` directory or individual file headers.
24
 * ---
25
 * @file      admin.queries.php
26
 * @author    Nils Laumaillé ([email protected])
27
 * @copyright 2009-2025 Teampass.net
28
 * @license   GPL-3.0
29
 * @see       https://www.teampass.net
30
 */
31
32
use TeampassClasses\SessionManager\SessionManager;
33
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
34
use TeampassClasses\Language\Language;
35
use TeampassClasses\PerformChecks\PerformChecks;
36
use TeampassClasses\ConfigManager\ConfigManager;
37
use TeampassClasses\NestedTree\NestedTree;
38
use Duo\DuoUniversal\Client;
39
use Duo\DuoUniversal\DuoException;
40
use TeampassClasses\EmailService\EmailSettings;
41
use TeampassClasses\EmailService\EmailService;
42
43
// Load functions
44
require_once 'main.functions.php';
45
$session = SessionManager::getSession();
46
$request = SymfonyRequest::createFromGlobals();
47
loadClasses('DB');
48
$lang = new Language($session->get('user-language') ?? 'english');
49
50
// Load config
51
$configManager = new ConfigManager();
52
$SETTINGS = $configManager->getAllSettings();
53
54
// Do checks
55
// Instantiate the class with posted data
56
$checkUserAccess = new PerformChecks(
57
    dataSanitizer(
58
        [
59
            'type' => htmlspecialchars($request->request->get('type', ''), ENT_QUOTES, 'UTF-8'),
60
        ],
61
        [
62
            'type' => 'trim|escape',
63
        ],
64
    ),
65
    [
66
        'user_id' => returnIfSet($session->get('user-id'), null),
67
        'user_key' => returnIfSet($session->get('key'), null),
68
    ]
69
);
70
// Handle the case
71
echo $checkUserAccess->caseHandler();
72
if ($checkUserAccess->checkSession() === false || $checkUserAccess->userAccessPage('admin') === false) {
73
    // Not allowed page
74
    $session->set('system-error_code', ERR_NOT_ALLOWED);
75
    include $SETTINGS['cpassman_dir'] . '/error.php';
76
    exit;
77
}
78
79
// Define Timezone
80
date_default_timezone_set($SETTINGS['timezone'] ?? 'UTC');
81
82
// Set header properties
83
header('Content-type: text/html; charset=utf-8');
84
header('Cache-Control: no-cache, no-store, must-revalidate');
85
86
// --------------------------------- //
87
88
89
// Load tree
90
$tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
91
92
// Prepare POST variables
93
$post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
94
$post_data = filter_input(INPUT_POST, 'data', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES);
95
$post_key = filter_input(INPUT_POST, 'key', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
96
$post_id = filter_input(INPUT_POST, 'id', FILTER_SANITIZE_NUMBER_INT);
97
$post_status = filter_input(INPUT_POST, 'status', FILTER_SANITIZE_NUMBER_INT);
98
$post_label = filter_input(INPUT_POST, 'label', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
99
$post_action = filter_input(INPUT_POST, 'action', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
100
$post_cpt = filter_input(INPUT_POST, 'cpt', FILTER_SANITIZE_NUMBER_INT);
101
$post_object = filter_input(INPUT_POST, 'object', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
102
$post_start = filter_input(INPUT_POST, 'start', FILTER_SANITIZE_NUMBER_INT);
103
$post_length = filter_input(INPUT_POST, 'length', FILTER_SANITIZE_NUMBER_INT);
104
$post_option = filter_input(INPUT_POST, 'option', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
105
$post_nbItems = filter_input(INPUT_POST, 'nbItems', FILTER_SANITIZE_NUMBER_INT);
106
$post_counter = filter_input(INPUT_POST, 'counter', FILTER_SANITIZE_NUMBER_INT);
107
$post_list = filter_input(INPUT_POST, 'list', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
108
109
switch ($post_type) {
110
    //##########################################################
111
    //CASE for creating a DB backup
112
    case 'admin_action_db_backup':
113
        // Check KEY
114
        if ($post_key !== $session->get('key')) {
115
            echo prepareExchangedData(
116
                array(
117
                    'error' => true,
118
                    'message' => $lang->get('key_is_not_correct'),
119
                ),
120
                'encode'
121
            );
122
            break;
123
        }
124
        // Is admin?
125
        if ($session->get('user-admin') === 1) {
126
            echo prepareExchangedData(
127
                array(
128
                    'error' => true,
129
                    'message' => $lang->get('error_not_allowed_to'),
130
                ),
131
                'encode'
132
            );
133
            break;
134
        }
135
136
        require_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
137
        $return = '';
138
139
        //Get all tables
140
        $tables = array();
141
        $result = DB::query('SHOW TABLES');
142
        foreach ($result as $row) {
143
            $tables[] = $row['Tables_in_' . DB_NAME];
144
        }
145
146
        //cycle through
147
        foreach ($tables as $table) {
148
            if (defined('DB_PREFIX') || substr_count($table, DB_PREFIX) > 0) {
149
                $table = (is_string($table) ? $table : strval($table));
150
                // Do query
151
                $result = DB::query('SELECT * FROM ' . $table);
152
                DB::query(
153
                    'SELECT *
154
                    FROM INFORMATION_SCHEMA.COLUMNS
155
                    WHERE table_schema = %s
156
                    AND table_name = %s',
157
                    DB_NAME,
158
                    $table
159
                );
160
                $numFields = DB::count();
161
162
                // prepare a drop table
163
                $return .= 'DROP TABLE ' . $table . ';';
164
                $row2 = DB::queryFirstRow('SHOW CREATE TABLE ' . $table);
165
                $return .= "\n\n" . strval($row2['Create Table']) . ";\n\n";
166
167
                //prepare all fields and datas
168
                for ($i = 0; $i < $numFields; ++$i) {
169
                    if (is_object($result)) {
170
                        while ($row = $result->fetch_row()) {
171
                            $return .= 'INSERT INTO ' . $table . ' VALUES(';
172
                            for ($j = 0; $j < $numFields; ++$j) {
173
                                $row[$j] = addslashes($row[$j]);
174
                                $row[$j] = preg_replace("/\n/", '\\n', $row[$j]);
175
                                if (isset($row[$j])) {
176
                                    $return .= '"' . $row[$j] . '"';
177
                                } else {
178
                                    $return .= 'NULL';
179
                                }
180
                                if ($j < ($numFields - 1)) {
181
                                    $return .= ',';
182
                                }
183
                            }
184
                            $return .= ");\n";
185
                        }
186
                    }
187
                }
188
                $return .= "\n\n\n";
189
            }
190
        }
191
192
        if (!empty($return)) {
193
            // get a token
194
            $token = GenerateCryptKey(20, false, true, true, false, true);
195
196
            //save file
197
            $filename = time() . '-' . $token . '.sql';
198
            $handle = fopen($SETTINGS['path_to_files_folder'] . '/' . $filename, 'w+');
199
            if ($handle !== false) {
200
                //write file
201
                fwrite($handle, $return);
202
                fclose($handle);
203
            }
204
205
            // Encrypt the file
206
            if (empty($post_option) === false) {
207
                // Encrypt the file
208
                prepareFileWithDefuse(
209
                    'encrypt',
210
                    $SETTINGS['path_to_files_folder'] . '/' . $filename,
211
                    $SETTINGS['path_to_files_folder'] . '/defuse_temp_' . $filename,
212
                    $post_option
213
                );
214
215
                // Do clean
216
                unlink($SETTINGS['path_to_files_folder'] . '/' . $filename);
217
                rename(
218
                    $SETTINGS['path_to_files_folder'] . '/defuse_temp_' . $filename,
219
                    $SETTINGS['path_to_files_folder'] . '/' . $filename
220
                );
221
            }
222
223
            //generate 2d key
224
            $session->set('user-key_tmp', GenerateCryptKey(20, false, true, true, false, true));
225
226
            //update LOG
227
            logEvents($SETTINGS, 'admin_action', 'dataBase backup', (string) $session->get('user-id'), $session->get('user-login'));
228
229
            echo '[{"result":"db_backup" , "href":"sources/downloadFile.php?name=' . urlencode($filename) . '&sub=files&file=' . $filename . '&type=sql&key=' . $session->get('key') . '&key_tmp=' . $session->get('user-key_tmp') . '&pathIsFiles=1"}]';
230
        }
231
        break;
232
233
        //##########################################################
234
        //CASE for restoring a DB backup
235
    case 'admin_action_db_restore':
236
        // Check KEY
237
        if ($post_key !== $session->get('key')) {
238
            echo prepareExchangedData(
239
                array(
240
                    'error' => true,
241
                    'message' => $lang->get('key_is_not_correct'),
242
                ),
243
                'encode'
244
            );
245
            break;
246
        }
247
        // Is admin?
248
        if ($session->get('user-admin') === 1) {
249
            echo prepareExchangedData(
250
                array(
251
                    'error' => true,
252
                    'message' => $lang->get('error_not_allowed_to'),
253
                ),
254
                'encode'
255
            );
256
            break;
257
        }
258
        include_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
259
260
        $dataPost = explode('&', $post_option);
261
        $file = htmlspecialchars($dataPost[0]);
262
        $key = htmlspecialchars($dataPost[1]);
263
264
        // Get filename from database
265
        $data = DB::queryFirstRow(
266
            'SELECT valeur
267
            FROM ' . prefixTable('misc') . '
268
            WHERE increment_id = %i',
269
            $file
270
        );
271
272
        $file = is_string($data['valeur']) ? $data['valeur'] : '';
273
274
        // Delete operation id
275
        DB::delete(
276
            prefixTable('misc'),
277
            'increment_id = %i',
278
            $file
279
        );
280
281
        // Undecrypt the file
282
        if (empty($key) === false) {
283
            // Decrypt the file
284
            $ret = prepareFileWithDefuse(
285
                'decrypt',
286
                $SETTINGS['path_to_files_folder'] . '/' . $file,
287
                $SETTINGS['path_to_files_folder'] . '/defuse_temp_' . $file,
288
                $key
289
            );
290
291
            if (empty($ret) === false) {
292
                // deepcode ignore ServerLeak: $ret can only be an error string, so no important data here to be sent to client
293
                echo '[{"result":"db_restore" , "message":"An error occurred ('.(string) $ret.')"}]';
294
                break;
295
            }
296
297
            // Do clean
298
            fileDelete($SETTINGS['path_to_files_folder'] . '/' . $file, $SETTINGS);
299
            $file = $SETTINGS['path_to_files_folder'] . '/defuse_temp_' . $file;
300
        } else {
301
            $file = $SETTINGS['path_to_files_folder'] . '/' . $file;
302
        }
303
304
        //read sql file
305
        $handle = fopen($file, 'r');
306
        $query = '';
307
        while (!feof($handle)) {
308
            $query .= fgets($handle, 4096);
309
            if (substr(rtrim($query), -1) === ';') {
310
                //launch query
311
                DB::query($query);
312
                $query = '';
313
            }
314
        }
315
        fclose($handle);
316
317
        //delete file
318
        unlink($SETTINGS['path_to_files_folder'] . '/' . $file);
319
320
        //Show done
321
        echo '[{"result":"db_restore" , "message":""}]';
322
        break;
323
324
        //##########################################################
325
        //CASE for optimizing the DB
326
    case 'admin_action_db_optimize':
327
        // Check KEY
328
        if ($post_key !== $session->get('key')) {
329
            echo prepareExchangedData(
330
                array(
331
                    'error' => true,
332
                    'message' => $lang->get('key_is_not_correct'),
333
                ),
334
                'encode'
335
            );
336
            break;
337
        }
338
        // Is admin?
339
        if ($session->get('user-admin') === 1) {
340
            echo prepareExchangedData(
341
                array(
342
                    'error' => true,
343
                    'message' => $lang->get('error_not_allowed_to'),
344
                ),
345
                'encode'
346
            );
347
            break;
348
        }
349
350
        //Get all tables
351
        $alltables = DB::query('SHOW TABLES');
352
        foreach ($alltables as $table) {
353
            foreach ($table as $i => $tablename) {
354
                $tablename = (is_string($tablename) ? $tablename : strval($tablename));
355
                if (substr_count($tablename, DB_PREFIX) > 0) {
356
                    // launch optimization quieries
357
                    DB::query('ANALYZE TABLE `' . $tablename . '`');
358
                    DB::query('OPTIMIZE TABLE `' . $tablename . '`');
359
                }
360
            }
361
        }
362
363
        //Clean up LOG_ITEMS table
364
        $rows = DB::query(
365
            'SELECT id
366
            FROM ' . prefixTable('items') . '
367
            ORDER BY id ASC'
368
        );
369
        foreach ($rows as $item) {
370
            DB::query(
371
                'SELECT * FROM ' . prefixTable('log_items') . ' WHERE id_item = %i AND action = %s',
372
                $item['id'],
373
                'at_creation'
374
            );
375
            $counter = DB::count();
376
            if ($counter === 0) {
377
                //Create new at_creation entry
378
                $rowTmp = DB::queryFirstRow(
379
                    'SELECT date, id_user FROM ' . prefixTable('log_items') . ' WHERE id_item=%i ORDER BY date ASC',
380
                    $item['id']
381
                );
382
                DB::insert(
383
                    prefixTable('log_items'),
384
                    array(
385
                        'id_item' => $item['id'],
386
                        'date' => $rowTmp['date'] - 1,
387
                        'id_user' => empty($rowTmp['id_user']) === true ? 1 : $rowTmp['id_user'],
388
                        'action' => 'at_creation',
389
                        'raison' => '',
390
                    )
391
                );
392
            }
393
        }
394
395
        // Log
396
        logEvents(
397
            $SETTINGS,
398
            'system',
399
            'admin_action_db_optimize',
400
            (string) $session->get('user-id'),
401
            $session->get('user-login'),
402
            'success'
403
        );
404
405
        //Show done
406
        echo prepareExchangedData(
407
            array(
408
                'error' => false,
409
                'message' => $lang->get('last_execution') . ' ' .
410
                    date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) time()) .
411
                    '<i class="fas fa-check text-success ml-2"></i>',
412
            ),
413
            'encode'
414
        );
415
        break;
416
417
        
418
419
        /*
420
    * Reload the Cache table
421
    */
422
    case 'admin_action_reload_cache_table':
423
        // Check KEY
424
        if ($post_key !== $session->get('key')) {
425
            echo prepareExchangedData(
426
                array(
427
                    'error' => true,
428
                    'message' => $lang->get('key_is_not_correct'),
429
                ),
430
                'encode'
431
            );
432
            break;
433
        }
434
        // Is admin?
435
        if ($session->get('user-admin') === 1) {
436
            echo prepareExchangedData(
437
                array(
438
                    'error' => true,
439
                    'message' => $lang->get('error_not_allowed_to'),
440
                ),
441
                'encode'
442
            );
443
            break;
444
        }
445
446
        require_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
447
        updateCacheTable('reload', NULL);
448
449
        // Log
450
        logEvents(
451
            $SETTINGS,
452
            'system',
453
            'admin_action_reload_cache_table',
454
            (string) $session->get('user-id'),
455
            $session->get('user-login'),
456
            'success'
457
        );
458
459
        echo prepareExchangedData(
460
            [
461
                'error' => false,
462
                'message' => $lang->get('last_execution') . ' ' .
463
                    date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) time()) .
464
                    '<i class="fas fa-check text-success mr-2"></i>',
465
            ],
466
            'encode'
467
        );
468
        break;
469
470
471
    /*
472
    * Change SALT Key START
473
    */
474
    case 'admin_action_change_salt_key___start':
475
        // Check KEY
476
        if ($post_key !== $session->get('key')) {
477
            echo prepareExchangedData(
478
                array(
479
                    'error' => true,
480
                    'message' => $lang->get('key_is_not_correct'),
481
                ),
482
                'encode'
483
            );
484
            break;
485
        }
486
        // Is admin?
487
        if ($session->get('user-admin') === 1) {
488
            echo prepareExchangedData(
489
                array(
490
                    'error' => true,
491
                    'message' => $lang->get('error_not_allowed_to'),
492
                ),
493
                'encode'
494
            );
495
            break;
496
        }
497
498
        $error = '';
499
        require_once 'main.functions.php';
500
501
        // store old sk
502
        $session->set('user-reencrypt_old_salt', file_get_contents(SECUREPATH.'/'.SECUREFILE));
503
504
        // generate new saltkey
505
        $old_sk_filename = SECUREPATH.'/'.SECUREFILE . date('Y_m_d', mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y'))) . '.' . time();
506
        copy(
507
            SECUREPATH.'/'.SECUREFILE,
508
            $old_sk_filename
509
        );
510
        $new_key = defuse_generate_key();
511
        file_put_contents(
512
            SECUREPATH.'/'.SECUREFILE,
513
            $new_key
514
        );
515
516
        // store new sk
517
        $session->set('user-reencrypt_new_salt', file_get_contents(SECUREPATH.'/'.SECUREFILE));
518
519
        //put tool in maintenance.
520
        DB::update(
521
            prefixTable('misc'),
522
            array(
523
                'valeur' => '1',
524
                'updated_at' => time(),
525
            ),
526
            'intitule = %s AND type= %s',
527
            'maintenance_mode',
528
            'admin'
529
        );
530
        //log
531
        logEvents($SETTINGS, 'system', 'change_salt_key', (string) $session->get('user-id'), $session->get('user-login'));
532
533
        // get number of items to change
534
        DB::query('SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i', 0);
535
        $nb_of_items = DB::count();
536
537
        // create backup table
538
        DB::query('DROP TABLE IF EXISTS ' . prefixTable('sk_reencrypt_backup'));
539
        DB::query(
540
            'CREATE TABLE `' . prefixTable('sk_reencrypt_backup') . '` (
541
            `id` int(12) NOT null AUTO_INCREMENT,
542
            `current_table` varchar(100) NOT NULL,
543
            `current_field` varchar(500) NOT NULL,
544
            `value_id` varchar(500) NOT NULL,
545
            `value` text NOT NULL,
546
            `value2` varchar(500) NOT NULL,
547
            `current_sql` text NOT NULL,
548
            `result` text NOT NULL,
549
            PRIMARY KEY (`id`)
550
            ) CHARSET=utf8;'
551
        );
552
553
        // store old SK in backup table
554
        DB::insert(
555
            prefixTable('sk_reencrypt_backup'),
556
            array(
557
                'current_table' => 'old_sk',
558
                'current_field' => 'old_sk',
559
                'value_id' => 'old_sk',
560
                'value' => $session->get('user-reencrypt_old_salt'),
561
                'current_sql' => 'old_sk',
562
                'value2' => $old_sk_filename,
563
                'result' => 'none',
564
            )
565
        );
566
567
        // delete previous backup files
568
        $files = glob($SETTINGS['path_to_upload_folder'] . '/*'); // get all file names
569
        foreach ($files as $file) { // iterate files
570
            if (is_file($file)) {
571
                $file_parts = pathinfo($file);
572
                if (strpos($file_parts['filename'], '.bck-change-sk') !== false) {
573
                    unlink($file); // delete file
574
                }
575
            }
576
        }
577
578
        // Send back
579
        echo prepareExchangedData(
580
            array(
581
                'error' => false,
582
                'message' => '',
583
                'nextAction' => 'encrypt_items',
584
                'nbOfItems' => $nb_of_items,
585
            ),
586
            'encode'
587
        );
588
        break;
589
590
        /*
591
    * Change SALT Key - ENCRYPT
592
    */
593
    case 'admin_action_change_salt_key___encrypt':
594
        // Check KEY
595
        if ($post_key !== $session->get('key')) {
596
            echo prepareExchangedData(
597
                array(
598
                    'error' => true,
599
                    'message' => $lang->get('key_is_not_correct'),
600
                    'nextAction' => '',
601
                    'nbOfItems' => '',
602
                ),
603
                'encode'
604
            );
605
            break;
606
        }
607
        // Is admin?
608
        if ($session->get('user-admin') === 1) {
609
            echo prepareExchangedData(
610
                array(
611
                    'error' => true,
612
                    'message' => $lang->get('error_not_allowed_to'),
613
                ),
614
                'encode'
615
            );
616
            break;
617
        }
618
619
        $error = '';
620
        require_once 'main.functions.php';
621
622
        // prepare SK
623
        if (empty($session->get('user-reencrypt_new_salt')) === true || empty($session->get('user-reencrypt_old_salt')) === true) {
624
            // SK is not correct
625
            echo prepareExchangedData(
626
                array(
627
                    'error' => true,
628
                    'message' => 'saltkeys are empty???',
629
                    'nbOfItems' => '',
630
                    'nextAction' => '',
631
                ),
632
                'encode'
633
            );
634
            break;
635
        }
636
637
        // Do init
638
        $nb_of_items = 0;
639
        $error = $nextStart = '';
640
641
642
        // what objects to treat
643
        if (empty($post_object) === true) {
644
            // no more object to treat
645
            $nextAction = 'finishing';
646
        } else {
647
            // manage list of objects
648
            $objects = explode(',', $post_object);
649
650
            // Allowed values for $_POST['object'] : "items,logs,files,categories"
651
            if (in_array($objects[0], array('items', 'logs', 'files', 'categories')) === false) {
652
                echo prepareExchangedData(
653
                    array(
654
                        'error' => true,
655
                        'message' => 'Input `' . $objects[0] . '` is not allowed',
656
                        'nbOfItems' => '',
657
                        'nextAction' => '',
658
                    ),
659
                    'encode'
660
                );
661
                break;
662
            }
663
664
            if ($objects[0] === 'items') {
665
                //change all encrypted data in Items (passwords)
666
                $rows = DB::query(
667
                    'SELECT id, pw, pw_iv
668
                    FROM ' . prefixTable('items') . '
669
                    WHERE perso = %s
670
                    LIMIT ' . $post_start . ', ' . $post_length,
671
                    '0'
672
                );
673
                foreach ($rows as $record) {
674
                    // backup data
675
                    DB::insert(
676
                        prefixTable('sk_reencrypt_backup'),
677
                        array(
678
                            'current_table' => 'items',
679
                            'current_field' => 'pw',
680
                            'value_id' => $record['id'],
681
                            'value' => $record['pw'],
682
                            'current_sql' => 'UPDATE ' . prefixTable('items') . " SET pw = '" . $record['pw'] . "' WHERE id = '" . $record['id'] . "';",
683
                            'value2' => 'none',
684
                            'result' => 'none',
685
                        )
686
                    );
687
                    $newID = DB::insertId();
688
689
                    $pw = cryption(
690
                        $record['pw'],
691
                        $session->get('user-reencrypt_old_salt'),
692
                        'decrypt',
693
                        $SETTINGS
694
                    );
695
                    //encrypt with new SALT
696
                    $encrypt = cryption(
697
                        $pw['string'],
698
                        $session->get('user-reencrypt_new_salt'),
699
                        'encrypt',
700
                        $SETTINGS
701
                    );
702
703
                    //save in DB
704
                    DB::update(
705
                        prefixTable('items'),
706
                        array(
707
                            'pw' => $encrypt['string'],
708
                            'pw_iv' => '',
709
                        ),
710
                        'id = %i',
711
                        $record['id']
712
                    );
713
714
                    // update backup table
715
                    DB::update(
716
                        prefixTable('sk_reencrypt_backup'),
717
                        array(
718
                            'result' => 'ok',
719
                        ),
720
                        'id=%i',
721
                        $newID
722
                    );
723
                }
724
                // ---
725
                // CASE OF LOGS
726
                // ---
727
            } elseif ($objects[0] === 'logs') {
728
                //change all encrypted data in Logs (passwords)
729
                $rows = DB::query(
730
                    'SELECT raison, increment_id
731
                    FROM ' . prefixTable('log_items') . "
732
                    WHERE action = %s AND raison LIKE 'at_pw :%'
733
                    LIMIT " . $post_start . ', ' . $post_length,
734
                    'at_modification'
735
                );
736
                foreach ($rows as $record) {
737
                    // backup data
738
                    DB::insert(
739
                        prefixTable('sk_reencrypt_backup'),
740
                        array(
741
                            'current_table' => 'log_items',
742
                            'current_field' => 'raison',
743
                            'value_id' => $record['increment_id'],
744
                            'value' => $record['raison'],
745
                            'current_sql' => 'UPDATE ' . prefixTable('log_items') . " SET raison = '" . $record['raison'] . "' WHERE increment_id = '" . $record['increment_id'] . "';",
746
                            'value2' => 'none',
747
                            'result' => 'none',
748
                        )
749
                    );
750
                    $newID = DB::insertId();
751
752
                    // extract the pwd
753
                    $tmp = explode('at_pw :', $record['raison']);
754
                    if (!empty($tmp[1])) {
755
                        $pw = cryption(
756
                            $tmp[1],
757
                            $session->get('user-reencrypt_old_salt'),
758
                            'decrypt',
759
                            $SETTINGS
760
                        );
761
                        //encrypt with new SALT
762
                        $encrypt = cryption(
763
                            $pw['string'],
764
                            $session->get('user-reencrypt_new_salt'),
765
                            'encrypt',
766
                            $SETTINGS
767
                        );
768
769
                        // save in DB
770
                        DB::update(
771
                            prefixTable('log_items'),
772
                            array(
773
                                'raison' => 'at_pw :' . $encrypt['string'],
774
                                'encryption_type' => 'defuse',
775
                            ),
776
                            'increment_id = %i',
777
                            $record['increment_id']
778
                        );
779
780
                        // update backup table
781
                        DB::update(
782
                            prefixTable('sk_reencrypt_backup'),
783
                            array(
784
                                'result' => 'ok',
785
                            ),
786
                            'id=%i',
787
                            $newID
788
                        );
789
                    }
790
                }
791
                // ---
792
                // CASE OF CATEGORIES
793
                // ---
794
            } elseif ($objects[0] === 'categories') {
795
                //change all encrypted data in CATEGORIES (passwords)
796
                $rows = DB::query(
797
                    'SELECT id, data
798
                    FROM ' . prefixTable('categories_items') . '
799
                    LIMIT ' . $post_start . ', ' . $post_length
800
                );
801
                foreach ($rows as $record) {
802
                    // backup data
803
                    DB::insert(
804
                        prefixTable('sk_reencrypt_backup'),
805
                        array(
806
                            'current_table' => 'categories_items',
807
                            'current_field' => 'data',
808
                            'value_id' => $record['id'],
809
                            'value' => $record['data'],
810
                            'current_sql' => 'UPDATE ' . prefixTable('categories_items') . " SET data = '" . $record['data'] . "' WHERE id = '" . $record['id'] . "';",
811
                            'value2' => 'none',
812
                            'result' => 'none',
813
                        )
814
                    );
815
                    $newID = DB::insertId();
816
817
                    $pw = cryption(
818
                        $record['data'],
819
                        $session->get('user-reencrypt_old_salt'),
820
                        'decrypt',
821
                        $SETTINGS
822
                    );
823
                    //encrypt with new SALT
824
                    $encrypt = cryption(
825
                        $pw['string'],
826
                        $session->get('user-reencrypt_new_salt'),
827
                        'encrypt',
828
                        $SETTINGS
829
                    );
830
                    // save in DB
831
                    DB::update(
832
                        prefixTable('categories_items'),
833
                        array(
834
                            'data' => $encrypt['string'],
835
                            'encryption_type' => 'defuse',
836
                        ),
837
                        'id = %i',
838
                        $record['id']
839
                    );
840
841
                    // update backup table
842
                    DB::update(
843
                        prefixTable('sk_reencrypt_backup'),
844
                        array(
845
                            'result' => 'ok',
846
                        ),
847
                        'id=%i',
848
                        $newID
849
                    );
850
                }
851
                // ---
852
                // CASE OF FILES
853
                // ---
854
            } elseif ($objects[0] === 'files') {
855
                // Change all encrypted data in FILES (passwords)
856
                $rows = DB::query(
857
                    'SELECT id, file, status
858
                    FROM ' . prefixTable('files') . "
859
                    WHERE status = 'encrypted'
860
                    LIMIT " . $post_start . ', ' . $post_length
861
                );
862
                foreach ($rows as $record) {
863
                    // backup data
864
                    DB::insert(
865
                        prefixTable('sk_reencrypt_backup'),
866
                        array(
867
                            'current_table' => 'files',
868
                            'current_field' => 'file',
869
                            'value_id' => $record['id'],
870
                            'value' => $record['file'],
871
                            'current_sql' => 'no_query',
872
                            'value2' => 'none',
873
                            'result' => 'none',
874
                        )
875
                    );
876
                    $newID = DB::insertId();
877
878
                    if (file_exists($SETTINGS['path_to_upload_folder'] . '/' . $record['file'])) {
879
                        // make a copy of file
880
                        if (!copy(
881
                            $SETTINGS['path_to_upload_folder'] . '/' . $record['file'],
882
                            $SETTINGS['path_to_upload_folder'] . '/' . $record['file'] . '.copy'
883
                        )) {
884
                            $error = 'Copy not possible';
885
                            exit;
886
                        } else {
887
                            // prepare a bck of file (that will not be deleted)
888
                            $backup_filename = $record['file'] . '.bck-change-sk.' . time();
889
                            copy(
890
                                $SETTINGS['path_to_upload_folder'] . '/' . $record['file'],
891
                                $SETTINGS['path_to_upload_folder'] . '/' . $backup_filename
892
                            );
893
                        }
894
895
                        // Treat the file
896
                        // STEP1 - Do decryption
897
                        prepareFileWithDefuse(
898
                            'decrypt',
899
                            $SETTINGS['path_to_upload_folder'] . '/' . $record['file'],
900
                            $SETTINGS['path_to_upload_folder'] . '/' . $record['file'] . '_encrypted'
901
                        );
902
903
                        // Do cleanup of files
904
                        unlink($SETTINGS['path_to_upload_folder'] . '/' . $record['file']);
905
906
                        // STEP2 - Do encryption
907
                        prepareFileWithDefuse(
908
                            'encryp',
909
                            $SETTINGS['path_to_upload_folder'] . '/' . $record['file'] . '_encrypted',
910
                            $SETTINGS['path_to_upload_folder'] . '/' . $record['file']
911
                        );
912
913
                        // Do cleanup of files
914
                        unlink($SETTINGS['path_to_upload_folder'] . '/' . $record['file'] . '_encrypted');
915
916
                        // Update backup table
917
                        DB::update(
918
                            prefixTable('sk_reencrypt_backup'),
919
                            array(
920
                                'value2' => $backup_filename,
921
                                'result' => 'ok',
922
                            ),
923
                            'id=%i',
924
                            $newID
925
                        );
926
                    }
927
                }
928
            }
929
930
            $nextStart = intval($post_start) + intval($post_length);
931
932
            // check if last item to change has been treated
933
            if ($nextStart >= intval($post_nbItems)) {
934
                array_shift($objects);
935
                $nextAction = implode(',', $objects); // remove first object of the list
936
937
                // do some things for new object
938
                if (isset($objects[0])) {
939
                    if ($objects[0] === 'logs') {
940
                        DB::query('SELECT increment_id FROM ' . prefixTable('log_items') . " WHERE action = %s AND raison LIKE 'at_pw :%'", 'at_modification');
941
                    } elseif ($objects[0] === 'files') {
942
                        DB::query('SELECT id FROM ' . prefixTable('files'));
943
                    } elseif ($objects[0] === 'categories') {
944
                        DB::query('SELECT id FROM ' . prefixTable('categories_items'));
945
                    } elseif ($objects[0] === 'custfields') {
946
                        DB::query('SELECT raison FROM ' . prefixTable('log_items') . " WHERE action = %s AND raison LIKE 'at_pw :%'", 'at_modification');
947
                    }
948
                    $nb_of_items = DB::count();
949
                } else {
950
                    // now finishing
951
                    $nextAction = 'finishing';
952
                }
953
            } else {
954
                $nextAction = $post_object;
955
                $nb_of_items = 0;
956
            }
957
        }
958
959
        // Send back
960
        echo prepareExchangedData(
961
            array(
962
                'error' => false,
963
                'message' => '',
964
                'nextAction' => $nextAction,
965
                'nextStart' => (int) $nextStart,
966
                'nbOfItems' => $nb_of_items,
967
                'oldsk' => $session->get('user-reencrypt_old_salt'),
968
                'newsk' => $session->get('user-reencrypt_new_salt'),
969
            ),
970
            'encode'
971
        );
972
        break;
973
974
        /*
975
    * Change SALT Key - END
976
    */
977
    case 'admin_action_change_salt_key___end':
978
        // Check KEY
979
        if ($post_key !== $session->get('key')) {
980
            echo prepareExchangedData(
981
                array(
982
                    'error' => true,
983
                    'message' => $lang->get('key_is_not_correct'),
984
                ),
985
                'encode'
986
            );
987
            break;
988
        }
989
        // Is admin?
990
        if ($session->get('user-admin') === 1) {
991
            echo prepareExchangedData(
992
                array(
993
                    'error' => true,
994
                    'message' => $lang->get('error_not_allowed_to'),
995
                ),
996
                'encode'
997
            );
998
            break;
999
        }
1000
        $error = '';
1001
1002
        // quit maintenance mode.
1003
        DB::update(
1004
            prefixTable('misc'),
1005
            array(
1006
                'valeur' => '0',
1007
                'updated_at' => time(),
1008
            ),
1009
            'intitule = %s AND type= %s',
1010
            'maintenance_mode',
1011
            'admin'
1012
        );
1013
1014
        // Send back
1015
        echo prepareExchangedData(
1016
            array(
1017
                'error' => false,
1018
                'message' => '',
1019
                'nextAction' => 'done',
1020
            ),
1021
            'encode'
1022
        );
1023
        break;
1024
1025
        /*
1026
    * Change SALT Key - Restore BACKUP data
1027
    */
1028
    case 'admin_action_change_salt_key___restore_backup':
1029
        // Check KEY
1030
        if ($post_key !== $session->get('key')) {
1031
            echo prepareExchangedData(
1032
                array(
1033
                    'error' => true,
1034
                    'message' => $lang->get('key_is_not_correct'),
1035
                ),
1036
                'encode'
1037
            );
1038
            break;
1039
        }
1040
        // Is admin?
1041
        if ($session->get('user-admin') === 1) {
1042
            echo prepareExchangedData(
1043
                array(
1044
                    'error' => true,
1045
                    'message' => $lang->get('error_not_allowed_to'),
1046
                ),
1047
                'encode'
1048
            );
1049
            break;
1050
        }
1051
1052
        // delete files
1053
        $previous_saltkey_filename = '';
1054
        $rows = DB::query(
1055
            'SELECT current_table, value, value2, current_sql
1056
            FROM ' . prefixTable('sk_reencrypt_backup')
1057
        );
1058
        foreach ($rows as $record) {
1059
            if ($record['current_table'] === 'items' || $record['current_table'] === 'logs' || $record['current_table'] === 'categories') {
1060
                // excute query
1061
                DB::query(
1062
                    str_replace("\'", "'", $record['current_sql'])
1063
                );
1064
            } elseif ($record['current_table'] === 'files') {
1065
                // restore backup file
1066
                if (file_exists($SETTINGS['path_to_upload_folder'] . '/' . $record['value'])) {
1067
                    unlink($SETTINGS['path_to_upload_folder'] . '/' . $record['value']);
1068
                    if (file_exists($SETTINGS['path_to_upload_folder'] . '/' . $record['value2'])) {
1069
                        rename(
1070
                            $SETTINGS['path_to_upload_folder'] . '/' . $record['value2'],
1071
                            $SETTINGS['path_to_upload_folder'] . '/' . $record['value']
1072
                        );
1073
                    }
1074
                }
1075
            } elseif ($record['current_table'] === 'old_sk') {
1076
                $previous_saltkey_filename = $record['value2'];
1077
            }
1078
        }
1079
1080
        // restore saltkey file
1081
        if (file_exists($previous_saltkey_filename)) {
1082
            unlink(SECUREPATH.'/'.SECUREFILE);
1083
            rename(
1084
                $previous_saltkey_filename,
1085
                SECUREPATH.'/'.SECUREFILE
1086
            );
1087
        }
1088
1089
        // drop table
1090
        DB::query('DROP TABLE IF EXISTS ' . prefixTable('sk_reencrypt_backup'));
1091
1092
        // Send back
1093
        echo prepareExchangedData(
1094
            array(
1095
                'error' => false,
1096
                'message' => '',
1097
            ),
1098
            'encode'
1099
        );
1100
1101
        break;
1102
1103
        /*
1104
    * Change SALT Key - Delete BACKUP data
1105
    */
1106
    case 'admin_action_change_salt_key___delete_backup':
1107
        // Check KEY
1108
        if ($post_key !== $session->get('key')) {
1109
            echo prepareExchangedData(
1110
                array(
1111
                    'error' => true,
1112
                    'message' => $lang->get('key_is_not_correct'),
1113
                ),
1114
                'encode'
1115
            );
1116
            break;
1117
        }
1118
        // Is admin?
1119
        if ($session->get('user-admin') === 1) {
1120
            echo prepareExchangedData(
1121
                array(
1122
                    'error' => true,
1123
                    'message' => $lang->get('error_not_allowed_to'),
1124
                ),
1125
                'encode'
1126
            );
1127
            break;
1128
        }
1129
1130
        // delete files
1131
        $rows = DB::query(
1132
            'SELECT value, value2
1133
            FROM ' . prefixTable('sk_reencrypt_backup') . "
1134
            WHERE current_table = 'files'"
1135
        );
1136
        foreach ($rows as $record) {
1137
            if (file_exists($SETTINGS['path_to_upload_folder'] . '/' . $record['value2'])) {
1138
                unlink($SETTINGS['path_to_upload_folder'] . '/' . $record['value2']);
1139
            }
1140
        }
1141
1142
        // drop table
1143
        DB::query('DROP TABLE IF EXISTS ' . prefixTable('sk_reencrypt_backup'));
1144
1145
        echo '[{"status":"done"}]';
1146
        break;
1147
1148
        /*
1149
    * Test the email configuraiton
1150
    */
1151
    case 'admin_email_test_configuration':
1152
        // Check KEY
1153
        if ($post_key !== $session->get('key')) {
1154
            echo prepareExchangedData(
1155
                array(
1156
                    'error' => true,
1157
                    'message' => $lang->get('key_is_not_correct'),
1158
                ),
1159
                'encode'
1160
            );
1161
            break;
1162
        }
1163
1164
        // User has an email set?
1165
        if (empty($session->get('user-email')) === true) {
1166
            echo prepareExchangedData(
1167
                array(
1168
                    'error' => true,
1169
                    'message' => $lang->get('no_email_set'),
1170
                ),
1171
                'encode'
1172
            );
1173
        } else {
1174
            require_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
1175
1176
            //send email
1177
            $emailSettings = new EmailSettings($SETTINGS);
1178
            $emailService = new EmailService();
1179
            $emailService->sendMail(
1180
                $lang->get('admin_email_test_subject'),
1181
                $lang->get('admin_email_test_body'),
1182
                $session->get('user-email'),
1183
                $emailSettings
1184
            );
1185
            
1186
            echo prepareExchangedData(
1187
                array(
1188
                    'error' => false,
1189
                    'message' => '',
1190
                ),
1191
                'encode'
1192
            );
1193
        }
1194
        break;
1195
1196
        /*
1197
    * Send emails in backlog
1198
    */
1199
    case 'admin_email_send_backlog':
1200
        // Check KEY
1201
        if ($post_key !== $session->get('key')) {
1202
            echo prepareExchangedData(
1203
                array(
1204
                    'error' => true,
1205
                    'message' => $lang->get('key_is_not_correct'),
1206
                ),
1207
                'encode'
1208
            );
1209
            break;
1210
        }
1211
1212
        include_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
1213
        $emailSettings = new EmailSettings($SETTINGS);
1214
        $emailService = new EmailService();
1215
1216
        $rows = DB::query(
1217
            'SELECT *
1218
            FROM ' . prefixTable('emails') . '
1219
            WHERE status = %s OR status = %s',
1220
            'not_sent',
1221
            ''
1222
        );
1223
        $counter = DB::count();
1224
        $error = false;
1225
        $message = '';
1226
1227
        if ($counter > 0) {
1228
            // Only treat first email
1229
            foreach ($rows as $record) {
1230
                //send email
1231
                $email = $emailService->sendMail(
1232
                    $record['subject'],
1233
                    $record['body'],
1234
                    $record['receivers'],
1235
                    $emailSettings
1236
                );
1237
                $ret = json_decode(
1238
                    $email,
1239
                    true
1240
                );
1241
1242
                if (empty($ret['error']) === false) {
1243
                    //update item_id in files table
1244
                    DB::update(
1245
                        prefixTable('emails'),
1246
                        array(
1247
                            'status' => 'not_sent',
1248
                        ),
1249
                        'timestamp = %s',
1250
                        $record['timestamp']
1251
                    );
1252
1253
                    $error = true;
1254
                    $message = $ret['message'];
1255
                } else {
1256
                    //delete from DB
1257
                    DB::delete(
1258
                        prefixTable('emails'),
1259
                        'timestamp = %s',
1260
                        $record['timestamp']
1261
                    );
1262
1263
                    //update LOG
1264
                    logEvents(
1265
                        $SETTINGS,
1266
                        'admin_action',
1267
                        'Emails backlog',
1268
                        (string) $session->get('user-id'),
1269
                        $session->get('user-login')
1270
                    );
1271
                }
1272
1273
                // Exit loop
1274
                break;
1275
            }
1276
        }
1277
1278
        echo prepareExchangedData(
1279
            array(
1280
                'error' => $error,
1281
                'message' => $message,
1282
                'counter' => $counter,
1283
            ),
1284
            'encode'
1285
        );
1286
        break;
1287
1288
        /*
1289
    * Send emails in backlog
1290
    */
1291
    case 'admin_email_send_backlog_old':
1292
        // Check KEY
1293
        if ($post_key !== $session->get('key')) {
1294
            echo prepareExchangedData(
1295
                array(
1296
                    'error' => true,
1297
                    'message' => $lang->get('key_is_not_correct'),
1298
                ),
1299
                'encode'
1300
            );
1301
            break;
1302
        }
1303
1304
        include_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
1305
1306
        // Instatiate email settings and service
1307
        $emailSettings = new EmailSettings($SETTINGS);
1308
        $emailService = new EmailService();
1309
1310
        $rows = DB::query('SELECT * FROM ' . prefixTable('emails') . ' WHERE status = %s OR status = %s', 'not_sent', '');
1311
        foreach ($rows as $record) {
1312
            //send email
1313
            $email = $emailService->sendMail(
1314
                $record['subject'],
1315
                $record['body'],
1316
                $record['receivers'],
1317
                $emailSettings
1318
            );
1319
            $ret = json_decode(
1320
                $email,
1321
                true
1322
            );
1323
1324
            if (empty($ret['error']) === false) {
1325
                //update item_id in files table
1326
                DB::update(
1327
                    prefixTable('emails'),
1328
                    array(
1329
                        'status' => 'not_sent',
1330
                    ),
1331
                    'timestamp = %s',
1332
                    $record['timestamp']
1333
                );
1334
            } else {
1335
                //delete from DB
1336
                DB::delete(prefixTable('emails'), 'timestamp = %s', $record['timestamp']);
1337
            }
1338
        }
1339
1340
        //update LOG
1341
        logEvents($SETTINGS, 'admin_action', 'Emails backlog', (string) $session->get('user-id'), $session->get('user-login'));
1342
1343
        echo prepareExchangedData(
1344
            array(
1345
                'error' => false,
1346
                'message' => '',
1347
            ),
1348
            'encode'
1349
        );
1350
        break;
1351
1352
        /*
1353
    * Attachments encryption
1354
    */
1355
    case 'admin_action_attachments_cryption':
1356
        // Check KEY
1357
        if ($post_key !== $session->get('key')) {
1358
            echo prepareExchangedData(
1359
                array(
1360
                    'error' => true,
1361
                    'message' => $lang->get('key_is_not_correct'),
1362
                ),
1363
                'encode'
1364
            );
1365
            break;
1366
        }
1367
        // Is admin?
1368
        if ($session->get('user-admin') === 1) {
1369
            echo prepareExchangedData(
1370
                array(
1371
                    'error' => true,
1372
                    'message' => $lang->get('error_not_allowed_to'),
1373
                ),
1374
                'encode'
1375
            );
1376
            break;
1377
        }
1378
1379
        require_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
1380
1381
        // init
1382
        $filesList = array();
1383
1384
        // get through files
1385
        if (null !== $post_option && empty($post_option) === false) {
1386
            // Loop on files
1387
            $rows = DB::query(
1388
                'SELECT id, file, status
1389
                FROM ' . prefixTable('files')
1390
            );
1391
            foreach ($rows as $record) {
1392
                if (is_file($SETTINGS['path_to_upload_folder'] . '/' . $record['file'])) {
1393
                    $addFile = false;
1394
                    if (($post_option === 'attachments-decrypt' && $record['status'] === 'encrypted')
1395
                        || ($post_option === 'attachments-encrypt' && $record['status'] === 'clear')
1396
                    ) {
1397
                        $addFile = true;
1398
                    }
1399
1400
                    if ($addFile === true) {
1401
                        array_push($filesList, $record['id']);
1402
                    }
1403
                }
1404
            }
1405
        } else {
1406
            echo prepareExchangedData(
1407
                array(
1408
                    'error' => true,
1409
                    'message' => $lang->get('error_not_allowed_to'),
1410
                ),
1411
                'encode'
1412
            );
1413
        }
1414
1415
        echo prepareExchangedData(
1416
            array(
1417
                'error' => false,
1418
                'message' => '',
1419
                'list' => $filesList,
1420
                'counter' => 0,
1421
            ),
1422
            'encode'
1423
        );
1424
        break;
1425
1426
        /*
1427
     * Attachments encryption - Treatment in several loops
1428
     */
1429
    case 'admin_action_attachments_cryption_continu':
1430
        // Check KEY
1431
        if ($post_key !== $session->get('key')) {
1432
            echo prepareExchangedData(
1433
                array(
1434
                    'error' => true,
1435
                    'message' => $lang->get('key_is_not_correct'),
1436
                ),
1437
                'encode'
1438
            );
1439
            break;
1440
        }
1441
        // Is admin?
1442
        if ($session->get('user-admin') === 1) {
1443
            echo prepareExchangedData(
1444
                array(
1445
                    'error' => true,
1446
                    'message' => $lang->get('error_not_allowed_to'),
1447
                ),
1448
                'encode'
1449
            );
1450
            break;
1451
        }
1452
1453
        // Prepare variables
1454
        $post_list = filter_var_array($post_list, FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1455
        $post_counter = filter_var($post_counter, FILTER_SANITIZE_NUMBER_INT);
1456
1457
        include $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
1458
        include_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
1459
1460
        $cpt = 0;
1461
        $continu = true;
1462
        $newFilesList = array();
1463
        $message = '';
1464
1465
        // treat 10 files
1466
        foreach ($post_list as $file) {
1467
            if ($cpt < 5) {
1468
                // Get file name
1469
                $file_info = DB::queryFirstRow(
1470
                    'SELECT file
1471
                    FROM ' . prefixTable('files') . '
1472
                    WHERE id = %i',
1473
                    $file
1474
                );
1475
1476
                // skip file is Coherancey not respected
1477
                if (is_file($SETTINGS['path_to_upload_folder'] . '/' . $file_info['file'])) {
1478
                    // Case where we want to decrypt
1479
                    if ($post_option === 'decrypt') {
1480
                        prepareFileWithDefuse(
1481
                            'decrypt',
1482
                            $SETTINGS['path_to_upload_folder'] . '/' . $file_info['file'],
1483
                            $SETTINGS['path_to_upload_folder'] . '/defuse_temp_' . $file_info['file'],
1484
                        );
1485
                        // Case where we want to encrypt
1486
                    } elseif ($post_option === 'encrypt') {
1487
                        prepareFileWithDefuse(
1488
                            'encrypt',
1489
                            $SETTINGS['path_to_upload_folder'] . '/' . $file_info['file'],
1490
                            $SETTINGS['path_to_upload_folder'] . '/defuse_temp_' . $file_info['file'],
1491
                        );
1492
                    }
1493
                    // Do file cleanup
1494
                    fileDelete($SETTINGS['path_to_upload_folder'] . '/' . $file_info['file'], $SETTINGS);
1495
                    rename(
1496
                        $SETTINGS['path_to_upload_folder'] . '/defuse_temp_' . $file_info['file'],
1497
                        $SETTINGS['path_to_upload_folder'] . '/' . $file_info['file']
1498
                    );
1499
1500
                    // store in DB
1501
                    DB::update(
1502
                        prefixTable('files'),
1503
                        array(
1504
                            'status' => $post_option === 'attachments-decrypt' ? 'clear' : 'encrypted',
1505
                        ),
1506
                        'id = %i',
1507
                        $file
1508
                    );
1509
1510
                    ++$cpt;
1511
                }
1512
            } else {
1513
                // build list
1514
                array_push($newFilesList, $file);
1515
            }
1516
        }
1517
1518
        // Should we stop
1519
        if (count($newFilesList) === 0) {
1520
            $continu = false;
1521
1522
            //update LOG
1523
            logEvents(
1524
                $SETTINGS,
1525
                'admin_action',
1526
                'attachments_encryption_changed',
1527
                (string) $session->get('user-id'),
1528
                $session->get('user-login'),
1529
                $post_option === 'attachments-decrypt' ? 'clear' : 'encrypted'
1530
            );
1531
1532
            $message = $lang->get('last_execution') . ' ' .
1533
                date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) time()) .
1534
                '<i class="fas fa-check text-success ml-2 mr-3"></i>';
1535
        }
1536
1537
        echo prepareExchangedData(
1538
            array(
1539
                'error' => false,
1540
                'message' => $message,
1541
                'list' => $newFilesList,
1542
                'counter' => $post_cpt + $cpt,
1543
                'continu' => $continu,
1544
            ),
1545
            'encode'
1546
        );
1547
        break;
1548
1549
        /*
1550
     * API save key
1551
     */
1552
    case 'admin_action_api_save_key':
1553
        // Check KEY
1554
        if ($post_key !== $session->get('key')) {
1555
            echo prepareExchangedData(
1556
                array(
1557
                    'error' => true,
1558
                    'message' => $lang->get('key_is_not_correct'),
1559
                ),
1560
                'encode'
1561
            );
1562
            break;
1563
        }
1564
        // Is admin?
1565
        if ($session->get('user-admin') === 1) {
1566
            echo prepareExchangedData(
1567
                array(
1568
                    'error' => true,
1569
                    'message' => $lang->get('error_not_allowed_to'),
1570
                ),
1571
                'encode'
1572
            );
1573
            break;
1574
        }
1575
1576
        // decrypt and retrieve data in JSON format
1577
        $dataReceived = prepareExchangedData(
1578
            $post_data,
1579
            'decode'
1580
        );
1581
1582
        $post_label = isset($dataReceived['label']) === true ? filter_var($dataReceived['label'], FILTER_SANITIZE_FULL_SPECIAL_CHARS) : '';
1583
        $post_action = filter_var($dataReceived['action'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1584
        $timestamp = time();
1585
1586
        // add new key
1587
        if (null !== $post_action && $post_action === 'add') {
1588
            // Generate KEY
1589
            require_once 'main.functions.php';
1590
            $key = GenerateCryptKey(39, false, true, true, false, true);
1591
1592
            // Generate objectKey
1593
            //$object = doDataEncryption($key, SECUREFILE.':'.$timestamp);
1594
            
1595
            // Save in DB
1596
            DB::insert(
1597
                prefixTable('api'),
1598
                array(
1599
                    'increment_id' => null,
1600
                    'type' => 'key',
1601
                    'label' => $post_label,
1602
                    'value' => $key, //$object['encrypted'],
1603
                    'timestamp' => $timestamp,
1604
                    //'user_id' => -1,
1605
                )
1606
            );
1607
1608
            $post_id = DB::insertId();
1609
            // Update existing key
1610
        } elseif (null !== $post_action && $post_action === 'update') {
1611
            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);
1612
1613
            DB::update(
1614
                prefixTable('api'),
1615
                array(
1616
                    'label' => $post_label,
1617
                    'timestamp' => $timestamp,
1618
                ),
1619
                'increment_id=%i',
1620
                $post_id
1621
            );
1622
            // Delete existing key
1623
        } elseif (null !== $post_action && $post_action === 'delete') {
1624
            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);
1625
1626
            DB::query(
1627
                'DELETE FROM ' . prefixTable('api') . ' WHERE increment_id = %i',
1628
                $post_id
1629
            );
1630
        }
1631
1632
        // send data
1633
        echo prepareExchangedData(
1634
            array(
1635
                'error' => false,
1636
                'message' => '',
1637
                'keyId' => $post_id,
1638
                'key' => isset($key) === true ? $key : '',
1639
            ),
1640
            'encode'
1641
        );
1642
        break;
1643
1644
        /*
1645
       * API save key
1646
    */
1647
    case 'admin_action_api_save_ip':
1648
        // Check KEY
1649
        if ($post_key !== $session->get('key')) {
1650
            echo prepareExchangedData(
1651
                array(
1652
                    'error' => true,
1653
                    'message' => $lang->get('key_is_not_correct'),
1654
                ),
1655
                'encode'
1656
            );
1657
            break;
1658
        }
1659
        // Is admin?
1660
        if ($session->get('user-admin') === 1) {
1661
            echo prepareExchangedData(
1662
                array(
1663
                    'error' => true,
1664
                    'message' => $lang->get('error_not_allowed_to'),
1665
                ),
1666
                'encode'
1667
            );
1668
            break;
1669
        }
1670
1671
        // decrypt and retrieve data in JSON format
1672
        $dataReceived = prepareExchangedData(
1673
            $post_data,
1674
            'decode'
1675
        );
1676
1677
        $post_action = filter_var($dataReceived['action'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1678
1679
        // add new key
1680
        if (null !== $post_action && $post_action === 'add') {
1681
            $post_label = filter_var($dataReceived['label'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1682
            $post_ip = filter_var($dataReceived['ip'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1683
1684
            // Store in DB
1685
            DB::insert(
1686
                prefixTable('api'),
1687
                array(
1688
                    'increment_id' => null,
1689
                    'type' => 'ip',
1690
                    'label' => $post_label,
1691
                    'value' => $post_ip,
1692
                    'timestamp' => time(),
1693
                )
1694
            );
1695
1696
            $post_id = DB::insertId();
1697
            // Update existing key
1698
        } elseif (null !== $post_action && $post_action === 'update') {
1699
            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1700
            $post_field = filter_var($dataReceived['field'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1701
            $post_value = filter_var($dataReceived['value'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1702
            if ($post_field === 'value') {
1703
                $arr = array(
1704
                    'value' => $post_value,
1705
                    'timestamp' => time(),
1706
                );
1707
            } else {
1708
                $arr = array(
1709
                    'label' => $post_value,
1710
                    'timestamp' => time(),
1711
                );
1712
            }
1713
            DB::update(
1714
                prefixTable('api'),
1715
                $arr,
1716
                'increment_id=%i',
1717
                $post_id
1718
            );
1719
            // Delete existing key
1720
        } elseif (null !== $post_action && $post_action === 'delete') {
1721
            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1722
            DB::query('DELETE FROM ' . prefixTable('api') . ' WHERE increment_id=%i', $post_id);
1723
        }
1724
1725
        echo prepareExchangedData(
1726
            array(
1727
                'error' => false,
1728
                'message' => '',
1729
                'ipId' => $post_id,
1730
            ),
1731
            'encode'
1732
        );
1733
        break;
1734
1735
    case 'save_api_status':
1736
        // Do query
1737
        DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'api');
1738
        $counter = DB::count();
1739
        if ($counter === 0) {
1740
            DB::insert(
1741
                prefixTable('misc'),
1742
                array(
1743
                    'type' => 'admin',
1744
                    'intitule' => 'api',
1745
                    'valeur' => $post_status,
1746
                    'created_at' => time(),
1747
                )
1748
            );
1749
        } else {
1750
            DB::update(
1751
                prefixTable('misc'),
1752
                array(
1753
                    'valeur' => $post_status,
1754
                    'updated_at' => time(),
1755
                ),
1756
                'type = %s AND intitule = %s',
1757
                'admin',
1758
                'api'
1759
            );
1760
        }
1761
        $SETTINGS['api'] = $post_status;
1762
        break;
1763
1764
    case 'run_duo_config_check':
1765
        //Libraries call
1766
        require_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
1767
        // Check KEY
1768
        if ($post_key !== $session->get('key')) {
1769
            echo prepareExchangedData(
1770
                array(
1771
                    'error' => true,
1772
                    'message' => $lang->get('key_is_not_correct'),
1773
                ),
1774
                'encode'
1775
            );
1776
            break;
1777
        }
1778
1779
        // decrypt and retreive data in JSON format
1780
        $dataReceived = prepareExchangedData(
1781
            $post_data,
1782
            'decode'
1783
        );
1784
1785
        // Check if we have what we need first
1786
        if (empty($dataReceived['duo_ikey']) || empty($dataReceived['duo_skey']) || empty($dataReceived['duo_host'])) {
1787
            echo prepareExchangedData(
1788
                array(
1789
                    'error' => true,
1790
                    'message' => $lang->get('data_are_missing'),
1791
                ),
1792
                'encode'
1793
            );
1794
            break;
1795
        }
1796
1797
        // Run Duo Config Check
1798
        try {
1799
            $duo_client = new Client(
1800
                $dataReceived['duo_ikey'],
1801
                $dataReceived['duo_skey'],
1802
                $dataReceived['duo_host'],
1803
                $SETTINGS['cpassman_url'].'/'.DUO_CALLBACK
1804
            );
1805
        } catch (DuoException $e) {
1806
            if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) {
1807
                error_log('TEAMPASS Error - duo config - '.$e->getMessage());
1808
            }
1809
            // deepcode ignore ServerLeak: Data is encrypted before being sent
1810
            echo prepareExchangedData(
1811
                    array(
1812
                        'error' => true,
1813
                        'message' => $lang->get('duo_config_error'),
1814
                    ),
1815
                    'encode'
1816
            );
1817
            break;
1818
        }
1819
1820
        // Run healthcheck against Duo with the config
1821
        try {
1822
            $duo_client->healthCheck();
1823
        } catch (DuoException $e) {
1824
            if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) {
1825
                error_log('TEAMPASS Error - duo config - '.$e->getMessage());
1826
            }
1827
            // deepcode ignore ServerLeak: Data is encrypted before being sent
1828
            echo prepareExchangedData(
1829
                    array(
1830
                        'error' => true,
1831
                        'message' => $lang->get('duo_error_check_config'),
1832
                    ),
1833
                    'encode'
1834
            );
1835
            break;
1836
        }
1837
1838
        // send data
1839
        echo prepareExchangedData(
1840
            array(
1841
                'error' => false,
1842
                'message' => '',
1843
            ),
1844
            'encode'
1845
        );
1846
        break;
1847
1848
    case 'save_google_options':
1849
        // Check KEY and rights
1850
        if ($post_key !== $session->get('key')) {
1851
            echo prepareExchangedData(
1852
                array(
1853
                    'error' => true,
1854
                    'message' => $lang->get('key_is_not_correct'),
1855
                ),
1856
                'encode'
1857
            );
1858
            break;
1859
        }
1860
        // decrypt and retreive data in JSON format
1861
        $dataReceived = prepareExchangedData(
1862
            $post_data,
1863
            'decode'
1864
        );
1865
1866
        // Google Authentication
1867
        if (htmlspecialchars_decode($dataReceived['google_authentication']) === 'false') {
1868
            $tmp = 0;
1869
        } else {
1870
            $tmp = 1;
1871
        }
1872
        DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'google_authentication');
1873
        $counter = DB::count();
1874
        if ($counter === 0) {
1875
            DB::insert(
1876
                prefixTable('misc'),
1877
                array(
1878
                    'type' => 'admin',
1879
                    'intitule' => 'google_authentication',
1880
                    'valeur' => $tmp,
1881
                    'created_at' => time(),
1882
                )
1883
            );
1884
        } else {
1885
            DB::update(
1886
                prefixTable('misc'),
1887
                array(
1888
                    'valeur' => $tmp,
1889
                    'updated_at' => time(),
1890
                ),
1891
                'type = %s AND intitule = %s',
1892
                'admin',
1893
                'google_authentication'
1894
            );
1895
        }
1896
        $SETTINGS['google_authentication'] = htmlspecialchars_decode($dataReceived['google_authentication']);
1897
1898
        // ga_website_name
1899
        if (is_null($dataReceived['ga_website_name']) === false) {
1900
            DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'ga_website_name');
1901
            $counter = DB::count();
1902
            if ($counter === 0) {
1903
                DB::insert(
1904
                    prefixTable('misc'),
1905
                    array(
1906
                        'type' => 'admin',
1907
                        'intitule' => 'ga_website_name',
1908
                        'valeur' => htmlspecialchars_decode($dataReceived['ga_website_name']),
1909
                        'created_at' => time(),
1910
                    )
1911
                );
1912
            } else {
1913
                DB::update(
1914
                    prefixTable('misc'),
1915
                    array(
1916
                        'valeur' => htmlspecialchars_decode($dataReceived['ga_website_name']),
1917
                        'updated_at' => time(),
1918
                    ),
1919
                    'type = %s AND intitule = %s',
1920
                    'admin',
1921
                    'ga_website_name'
1922
                );
1923
            }
1924
            $SETTINGS['ga_website_name'] = htmlspecialchars_decode($dataReceived['ga_website_name']);
1925
        } else {
1926
            $SETTINGS['ga_website_name'] = '';
1927
        }
1928
1929
        // send data
1930
        echo '[{"result" : "' . addslashes($lang['done']) . '" , "error" : ""}]';
1931
        break;
1932
1933
    case 'save_agses_options':
1934
        // Check KEY and rights
1935
        if ($post_key !== $session->get('key')) {
1936
            echo prepareExchangedData(
1937
                array(
1938
                    'error' => true,
1939
                    'message' => $lang->get('key_is_not_correct'),
1940
                ),
1941
                'encode'
1942
            );
1943
            break;
1944
        }
1945
        // decrypt and retreive data in JSON format
1946
        $dataReceived = prepareExchangedData(
1947
            $post_data,
1948
            'decode'
1949
        );
1950
1951
        // agses_hosted_url
1952
        if (!is_null($dataReceived['agses_hosted_url'])) {
1953
            DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'agses_hosted_url');
1954
            $counter = DB::count();
1955
            if ($counter === 0) {
1956
                DB::insert(
1957
                    prefixTable('misc'),
1958
                    array(
1959
                        'type' => 'admin',
1960
                        'intitule' => 'agses_hosted_url',
1961
                        'valeur' => htmlspecialchars_decode($dataReceived['agses_hosted_url']),
1962
                        'created_at' => time(),
1963
                    )
1964
                );
1965
            } else {
1966
                DB::update(
1967
                    prefixTable('misc'),
1968
                    array(
1969
                        'valeur' => htmlspecialchars_decode($dataReceived['agses_hosted_url']),
1970
                        'updated_at' => time(),
1971
                    ),
1972
                    'type = %s AND intitule = %s',
1973
                    'admin',
1974
                    'agses_hosted_url'
1975
                );
1976
            }
1977
            $SETTINGS['agses_hosted_url'] = htmlspecialchars_decode($dataReceived['agses_hosted_url']);
1978
        } else {
1979
            $SETTINGS['agses_hosted_url'] = '';
1980
        }
1981
1982
        // agses_hosted_id
1983
        if (!is_null($dataReceived['agses_hosted_id'])) {
1984
            DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'agses_hosted_id');
1985
            $counter = DB::count();
1986
            if ($counter === 0) {
1987
                DB::insert(
1988
                    prefixTable('misc'),
1989
                    array(
1990
                        'type' => 'admin',
1991
                        'intitule' => 'agses_hosted_id',
1992
                        'valeur' => htmlspecialchars_decode($dataReceived['agses_hosted_id']),
1993
                        'created_at' => time(),
1994
                    )
1995
                );
1996
            } else {
1997
                DB::update(
1998
                    prefixTable('misc'),
1999
                    array(
2000
                        'valeur' => htmlspecialchars_decode($dataReceived['agses_hosted_id']),
2001
                        'updated_at' => time(),
2002
                    ),
2003
                    'type = %s AND intitule = %s',
2004
                    'admin',
2005
                    'agses_hosted_id'
2006
                );
2007
            }
2008
            $SETTINGS['agses_hosted_id'] = htmlspecialchars_decode($dataReceived['agses_hosted_id']);
2009
        } else {
2010
            $SETTINGS['agses_hosted_id'] = '';
2011
        }
2012
2013
        // agses_hosted_apikey
2014
        if (!is_null($dataReceived['agses_hosted_apikey'])) {
2015
            DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'agses_hosted_apikey');
2016
            $counter = DB::count();
2017
            if ($counter === 0) {
2018
                DB::insert(
2019
                    prefixTable('misc'),
2020
                    array(
2021
                        'type' => 'admin',
2022
                        'intitule' => 'agses_hosted_apikey',
2023
                        'valeur' => htmlspecialchars_decode($dataReceived['agses_hosted_apikey']),
2024
                        'created_at' => time(),
2025
                    )
2026
                );
2027
            } else {
2028
                DB::update(
2029
                    prefixTable('misc'),
2030
                    array(
2031
                        'valeur' => htmlspecialchars_decode($dataReceived['agses_hosted_apikey']),
2032
                        'updated_at' => time(),
2033
                    ),
2034
                    'type = %s AND intitule = %s',
2035
                    'admin',
2036
                    'agses_hosted_apikey'
2037
                );
2038
            }
2039
            $SETTINGS['agses_hosted_apikey'] = htmlspecialchars_decode($dataReceived['agses_hosted_apikey']);
2040
        } else {
2041
            $SETTINGS['agses_hosted_apikey'] = '';
2042
        }
2043
2044
        // send data
2045
        echo '[{"result" : "' . addslashes($lang['done']) . '" , "error" : ""}]';
2046
        break;
2047
2048
    case 'save_option_change':
2049
        // Check KEY and rights
2050
        if ($post_key !== $session->get('key')) {
2051
            echo prepareExchangedData(
2052
                array(
2053
                    'error' => true,
2054
                    'message' => $lang->get('key_is_not_correct'),
2055
                ),
2056
                'encode'
2057
            );
2058
            break;
2059
        }
2060
        
2061
        // decrypt and retreive data in JSON format
2062
        $dataReceived = prepareExchangedData(
2063
            $post_data,
2064
            'decode'
2065
        );
2066
        
2067
        // prepare data
2068
        $post_value = filter_var($dataReceived['value'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2069
        $post_field = filter_var($dataReceived['field'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2070
        $post_translate = isset($dataReceived['translate']) === true ? filter_var($dataReceived['translate'], FILTER_SANITIZE_FULL_SPECIAL_CHARS) : '';
2071
        
2072
        require_once 'main.functions.php';
2073
2074
        // In case of key, then encrypt it
2075
        if ($post_field === 'bck_script_passkey') {
2076
            $post_value = cryption(
2077
                $post_value,
2078
                '',
2079
                'encrypt',
2080
                $SETTINGS
2081
            )['string'];
2082
        }
2083
2084
        // Check if setting is already in DB. If NO then insert, if YES then update.
2085
        $data = DB::query(
2086
            'SELECT * FROM ' . prefixTable('misc') . '
2087
            WHERE type = %s AND intitule = %s',
2088
            'admin',
2089
            $post_field
2090
        );
2091
        $counter = DB::count();
2092
        if ($counter === 0) {
2093
            DB::insert(
2094
                prefixTable('misc'),
2095
                array(
2096
                    'valeur' => $post_value,
2097
                    'type' => 'admin',
2098
                    'intitule' => $post_field,
2099
                    'created_at' => time(),
2100
                )
2101
            );
2102
            // in case of stats enabled, add the actual time
2103
            if ($post_field === 'send_stats') {
2104
                DB::insert(
2105
                    prefixTable('misc'),
2106
                    array(
2107
                        'valeur' => time(),
2108
                        'type' => 'admin',
2109
                        'intitule' => $post_field . '_time',
2110
                        'updated_at' => time(),
2111
                    )
2112
                );
2113
            }
2114
        } else {
2115
            // Update DB settings
2116
            DB::update(
2117
                prefixTable('misc'),
2118
                array(
2119
                    'valeur' => $post_value,
2120
                    'updated_at' => time(),
2121
                ),
2122
                'type = %s AND intitule = %s',
2123
                'admin',
2124
                $post_field
2125
            );
2126
2127
            // in case of stats enabled, update the actual time
2128
            if ($post_field === 'send_stats') {
2129
                // Check if previous time exists, if not them insert this value in DB
2130
                DB::query(
2131
                    'SELECT * FROM ' . prefixTable('misc') . '
2132
                    WHERE type = %s AND intitule = %s',
2133
                    'admin',
2134
                    $post_field . '_time'
2135
                );
2136
                $counter = DB::count();
2137
                if ($counter === 0) {
2138
                    DB::insert(
2139
                        prefixTable('misc'),
2140
                        array(
2141
                            'valeur' => 0,
2142
                            'type' => 'admin',
2143
                            'intitule' => $post_field . '_time',
2144
                            'created_at' => time(),
2145
                        )
2146
                    );
2147
                } else {
2148
                    DB::update(
2149
                        prefixTable('misc'),
2150
                        array(
2151
                            'valeur' => 0,
2152
                            'updated_at' => time(),
2153
                        ),
2154
                        'type = %s AND intitule = %s',
2155
                        'admin',
2156
                        $post_field
2157
                    );
2158
                }
2159
            }
2160
        }
2161
2162
        // special Cases
2163
        if ($post_field === 'cpassman_url') {
2164
            // update also jsUrl for CSFP protection
2165
            $jsUrl = $post_value . '/includes/libraries/csrfp/js/csrfprotector.js';
2166
            $csrfp_file = '../includes/libraries/csrfp/libs/csrfp.config.php';
2167
            $data = file_get_contents($csrfp_file);
2168
            $posJsUrl = strpos($data, '"jsUrl" => "');
2169
            $posEndLine = strpos($data, '",', $posJsUrl);
2170
            $line = substr($data, $posJsUrl, ($posEndLine - $posJsUrl + 2));
2171
            $newdata = str_replace($line, '"jsUrl" => "' . filter_var($jsUrl, FILTER_SANITIZE_FULL_SPECIAL_CHARS) . '",', $data);
2172
            file_put_contents($csrfp_file, $newdata);
2173
        } elseif ($post_field === 'restricted_to_input' && (int) $post_value === 0) {
2174
            DB::update(
2175
                prefixTable('misc'),
2176
                array(
2177
                    'valeur' => 0,
2178
                    'updated_at' => time(),
2179
                ),
2180
                'type = %s AND intitule = %s',
2181
                'admin',
2182
                'restricted_to_roles'
2183
            );
2184
        }
2185
2186
        // Update last settings change timestamp
2187
        // This to ensure that the settings are refreshed in the session
2188
        $settings = $session->get('teampass-settings');
2189
        $settings['timestamp'] = time();
2190
        $session->set('teampass-settings', $settings);
2191
2192
        // Encrypt data to return
2193
        echo prepareExchangedData(
2194
            array(
2195
                'error' => false,
2196
                'misc' => $counter . ' ; ' . $SETTINGS[$post_field],
2197
                'message' => empty($post_translate) === false ? $lang->get($post_translate) : '',
2198
            ),
2199
            'encode'
2200
        );
2201
        break;
2202
2203
    case 'get_values_for_statistics':
2204
        // Check KEY and rights
2205
        if ($post_key !== $session->get('key')) {
2206
            echo prepareExchangedData(
2207
                array(
2208
                    'error' => true,
2209
                    'message' => $lang->get('key_is_not_correct'),
2210
                ),
2211
                'encode'
2212
            );
2213
            break;
2214
        }
2215
2216
        // Encrypt data to return
2217
        echo prepareExchangedData(
2218
            getStatisticsData($SETTINGS),
2219
            'encode'
2220
        );
2221
2222
        break;
2223
2224
    case 'save_sending_statistics':
2225
        // Check KEY and rights
2226
        if ($post_key !== $session->get('key')) {
2227
            echo prepareExchangedData(
2228
                array(
2229
                    'error' => true,
2230
                    'message' => $lang->get('key_is_not_correct'),
2231
                ),
2232
                'encode'
2233
            );
2234
            break;
2235
        }
2236
2237
        // send statistics
2238
        if (null !== $post_status) {
2239
            DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'send_stats');
2240
            $counter = DB::count();
2241
            if ($counter === 0) {
2242
                DB::insert(
2243
                    prefixTable('misc'),
2244
                    array(
2245
                        'type' => 'admin',
2246
                        'intitule' => 'send_stats',
2247
                        'valeur' => $post_status,
2248
                        'created_at' => time(),
2249
                    )
2250
                );
2251
            } else {
2252
                DB::update(
2253
                    prefixTable('misc'),
2254
                    array(
2255
                        'valeur' => $post_status,
2256
                        'updated_at' => time(),
2257
                    ),
2258
                    'type = %s AND intitule = %s',
2259
                    'admin',
2260
                    'send_stats'
2261
                );
2262
            }
2263
            $SETTINGS['send_stats'] = $post_status;
2264
        } else {
2265
            $SETTINGS['send_stats'] = '0';
2266
        }
2267
2268
        // send statistics items
2269
        if (null !== $post_list) {
2270
            DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'send_statistics_items');
2271
            $counter = DB::count();
2272
            if ($counter === 0) {
2273
                DB::insert(
2274
                    prefixTable('misc'),
2275
                    array(
2276
                        'type' => 'admin',
2277
                        'intitule' => 'send_statistics_items',
2278
                        'valeur' => $post_list,
2279
                        'created_at' => time(),
2280
                    )
2281
                );
2282
            } else {
2283
                DB::update(
2284
                    prefixTable('misc'),
2285
                    array(
2286
                        'valeur' => $post_list,
2287
                        'updated_at' => time(),
2288
                    ),
2289
                    'type = %s AND intitule = %s',
2290
                    'admin',
2291
                    'send_statistics_items'
2292
                );
2293
            }
2294
            $SETTINGS['send_statistics_items'] = $post_list;
2295
        } else {
2296
            $SETTINGS['send_statistics_items'] = '';
2297
        }
2298
2299
        // send data
2300
        echo '[{"error" : false}]';
2301
        break;
2302
2303
    case 'is_backup_table_existing':
2304
        // Check KEY and rights
2305
        if ($post_key !== $session->get('key')) {
2306
            echo prepareExchangedData(
2307
                array(
2308
                    'error' => true,
2309
                    'message' => $lang->get('key_is_not_correct'),
2310
                ),
2311
                'encode'
2312
            );
2313
            break;
2314
        }
2315
2316
        if (DB::query("SHOW TABLES LIKE '" . prefixTable('sk_reencrypt_backup') . "'")) {
2317
            if (DB::count() === 1) {
2318
                echo 1;
2319
            } else {
2320
                echo 0;
2321
            }
2322
        } else {
2323
            echo 0;
2324
        }
2325
2326
        break;
2327
2328
    case 'get_list_of_roles':
2329
        // Check KEY and rights
2330
        if ($post_key !== $session->get('key')) {
2331
            echo prepareExchangedData(
2332
                array(
2333
                    'error' => true,
2334
                    'message' => $lang->get('key_is_not_correct'),
2335
                ),
2336
                'encode'
2337
            );
2338
            break;
2339
        }
2340
2341
        // decrypt and retreive data in JSON format
2342
        $dataReceived = prepareExchangedData(
2343
            $post_data,
2344
            'decode'
2345
        );
2346
2347
        // prepare data
2348
        $sourcePage = filter_var($dataReceived['source_page'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2349
        if ($sourcePage === 'ldap') {
2350
            $selected_administrated_by = isset($SETTINGS['ldap_new_user_is_administrated_by']) && $SETTINGS['ldap_new_user_is_administrated_by'] === '0' ? 1 : 0;
2351
            $selected_new_user_role = isset($SETTINGS['ldap_new_user_role']) && $SETTINGS['ldap_new_user_role'] === '0' ? 1 : 0;
2352
        } elseif ($sourcePage === 'oauth') {
2353
            $selected_administrated_by = isset($SETTINGS['oauth_new_user_is_administrated_by']) && $SETTINGS['oauth_new_user_is_administrated_by'] === '0' ? 1 : 0;
2354
            $selected_new_user_role = isset($SETTINGS['oauth_selfregistered_user_belongs_to_role']) && $SETTINGS['oauth_selfregistered_user_belongs_to_role'] === '0' ? 1 : '';
2355
        } else {
2356
            echo prepareExchangedData(
2357
                [], 
2358
                'encode'
2359
            );
2360
    
2361
            break;
2362
        }
2363
2364
        $json = array();
2365
        array_push(
2366
            $json,
2367
            array(
2368
                'id' => '0',
2369
                'title' => $lang->get('god'),
2370
                'selected_administrated_by' => $selected_administrated_by,
2371
                'selected_role' => $selected_new_user_role,
2372
            )
2373
        );
2374
2375
        $rows = DB::query(
2376
            'SELECT id, title
2377
                FROM ' . prefixTable('roles_title') . '
2378
                ORDER BY title ASC'
2379
        );
2380
        foreach ($rows as $record) {
2381
            if ($sourcePage === 'ldap') {
2382
                $selected_administrated_by = isset($SETTINGS['ldap_new_user_is_administrated_by']) && $SETTINGS['ldap_new_user_is_administrated_by'] === $record['id'] ? 1 : 0;
2383
                $selected_new_user_role = isset($SETTINGS['ldap_new_user_role']) && $SETTINGS['ldap_new_user_role'] === $record['id'] ? 1 : 0;
2384
            } elseif ($sourcePage === 'oauth') {
2385
                $selected_administrated_by = isset($SETTINGS['oauth_new_user_is_administrated_by']) && $SETTINGS['oauth_new_user_is_administrated_by'] === $record['id'] ? 1 : 0;
2386
                $selected_new_user_role = isset($SETTINGS['oauth_selfregistered_user_belongs_to_role']) && $SETTINGS['oauth_selfregistered_user_belongs_to_role'] === $record['id'] ? 1 : 0;
2387
            }
2388
            array_push(
2389
                $json,
2390
                array(
2391
                    'id' => $record['id'],
2392
                    'title' => addslashes($record['title']),
2393
                    'selected_administrated_by' => $selected_administrated_by,
2394
                    'selected_role' => $selected_new_user_role,
2395
                )
2396
            );
2397
        }
2398
2399
        echo prepareExchangedData(
2400
            $json, 
2401
            'encode'
2402
        );
2403
2404
        break;
2405
2406
    case 'save_user_change':
2407
        // Check KEY and rights
2408
        if ($post_key !== $session->get('key')) {
2409
            echo prepareExchangedData(
2410
                array(
2411
                    'error' => true,
2412
                    'message' => $lang->get('key_is_not_correct'),
2413
                ),
2414
                'encode'
2415
            );
2416
            break;
2417
        }
2418
2419
        // decrypt and retrieve data in JSON format
2420
        $dataReceived = prepareExchangedData(
2421
            $post_data,
2422
            'decode'
2423
        );
2424
2425
        $post_increment_id = isset($dataReceived['increment_id']) === true ? filter_var($dataReceived['increment_id'], FILTER_SANITIZE_NUMBER_INT) : '';
2426
        $post_field = filter_var($dataReceived['field'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2427
        $post_value = filter_var($dataReceived['value'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2428
2429
        if (is_numeric($post_increment_id) === false) {
2430
            echo prepareExchangedData(
2431
                array(
2432
                    'error' => true,
2433
                    'message' => $lang->get('no_user'),
2434
                ),
2435
                'encode'
2436
            );
2437
            break;
2438
        }
2439
        
2440
        //update.
2441
        DB::debugMode(false);
2442
        DB::update(
2443
            prefixTable('api'),
2444
            array(
2445
                $post_field => $post_value,
2446
            ),
2447
            'increment_id = %i',
2448
            (int) $post_increment_id
2449
        );
2450
        DB::debugMode(false);
2451
        //log
2452
        logEvents($SETTINGS, 'system', 'api_user_readonly', (string) $session->get('user-id'), $session->get('user-login'));
2453
2454
        echo prepareExchangedData(
2455
            array(
2456
                'error' => false,
2457
                'message' => '',
2458
            ),
2459
            'encode'
2460
        );
2461
2462
        break;
2463
    
2464
    case "tablesIntegrityCheck":
2465
        // Check KEY and rights
2466
        if ($post_key !== $session->get('key')) {
2467
            echo prepareExchangedData(
2468
                array(
2469
                    'error' => true,
2470
                    'message' => $lang->get('key_is_not_correct'),
2471
                ),
2472
                'encode'
2473
            );
2474
            break;
2475
        }
2476
2477
        $ret = tablesIntegrityCheck();
2478
        
2479
        echo prepareExchangedData(
2480
            array(
2481
                'error' => $ret['error'],
2482
                'message' => $ret['message'],
2483
                'tables' => json_encode($ret['array'], JSON_FORCE_OBJECT),
2484
            ),
2485
            'encode'
2486
        );
2487
2488
        break;
2489
2490
    case "filesIntegrityCheck":
2491
        // Check KEY and rights
2492
        if ($post_key !== $session->get('key')) {
2493
            echo prepareExchangedData(
2494
                array(
2495
                    'error' => true,
2496
                    'message' => $lang->get('key_is_not_correct'),
2497
                ),
2498
                'encode'
2499
            );
2500
            break;
2501
        }
2502
2503
        // New hash-based integrity check
2504
        $SETTINGS['cpassman_dir'] = rtrim($SETTINGS['cpassman_dir'], '/');
2505
        $ret = verifyFileHashes($SETTINGS['cpassman_dir'], __DIR__.'/../files_reference.txt');
2506
2507
        $ignoredFiles = DB::queryFirstField(
2508
            'SELECT valeur 
2509
            FROM ' . prefixTable('misc') . ' 
2510
            WHERE type = %s AND intitule = %s',
2511
            'admin',
2512
            'ignored_unknown_files'
2513
        );
2514
        $ignoredFilesKeys = !is_null($ignoredFiles) && !empty($ignoredFiles) ? json_decode($ignoredFiles) : [];
2515
        
2516
        echo prepareExchangedData(
2517
            array(
2518
                'error' => $ret['error'],
2519
                'message' => $ret['message'],
2520
                'files' => json_encode($ret['array'], JSON_FORCE_OBJECT),
2521
                'ignoredNumber' => count($ignoredFilesKeys),
2522
            ),
2523
            'encode'
2524
        );
2525
2526
        break;
2527
2528
    case "ignoreFile":
2529
        // Check KEY and rights
2530
        if ($post_key !== $session->get('key')) {
2531
            echo prepareExchangedData(
2532
                array(
2533
                    'error' => true,
2534
                    'message' => $lang->get('key_is_not_correct'),
2535
                ),
2536
                'encode'
2537
            );
2538
            break;
2539
        }
2540
2541
        // decrypt and retrieve data in JSON format
2542
        $dataReceived = prepareExchangedData(
2543
            $post_data,
2544
            'decode'
2545
        );
2546
2547
        $post_id = isset($dataReceived['id']) === true ? filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT) : '';
2548
2549
        // Get ignored unknown files
2550
        $existingData = DB::queryFirstRow(
2551
            'SELECT valeur 
2552
            FROM ' . prefixTable('misc') . ' 
2553
            WHERE type = %s AND intitule = %s',
2554
            'admin',
2555
            'ignored_unknown_files'
2556
        );
2557
2558
        // Get the json list ignored unknown files
2559
        $unknownFilesArray = [];
2560
        if (!empty($existingData) && !empty($existingData['valeur'])) {
2561
            $unknownFilesArray = json_decode($existingData['valeur'], true) ?: [];
2562
        }
2563
2564
        // Add the new file to the list
2565
        $unknownFilesArray[] = $post_id;
2566
2567
        // Save the new list
2568
        DB::insertUpdate(
2569
            prefixTable('misc'),
2570
            [
2571
                'type' => 'admin',
2572
                'intitule' => 'ignored_unknown_files',
2573
                'valeur' => json_encode($unknownFilesArray),
2574
                'created_at' => time(),
2575
            ],
2576
            [
2577
                'valeur' => json_encode($unknownFilesArray),
2578
                'updated_at' => time(),
2579
            ]
2580
        );
2581
2582
        
2583
        echo prepareExchangedData(
2584
            array(
2585
                'error' => false,
2586
                'message' => '',
2587
            ),
2588
            'encode'
2589
        );
2590
2591
        break;
2592
2593
    case "deleteFilesIntegrityCheck":
2594
        // Check KEY and rights
2595
        if ($post_key !== $session->get('key')) {
2596
            echo prepareExchangedData(
2597
                array(
2598
                    'error' => true,
2599
                    'message' => $lang->get('key_is_not_correct'),
2600
                ),
2601
                'encode'
2602
            );
2603
            break;
2604
        }
2605
2606
        // Get the list of files to delete
2607
        $filesToDelete = DB::queryFirstField(
2608
            'SELECT valeur 
2609
            FROM ' . prefixTable('misc') . ' 
2610
            WHERE type = %s AND intitule = %s',
2611
            'admin',
2612
            'unknown_files'
2613
        );
2614
2615
        if (is_null($filesToDelete)) {
2616
            echo prepareExchangedData(
2617
                array(
2618
                    'deletionResults' => null,
2619
                ),
2620
                'encode'
2621
            );
2622
            break;
2623
        }
2624
        $referenceFiles = (array) json_decode($filesToDelete);
2625
        
2626
        //  Launch
2627
        $ret = deleteFiles($referenceFiles, true);
2628
        
2629
        echo prepareExchangedData(
2630
            array(
2631
                'deletionResults' => $ret,
2632
            ),
2633
            'encode'
2634
        );
2635
2636
        break;
2637
    
2638
}
2639
2640
/**
2641
 * Delete multiple files with cross-platform compatibility
2642
 * 
2643
 * This function deletes a list of files while ensuring compatibility
2644
 * across different operating systems (Windows, Linux, etc.)
2645
 * 
2646
 * @param array $files Array of file paths to delete
2647
 * @param bool $ignoreErrors If true, continues execution even if a file cannot be deleted
2648
 * @return array Results of deletion operations (success/failure for each file)
2649
 */
2650
function deleteFiles(array $files, bool $ignoreErrors = false): array
2651
{
2652
    $session = SessionManager::getSession();
2653
    $lang = new Language($session->get('user-language') ?? 'english');
2654
2655
    $results = [];
2656
    $fullPath = __DIR__ . '/../';
2657
    
2658
    foreach ($files as $file) {
2659
        // Normalize path separators for cross-platform compatibility
2660
        $normalizedPath = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $fullPath.$file);
2661
        
2662
        // Check if file exists
2663
        if (!file_exists($normalizedPath)) {
2664
            $results[$normalizedPath] = [
2665
                'success' => false,
2666
                'error' => $lang->get('file_not_exists')
2667
            ];
2668
            
2669
            if (!$ignoreErrors) {
2670
                return $results;
2671
            }
2672
            
2673
            continue;
2674
        }
2675
        
2676
        // Check if path is actually a file (not a directory)
2677
        if (!is_file($normalizedPath)) {
2678
            $results[$normalizedPath] = [
2679
                'success' => false,
2680
                'error' => $lang->get('path_not_a_file')
2681
            ];
2682
            
2683
            if (!$ignoreErrors) {
2684
                return $results;
2685
            }
2686
            
2687
            continue;
2688
        }
2689
        
2690
        // Check if file is writable (can be deleted)
2691
        if (!is_writable($normalizedPath)) {
2692
            $results[$normalizedPath] = [
2693
                'success' => false,
2694
                'error' => $lang->get('file_not_writable')
2695
            ];
2696
            
2697
            if (!$ignoreErrors) {
2698
                return $results;
2699
            }
2700
            
2701
            continue;
2702
        }
2703
        
2704
        // Try to delete the file
2705
        $deleteResult = '';//@unlink($normalizedPath);
2706
        
2707
        if ($deleteResult) {
2708
            $results[$normalizedPath] = [
2709
                'success' => true,
2710
                'error' => '',
2711
            ];
2712
        } else {
2713
            $results[$normalizedPath] = [
2714
                'success' => false,
2715
                'error' => $lang->get('failed_to_delete')
2716
            ];
2717
            
2718
            if (!$ignoreErrors) {
2719
                return $results;
2720
            }
2721
        }
2722
    }
2723
    
2724
    return $results;
2725
}
2726
2727
/**
2728
 * Check the integrity of the files
2729
 * 
2730
 * @param string $baseDir
2731
 * @return array
2732
 */
2733
function filesIntegrityCheck($baseDir): array
2734
{
2735
    $referenceFile = __DIR__ . '/../files_reference.txt';
2736
2737
    $unknownFiles = findUnknownFiles($baseDir, $referenceFile);
2738
2739
    if (empty($unknownFiles)) {
2740
        return [
2741
            'error' => false,
2742
            'array' => [],
2743
            'message' => ''
2744
        ];
2745
    }
2746
2747
    // Check if the files are in the integrity file
2748
    return [
2749
        'error' => true,
2750
        'array' => $unknownFiles,
2751
        'message' => ''
2752
    ];
2753
}
2754
2755
/*
2756
 * Get all files in a directory
2757
 * 
2758
 * @param string $dir
2759
 * @return array
2760
 */
2761
function getAllFiles($dir): array
2762
{
2763
    $files = [];
2764
    $excludeDirs = ['upload', 'files', 'install', '_tools', 'random_compat', 'avatars']; // Répertoires à exclure
2765
    $excludeFilePrefixes = ['csrfp.config.php', 'settings.php', 'version-commit.php', 'phpstan.neon']; // Fichiers à exclure par préfixe
2766
2767
    $iterator = new RecursiveIteratorIterator(
2768
        new RecursiveCallbackFilterIterator(
2769
            new RecursiveDirectoryIterator(
2770
                $dir,
2771
                FilesystemIterator::SKIP_DOTS
2772
            ),
2773
            function ($current, $key, $iterator) {
0 ignored issues
show
Bug introduced by
function(...) { /* ... */ } of type callable is incompatible with the type string expected by parameter $callback of RecursiveCallbackFilterIterator::__construct(). ( Ignorable by Annotation )

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

2773
            /** @scrutinizer ignore-type */ function ($current, $key, $iterator) {
Loading history...
2774
                // Ignore hidden files and folders
2775
                if ($current->getFilename()[0] === '.') {
2776
                    return false;
2777
                }
2778
                return true;
2779
            }
2780
        ),
2781
        RecursiveIteratorIterator::SELF_FIRST
2782
    );
2783
2784
    foreach ($iterator as $file) {
2785
        try {
2786
            if ($file->isFile()) {
2787
                $relativePath = str_replace($dir . DIRECTORY_SEPARATOR, '', $file->getPathname());
2788
                $relativePath = str_replace('\\', '/', $relativePath); // Normalisation Windows/Linux
2789
2790
                // Split relatif path into parts
2791
                $pathParts = explode('/', $relativePath);
2792
2793
                // Check if any part of the path is in the excludeDirs array
2794
                foreach ($pathParts as $part) {
2795
                    if (in_array($part, $excludeDirs, true)) {
2796
                        continue 2; // Skip the file
2797
                    }
2798
                }
2799
2800
                // Check if the file name starts with any of the prefixes in excludeFilePrefixes
2801
                $filename = basename($relativePath);
2802
                foreach ($excludeFilePrefixes as $prefix) {
2803
                    if (strpos($filename, $prefix) === 0) {
2804
                        continue 2; // Skip the file
2805
                    }
2806
                }
2807
2808
                // If OK then add to the list
2809
                $files[] = $relativePath;
2810
            }
2811
        } catch (UnexpectedValueException $e) {
2812
            continue; // Ignore the file if an error occurs
2813
        }
2814
    }
2815
2816
    return $files;
2817
}
2818
2819
/**
2820
 * Find unknown files
2821
 * 
2822
 * @param string $baseDir
2823
 * @param string $referenceFile
2824
 * @return array
2825
 */
2826
function findUnknownFiles($baseDir, $referenceFile): array
2827
{
2828
    $currentFiles = getAllFiles($baseDir);
2829
    $referenceFiles = file($referenceFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2830
2831
    // Remove empty lines and comments from the reference file
2832
    $unknownFiles = array_diff($currentFiles, $referenceFiles);
2833
2834
    // Save list to DB
2835
    DB::insertUpdate(
2836
        prefixTable('misc'),
2837
        array(
2838
            'type' => 'admin',
2839
            'intitule' => 'unknown_files',
2840
            'valeur' => json_encode($unknownFiles),
2841
            'created_at' => time(),
2842
        ),
2843
        [
2844
            'valeur' => json_encode($unknownFiles),
2845
            'updated_at' => time(),
2846
        ]
2847
    );
2848
2849
    // NOw remove ignored files from the list returned to user
2850
    // Get ignored files
2851
    $ignoredFiles = DB::queryFirstField(
2852
        'SELECT valeur 
2853
        FROM ' . prefixTable('misc') . ' 
2854
        WHERE type = %s AND intitule = %s',
2855
        'admin',
2856
        'ignored_unknown_files'
2857
    );
2858
    $ignoredFilesKeys = !is_null($ignoredFiles) && !empty($ignoredFiles) ? array_flip(json_decode($ignoredFiles)) : [];
2859
    $unknownFiles = array_diff_key($unknownFiles, $ignoredFilesKeys);
2860
2861
    return $unknownFiles;
2862
}
2863
2864
/**
2865
 * Check the integrity of the tables
2866
 * 
2867
 * @return array
2868
 */
2869
function tablesIntegrityCheck(): array
2870
{
2871
    // Get integrity tables file
2872
    $integrityTablesFile = TEAMPASS_ROOT_PATH . '/includes/tables_integrity.json';
2873
    if (file_exists($integrityTablesFile) === false) {
2874
        return [
2875
            'error' => true,
2876
            'message' => "Integrity file has not been found."
2877
        ];
2878
    }
2879
    // Convert json to array
2880
    $integrityTables = json_decode(file_get_contents($integrityTablesFile), true);
2881
    if (json_last_error() !== JSON_ERROR_NONE) {
2882
        return [
2883
            'error' => true,
2884
            'message' => "Integrity file is corrupted"
2885
        ];
2886
    }
2887
    // Get all tables
2888
    $tables = [];
2889
    foreach (DB::queryFirstColumn("SHOW TABLES") as $table) {
2890
        $tables[] = str_replace(DB_PREFIX, "", $table);;
2891
    }
2892
    // Prepare the integrity check
2893
    $tablesInError = [];
2894
    // Check if the tables are in the integrity file
2895
    foreach ($tables as $table) {
2896
        $tableFound = false;
2897
        $tableHash = "";
2898
        foreach ($integrityTables as $integrityTable) {
2899
            if ($integrityTable["table_name"] === $table) {
2900
                $tableFound = true;
2901
                $tableHash = $integrityTable["structure_hash"];
2902
                break; // Sortir de la boucle si la table est trouvée
2903
            }
2904
        }
2905
2906
        if ($tableFound === true) {
2907
            $createTable = DB::queryFirstRow("SHOW CREATE TABLE ".DB_PREFIX."$table");
2908
            $currentHash = hash('sha256', $createTable['Create Table']);
2909
            if ($currentHash !== $tableHash) {
2910
                $tablesInError[] = DB_PREFIX.$table;
2911
            }            
2912
        }
2913
    }
2914
    return [
2915
        'error' => false,
2916
        'array' => $tablesInError,
2917
        'message' => ""
2918
    ];
2919
}
2920
2921
/**
2922
 * Verify file integrity by checking presence & MD5 hashes.
2923
 *
2924
 * This function compares the current files in the project directory against a reference file
2925
 * containing expected file paths and their MD5 hashes. It reports any missing files or files
2926
 * whose hashes do not match the reference.
2927
 *
2928
 * Steps:
2929
 *  - Load reference file data (file => hash)
2930
 *  - Get all current files in the base directory
2931
 *  - For each file, check if it exists in the reference; if so, compare hashes
2932
 *  - Collect issues for missing or changed files
2933
 *
2934
 * @param string $baseDir        Base directory to scan for files
2935
 * @param string $referenceFile  Path to reference file with known hashes
2936
 * @return array                 Result with 'error', 'array' of issues, and 'message'
2937
 */
2938
function verifyFileHashes($baseDir, $referenceFile): array
2939
{
2940
    // Load reference data (file => hash)
2941
    $referenceData = parseReferenceFile($referenceFile);
2942
    // Get list of all current files in the project
2943
    $allFiles = getAllFiles($baseDir);
2944
    $issues = [];
2945
2946
    // Compare current files to reference
2947
    foreach ($allFiles as $file) {
2948
        // Check if file exists in reference list
2949
        if (!isset($referenceData[$file])) {
2950
            $issues[] = "<i>$file</i> is not listed in reference file";
2951
            continue;
2952
        }
2953
2954
        // Compare hashes
2955
        $expectedHash = $referenceData[$file];
2956
        $actualHash = md5_file($baseDir . '/' . $file);
2957
2958
        if ($expectedHash !== $actualHash) {
2959
            $issues[] = "$file (expected: <i>$expectedHash</i>, actual: <i>$actualHash</i>)";
2960
        }
2961
    }
2962
2963
    // Return summary
2964
    return [
2965
        'error' => !empty($issues),
2966
        'array' => $issues,
2967
        'message' => empty($issues) ? 'Project files integrity check is successful.' : 'Integrity issues found.'
2968
    ];
2969
}
2970
2971
/**
2972
 * Parse the reference file into an associative array.
2973
 *
2974
 * Each line in the reference file should be of the form: "path hash".
2975
 * The function returns an array mapping file paths to their expected MD5 hashes.
2976
 *
2977
 * @param string $referenceFile  Path to reference file
2978
 * @return array                 [ 'file/path' => 'md5hash' ]
2979
 */
2980
function parseReferenceFile($referenceFile) {
2981
    $data = [];
2982
    $lines = file($referenceFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2983
    foreach ($lines as $line) {
2984
        [$path, $hash] = explode(' ', $line, 2);
2985
        $data[$path] = trim($hash);
2986
    }
2987
    return $data;
2988
}
2989
2990
/**
2991
 * Get all files in a directory with their md5 hashes.
2992
 *
2993
 * Recursively scans a directory, returning an associative array of file paths
2994
 * (relative to $dir) mapped to their MD5 hashes. Paths are normalized for cross-platform compatibility.
2995
 *
2996
 * @param string $dir    Directory to scan
2997
 * @return array         [ 'file/path' => 'md5hash' ]
2998
 */
2999
function getAllFilesWithHashes(string $dir): array
3000
{
3001
    $files = [];
3002
    $iterator = new RecursiveIteratorIterator(
3003
        new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)
3004
    );
3005
3006
    foreach ($iterator as $file) {
3007
        if ($file->isFile()) {
3008
            // Build relative path
3009
            $relativePath = str_replace($dir . DIRECTORY_SEPARATOR, '', $file->getPathname());
3010
            $relativePath = str_replace('\\', '/', $relativePath); // Normalize for Windows
3011
            // Calculate hash
3012
            $files[$relativePath] = md5_file($file->getPathname());
3013
        }
3014
    }
3015
    return $files;
3016
}