Issues (22)

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

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

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