getAllFiles()   B
last analyzed

Complexity

Conditions 9
Paths 30

Size

Total Lines 56
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 9
eloc 30
c 2
b 0
f 0
nc 30
nop 1
dl 0
loc 56
rs 8.0555

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
            // Save in DB
1593
            DB::insert(
1594
                prefixTable('api'),
1595
                array(
1596
                    'increment_id' => null,
1597
                    'type' => 'key',
1598
                    'label' => $post_label,
1599
                    'value' => $key,
1600
                    'timestamp' => $timestamp,
1601
                )
1602
            );
1603
1604
            $post_id = DB::insertId();
1605
            // Update existing key
1606
        } elseif (null !== $post_action && $post_action === 'update') {
1607
            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);
1608
1609
            DB::update(
1610
                prefixTable('api'),
1611
                array(
1612
                    'label' => $post_label,
1613
                    'timestamp' => $timestamp,
1614
                ),
1615
                'increment_id=%i',
1616
                $post_id
1617
            );
1618
            // Delete existing key
1619
        } elseif (null !== $post_action && $post_action === 'delete') {
1620
            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT);
1621
1622
            DB::query(
1623
                'DELETE FROM ' . prefixTable('api') . ' WHERE increment_id = %i',
1624
                $post_id
1625
            );
1626
        }
1627
1628
        // send data
1629
        echo prepareExchangedData(
1630
            array(
1631
                'error' => false,
1632
                'message' => '',
1633
                'keyId' => $post_id,
1634
                'key' => isset($key) === true ? $key : '',
1635
            ),
1636
            'encode'
1637
        );
1638
        break;
1639
1640
        /*
1641
       * API save key
1642
    */
1643
    case 'admin_action_api_save_ip':
1644
        // Check KEY
1645
        if ($post_key !== $session->get('key')) {
1646
            echo prepareExchangedData(
1647
                array(
1648
                    'error' => true,
1649
                    'message' => $lang->get('key_is_not_correct'),
1650
                ),
1651
                'encode'
1652
            );
1653
            break;
1654
        }
1655
        // Is admin?
1656
        if ($session->get('user-admin') === 1) {
1657
            echo prepareExchangedData(
1658
                array(
1659
                    'error' => true,
1660
                    'message' => $lang->get('error_not_allowed_to'),
1661
                ),
1662
                'encode'
1663
            );
1664
            break;
1665
        }
1666
1667
        // decrypt and retrieve data in JSON format
1668
        $dataReceived = prepareExchangedData(
1669
            $post_data,
1670
            'decode'
1671
        );
