Issues (27)

Security Analysis    no vulnerabilities found

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  Header Injection
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

sources/admin.queries.php (3 issues)

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

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

2669
                'estimatedTime' => round(/** @scrutinizer ignore-type */ $duration, 0),
Loading history...
2670
                'proposedDuration' => $proposedDuration, // New proposed value if current setting is not sufficient
2671
                'currentDuration' => (int) $SETTINGS['task_maximum_run_time'], // Current setting
2672
                'setupProposal' => $isCurrentSettingSufficient, // true if current setting is sufficient, false otherwise
2673
                'error' => false,
2674
            ),
2675
            'encode'
2676
        );
2677
2678
        break;
2679
    
2680
}
2681
2682
/**
2683
 * Delete multiple files with cross-platform compatibility
2684
 * 
2685
 * This function deletes a list of files while ensuring compatibility
2686
 * across different operating systems (Windows, Linux, etc.)
2687
 * 
2688
 * @param array $files Array of file paths to delete
2689
 * @param bool $ignoreErrors If true, continues execution even if a file cannot be deleted
2690
 * @return array Results of deletion operations (success/failure for each file)
2691
 */
2692
function deleteFiles(array $files, bool $ignoreErrors = false): array
2693
{
2694
    $session = SessionManager::getSession();
2695
    $lang = new Language($session->get('user-language') ?? 'english');
2696
2697
    $results = [];
2698
    $fullPath = __DIR__ . '/../';
2699
    
2700
    foreach ($files as $file) {
2701
        // Normalize path separators for cross-platform compatibility
2702
        $normalizedPath = str_replace(['\\', '/'], DIRECTORY_SEPARATOR, $fullPath.$file);
2703
        
2704
        // Check if file exists
2705
        if (!file_exists($normalizedPath)) {
2706
            $results[$normalizedPath] = [
2707
                'success' => false,
2708
                'error' => $lang->get('file_not_exists')
2709
            ];
2710
            
2711
            if (!$ignoreErrors) {
2712
                return $results;
2713
            }
2714
            
2715
            continue;
2716
        }
2717
        
2718
        // Check if path is actually a file (not a directory)
2719
        if (!is_file($normalizedPath)) {
2720
            $results[$normalizedPath] = [
2721
                'success' => false,
2722
                'error' => $lang->get('path_not_a_file')
2723
            ];
2724
            
2725
            if (!$ignoreErrors) {
2726
                return $results;
2727
            }
2728
            
2729
            continue;
2730
        }
2731
        
2732
        // Check if file is writable (can be deleted)
2733
        if (!is_writable($normalizedPath)) {
2734
            $results[$normalizedPath] = [
2735
                'success' => false,
2736
                'error' => $lang->get('file_not_writable')
2737
            ];
2738
            
2739
            if (!$ignoreErrors) {
2740
                return $results;
2741
            }
2742
            
2743
            continue;
2744
        }
2745
        
2746
        // Try to delete the file
2747
        $deleteResult = '';//@unlink($normalizedPath);
2748
        
2749
        if ($deleteResult) {
2750
            $results[$normalizedPath] = [
2751
                'success' => true,
2752
                'error' => '',
2753
            ];
2754
        } else {
2755
            $results[$normalizedPath] = [
2756
                'success' => false,
2757
                'error' => $lang->get('failed_to_delete')
2758
            ];
2759
            
2760
            if (!$ignoreErrors) {
2761
                return $results;
2762
            }
2763
        }
2764
    }
2765
    
2766
    return $results;
2767
}
2768
2769
/**
2770
 * Check the integrity of the files
2771
 * 
2772
 * @param string $baseDir
2773
 * @return array
2774
 */
2775
function filesIntegrityCheck($baseDir): array
2776
{
2777
    $referenceFile = __DIR__ . '/../files_reference.txt';
2778
2779
    $unknownFiles = findUnknownFiles($baseDir, $referenceFile);
2780
2781
    if (empty($unknownFiles)) {
2782
        return [
2783
            'error' => false,
2784
            'array' => [],
2785
            'message' => ''
2786
        ];
2787
    }
2788
2789
    // Check if the files are in the integrity file
2790
    return [
2791
        'error' => true,
2792
        'array' => $unknownFiles,
2793
        'message' => ''
2794
    ];
2795
}
2796
2797
/*
2798
 * Get all files in a directory
2799
 * 
2800
 * @param string $dir
2801
 * @return array
2802
 */
2803
function getAllFiles($dir): array
2804
{
2805
    $files = [];
2806
    $excludeDirs = ['upload', 'files', 'install', '_tools', 'random_compat', 'avatars']; // Répertoires à exclure
2807
    $excludeFilePrefixes = ['csrfp.config.php', 'settings.php', 'version-commit.php', 'phpstan.neon']; // Fichiers à exclure par préfixe
2808
2809
    $iterator = new RecursiveIteratorIterator(
2810
        new RecursiveCallbackFilterIterator(
2811
            new RecursiveDirectoryIterator(
2812
                $dir,
2813
                FilesystemIterator::SKIP_DOTS
2814
            ),
2815
            function ($current, $key, $iterator) {
0 ignored issues
show
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

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