1672
1673
        $post_action = filter_var($dataReceived['action'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1674
1675
        // add new key
1676
        if (null !== $post_action && $post_action === 'add') {
1677
            $post_label = filter_var($dataReceived['label'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1678
            $post_ip = filter_var($dataReceived['ip'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1679
1680
            // Store in DB
1681
            DB::insert(
1682
                prefixTable('api'),
1683
                array(
1684
                    'increment_id' => null,
1685
                    'type' => 'ip',
1686
                    'label' => $post_label,
1687
                    'value' => $post_ip,
1688
                    'timestamp' => time(),
1689
                )
1690
            );
1691
1692
            $post_id = DB::insertId();
1693
            // Update existing key
1694
        } elseif (null !== $post_action && $post_action === 'update') {
1695
            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1696
            $post_field = filter_var($dataReceived['field'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1697
            $post_value = filter_var($dataReceived['value'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1698
            if ($post_field === 'value') {
1699
                $arr = array(
1700
                    'value' => $post_value,
1701
                    'timestamp' => time(),
1702
                );
1703
            } else {
1704
                $arr = array(
1705
                    'label' => $post_value,
1706
                    'timestamp' => time(),
1707
                );
1708
            }
1709
            DB::update(
1710
                prefixTable('api'),
1711
                $arr,
1712
                'increment_id=%i',
1713
                $post_id
1714
            );
1715
            // Delete existing key
1716
        } elseif (null !== $post_action && $post_action === 'delete') {
1717
            $post_id = filter_var($dataReceived['id'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
1718
            DB::query('DELETE FROM ' . prefixTable('api') . ' WHERE increment_id=%i', $post_id);
1719
        }
1720
1721
        echo prepareExchangedData(
1722
            array(
1723
                'error' => false,
1724
                'message' => '',
1725
                'ipId' => $post_id,
1726
            ),
1727
            'encode'
1728
        );
1729
        break;
1730
1731
    case 'save_api_status':
1732
        // Do query
1733
        DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'api');
1734
        $counter = DB::count();
1735
        if ($counter === 0) {
1736
            DB::insert(
1737
                prefixTable('misc'),
1738
                array(
1739
                    'type' => 'admin',
1740
                    'intitule' => 'api',
1741
                    'valeur' => $post_status,
1742
                    'created_at' => time(),
1743
                )
1744
            );
1745
        } else {
1746
            DB::update(
1747
                prefixTable('misc'),
1748
                array(
1749
                    'valeur' => $post_status,
1750
                    'updated_at' => time(),
1751
                ),
1752
                'type = %s AND intitule = %s',
1753
                'admin',
1754
                'api'
1755
            );
1756
        }
1757
        $SETTINGS['api'] = $post_status;
1758
        break;
1759
1760
    case 'run_duo_config_check':
1761
        //Libraries call
1762
        require_once $SETTINGS['cpassman_dir'] . '/sources/main.functions.php';
1763
        // Check KEY
1764
        if ($post_key !== $session->get('key')) {
1765
            echo prepareExchangedData(
1766
                array(
1767
                    'error' => true,
1768
                    'message' => $lang->get('key_is_not_correct'),
1769
                ),
1770
                'encode'
1771
            );
1772
            break;
1773
        }
1774
1775
        // decrypt and retreive data in JSON format
1776
        $dataReceived = prepareExchangedData(
1777
            $post_data,
1778
            'decode'
1779
        );
1780
1781
        // Check if we have what we need first
1782
        if (empty($dataReceived['duo_ikey']) || empty($dataReceived['duo_skey']) || empty($dataReceived['duo_host'])) {
1783
            echo prepareExchangedData(
1784
                array(
1785
                    'error' => true,
1786
                    'message' => $lang->get('data_are_missing'),
1787
                ),
1788
                'encode'
1789
            );
1790
            break;
1791
        }
1792
1793
        // Run Duo Config Check
1794
        try {
1795
            $duo_client = new Client(
1796
                $dataReceived['duo_ikey'],
1797
                $dataReceived['duo_skey'],
1798
                $dataReceived['duo_host'],
1799
                $SETTINGS['cpassman_url'].'/'.DUO_CALLBACK
1800
            );
1801
        } catch (DuoException $e) {
1802
            if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) {
1803
                error_log('TEAMPASS Error - duo config - '.$e->getMessage());
1804
            }
1805
            // deepcode ignore ServerLeak: Data is encrypted before being sent
1806
            echo prepareExchangedData(
1807
                    array(
1808
                        'error' => true,
1809
                        'message' => $lang->get('duo_config_error'),
1810
                    ),
1811
                    'encode'
1812
            );
1813
            break;
1814
        }
1815
1816
        // Run healthcheck against Duo with the config
1817
        try {
1818
            $duo_client->healthCheck();
1819
        } catch (DuoException $e) {
1820
            if (defined('LOG_TO_SERVER') && LOG_TO_SERVER === true) {
1821
                error_log('TEAMPASS Error - duo config - '.$e->getMessage());
1822
            }
1823
            // deepcode ignore ServerLeak: Data is encrypted before being sent
1824
            echo prepareExchangedData(
1825
                    array(
1826
                        'error' => true,
1827
                        'message' => $lang->get('duo_error_check_config'),
1828
                    ),
1829
                    'encode'
1830
            );
1831
            break;
1832
        }
1833
1834
        // send data
1835
        echo prepareExchangedData(
1836
            array(
1837
                'error' => false,
1838
                'message' => '',
1839
            ),
1840
            'encode'
1841
        );
1842
        break;
1843
1844
    case 'save_google_options':
1845
        // Check KEY and rights
1846
        if ($post_key !== $session->get('key')) {
1847
            echo prepareExchangedData(
1848
                array(
1849
                    'error' => true,
1850
                    'message' => $lang->get('key_is_not_correct'),
1851
                ),
1852
                'encode'
1853
            );
1854
            break;
1855
        }
1856
        // decrypt and retreive data in JSON format
1857
        $dataReceived = prepareExchangedData(
1858
            $post_data,
1859
            'decode'
1860
        );
1861
1862
        // Google Authentication
1863
        if (htmlspecialchars_decode($dataReceived['google_authentication']) === 'false') {
1864
            $tmp = 0;
1865
        } else {
1866
            $tmp = 1;
1867
        }
1868
        DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'google_authentication');
1869
        $counter = DB::count();
1870
        if ($counter === 0) {
1871
            DB::insert(
1872
                prefixTable('misc'),
1873
                array(
1874
                    'type' => 'admin',
1875
                    'intitule' => 'google_authentication',
1876
                    'valeur' => $tmp,
1877
                    'created_at' => time(),
1878
                )
1879
            );
1880
        } else {
1881
            DB::update(
1882
                prefixTable('misc'),
1883
                array(
1884
                    'valeur' => $tmp,
1885
                    'updated_at' => time(),
1886
                ),
1887
                'type = %s AND intitule = %s',
1888
                'admin',
1889
                'google_authentication'
1890
            );
1891
        }
1892
        $SETTINGS['google_authentication'] = htmlspecialchars_decode($dataReceived['google_authentication']);
1893
1894
        // ga_website_name
1895
        if (is_null($dataReceived['ga_website_name']) === false) {
1896
            DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'ga_website_name');
1897
            $counter = DB::count();
1898
            if ($counter === 0) {
1899
                DB::insert(
1900
                    prefixTable('misc'),
1901
                    array(
1902
                        'type' => 'admin',
1903
                        'intitule' => 'ga_website_name',
1904
                        'valeur' => htmlspecialchars_decode($dataReceived['ga_website_name']),
1905
                        'created_at' => time(),
1906
                    )
1907
                );
1908
            } else {
1909
                DB::update(
1910
                    prefixTable('misc'),
1911
                    array(
1912
                        'valeur' => htmlspecialchars_decode($dataReceived['ga_website_name']),
1913
                        'updated_at' => time(),
1914
                    ),
1915
                    'type = %s AND intitule = %s',
1916
                    'admin',
1917
                    'ga_website_name'
1918
                );
1919
            }
1920
            $SETTINGS['ga_website_name'] = htmlspecialchars_decode($dataReceived['ga_website_name']);
1921
        } else {
1922
            $SETTINGS['ga_website_name'] = '';
1923
        }
1924
1925
        // send data
1926
        echo '[{"result" : "' . addslashes($lang['done']) . '" , "error" : ""}]';
1927
        break;
1928
1929
    case 'save_agses_options':
1930
        // Check KEY and rights
1931
        if ($post_key !== $session->get('key')) {
1932
            echo prepareExchangedData(
1933
                array(
1934
                    'error' => true,
1935
                    'message' => $lang->get('key_is_not_correct'),
1936
                ),
1937
                'encode'
1938
            );
1939
            break;
1940
        }
1941
        // decrypt and retreive data in JSON format
1942
        $dataReceived = prepareExchangedData(
1943
            $post_data,
1944
            'decode'
1945
        );
1946
1947
        // agses_hosted_url
1948
        if (!is_null($dataReceived['agses_hosted_url'])) {
1949
            DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'agses_hosted_url');
1950
            $counter = DB::count();
1951
            if ($counter === 0) {
1952
                DB::insert(
1953
                    prefixTable('misc'),
1954
                    array(
1955
                        'type' => 'admin',
1956
                        'intitule' => 'agses_hosted_url',
1957
                        'valeur' => htmlspecialchars_decode($dataReceived['agses_hosted_url']),
1958
                        'created_at' => time(),
1959
                    )
1960
                );
1961
            } else {
1962
                DB::update(
1963
                    prefixTable('misc'),
1964
                    array(
1965
                        'valeur' => htmlspecialchars_decode($dataReceived['agses_hosted_url']),
1966
                        'updated_at' => time(),
1967
                    ),
1968
                    'type = %s AND intitule = %s',
1969
                    'admin',
1970
                    'agses_hosted_url'
1971
                );
1972
            }
1973
            $SETTINGS['agses_hosted_url'] = htmlspecialchars_decode($dataReceived['agses_hosted_url']);
1974
        } else {
1975
            $SETTINGS['agses_hosted_url'] = '';
1976
        }
1977
1978
        // agses_hosted_id
1979
        if (!is_null($dataReceived['agses_hosted_id'])) {
1980
            DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'agses_hosted_id');
1981
            $counter = DB::count();
1982
            if ($counter === 0) {
1983
                DB::insert(
1984
                    prefixTable('misc'),
1985
                    array(
1986
                        'type' => 'admin',
1987
                        'intitule' => 'agses_hosted_id',
1988
                        'valeur' => htmlspecialchars_decode($dataReceived['agses_hosted_id']),
1989
                        'created_at' => time(),
1990
                    )
1991
                );
1992
            } else {
1993
                DB::update(
1994
                    prefixTable('misc'),
1995
                    array(
1996
                        'valeur' => htmlspecialchars_decode($dataReceived['agses_hosted_id']),
1997
                        'updated_at' => time(),
1998
                    ),
1999
                    'type = %s AND intitule = %s',
2000
                    'admin',
2001
                    'agses_hosted_id'
2002
                );
2003
            }
2004
            $SETTINGS['agses_hosted_id'] = htmlspecialchars_decode($dataReceived['agses_hosted_id']);
2005
        } else {
2006
            $SETTINGS['agses_hosted_id'] = '';
2007
        }
2008
2009
        // agses_hosted_apikey
2010
        if (!is_null($dataReceived['agses_hosted_apikey'])) {
2011
            DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'agses_hosted_apikey');
2012
            $counter = DB::count();
2013
            if ($counter === 0) {
2014
                DB::insert(
2015
                    prefixTable('misc'),
2016
                    array(
2017
                        'type' => 'admin',
2018
                        'intitule' => 'agses_hosted_apikey',
2019
                        'valeur' => htmlspecialchars_decode($dataReceived['agses_hosted_apikey']),
2020
                        'created_at' => time(),
2021
                    )
2022
                );
2023
            } else {
2024
                DB::update(
2025
                    prefixTable('misc'),
2026
                    array(
2027
                        'valeur' => htmlspecialchars_decode($dataReceived['agses_hosted_apikey']),
2028
                        'updated_at' => time(),
2029
                    ),
2030
                    'type = %s AND intitule = %s',
2031
                    'admin',
2032
                    'agses_hosted_apikey'
2033
                );
2034
            }
2035
            $SETTINGS['agses_hosted_apikey'] = htmlspecialchars_decode($dataReceived['agses_hosted_apikey']);
2036
        } else {
2037
            $SETTINGS['agses_hosted_apikey'] = '';
2038
        }
2039
2040
        // send data
2041
        echo '[{"result" : "' . addslashes($lang['done']) . '" , "error" : ""}]';
2042
        break;
2043
2044
    case 'save_option_change':
2045
        // Check KEY and rights
2046
        if ($post_key !== $session->get('key')) {
2047
            echo prepareExchangedData(
2048
                array(
2049
                    'error' => true,
2050
                    'message' => $lang->get('key_is_not_correct'),
2051
                ),
2052
                'encode'
2053
            );
2054
            break;
2055
        }
2056
        
2057
        // decrypt and retreive data in JSON format
2058
        $dataReceived = prepareExchangedData(
2059
            $post_data,
2060
            'decode'
2061
        );
2062
        
2063
        // prepare data
2064
        $post_value = filter_var($dataReceived['value'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2065
        $post_field = filter_var($dataReceived['field'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2066
        $post_translate = isset($dataReceived['translate']) === true ? filter_var($dataReceived['translate'], FILTER_SANITIZE_FULL_SPECIAL_CHARS) : '';
2067
        
2068
        require_once 'main.functions.php';
2069
2070
        // In case of key, then encrypt it
2071
        if ($post_field === 'bck_script_passkey') {
2072
            $post_value = cryption(
2073
                $post_value,
2074
                '',
2075
                'encrypt',
2076
                $SETTINGS
2077
            )['string'];
2078
        }
2079
2080
        // Check if setting is already in DB. If NO then insert, if YES then update.
2081
        $data = DB::query(
2082
            'SELECT * FROM ' . prefixTable('misc') . '
2083
            WHERE type = %s AND intitule = %s',
2084
            'admin',
2085
            $post_field
2086
        );
2087
        $counter = DB::count();
2088
        if ($counter === 0) {
2089
            DB::insert(
2090
                prefixTable('misc'),
2091
                array(
2092
                    'valeur' => $post_value,
2093
                    'type' => 'admin',
2094
                    'intitule' => $post_field,
2095
                    'created_at' => time(),
2096
                )
2097
            );
2098
            // in case of stats enabled, add the actual time
2099
            if ($post_field === 'send_stats') {
2100
                DB::insert(
2101
                    prefixTable('misc'),
2102
                    array(
2103
                        'valeur' => time(),
2104
                        'type' => 'admin',
2105
                        'intitule' => $post_field . '_time',
2106
                        'updated_at' => time(),
2107
                    )
2108
                );
2109
            }
2110
        } else {
2111
            // Update DB settings
2112
            DB::update(
2113
                prefixTable('misc'),
2114
                array(
2115
                    'valeur' => $post_value,
2116
                    'updated_at' => time(),
2117
                ),
2118
                'type = %s AND intitule = %s',
2119
                'admin',
2120
                $post_field
2121
            );
2122
2123
            // in case of stats enabled, update the actual time
2124
            if ($post_field === 'send_stats') {
2125
                // Check if previous time exists, if not them insert this value in DB
2126
                DB::query(
2127
                    'SELECT * FROM ' . prefixTable('misc') . '
2128
                    WHERE type = %s AND intitule = %s',
2129
                    'admin',
2130
                    $post_field . '_time'
2131
                );
2132
                $counter = DB::count();
2133
                if ($counter === 0) {
2134
                    DB::insert(
2135
                        prefixTable('misc'),
2136
                        array(
2137
                            'valeur' => 0,
2138
                            'type' => 'admin',
2139
                            'intitule' => $post_field . '_time',
2140
                            'created_at' => time(),
2141
                        )
2142
                    );
2143
                } else {
2144
                    DB::update(
2145
                        prefixTable('misc'),
2146
                        array(
2147
                            'valeur' => 0,
2148
                            'updated_at' => time(),
2149
                        ),
2150
                        'type = %s AND intitule = %s',
2151
                        'admin',
2152
                        $post_field
2153
                    );
2154
                }
2155
            }
2156
        }
2157
2158
        // special Cases
2159
        if ($post_field === 'cpassman_url') {
2160
            // update also jsUrl for CSFP protection
2161
            $jsUrl = $post_value . '/includes/libraries/csrfp/js/csrfprotector.js';
2162
            $csrfp_file = '../includes/libraries/csrfp/libs/csrfp.config.php';
2163
            $data = file_get_contents($csrfp_file);
2164
            $posJsUrl = strpos($data, '"jsUrl" => "');
2165
            $posEndLine = strpos($data, '",', $posJsUrl);
2166
            $line = substr($data, $posJsUrl, ($posEndLine - $posJsUrl + 2));
2167
            $newdata = str_replace($line, '"jsUrl" => "' . filter_var($jsUrl, FILTER_SANITIZE_FULL_SPECIAL_CHARS) . '",', $data);
2168
            file_put_contents($csrfp_file, $newdata);
2169
        } elseif ($post_field === 'restricted_to_input' && (int) $post_value === 0) {
2170
            DB::update(
2171
                prefixTable('misc'),
2172
                array(
2173
                    'valeur' => 0,
2174
                    'updated_at' => time(),
2175
                ),
2176
                'type = %s AND intitule = %s',
2177
                'admin',
2178
                'restricted_to_roles'
2179
            );
2180
        }
2181
2182
        // Encrypt data to return
2183
        echo prepareExchangedData(
2184
            array(
2185
                'error' => false,
2186
                'misc' => $counter . ' ; ' . $SETTINGS[$post_field],
2187
                'message' => empty($post_translate) === false ? $lang->get($post_translate) : '',
2188
            ),
2189
            'encode'
2190
        );
2191
        break;
2192
2193
    case 'get_values_for_statistics':
2194
        // Check KEY and rights
2195
        if ($post_key !== $session->get('key')) {
2196
            echo prepareExchangedData(
2197
                array(
2198
                    'error' => true,
2199
                    'message' => $lang->get('key_is_not_correct'),
2200
                ),
2201
                'encode'
2202
            );
2203
            break;
2204
        }
2205
2206
        // Encrypt data to return
2207
        echo prepareExchangedData(
2208
            getStatisticsData($SETTINGS),
2209
            'encode'
2210
        );
2211
2212
        break;
2213
2214
    case 'save_sending_statistics':
2215
        // Check KEY and rights
2216
        if ($post_key !== $session->get('key')) {
2217
            echo prepareExchangedData(
2218
                array(
2219
                    'error' => true,
2220
                    'message' => $lang->get('key_is_not_correct'),
2221
                ),
2222
                'encode'
2223
            );
2224
            break;
2225
        }
2226
2227
        // send statistics
2228
        if (null !== $post_status) {
2229
            DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'send_stats');
2230
            $counter = DB::count();
2231
            if ($counter === 0) {
2232
                DB::insert(
2233
                    prefixTable('misc'),
2234
                    array(
2235
                        'type' => 'admin',
2236
                        'intitule' => 'send_stats',
2237
                        'valeur' => $post_status,
2238
                        'created_at' => time(),
2239
                    )
2240
                );
2241
            } else {
2242
                DB::update(
2243
                    prefixTable('misc'),
2244
                    array(
2245
                        'valeur' => $post_status,
2246
                        'updated_at' => time(),
2247
                    ),
2248
                    'type = %s AND intitule = %s',
2249
                    'admin',
2250
                    'send_stats'
2251
                );
2252
            }
2253
            $SETTINGS['send_stats'] = $post_status;
2254
        } else {
2255
            $SETTINGS['send_stats'] = '0';
2256
        }
2257
2258
        // send statistics items
2259
        if (null !== $post_list) {
2260
            DB::query('SELECT * FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s', 'admin', 'send_statistics_items');
2261
            $counter = DB::count();
2262
            if ($counter === 0) {
2263
                DB::insert(
2264
                    prefixTable('misc'),
2265
                    array(
2266
                        'type' => 'admin',
2267
                        'intitule' => 'send_statistics_items',
2268
                        'valeur' => $post_list,
2269
                        'created_at' => time(),
2270
                    )
2271
                );
2272
            } else {
2273
                DB::update(
2274
                    prefixTable('misc'),
2275
                    array(
2276
                        'valeur' => $post_list,
2277
                        'updated_at' => time(),
2278
                    ),
2279
                    'type = %s AND intitule = %s',
2280
                    'admin',
2281
                    'send_statistics_items'
2282
                );
2283
            }
2284
            $SETTINGS['send_statistics_items'] = $post_list;
2285
        } else {
2286
            $SETTINGS['send_statistics_items'] = '';
2287
        }
2288
2289
        // send data
2290
        echo '[{"error" : false}]';
2291
        break;
2292
2293
    case 'is_backup_table_existing':
2294
        // Check KEY and rights
2295
        if ($post_key !== $session->get('key')) {
2296
            echo prepareExchangedData(
2297
                array(
2298
                    'error' => true,
2299
                    'message' => $lang->get('key_is_not_correct'),
2300
                ),
2301
                'encode'
2302
            );
2303
            break;
2304
        }
2305
2306
        if (DB::query("SHOW TABLES LIKE '" . prefixTable('sk_reencrypt_backup') . "'")) {
2307
            if (DB::count() === 1) {
2308
                echo 1;
2309
            } else {
2310
                echo 0;
2311
            }
2312
        } else {
2313
            echo 0;
2314
        }
2315
2316
        break;
2317
2318
    case 'get_list_of_roles':
2319
        // Check KEY and rights
2320
        if ($post_key !== $session->get('key')) {
2321
            echo prepareExchangedData(
2322
                array(
2323
                    'error' => true,
2324
                    'message' => $lang->get('key_is_not_correct'),
2325
                ),
2326
                'encode'
2327
            );
2328
            break;
2329
        }
2330
2331
        // decrypt and retreive data in JSON format
2332
        $dataReceived = prepareExchangedData(
2333
            $post_data,
2334
            'decode'
2335
        );
2336
2337
        // prepare data
2338
        $sourcePage = filter_var($dataReceived['source_page'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2339
        if ($sourcePage === 'ldap') {
2340
            $selected_administrated_by = isset($SETTINGS['ldap_new_user_is_administrated_by']) && $SETTINGS['ldap_new_user_is_administrated_by'] === '0' ? 1 : 0;
2341
            $selected_new_user_role = isset($SETTINGS['ldap_new_user_role']) && $SETTINGS['ldap_new_user_role'] === '0' ? 1 : 0;
2342
        } elseif ($sourcePage === 'oauth') {
2343
            $selected_administrated_by = isset($SETTINGS['oauth_new_user_is_administrated_by']) && $SETTINGS['oauth_new_user_is_administrated_by'] === '0' ? 1 : 0;
2344
            $selected_new_user_role = isset($SETTINGS['oauth_selfregistered_user_belongs_to_role']) && $SETTINGS['oauth_selfregistered_user_belongs_to_role'] === '0' ? 1 : '';
2345
        } else {
2346
            echo prepareExchangedData(
2347
                [], 
2348
                'encode'
2349
            );
2350
    
2351
            break;
2352
        }
2353
2354
        $json = array();
2355
        array_push(
2356
            $json,
2357
            array(
2358
                'id' => '0',
2359
                'title' => $lang->get('god'),
2360
                'selected_administrated_by' => $selected_administrated_by,
2361
                'selected_role' => $selected_new_user_role,
2362
            )
2363
        );
2364
2365
        $rows = DB::query(
2366
            'SELECT id, title
2367
                FROM ' . prefixTable('roles_title') . '
2368
                ORDER BY title ASC'
2369
        );
2370
        foreach ($rows as $record) {
2371
            if ($sourcePage === 'ldap') {
2372
                $selected_administrated_by = isset($SETTINGS['ldap_new_user_is_administrated_by']) && $SETTINGS['ldap_new_user_is_administrated_by'] === $record['id'] ? 1 : 0;
2373
                $selected_new_user_role = isset($SETTINGS['ldap_new_user_role']) && $SETTINGS['ldap_new_user_role'] === $record['id'] ? 1 : 0;
2374
            } elseif ($sourcePage === 'oauth') {
2375
                $selected_administrated_by = isset($SETTINGS['oauth_new_user_is_administrated_by']) && $SETTINGS['oauth_new_user_is_administrated_by'] === $record['id'] ? 1 : 0;
2376
                $selected_new_user_role = isset($SETTINGS['oauth_selfregistered_user_belongs_to_role']) && $SETTINGS['oauth_selfregistered_user_belongs_to_role'] === $record['id'] ? 1 : 0;
2377
            }
2378
            array_push(
2379
                $json,
2380
                array(
2381
                    'id' => $record['id'],
2382
                    'title' => addslashes($record['title']),
2383
                    'selected_administrated_by' => $selected_administrated_by,
2384
                    'selected_role' => $selected_new_user_role,
2385
                )
2386
            );
2387
        }
2388
2389
        echo prepareExchangedData(
2390
            $json, 
2391
            'encode'
2392
        );
2393
2394
        break;
2395
2396
    case 'save_user_change':
2397
        // Check KEY and rights
2398
        if ($post_key !== $session->get('key')) {
2399
            echo prepareExchangedData(
2400
                array(
2401
                    'error' => true,
2402
                    'message' => $lang->get('key_is_not_correct'),
2403
                ),
2404
                'encode'
2405
            );
2406
            break;
2407
        }
2408
2409
        // decrypt and retrieve data in JSON format
2410
        $dataReceived = prepareExchangedData(
2411
            $post_data,
2412
            'decode'
2413
        );
2414
2415
        $post_increment_id = isset($dataReceived['increment_id']) === true ? filter_var($dataReceived['increment_id'], FILTER_SANITIZE_NUMBER_INT) : '';
2416
        $post_field = filter_var($dataReceived['field'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2417
        $post_value = filter_var($dataReceived['value'], FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2418
2419
        if (is_numeric($post_increment_id) === false) {
2420
            echo prepareExchangedData(
2421
                array(
2422
                    'error' => true,
2423
                    'message' => $lang->get('no_user'),
2424
                ),
2425
                'encode'
2426
            );
2427
            break;
2428
        }
2429
        
2430
        //update.
2431
        DB::debugMode(false);
2432
        DB::update(
2433
            prefixTable('api'),
2434
            array(
2435
                $post_field => $post_value,
2436
            ),
2437
            'increment_id = %i',
2438
            (int) $post_increment_id
2439
        );
2440
        DB::debugMode(false);
2441
        //log
2442
        logEvents($SETTINGS, 'system', 'api_user_readonly', (string) $session->get('user-id'), $session->get('user-login'));
2443
2444
        echo prepareExchangedData(
2445
            array(
2446
                'error' => false,
2447
                'message' => '',
2448
            ),
2449
            'encode'
2450
        );
2451
2452
        break;
2453
    
2454
    case "tablesIntegrityCheck":
2455
        // Check KEY and rights
2456
        if ($post_key !== $session->get('key')) {
2457
            echo prepareExchangedData(
2458
                array(
2459
                    'error' => true,
2460
                    'message' => $lang->get('key_is_not_correct'),
2461
                ),
2462
                'encode'
2463
            );
2464
            break;
2465
        }
2466
2467
        $ret = tablesIntegrityCheck();
2468
        
2469
        echo prepareExchangedData(
2470
            array(
2471
                'error' => $ret['error'],
2472
                'message' => $ret['message'],
2473
                'tables' => json_encode($ret['array'], JSON_FORCE_OBJECT),
2474
            ),
2475
            'encode'
2476
        );
2477
2478
        break;
2479
2480
    case "filesIntegrityCheck":
2481
        // Check KEY and rights
2482
        if ($post_key !== $session->get('key')) {
2483
            echo prepareExchangedData(
2484
                array(
2485
                    'error' => true,
2486
                    'message' => $lang->get('key_is_not_correct'),
2487
                ),
2488
                'encode'
2489
            );
2490
            break;
2491
        }
2492
2493
        // New hash-based integrity check
2494
        $SETTINGS['cpassman_dir'] = rtrim($SETTINGS['cpassman_dir'], '/');
2495
        $ret = verifyFileHashes($SETTINGS['cpassman_dir'], __DIR__.'/../files_reference.txt');
2496
2497
        $ignoredFiles = DB::queryFirstField(
2498
            'SELECT valeur 
2499
            FROM ' . prefixTable('misc') . ' 
2500
            WHERE type = %s AND intitule = %s',
2501
            'admin',
2502
            'ignored_unknown_files'
2503
        );
2504
        $ignoredFilesKeys = !is_null($ignoredFiles) && !empty($ignoredFiles) ? json_decode($ignoredFiles) : [];
2505
        
2506
        echo prepareExchangedData(
2507
            array(
2508
                'error' => $ret['error'],
2509
                'message' => $ret['message'],
2510
                'files' => json_encode($ret['array'], JSON_FORCE_OBJECT),
2511
                'ignoredNumber' => count($ignoredFilesKeys),
2512
            ),
2513
            'encode'
2514
        );
2515
2516
        break;
2517
2518
    case "ignoreFile":
2519
        // Check KEY and rights
2520
        if ($post_key !== $session->get('key')) {
2521
            echo prepareExchangedData(
2522
                array(
2523
                    'error' => true,
2524
                    'message' => $lang->get('key_is_not_correct'),
2525
                ),
2526
                'encode'
2527
            );
2528
            break;
2529
        }
2530
2531
        // decrypt and retrieve data in JSON format
2532
        $dataReceived = prepareExchangedData(
2533
            $post_data,
2534
            'decode'
2535
        );
2536
2537
        $post_id = isset($dataReceived['id']) === true ? filter_var($dataReceived['id'], FILTER_SANITIZE_NUMBER_INT) : '';
2538
2539
        // Get ignored unknown files
2540
        $existingData = DB::queryFirstRow(
2541
            'SELECT valeur 
2542
            FROM ' . prefixTable('misc') . ' 
2543
            WHERE type = %s AND intitule = %s',
2544
            'admin',
2545
            'ignored_unknown_files'
2546
        );
2547
2548
        // Get the json list ignored unknown files
2549
        $unknownFilesArray = [];
2550
        if (!empty($existingData) && !empty($existingData['valeur'])) {
2551
            $unknownFilesArray = json_decode($existingData['valeur'], true) ?: [];
2552
        }
2553
2554
        // Add the new file to the list
2555
        $unknownFilesArray[] = $post_id;
2556
2557
        // Save the new list
2558
        DB::insertUpdate(
2559
            prefixTable('misc'),
2560
            [
2561
                'type' => 'admin',
2562
                'intitule' => 'ignored_unknown_files',
2563
                'valeur' => json_encode($unknownFilesArray),
2564
                'created_at' => time(),
2565
            ],
2566
            [
2567
                'valeur' => json_encode($unknownFilesArray),
2568
                'updated_at' => time(),
2569
            ]
2570
        );
2571
2572
        
2573
        echo prepareExchangedData(
2574
            array(
2575
                'error' => false,
2576
                'message' => '',
2577
            ),
2578
            'encode'
2579
        );
2580
2581
        break;
2582
2583
    case "deleteFilesIntegrityCheck":
2584
        // Check KEY and rights
2585
        if ($post_key !== $session->get('key')) {
2586
            echo prepareExchangedData(
2587
                array(
2588
                    'error' => true,
2589
                    'message' => $lang->get('key_is_not_correct'),
2590
                ),
2591
                'encode'
2592
            );
2593
            break;
2594
        }
2595
2596
        // Get the list of files to delete
2597
        $filesToDelete = DB::queryFirstField(
2598
            'SELECT valeur 
2599
            FROM ' . prefixTable('misc') . ' 
2600
            WHERE type = %s AND intitule = %s',
2601
            'admin',
2602
            'unknown_files'
2603
        );
2604
2605
        if (is_null($filesToDelete)) {
2606
            echo prepareExchangedData(
2607
                array(
2608
                    'deletionResults' => null,
2609
                ),
2610
                'encode'
2611
            );
2612
            break;
2613
        }
2614
        $referenceFiles = (array) json_decode($filesToDelete);
2615
        
2616
        //  Launch
2617
        $ret = deleteFiles($referenceFiles, true);
2618
        
2619
        echo prepareExchangedData(
2620
            array(
2621
                'deletionResults' => $ret,
2622
            ),
2623
            'encode'
2624
        );
2625
2626
        break;
2627
        
2628
2629
    case "performSimulateUserKeyChangeDuration":
2630
        // Check KEY and rights
2631
        if ($post_key !== $session->get('key')) {
2632
            echo prepareExchangedData(
2633
                array(
2634
                    'error' => true,
2635
                    'message' => $lang->get('key_is_not_correct'),
2636
                ),
2637
                'encode'
2638
            );
2639
            break;
2640
        }
2641
        
2642
        // Get some TP USER info
2643
        $userInfo = DB::queryFirstRow(
2644
            'SELECT id, public_key, private_key, pw
2645
            FROM ' . prefixTable('users') . ' 
2646
            WHERE id = %i',
2647
            TP_USER_ID,
2648
        );
2649
2650
        // decrypt owner password
2651
        $decryptedData = cryption($userInfo['pw'], '', 'decrypt', $SETTINGS);
2652
        $pwd = $decryptedData['string'] ?? '';
2653
        $userInfo['private_key'] = decryptPrivateKey($pwd, $userInfo['private_key']);
2654
2655
        $duration = (simulateUserKeyChangeDuration($userInfo));
2656
        $durationWithMargin = $duration * 1.10; // add 10% margin
2657
2658
        // Evaluate if current setting is sufficient or not
2659
        $isCurrentSettingSufficient = ($durationWithMargin <= (int) $SETTINGS['task_maximum_run_time']);
2660
        $proposedDuration = $isCurrentSettingSufficient ? 0 : ceil($durationWithMargin / 10) * 10;
2661
        $session->set('background_task_duration_proposed', $proposedDuration);
2662
2663
        echo prepareExchangedData(
2664
            array(
2665
                'estimatedTime' => round($duration, 0) ?? null, // Estimated time in seconds
2666
                'proposedDuration' => $proposedDuration, // New proposed value if current setting is not sufficient
2667
                'currentDuration' => (int) $SETTINGS['task_maximum_run_time'], // Current setting
2668
                'setupProposal' => $isCurrentSettingSufficient, // true if current setting is sufficient, false otherwise
2669
                'error' => false,
2670
            ),
2671
            'encode'
2672
        );
2673
2674
        break;
2675
2676
    case 'admin_action_refresh-users-api':
2677
        // Check KEY and rights
2678
        if ($post_key !== $session->get('key')) {
2679
            echo prepareExchangedData(
2680
                array(
2681
                    'error' => true,
2682
                    'message' => $lang->get('key_is_not_correct'),
2683
                ),
2684
                'encode'
2685
            );
2686
            break;
2687
        }
2688
2689
        $users = DB::query(
2690
            'SELECT u.id, u.public_key, a.increment_id
2691
            FROM ' . prefixTable('users') . ' AS u
2692
            LEFT JOIN ' . prefixTable('api') . ' AS a 
2693
                ON a.user_id = u.id AND a.type = %s
2694
            WHERE u.disabled = %i AND u.deleted_at IS NULL AND u.public_key IS NOT NULL AND u.admin = %i
2695
            ORDER BY u.login ASC',
2696
            'user',
2697
            0,
2698
            0
2699
        );
2700
        $countUpdatedUsers = 0;
2701
        foreach ($users as $user) {
2702
            // Check if user has an api key
2703
            // If not then create one
2704
            if (is_null($user['increment_id'])) {
2705
                // Create the API key
2706
                DB::insert(
2707
                    prefixTable('api'),
2708
                    array(
2709
                        'type' => 'user',
2710
                        'user_id' => $user['id'],
2711
                        'value' => encryptUserObjectKey(base64_encode(base64_encode(uniqidReal(39))), $user['public_key']),
2712
                        'timestamp' => time(),
2713
                    )
2714
                );
2715
2716
                $countUpdatedUsers++;
2717
            }
2718
        }
2719
2720
        // Encrypt data to return
2721
        echo prepareExchangedData(
2722
            array(
2723
                'countUpdatedUsers' => $countUpdatedUsers,
2724
                'error' => false,
2725
            ),
2726
            'encode'
2727
        );
2728
2729
        break;
2730
    
2731
}
2732
2733
/**
2734
 * Delete multiple files with cross-platform compatibility
2735
 * 
2736
 * This function deletes a list of files while ensuring compatibility
2737
 * across different operating systems (Windows, Linux, etc.)
2738
 * 
2739
 * @param array $files Array of file paths to delete
2740
 * @param bool $ignoreErrors If true, continues execution even if a file cannot be deleted
2741
 * @return array Results of deletion operations (success/failure for each file)
2742
 */
2743
function deleteFiles(array $files, bool $ignoreErrors = false): array
2744
{
2745
    $session = SessionManager::getSession();
2746
    $lang = new Language($session->get('user-language') ?? 'english');
2747
2748
    $results = [];
2749
    $fullPath = __DIR__ . '/../';
2750
    
2751
    foreach ($files as $file) {
2752
        // Normalize path separators for cross-platform compatibility
2753
        $normalizedPath = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $fullPath.$file);
2754
        
2755
        // Check if file exists
2756
        if (!file_exists($normalizedPath)) {
2757
            $results[$normalizedPath] = [
2758
                'success' => false,
2759
                'error' => $lang->get('file_not_exists')
2760
            ];
2761
            
2762
            if (!$ignoreErrors) {
2763
                return $results;
2764
            }
2765
            
2766
            continue;
2767
        }
2768
        
2769
        // Check if path is actually a file (not a directory)
2770
        if (!is_file($normalizedPath)) {
2771
            $results[$normalizedPath] = [
2772
                'success' => false,
2773
                'error' => $lang->get('path_not_a_file')
2774
            ];
2775
            
2776
            if (!$ignoreErrors) {
2777
                return $results;
2778
            }
2779
            
2780
            continue;
2781
        }
2782
        
2783
        // Check if file is writable (can be deleted)
2784
        if (!is_writable($normalizedPath)) {
2785
            $results[$normalizedPath] = [
2786
                'success' => false,
2787
                'error' => $lang->get('file_not_writable')
2788
            ];
2789
            
2790
            if (!$ignoreErrors) {
2791
                return $results;
2792
            }
2793
            
2794
            continue;
2795
        }
2796
        
2797
        // Try to delete the file
2798
        $deleteResult = '';//@unlink($normalizedPath);
2799
        
2800
        if ($deleteResult) {
2801
            $results[$normalizedPath] = [
2802
                'success' => true,
2803
                'error' => '',
2804
            ];
2805
        } else {
2806
            $results[$normalizedPath] = [
2807
                'success' => false,
2808
                'error' => $lang->get('failed_to_delete')
2809
            ];
2810
            
2811
            if (!$ignoreErrors) {
2812
                return $results;
2813
            }
2814
        }
2815
    }
2816
    
2817
    return $results;
2818
}
2819
2820
/**
2821
 * Check the integrity of the files
2822
 * 
2823
 * @param string $baseDir
2824
 * @return array
2825
 */
2826
function filesIntegrityCheck($baseDir): array
2827
{
2828
    $referenceFile = __DIR__ . '/../files_reference.txt';
2829
2830
    $unknownFiles = findUnknownFiles($baseDir, $referenceFile);
2831
2832
    if (empty($unknownFiles)) {
2833
        return [
2834
            'error' => false,
2835
            'array' => [],
2836
            'message' => ''
2837
        ];
2838
    }
2839
2840
    // Check if the files are in the integrity file
2841
    return [
2842
        'error' => true,
2843
        'array' => $unknownFiles,
2844
        'message' => ''
2845
    ];
2846
}
2847
2848
/*
2849
 * Get all files in a directory
2850
 * 
2851
 * @param string $dir
2852
 * @return array
2853
 */
2854
function getAllFiles($dir): array
2855
{
2856
    $files = [];
2857
    $excludeDirs = ['upload', 'files', 'install', '_tools', 'random_compat', 'avatars']; // Répertoires à exclure
2858
    $excludeFilePrefixes = ['csrfp.config.php', 'settings.php', 'version-commit.php', 'phpstan.neon']; // Fichiers à exclure par préfixe
2859
2860
    $iterator = new RecursiveIteratorIterator(
2861
        new RecursiveCallbackFilterIterator(
2862
            new RecursiveDirectoryIterator(
2863
                $dir,
2864
                FilesystemIterator::SKIP_DOTS
2865
            ),
2866
            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

2866
            /** @scrutinizer ignore-type */ function ($current, $key, $iterator) {
Loading history...
2867
                // Ignore hidden files and folders
2868
                if ($current->getFilename()[0] === '.') {
2869
                    return false;
2870
                }
2871
                return true;
2872
            }
2873
        ),
2874
        RecursiveIteratorIterator::SELF_FIRST
2875
    );
2876
2877
    foreach ($iterator as $file) {
2878
        try {
2879
            if ($file->isFile()) {
2880
                $relativePath = str_replace($dir . DIRECTORY_SEPARATOR, '', $file->getPathname());
2881
                $relativePath = str_replace('\\', '/', $relativePath); // Normalisation Windows/Linux
2882
2883
                // Split relatif path into parts
2884
                $pathParts = explode('/', $relativePath);
2885
2886
                // Check if any part of the path is in the excludeDirs array
2887
                foreach ($pathParts as $part) {
2888
                    if (in_array($part, $excludeDirs, true)) {
2889
                        continue 2; // Skip the file
2890
                    }
2891
                }
2892
2893
                // Check if the file name starts with any of the prefixes in excludeFilePrefixes
2894
                $filename = basename($relativePath);
2895
                foreach ($excludeFilePrefixes as $prefix) {
2896
                    if (strpos($filename, $prefix) === 0) {
2897
                        continue 2; // Skip the file
2898
                    }
2899
                }
2900
2901
                // If OK then add to the list
2902
                $files[] = $relativePath;
2903
            }
2904
        } catch (UnexpectedValueException $e) {
2905
            continue; // Ignore the file if an error occurs
2906
        }
2907
    }
2908
2909
    return $files;
2910
}
2911
2912
/**
2913
 * Find unknown files
2914
 * 
2915
 * @param string $baseDir
2916
 * @param string $referenceFile
2917
 * @return array
2918
 */
2919
function findUnknownFiles($baseDir, $referenceFile): array
2920
{
2921
    $currentFiles = getAllFiles($baseDir);
2922
    $referenceFiles = file($referenceFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2923
2924
    // Remove empty lines and comments from the reference file
2925
    $unknownFiles = array_diff($currentFiles, $referenceFiles);
2926
2927
    // Save list to DB
2928
    DB::insertUpdate(
2929
        prefixTable('misc'),
2930
        array(
2931
            'type' => 'admin',
2932
            'intitule' => 'unknown_files',
2933
            'valeur' => json_encode($unknownFiles),
2934
            'created_at' => time(),
2935
        ),
2936
        [
2937
            'valeur' => json_encode($unknownFiles),
2938
            'updated_at' => time(),
2939
        ]
2940
    );
2941
2942
    // NOw remove ignored files from the list returned to user
2943
    // Get ignored files
2944
    $ignoredFiles = DB::queryFirstField(
2945
        'SELECT valeur 
2946
        FROM ' . prefixTable('misc') . ' 
2947
        WHERE type = %s AND intitule = %s',
2948
        'admin',
2949
        'ignored_unknown_files'
2950
    );
2951
    $ignoredFilesKeys = !is_null($ignoredFiles) && !empty($ignoredFiles) ? array_flip(json_decode($ignoredFiles)) : [];
2952
    $unknownFiles = array_diff_key($unknownFiles, $ignoredFilesKeys);
2953
2954
    return $unknownFiles;
2955
}
2956
2957
/**
2958
 * Check the integrity of the tables
2959
 * 
2960
 * @return array
2961
 */
2962
function tablesIntegrityCheck(): array
2963
{
2964
    // Get integrity tables file
2965
    $integrityTablesFile = TEAMPASS_ROOT_PATH . '/includes/tables_integrity.json';
2966
    if (file_exists($integrityTablesFile) === false) {
2967
        return [
2968
            'error' => true,
2969
            'message' => "Integrity file has not been found."
2970
        ];
2971
    }
2972
    // Convert json to array
2973
    $integrityTables = json_decode(file_get_contents($integrityTablesFile), true);
2974
    if (json_last_error() !== JSON_ERROR_NONE) {
2975
        return [
2976
            'error' => true,
2977
            'message' => "Integrity file is corrupted"
2978
        ];
2979
    }
2980
    // Get all tables
2981
    $tables = [];
2982
    foreach (DB::queryFirstColumn("SHOW TABLES") as $table) {
2983
        $tables[] = str_replace(DB_PREFIX, "", $table);;
2984
    }
2985
    // Prepare the integrity check
2986
    $tablesInError = [];
2987
    // Check if the tables are in the integrity file
2988
    foreach ($tables as $table) {
2989
        $tableFound = false;
2990
        $tableHash = "";
2991
        foreach ($integrityTables as $integrityTable) {
2992
            if ($integrityTable["table_name"] === $table) {
2993
                $tableFound = true;
2994
                $tableHash = $integrityTable["structure_hash"];
2995
                break; // Sortir de la boucle si la table est trouvée
2996
            }
2997
        }
2998
2999
        if ($tableFound === true) {
3000
            $createTable = DB::queryFirstRow("SHOW CREATE TABLE ".DB_PREFIX."$table");
3001
            $currentHash = hash('sha256', $createTable['Create Table']);
3002
            if ($currentHash !== $tableHash) {
3003
                $tablesInError[] = DB_PREFIX.$table;
3004
            }            
3005
        }
3006
    }
3007
    return [
3008
        'error' => false,
3009
        'array' => $tablesInError,
3010
        'message' => ""
3011
    ];
3012
}
3013
3014
/**
3015
 * Verify file integrity by checking presence & MD5 hashes.
3016
 *
3017
 * This function compares the current files in the project directory against a reference file
3018
 * containing expected file paths and their MD5 hashes. It reports any missing files or files
3019
 * whose hashes do not match the reference.
3020
 *
3021
 * Steps:
3022
 *  - Load reference file data (file => hash)
3023
 *  - Get all current files in the base directory
3024
 *  - For each file, check if it exists in the reference; if so, compare hashes
3025
 *  - Collect issues for missing or changed files
3026
 *
3027
 * @param string $baseDir        Base directory to scan for files
3028
 * @param string $referenceFile  Path to reference file with known hashes
3029
 * @return array                 Result with 'error', 'array' of issues, and 'message'
3030
 */
3031
function verifyFileHashes($baseDir, $referenceFile): array
3032
{
3033
    // Load reference data (file => hash)
3034
    $referenceData = parseReferenceFile($referenceFile);
3035
    // Get list of all current files in the project
3036
    $allFiles = getAllFiles($baseDir);
3037
    $issues = [];
3038
3039
    // Compare current files to reference
3040
    foreach ($allFiles as $file) {
3041
        // Check if file exists in reference list
3042
        if (!isset($referenceData[$file])) {
3043
            $issues[] = "<i>$file</i> is not listed in reference file";
3044
            continue;
3045
        }
3046
3047
        // Compare hashes
3048
        $expectedHash = $referenceData[$file];
3049
        $actualHash = md5_file($baseDir . '/' . $file);
3050
3051
        if ($expectedHash !== $actualHash) {
3052
            $issues[] = "$file (expected: <i>$expectedHash</i>, actual: <i>$actualHash</i>)";
3053
        }
3054
    }
3055
3056
    // Return summary
3057
    return [
3058
        'error' => !empty($issues),
3059
        'array' => $issues,
3060
        'message' => empty($issues) ? 'Project files integrity check is successful.' : 'Integrity issues found.'
3061
    ];
3062
}
3063
3064
/**
3065
 * Parse the reference file into an associative array.
3066
 *
3067
 * Each line in the reference file should be of the form: "path hash".
3068
 * The function returns an array mapping file paths to their expected MD5 hashes.
3069
 *
3070
 * @param string $referenceFile  Path to reference file
3071
 * @return array                 [ 'file/path' => 'md5hash' ]
3072
 */
3073
function parseReferenceFile($referenceFile) {
3074
    $data = [];
3075
    $lines = file($referenceFile, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
3076
    foreach ($lines as $line) {
3077
        [$path, $hash] = explode(' ', $line, 2);
3078
        $data[$path] = trim($hash);
3079
    }
3080
    return $data;
3081
}
3082
3083
/**
3084
 * Get all files in a directory with their md5 hashes.
3085
 *
3086
 * Recursively scans a directory, returning an associative array of file paths
3087
 * (relative to $dir) mapped to their MD5 hashes. Paths are normalized for cross-platform compatibility.
3088
 *
3089
 * @param string $dir    Directory to scan
3090
 * @return array         [ 'file/path' => 'md5hash' ]
3091
 */
3092
function getAllFilesWithHashes(string $dir): array
3093
{
3094
    $files = [];
3095
    $iterator = new RecursiveIteratorIterator(
3096
        new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS)
3097
    );
3098
3099
    foreach ($iterator as $file) {
3100
        if ($file->isFile()) {
3101
            // Build relative path
3102
            $relativePath = str_replace($dir . DIRECTORY_SEPARATOR, '', $file->getPathname());
3103
            $relativePath = str_replace('\\', '/', $relativePath); // Normalize for Windows
3104
            // Calculate hash
3105
            $files[$relativePath] = md5_file($file->getPathname());
3106
        }
3107
    }
3108
    return $files;
3109
}
3110
3111
/**
3112
 * Simulate user key change duration
3113
 * 
3114
 * This function simulates the process of changing a user's encryption key by re-encrypting
3115
 * a single item's share key. It estimates the total time required to re-encrypt all items
3116
 * for the user based on the time taken for this single operation.
3117
 * 
3118
 * @param array $userInfo Associative array containing user information:
3119
 *                        - 'id': User ID
3120
 *                        - 'public_key': User's public key
3121
 *                        - 'private_key': User's private key
3122
 * @return float|null Estimated time in seconds to re-encrypt all items, or null if no items found
3123
 */
3124
function simulateUserKeyChangeDuration($userInfo): ?float
3125
{
3126
    $startTime = microtime(true);
3127
    $timeExecutionEstimation = null;
3128
    
3129
    // Loop on items
3130
    $item = DB::queryFirstRow(
3131
        'SELECT i.id, i.pw, s.share_key, s.increment_id
3132
        FROM ' . prefixTable('items') . ' i
3133
        INNER JOIN ' . prefixTable('sharekeys_items') . ' s ON i.id = s.object_id
3134
        WHERE i.perso = %i
3135
            AND s.user_id = %i
3136
        ORDER BY RAND()
3137
        LIMIT 1',
3138
        0,
3139
        TP_USER_ID
3140
    );
3141
3142
    if ($item !== null) {
3143
        // Decrypt itemkey with admin key
3144
        $itemKey = decryptUserObjectKey(
3145
            $item['share_key'],
3146
            $userInfo['private_key']
3147
        );
3148
        
3149
        // Prevent to change key if its key is empty
3150
        if (empty($itemKey) === true) {
3151
            $share_key_for_item = '';
0 ignored issues
show
Unused Code introduced by
The assignment to $share_key_for_item is dead and can be removed.
Loading history...
3152
        } else {
3153
            // Encrypt Item key
3154
            $share_key_for_item = encryptUserObjectKey($itemKey, $userInfo['public_key']);
3155
        }
3156
3157
        $duration = microtime(true) - $startTime;
3158
3159
        // Get all items in database
3160
        $rows = DB::queryFirstRow(
3161
            'SELECT count(*) as counter
3162
            FROM ' . prefixTable('sharekeys_items') . ' s
3163
            WHERE s.user_id = %i',
3164
            TP_USER_ID
3165
        );
3166
        
3167
        $timeExecutionEstimation = round($duration * $rows['counter'], 0);
3168
        
3169
        // Update variable
3170
        DB::update(
3171
            prefixTable('misc'),
3172
            array(
3173
                'valeur' => $timeExecutionEstimation,
3174
                'updated_at' => time(),
3175
            ),
3176
            'type = %s AND intitule = %s',
3177
            'admin',
3178
            'task_duration_estimation'
3179
        );
3180
    }
3181
3182
    return $timeExecutionEstimation ?? 0;
3183
}