Completed
Push — master ( 654d98...9448e6 )
by Nils
31s queued 17s
created

mailHandler()   C

Complexity

Conditions 15
Paths 6

Size

Total Lines 96
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 15
eloc 53
c 3
b 0
f 0
nc 6
nop 3
dl 0
loc 96
rs 5.9166

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Teampass - a collaborative passwords manager.
7
 * ---
8
 * This file is part of the TeamPass project.
9
 * 
10
 * TeamPass is free software: you can redistribute it and/or modify it
11
 * under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, version 3 of the License.
13
 * 
14
 * TeamPass is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU General Public License for more details.
18
 * 
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21
 * 
22
 * Certain components of this file may be under different licenses. For
23
 * details, see the `licenses` directory or individual file headers.
24
 * ---
25
 * @file      main.queries.php
26
 * @author    Nils Laumaillé ([email protected])
27
 * @copyright 2009-2024 Teampass.net
28
 * @license   GPL-3.0
29
 * @see       https://www.teampass.net
30
 */
31
32
use TeampassClasses\PasswordManager\PasswordManager;
33
use TeampassClasses\SessionManager\SessionManager;
34
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
35
use TeampassClasses\Language\Language;
36
use Hackzilla\PasswordGenerator\Generator\ComputerPasswordGenerator;
37
use Hackzilla\PasswordGenerator\RandomGenerator\Php7RandomGenerator;
38
use RobThree\Auth\TwoFactorAuth;
39
use EZimuel\PHPSecureSession;
40
use TeampassClasses\PerformChecks\PerformChecks;
41
use TeampassClasses\ConfigManager\ConfigManager;
42
use TeampassClasses\EmailService\EmailService;
43
use TeampassClasses\EmailService\EmailSettings;
44
45
// Load functions
46
require_once 'main.functions.php';
47
48
loadClasses('DB');
49
$session = SessionManager::getSession();
50
$request = SymfonyRequest::createFromGlobals();
51
$lang = new Language($session->get('user-language') ?? 'english');
52
53
// TODO : ajouter un check sue l'envoi de la key
54
55
// Load config
56
$configManager = new ConfigManager();
57
$SETTINGS = $configManager->getAllSettings();
58
59
// Do checks
60
// Instantiate the class with posted data
61
$checkUserAccess = new PerformChecks(
62
    dataSanitizer(
63
        [
64
            'type' => $request->request->get('type', '') !== '' ? htmlspecialchars($request->request->get('type')) : '',
65
        ],
66
        [
67
            'type' => 'trim|escape',
68
        ],
69
    ),
70
    [
71
        'user_id' => returnIfSet($session->get('user-id'), null),
72
        'user_key' => returnIfSet($session->get('key'), null),
73
    ]
74
);
75
// Handle the case
76
echo $checkUserAccess->caseHandler();
77
if (
78
    ($checkUserAccess->userAccessPage('home') === false ||
79
    $checkUserAccess->checkSession() === false)
80
    && in_array(filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS), ['get_teampass_settings', 'ga_generate_qr']) === false
81
) {
82
    // Not allowed page
83
    $session->set('system-error_code', ERR_NOT_ALLOWED);
84
    include $SETTINGS['cpassman_dir'] . '/error.php';
85
    exit;
86
}
87
88
// Define Timezone
89
date_default_timezone_set(isset($SETTINGS['timezone']) === true ? $SETTINGS['timezone'] : 'UTC');
90
set_time_limit(600);
91
92
// DO CHECKS
93
$post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
94
if (
95
    isset($post_type) === true
96
    && ($post_type === 'ga_generate_qr'
97
        || $post_type === 'get_teampass_settings')
98
) {
99
    // continue
100
    mainQuery($SETTINGS);
101
} elseif (
102
    $session->has('user-id') && null !== $session->get('user-id')
103
    && $checkUserAccess->userAccessPage('home') === false
104
) {
105
    $session->set('system-error_code', ERR_NOT_ALLOWED); //not allowed page
106
    include __DIR__.'/../error.php';
107
    exit();
108
} elseif (($session->has('user-id') && null !== $session->get('user-id')
109
        && $session->get('key') !== null)
110
    || (isset($post_type) === true
111
        && null !== filter_input(INPUT_POST, 'data', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES))
112
) {
113
    // continue
114
    mainQuery($SETTINGS);
115
} else {
116
    $session->set('system-error_code', ERR_NOT_ALLOWED); //not allowed page
117
    include __DIR__.'/../error.php';
118
    exit();
119
}
120
121
// Includes
122
include_once __DIR__.'/../sources/main.functions.php';
123
124
/**
125
 * Undocumented function.
126
 */
127
function mainQuery(array $SETTINGS)
128
{
129
    header('Content-type: text/html; charset=utf-8');
130
    header('Cache-Control: no-cache');
131
    error_reporting(E_ERROR);
132
133
    // Load libraries
134
    loadClasses('DB');
135
136
    // User's language loading
137
    $session = SessionManager::getSession();
138
    $lang = new Language($session->get('user-language') ?? 'english');
139
140
    // Prepare post variables
141
    $post_key = filter_input(INPUT_POST, 'key', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
142
    $post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
143
    $post_type_category = filter_input(INPUT_POST, 'type_category', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
144
    $post_data = filter_input(INPUT_POST, 'data', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES);
145
146
    // Check KEY
147
    if (isValueSetNullEmpty($post_key) === true) {
148
        echo prepareExchangedData(
149
            array(
150
                'error' => true,
151
                'message' => $lang->get('key_is_not_correct'),
152
            ),
153
            'encode',
154
            $post_key
155
        );
156
        return false;
157
    }
158
    // decrypt and retreive data in JSON format
159
    $dataReceived = empty($post_data) === false ? prepareExchangedData(
160
        $post_data,
161
        'decode'
162
    ) : '';
163
    
164
    switch ($post_type_category) {
165
        case 'action_password':
166
            echo passwordHandler($post_type, $dataReceived, $SETTINGS);
167
            break;
168
169
        case 'action_user':
170
            echo userHandler($post_type, $dataReceived, $SETTINGS, $post_key);
171
            break;
172
173
        case 'action_mail':
174
            echo mailHandler($post_type, $dataReceived, $SETTINGS);
175
            break;
176
177
        case 'action_key':
178
            // deepcode ignore ServerLeak: All cases handled by keyHandler return an encrypted string that is sent back to the client
179
            echo keyHandler($post_type, $dataReceived, $SETTINGS);
180
            break;
181
182
        case 'action_system':
183
            echo systemHandler($post_type, $dataReceived, $SETTINGS);
184
            break;
185
186
        case 'action_utils':
187
            echo utilsHandler($post_type, $dataReceived, $SETTINGS);
188
            break;
189
    }
190
    
191
}
192
193
/**
194
 * Handler for all password tasks
195
 *
196
 * @param string $post_type
197
 * @param array|null|string $dataReceived
198
 * @param array $SETTINGS
199
 * @return string
200
 */
201
function passwordHandler(string $post_type, /*php8 array|null|string*/ $dataReceived, array $SETTINGS): string
202
{
203
    $session = SessionManager::getSession();
204
205
    switch ($post_type) {
206
        case 'change_pw'://action_password
207
            return changePassword(
208
                (string) filter_var($dataReceived['new_pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
209
                isset($dataReceived['current_pw']) === true ? (string) filter_var($dataReceived['current_pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS) : '',
210
                (int) filter_var($dataReceived['complexity'], FILTER_SANITIZE_NUMBER_INT),
211
                (string) filter_var($dataReceived['change_request'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
212
                (int) $session->get('user-id'),
213
                $SETTINGS
214
            );
215
216
        /*
217
         * Change user's authentication password
218
         */
219
        case 'change_user_auth_password'://action_password
220
            return changeUserAuthenticationPassword(
221
                (int) $session->get('user-id'),
222
                (string) filter_var($dataReceived['old_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
223
                (string) filter_var($dataReceived['new_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
224
                $SETTINGS
225
            );
226
227
        /*
228
         * User's authentication password in LDAP has changed
229
         */
230
        case 'change_user_ldap_auth_password'://action_password
231
            return /** @scrutinizer ignore-call */ changeUserLDAPAuthenticationPassword(
232
                (int) $session->get('user-id'),
233
                filter_var($dataReceived['previous_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
234
                filter_var($dataReceived['current_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
235
                $SETTINGS
236
            );
237
238
        /*
239
         * test_current_user_password_is_correct
240
         */
241
        case 'test_current_user_password_is_correct'://action_password
242
            return isUserPasswordCorrect(
243
                (int) $session->get('user-id'),
244
                (string) $dataReceived['password'],
245
                $SETTINGS
246
            );
247
248
        /*
249
         * Default case
250
         */
251
        default :
252
            return prepareExchangedData(
253
                array(
254
                    'error' => true,
255
                ),
256
                'encode'
257
            );
258
    }
259
}
260
261
/**
262
 * Handler for all user tasks
263
 *
264
 * @param string $post_type
265
 * @param array|null|string $dataReceived
266
 * @param array $SETTINGS
267
 * @param string $post_key
268
 * @return string
269
 */
270
function userHandler(string $post_type, array|null|string $dataReceived, array $SETTINGS, string $post_key): string
271
{
272
    $session = SessionManager::getSession();
273
274
    // List of post types allowed to all users
275
    $all_users_can_access = [
276
        'get_user_info',
277
        'increase_session_time',
278
        'generate_password',
279
        'refresh_list_items_seen',
280
        'ga_generate_qr',
281
        'user_get_session_time',
282
        'save_user_location'
283
    ];
284
285
    // Default values
286
    $filtered_user_id = $session->get('user-id');
287
288
    // User can't manage users and requested type is administrative.
289
    if ((int) $session->get('user-admin') !== 1 &&
290
        (int) $session->get('user-manager') !== 1 &&
291
        (int) $session->get('user-can_manage_all_users') !== 1 &&
292
        !in_array($post_type, $all_users_can_access)) {
293
294
        echo prepareExchangedData(
295
            array(
296
                'error' => true,
297
            ),
298
            'encode'
299
        );
300
        exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
Bug Best Practice introduced by
In this branch, the function will implicitly return null which is incompatible with the type-hinted return string. Consider adding a return statement or allowing null as return value.

For hinted functions/methods where all return statements with the correct type are only reachable via conditions, ?null? gets implicitly returned which may be incompatible with the hinted type. Let?s take a look at an example:

interface ReturnsInt {
    public function returnsIntHinted(): int;
}

class MyClass implements ReturnsInt {
    public function returnsIntHinted(): int
    {
        if (foo()) {
            return 123;
        }
        // here: null is implicitly returned
    }
}
Loading history...
301
    }
302
303
    if (isset($dataReceived['user_id'])) {
304
        // Get info about user to modify
305
        $targetUserInfos = DB::queryfirstrow(
306
            'SELECT admin, gestionnaire, can_manage_all_users, isAdministratedByRole FROM ' . prefixTable('users') . '
307
            WHERE id = %i',
308
            $dataReceived['user_id']
309
        );
310
311
        if (
312
            // Administrator user
313
            (int) $session->get('user-admin') === 1
314
            // Manager of basic/ro users in this role
315
            || ((int) $session->get('user-manager') === 1
316
                && in_array($targetUserInfos['isAdministratedByRole'], $session->get('user-roles_array'))
317
                && (int) $targetUserInfos['admin'] !== 1
318
                && (int) $targetUserInfos['can_manage_all_users'] !== 1
319
                && (int) $targetUserInfos['gestionnaire'] !== 1)
320
            // Manager of all basic/ro users
321
            || ((int) $session->get('user-can_manage_all_users') === 1
322
                && (int) $targetUserInfos['admin'] !== 1
323
                && (int) $targetUserInfos['can_manage_all_users'] !== 1
324
                && (int) $targetUserInfos['gestionnaire'] !== 1)
325
        ) {
326
            // This user is allowed to modify other users.
327
            $filtered_user_id = $dataReceived['user_id'];
328
        }
329
    }
330
331
    switch ($post_type) {
332
        /*
333
        * Get info 
334
        */
335
        case 'get_user_info'://action_user
336
            return getUserInfo(
337
                (int) $filtered_user_id,
338
                (string) filter_var($dataReceived['fields'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
339
                $SETTINGS
340
            );
341
342
        /*
343
        * Increase the session time of User
344
        */
345
        case 'increase_session_time'://action_user
346
            return increaseSessionDuration(
347
                (int) filter_input(INPUT_POST, 'duration', FILTER_SANITIZE_NUMBER_INT)
348
            );
349
350
        /*
351
        * Generate a password generic
352
        */
353
        case 'generate_password'://action_user
354
            return generateGenericPassword(
355
                (int) filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT),
356
                (bool) filter_input(INPUT_POST, 'secure_pwd', FILTER_VALIDATE_BOOLEAN),
357
                (bool) filter_input(INPUT_POST, 'lowercase', FILTER_VALIDATE_BOOLEAN),
358
                (bool) filter_input(INPUT_POST, 'capitalize', FILTER_VALIDATE_BOOLEAN),
359
                (bool) filter_input(INPUT_POST, 'numerals', FILTER_VALIDATE_BOOLEAN),
360
                (bool) filter_input(INPUT_POST, 'symbols', FILTER_VALIDATE_BOOLEAN),
361
                $SETTINGS
362
            );
363
364
        /*
365
        * Refresh list of last items seen
366
        */
367
        case 'refresh_list_items_seen'://action_user
368
            if ($session->has('user-id') || (int) $session->get('user-id') && null !== $session->get('user-id') || (int) $session->get('user-id') > 0) {
369
                return refreshUserItemsSeenList(
370
                    $SETTINGS
371
                );
372
373
            } else {
374
                return json_encode(
375
                    array(
376
                        'error' => '',
377
                        'existing_suggestions' => 0,
378
                        'html_json' => '',
379
                    ),
380
                    JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
381
                );
382
            }
383
384
        /*
385
        * This will generate the QR Google Authenticator
386
        */
387
        case 'ga_generate_qr'://action_user
388
            return generateQRCode(
389
                (int) $filtered_user_id,
390
                (string) filter_var($dataReceived['demand_origin'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
391
                (string) filter_var($dataReceived['send_email'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
392
                (string) filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
393
                (string) filter_var($dataReceived['pwd'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
394
                (string) filter_var($dataReceived['token'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
395
                $SETTINGS,
396
                (string) $post_key
0 ignored issues
show
Unused Code introduced by
The call to generateQRCode() has too many arguments starting with (string)$post_key. ( Ignorable by Annotation )

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

396
            return /** @scrutinizer ignore-call */ generateQRCode(

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
397
            );
398
399
        /*
400
        * This will set the user ready
401
        */
402
        case 'user_is_ready'://action_user
403
            return userIsReady(
404
                (int) $filtered_user_id,
405
                (string) $SETTINGS['cpassman_dir']
406
            );
407
408
        /*
409
        * This post type is used to check if the user session is still valid
410
        */
411
        case 'user_get_session_time'://action_user
412
            return userGetSessionTime(
413
                (int) $session->get('user-id'),
414
                (string) $SETTINGS['cpassman_dir'],
415
                (int) $SETTINGS['maximum_session_expiration_time'],
416
            );
417
418
        case 'save_user_location'://action_user
419
            return userSaveIp(
420
                (int) $session->get('user-id'),
421
                (string) filter_var($dataReceived['action'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
422
            );
423
424
        /*
425
        * Default case
426
        */
427
        default :
428
            return prepareExchangedData(
429
                array(
430
                    'error' => true,
431
                ),
432
                'encode'
433
            );
434
    }
435
}
436
437
/**
438
 * Handler for all mail tasks
439
 *
440
 * @param string $post_type
441
 * @param array|null|string $dataReceived
442
 * @param array $SETTINGS
443
 * @return string
444
 */
445
function mailHandler(string $post_type, /*php8 array|null|string */$dataReceived, array $SETTINGS): string
446
{
447
    $session = SessionManager::getSession();
448
449
    switch ($post_type) {
450
        /*
451
         * CASE
452
         * Send email
453
         */
454
        case 'mail_me'://action_mail
455
            // Get info about user to send email
456
            $data_user = DB::queryfirstrow(
457
                'SELECT admin, gestionnaire, can_manage_all_users, isAdministratedByRole FROM ' . prefixTable('users') . '
458
                WHERE email = %s',
459
                filter_var($dataReceived['receipt'], FILTER_SANITIZE_FULL_SPECIAL_CHARS)
460
            );
461
462
            // Unknown email address
463
            if (!$data_user) {
464
                return prepareExchangedData(
465
                    array(
466
                        'error' => true,
467
                    ),
468
                    'encode'
469
                );
470
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
471
            }
472
473
            // Only administrators and managers can send mails
474
            if (
475
                // Administrator user
476
                (int) $session->get('user-admin') === 1
477
                // Manager of basic/ro users in this role
478
                || ((int) $session->get('user-manager') === 1
479
                    && in_array($data_user['isAdministratedByRole'], $session->get('user-roles_array'))
480
                    && (int) $data_user['admin'] !== 1
481
                    && (int) $data_user['can_manage_all_users'] !== 1
482
                    && (int) $data_user['gestionnaire'] !== 1)
483
                // Manager of all basic/ro users
484
                || ((int) $session->get('user-can_manage_all_users') === 1
485
                    && (int) $data_user['admin'] !== 1
486
                    && (int) $data_user['can_manage_all_users'] !== 1
487
                    && (int) $data_user['gestionnaire'] !== 1)
488
            ) {
489
                return sendMailToUser(
490
                    filter_var($dataReceived['receipt'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
491
                    $dataReceived['body'],
492
                    (string) filter_var($dataReceived['subject'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
493
                    (array) filter_var_array(
494
                        $dataReceived['pre_replace'],
495
                        FILTER_SANITIZE_FULL_SPECIAL_CHARS
496
                    ),
497
                    true
498
                );
499
            }
500
501
            return prepareExchangedData(
502
                array(
503
                    'error' => true,
504
                ),
505
                'encode'
506
            );
507
        /*
508
        * Send emails not sent
509
        */
510
        case 'send_waiting_emails'://mail
511
            // Administrative task
512
            if ((int) $session->get('user-admin') !== 1) {
513
                return prepareExchangedData(
514
                    array(
515
                        'error' => true,
516
                    ),
517
                    'encode'
518
                );
519
            }
520
521
            sendEmailsNotSent(
522
                $SETTINGS
523
            );
524
            return prepareExchangedData(
525
                array(
526
                    'error' => false,
527
                    'message' => 'mail_sent',
528
                ),
529
                'encode'
530
            );
531
532
        /*
533
        * Default case
534
        */
535
        default :
536
            return prepareExchangedData(
537
                array(
538
                    'error' => true,
539
                ),
540
                'encode'
541
            );
542
    }
543
}
544
545
/**
546
 * Handler for all key related tasks
547
 *
548
 * @param string $post_type
549
 * @param array|null|string $dataReceived
550
 * @param array $SETTINGS
551
 * @return string
552
 */
553
function keyHandler(string $post_type, /*php8 array|null|string */$dataReceived, array $SETTINGS): string
554
{
555
    $session = SessionManager::getSession();
556
557
    // List of post types allowed to all users
558
    $all_users_can_access = [
559
        'change_private_key_encryption_password',
560
        'user_new_keys_generation',
561
        'user_recovery_keys_download',
562
    ];
563
564
    // Default values
565
    $filtered_user_id = $session->get('user-id');
566
567
    if (isset($dataReceived['user_id'])) {
568
        // Get info about user to modify
569
        $targetUserInfos = DB::queryfirstrow(
570
            'SELECT admin, gestionnaire, can_manage_all_users, isAdministratedByRole FROM ' . prefixTable('users') . '
571
            WHERE id = %i',
572
            $dataReceived['user_id']
573
        );
574
    
575
        if (
576
            // Administrator user
577
            (int) $session->get('user-admin') === 1
578
            // Manager of basic/ro users in this role
579
            || ((int) $session->get('user-manager') === 1
580
                && in_array($targetUserInfos['isAdministratedByRole'], $session->get('user-roles_array'))
581
                && (int) $targetUserInfos['admin'] !== 1
582
                && (int) $targetUserInfos['can_manage_all_users'] !== 1
583
                && (int) $targetUserInfos['gestionnaire'] !== 1)
584
            // Manager of all basic/ro users
585
            || ((int) $session->get('user-can_manage_all_users') === 1
586
                && (int) $targetUserInfos['admin'] !== 1
587
                && (int) $targetUserInfos['can_manage_all_users'] !== 1
588
                && (int) $targetUserInfos['gestionnaire'] !== 1)
589
        ) {
590
            // This user is allowed to modify other users.
591
            $filtered_user_id = $dataReceived['user_id'];
592
    
593
        } else if (!in_array($post_type, $all_users_can_access)) {
594
            // User can't manage users and requested type is administrative.
595
            return prepareExchangedData(
596
                array(
597
                    'error' => true,
598
                ),
599
                'encode'
600
            ); 
601
        }
602
    }
603
604
    switch ($post_type) {
605
        /*
606
         * Generate a temporary encryption key for user
607
         */
608
        case 'generate_temporary_encryption_key'://action_key
609
            return generateOneTimeCode(
610
                (int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT)
611
            );
612
613
        /*
614
         * user_sharekeys_reencryption_next
615
         */
616
        case 'user_sharekeys_reencryption_next'://action_key
617
            return continueReEncryptingUserSharekeys(
618
                (int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT),
619
                (bool) filter_var($dataReceived['self_change'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
620
                (string) filter_var($dataReceived['action'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
621
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
622
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
623
                $SETTINGS
624
            );
625
626
        /*
627
         * user_psk_reencryption
628
         */
629
        case 'user_psk_reencryption'://action_key
630
            return migrateTo3_DoUserPersonalItemsEncryption(
631
                (int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT),
632
                (int) filter_var($dataReceived['start'], FILTER_SANITIZE_NUMBER_INT),
633
                (int) filter_var($dataReceived['length'], FILTER_SANITIZE_NUMBER_INT),
634
                (int) filter_var($dataReceived['counterItemsToTreat'], FILTER_SANITIZE_NUMBER_INT),
635
                (string) filter_var($dataReceived['userPsk'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
636
                $SETTINGS
637
            );
638
639
        /*
640
         * User's public/private keys change
641
         */
642
        case 'change_private_key_encryption_password'://action_key
643
            return changePrivateKeyEncryptionPassword(
644
                (int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT),
645
                (string) $dataReceived['current_code'],
646
                (string) filter_var($dataReceived['new_code'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
647
                (string) filter_var($dataReceived['action_type'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
648
                $SETTINGS
649
            );
650
651
        /*
652
         * Launch user keys change on his demand
653
         */
654
        case 'user_new_keys_generation'://action_key
655
656
            // Handle the case where no PWD is provided (user reset his own encryption keys).
657
            if (empty($dataReceived['user_pwd']) && (int) $filtered_user_id === $session->get('user-id')) {
658
                $dataReceived['user_pwd'] = $session->get('user-password');
659
            }
660
661
            return handleUserKeys(
662
                (int) filter_var($filtered_user_id, FILTER_SANITIZE_NUMBER_INT),
663
                (string) filter_var($dataReceived['user_pwd'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
664
                (int) isset($SETTINGS['maximum_number_of_items_to_treat']) === true ? $SETTINGS['maximum_number_of_items_to_treat'] : NUMBER_ITEMS_IN_BATCH,
665
                (string) filter_var($dataReceived['encryption_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
666
                (bool) filter_var($dataReceived['delete_existing_keys'], FILTER_VALIDATE_BOOLEAN),
667
                (bool) filter_var($dataReceived['send_email_to_user'], FILTER_VALIDATE_BOOLEAN),
668
                (bool) filter_var($dataReceived['encrypt_with_user_pwd'], FILTER_VALIDATE_BOOLEAN),
669
                (bool) isset($dataReceived['generate_user_new_password']) === true ? filter_var($dataReceived['generate_user_new_password'], FILTER_VALIDATE_BOOLEAN) : false,
670
                (string) filter_var($dataReceived['email_body'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
671
                (bool) filter_var($dataReceived['user_self_change'], FILTER_VALIDATE_BOOLEAN),
672
                (string) filter_var($dataReceived['recovery_public_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
673
                (string) filter_var($dataReceived['recovery_private_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
674
            );
675
676
        /*
677
         * Launch user recovery download
678
         */
679
        case 'user_recovery_keys_download'://action_key
680
            return handleUserRecoveryKeysDownload(
681
                (int) $filtered_user_id,
682
                (array) $SETTINGS,
683
            );
684
685
        /*
686
         * Default case
687
         */
688
        default :
689
            return prepareExchangedData(
690
                array(
691
                    'error' => true,
692
                ),
693
                'encode'
694
            );
695
    }
696
}
697
698
/**
699
 * Handler for all system tasks
700
 *
701
 * @param string $post_type
702
 * @param array|null|string $dataReceived
703
 * @param array $SETTINGS
704
 * @return string
705
 */
706
function systemHandler(string $post_type, array|null|string $dataReceived, array $SETTINGS): string
707
{
708
    $session = SessionManager::getSession();
709
    switch ($post_type) {
710
        /*
711
        * How many items for this user
712
        */
713
        case 'get_number_of_items_to_treat'://action_system
714
            return getNumberOfItemsToTreat(
715
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
716
                $SETTINGS
717
            );
718
719
        /*
720
        * refresh_folders_categories
721
        */
722
        case 'refresh_folders_categories'://action_system
723
            handleFoldersCategories(
724
                []
725
            );
726
            return prepareExchangedData(
727
                array(
728
                    'error' => false,
729
                ),
730
                'encode'
731
            );
732
733
        /*
734
         * Sending statistics
735
         */
736
        case 'sending_statistics'://action_system
737
            sendingStatistics(
738
                $SETTINGS
739
            );
740
            return prepareExchangedData(
741
                array(
742
                    'error' => false,
743
                ),
744
                'encode'
745
            );
746
747
        /*
748
            * Generate BUG report
749
            */
750
        case 'generate_bug_report'://action_system
751
752
            // Only administrators can see this confidential informations.
753
            if ((int) $session->get('user-admin') !== 1) {
754
                return prepareExchangedData(
755
                    array(
756
                        'error' => false,
757
                    ),
758
                    'encode'
759
                );
760
            }
761
762
            return generateBugReport(
763
                (array) $dataReceived,
764
                $SETTINGS
765
            );
766
767
        /*
768
        * get_teampass_settings
769
        */
770
        case 'get_teampass_settings'://action_system
771
772
            // Only administrators can see this confidential informations.
773
            if ((int) $session->get('user-admin') !== 1) {
774
                return prepareExchangedData(
775
                    array(
776
                        'error' => false,
777
                    ),
778
                    'encode'
779
                );
780
            }
781
782
            // Encrypt data to return
783
            return prepareExchangedData(
784
                array_intersect_key(
785
                    $SETTINGS, 
786
                    array(
787
                        'ldap_user_attribute' => '',
788
                        'enable_pf_feature' => '',
789
                        'clipboard_life_duration' => '',
790
                        'enable_favourites' => '',
791
                        'copy_to_clipboard_small_icons' => '',
792
                        'enable_attachment_encryption' => '',
793
                        'google_authentication' => '',
794
                        'agses_authentication_enabled' => '',
795
                        'yubico_authentication' => '',
796
                        'duo' => '',
797
                        'personal_saltkey_security_level' => '',
798
                        'enable_tasks_manager' => '',
799
                        'insert_manual_entry_item_history' => '',
800
                        'show_item_data' => '',
801
                    )
802
                ),
803
                'encode'
804
            );
805
806
        /*
807
         * Generates a TOKEN with CRYPT
808
         */
809
        case 'save_token'://action_system
810
            $token = GenerateCryptKey(
811
                null !== filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT) ? (int) filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT) : 20,
812
                null !== filter_input(INPUT_POST, 'secure', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? filter_input(INPUT_POST, 'secure', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) : false,
813
                null !== filter_input(INPUT_POST, 'numeric', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? filter_input(INPUT_POST, 'numeric', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) : false,
814
                null !== filter_input(INPUT_POST, 'capital', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? filter_input(INPUT_POST, 'capital', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) : false,
815
                null !== filter_input(INPUT_POST, 'symbols', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? filter_input(INPUT_POST, 'symbols', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) : false,
816
                null !== filter_input(INPUT_POST, 'lowercase', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) ? filter_input(INPUT_POST, 'lowercase', FILTER_VALIDATE_BOOLEAN, FILTER_NULL_ON_FAILURE) : false
817
            );
818
            
819
            // store in DB
820
            DB::insert(
821
                prefixTable('tokens'),
822
                array(
823
                    'user_id' => (int) $session->get('user-id'),
824
                    'token' => $token,
825
                    'reason' => filter_input(INPUT_POST, 'reason', FILTER_SANITIZE_FULL_SPECIAL_CHARS),
826
                    'creation_timestamp' => time(),
827
                    'end_timestamp' => time() + filter_input(INPUT_POST, 'duration', FILTER_SANITIZE_NUMBER_INT), // in secs
828
                )
829
            );
830
831
            return '[{"token" : "' . $token . '"}]';
832
833
        /*
834
        * Default case
835
        */
836
        default :
837
            return prepareExchangedData(
838
                array(
839
                    'error' => true,
840
                ),
841
                'encode'
842
            );
843
    }
844
}
845
846
847
function utilsHandler(string $post_type, array|null|string $dataReceived, array $SETTINGS): string
848
{
849
    switch ($post_type) {
850
        /*
851
         * generate_an_otp
852
         */
853
        case 'generate_an_otp'://action_utils
854
            return generateAnOTP(
855
                (string) filter_var($dataReceived['label'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
856
                (bool) filter_var($dataReceived['with_qrcode'], FILTER_VALIDATE_BOOLEAN),
857
                (string) filter_var($dataReceived['secret_key'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
858
            );
859
860
861
        /*
862
         * Default case
863
         */
864
        default :
865
            return prepareExchangedData(
866
                array(
867
                    'error' => true,
868
                ),
869
                'encode'
870
            );
871
    }
872
}
873
874
/**
875
 * Permits to set the user ready
876
 *
877
 * @param integer $userid
878
 * @param string $dir
879
 * @return string
880
 */
881
function userIsReady(int $userid, string $dir): string
882
{
883
    DB::update(
884
        prefixTable('users'),
885
        array(
886
            'is_ready_for_usage' => 1,
887
        ),
888
        'id = %i',
889
        $userid
890
    );
891
892
    // Send back
893
    return prepareExchangedData(
894
        array(
895
            'error' => false,
896
        ),
897
        'encode'
898
    ); 
899
}
900
901
902
/**
903
 * Permits to set the user ready
904
 *
905
 * @param integer $userid
906
 * @return string
907
 */
908
function userGetSessionTime(int $userid, string $dir, int $maximum_session_expiration_time): string
909
{
910
    $session = SessionManager::getSession();
911
    // Send back
912
    return prepareExchangedData(
913
        array(
914
            'error' => false,
915
            'timestamp' => $session->get('user-session_duration'),
916
            'max_time_to_add' => intdiv((($maximum_session_expiration_time*60) - ((int) $session->get('user-session_duration') - time())), 60),
917
            'max_session_duration' => $maximum_session_expiration_time,
918
        ),
919
        'encode'
920
    ); 
921
}
922
923
/**
924
 * Save the user's IP
925
 *
926
 * @param integer $userID
927
 * @param string $action
928
 * @return string
929
 */
930
function userSaveIp(int $userID, string $action): string
931
{
932
    if ($action === 'perform') {
933
        DB::update(
934
            prefixTable('users'),
935
            array(
936
                'user_ip' => getClientIpServer(),
937
                'user_ip_lastdate' => time(),
938
            ),
939
            'id = %i',
940
            $userID
941
        );
942
    }
943
944
    return prepareExchangedData(
945
        array(
946
            'error' => false,
947
        ),
948
        'encode'
949
    );
950
}
951
952
/**
953
 * Provides the number of items
954
 *
955
 * @param int   $userId     User ID
956
 * @param array $SETTINGS   TeampassSettings
957
 *
958
 * @return string
959
 */
960
function getNumberOfItemsToTreat(
961
    int $userId,
962
    array $SETTINGS
963
): string
964
{
965
    // get number of items
966
    DB::queryFirstRow(
967
        'SELECT increment_id
968
        FROM ' . prefixTable('sharekeys_items') .
969
        ' WHERE user_id = %i',
970
        $userId
971
    );
972
973
    // Send back
974
    return prepareExchangedData(
975
        array(
976
            'error' => false,
977
            'nbItems' => DB::count(),
978
        ),
979
        'encode'
980
    );
981
}
982
983
984
/**
985
 * 
986
 */
987
function changePassword(
988
    string $post_new_password,
989
    string $post_current_password,
990
    int $post_password_complexity,
991
    string $post_change_request,
992
    int $post_user_id,
993
    array $SETTINGS
994
): string
995
{
996
    $session = SessionManager::getSession();
997
    
998
    // Create password hash
999
    $passwordManager = new PasswordManager();
1000
    $post_new_password_hashed = $passwordManager->hashPassword($post_new_password);
1001
1002
    // Load user's language
1003
    $lang = new Language($session->get('user-language') ?? 'english');
1004
1005
    // User has decided to change is PW
1006
    if ($post_change_request === 'reset_user_password_expected'
1007
        || $post_change_request === 'user_decides_to_change_password'
1008
    ) {
1009
        // Check that current user is correct
1010
        if ((int) $post_user_id !== (int) $session->get('user-id')) {
1011
            return prepareExchangedData(
1012
                array(
1013
                    'error' => true,
1014
                    'message' => $lang->get('error_not_allowed_to'),
1015
                ),
1016
                'encode'
1017
            );
1018
        }
1019
1020
        // check if expected security level is reached
1021
        $dataUser = DB::queryfirstrow(
1022
            'SELECT *
1023
            FROM ' . prefixTable('users') . ' WHERE id = %i',
1024
            $post_user_id
1025
        );
1026
1027
        // check if badly written
1028
        $dataUser['fonction_id'] = array_filter(
1029
            explode(',', str_replace(';', ',', $dataUser['fonction_id']))
1030
        );
1031
        $dataUser['fonction_id'] = implode(',', $dataUser['fonction_id']);
1032
        DB::update(
1033
            prefixTable('users'),
1034
            array(
1035
                'fonction_id' => $dataUser['fonction_id'],
1036
            ),
1037
            'id = %i',
1038
            $post_user_id
1039
        );
1040
1041
        if (empty($dataUser['fonction_id']) === false) {
1042
            $data = DB::queryFirstRow(
1043
                'SELECT complexity
1044
                FROM ' . prefixTable('roles_title') . '
1045
                WHERE id IN (' . $dataUser['fonction_id'] . ')
1046
                ORDER BY complexity DESC'
1047
            );
1048
        } else {
1049
            // In case user has no roles yet
1050
            $data = array();
1051
            $data['complexity'] = 0;
1052
        }
1053
1054
        if ((int) $post_password_complexity < (int) $data['complexity']) {
1055
            return prepareExchangedData(
1056
                array(
1057
                    'error' => true,
1058
                    'message' => '<div style="margin:10px 0 10px 15px;">' . $lang->get('complexity_level_not_reached') . '.<br>' .
1059
                        $lang->get('expected_complexity_level') . ': <b>' . TP_PW_COMPLEXITY[$data['complexity']][1] . '</b></div>',
1060
                ),
1061
                'encode'
1062
            );
1063
        }
1064
1065
        // Check that the 2 passwords are differents
1066
        if ($post_current_password === $post_new_password) {
1067
            return prepareExchangedData(
1068
                array(
1069
                    'error' => true,
1070
                    'message' => $lang->get('password_already_used'),
1071
                ),
1072
                'encode'
1073
            );
1074
        }
1075
1076
        // update sessions
1077
        $session->set('user-last_pw_change', mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')));
1078
        $session->set('user-validite_pw', 1);
1079
1080
        // BEfore updating, check that the pwd is correct
1081
        if ($passwordManager->verifyPassword($post_new_password_hashed, $post_new_password) === true && empty($dataUser['private_key']) === false) {
1082
            $special_action = 'none';
1083
            if ($post_change_request === 'reset_user_password_expected') {
1084
                $session->set('user-private_key', decryptPrivateKey($post_current_password, $dataUser['private_key']));
1085
            }
1086
1087
            // update DB
1088
            DB::update(
1089
                prefixTable('users'),
1090
                array(
1091
                    'pw' => $post_new_password_hashed,
1092
                    'last_pw_change' => mktime(0, 0, 0, (int) date('m'), (int) date('d'), (int) date('y')),
1093
                    'last_pw' => $post_current_password,
1094
                    'special' => $special_action,
1095
                    'private_key' => encryptPrivateKey($post_new_password, $session->get('user-private_key')),
1096
                ),
1097
                'id = %i',
1098
                $post_user_id
1099
            );
1100
            // update LOG
1101
            logEvents($SETTINGS, 'user_mngt', 'at_user_pwd_changed', (string) $session->get('user-id'), $session->get('user-login'), $post_user_id);
1102
1103
            // Send back
1104
            return prepareExchangedData(
1105
                array(
1106
                    'error' => false,
1107
                    'message' => '',
1108
                ),
1109
                'encode'
1110
            );
1111
        }
1112
        // Send back
1113
        return prepareExchangedData(
1114
            array(
1115
                'error' => true,
1116
                'message' => $lang->get('pw_hash_not_correct'),
1117
            ),
1118
            'encode'
1119
        );
1120
    }
1121
    return prepareExchangedData(
1122
        array(
1123
            'error' => true,
1124
            'message' => $lang->get('error_not_allowed_to'),
1125
        ),
1126
        'encode'
1127
    );
1128
}
1129
1130
function generateQRCode(
1131
    $post_id,
1132
    $post_demand_origin,
1133
    $post_send_mail,
1134
    $post_login,
1135
    $post_pwd,
1136
    $post_token,
1137
    array $SETTINGS
1138
): string
1139
{
1140
    // Load user's language
1141
    $session = SessionManager::getSession();
1142
    $lang = new Language($session->get('user-language') ?? 'english');
1143
1144
    // is this allowed by setting
1145
    if (isKeyExistingAndEqual('ga_reset_by_user', 0, $SETTINGS) === true
1146
        && (null === $post_demand_origin || $post_demand_origin !== 'users_management_list')
1147
    ) {
1148
        // User cannot ask for a new code
1149
        return prepareExchangedData(
1150
            array(
1151
                'error' => true,
1152
                'message' => "113 ".$lang->get('error_not_allowed_to')." - ".isKeyExistingAndEqual('ga_reset_by_user', 1, $SETTINGS),
1153
            ),
1154
            'encode'
1155
        );
1156
    }
1157
    
1158
    // Check if user exists
1159
    if (isValueSetNullEmpty($post_id) === true) {
1160
        // Get data about user
1161
        $dataUser = DB::queryfirstrow(
1162
            'SELECT id, email, pw
1163
            FROM ' . prefixTable('users') . '
1164
            WHERE login = %s',
1165
            $post_login
1166
        );
1167
    } else {
1168
        $dataUser = DB::queryfirstrow(
1169
            'SELECT id, login, email, pw
1170
            FROM ' . prefixTable('users') . '
1171
            WHERE id = %i',
1172
            $post_id
1173
        );
1174
        $post_login = $dataUser['login'];
1175
    }
1176
    // Get number of returned users
1177
    $counter = DB::count();
1178
1179
    // Do treatment
1180
    if ($counter === 0) {
1181
        // Not a registered user !
1182
        logEvents($SETTINGS, 'failed_auth', 'user_not_exists', '', stripslashes($post_login), stripslashes($post_login));
1183
        return prepareExchangedData(
1184
            array(
1185
                'error' => true,
1186
                'message' => $lang->get('no_user'),
1187
                'tst' => 1,
1188
            ),
1189
            'encode'
1190
        );
1191
    }
1192
1193
    $passwordManager = new PasswordManager();
1194
    if (
1195
        isSetArrayOfValues([$post_pwd, $dataUser['pw']]) === true
1196
        && $passwordManager->verifyPassword($dataUser['pw'], $post_pwd) === false
1197
        && $post_demand_origin !== 'users_management_list'
1198
    ) {
1199
        // checked the given password
1200
        logEvents($SETTINGS, 'failed_auth', 'password_is_not_correct', '', stripslashes($post_login), stripslashes($post_login));
1201
        return prepareExchangedData(
1202
            array(
1203
                'error' => true,
1204
                'message' => $lang->get('no_user'),
1205
                'tst' => $post_demand_origin,
1206
            ),
1207
            'encode'
1208
        );
1209
    }
1210
    
1211
    if (empty($dataUser['email']) === true) {
1212
        return prepareExchangedData(
1213
            array(
1214
                'error' => true,
1215
                'message' => $lang->get('no_email_set'),
1216
            ),
1217
            'encode'
1218
        );
1219
    }
1220
1221
    // Check if token already used
1222
    $dataToken = DB::queryfirstrow(
1223
        'SELECT end_timestamp, reason
1224
        FROM ' . prefixTable('tokens') . '
1225
        WHERE token = %s AND user_id = %i',
1226
        $post_token,
1227
        $dataUser['id']
1228
    );
1229
    $tokenId = '';
1230
    if (DB::count() > 0 && is_null($dataToken['end_timestamp']) === false && $dataToken['reason'] === 'auth_qr_code') {
1231
        // This token has already been used
1232
        return prepareExchangedData(
1233
            array(
1234
                'error' => true,
1235
                'message' => 'TOKEN already used',//$lang->get('no_email_set'),
1236
            ),
1237
            'encode'
1238
        );
1239
    } elseif(DB::count() === 0) {
1240
        // Store token for this action
1241
        DB::insert(
1242
            prefixTable('tokens'),
1243
            array(
1244
                'user_id' => (int) $dataUser['id'],
1245
                'token' => $post_token,
1246
                'reason' => 'auth_qr_code',
1247
                'creation_timestamp' => time(),
1248
            )
1249
        );
1250
        $tokenId = DB::insertId();
1251
    }
1252
    
1253
    // generate new GA user code
1254
    $tfa = new TwoFactorAuth($SETTINGS['ga_website_name']);
1255
    $gaSecretKey = $tfa->createSecret();
1256
    $gaTemporaryCode = GenerateCryptKey(12, false, true, true, false, true);
1257
1258
    DB::update(
1259
        prefixTable('users'),
1260
        [
1261
            'ga' => $gaSecretKey,
1262
            'ga_temporary_code' => $gaTemporaryCode,
1263
        ],
1264
        'id = %i',
1265
        $dataUser['id']
1266
    );
1267
1268
    // Log event
1269
    logEvents($SETTINGS, 'user_connection', 'at_2fa_google_code_send_by_email', (string) $dataUser['id'], stripslashes($post_login), stripslashes($post_login));
1270
1271
    // Update token status
1272
    DB::update(
1273
        prefixTable('tokens'),
1274
        [
1275
            'end_timestamp' => time(),
1276
        ],
1277
        'id = %i',
1278
        $tokenId
1279
    );
1280
1281
    // send mail?
1282
    if ((int) $post_send_mail === 1) {
1283
        prepareSendingEmail(
1284
            $lang->get('email_ga_subject'),
1285
            str_replace(
1286
                '#2FACode#',
1287
                $gaTemporaryCode,
1288
                $lang->get('email_ga_text')
1289
            ),
1290
            $dataUser['email']
1291
        );
1292
1293
        // send back
1294
        return prepareExchangedData(
1295
            array(
1296
                'error' => false,
1297
                'message' => $post_send_mail,
1298
                'email' => $dataUser['email'],
1299
                'email_result' => str_replace(
1300
                    '#email#',
1301
                    '<b>' . obfuscateEmail($dataUser['email']) . '</b>',
1302
                    addslashes($lang->get('admin_email_result_ok'))
1303
                ),
1304
            ),
1305
            'encode'
1306
        );
1307
    }
1308
    
1309
    // send back
1310
    return prepareExchangedData(
1311
        array(
1312
            'error' => false,
1313
            'message' => '',
1314
            'email' => $dataUser['email'],
1315
            'email_result' => str_replace(
1316
                '#email#',
1317
                '<b>' . obfuscateEmail($dataUser['email']) . '</b>',
1318
                addslashes($lang->get('admin_email_result_ok'))
1319
            ),
1320
        ),
1321
        'encode'
1322
    );
1323
}
1324
1325
function sendEmailsNotSent(
1326
    array $SETTINGS
1327
)
1328
{
1329
    $emailSettings = new EmailSettings($SETTINGS);
1330
    $emailService = new EmailService();
1331
1332
    if (isKeyExistingAndEqual('enable_send_email_on_user_login', 1, $SETTINGS) === true) {
1333
        $row = DB::queryFirstRow(
1334
            'SELECT valeur FROM ' . prefixTable('misc') . ' WHERE type = %s AND intitule = %s',
1335
            'cron',
1336
            'sending_emails'
1337
        );
1338
1339
        if ((int) (time() - $row['valeur']) >= 300 || (int) $row['valeur'] === 0) {
1340
            $rows = DB::query(
1341
                'SELECT *
1342
                FROM ' . prefixTable('emails') .
1343
                ' WHERE status != %s',
1344
                'sent'
1345
            );
1346
            foreach ($rows as $record) {
1347
                // Send email
1348
                $ret = json_decode(
1349
                    $emailService->sendMail(
1350
                        $record['subject'],
1351
                        $record['body'],
1352
                        $record['receivers'],
1353
                        $emailSettings
1354
                    ),
1355
                    true
1356
                );
1357
1358
                // update item_id in files table
1359
                DB::update(
1360
                    prefixTable('emails'),
1361
                    array(
1362
                        'status' => $ret['error'] === 'error_mail_not_send' ? 'not_sent' : 'sent',
1363
                    ),
1364
                    'timestamp = %s',
1365
                    $record['timestamp']
1366
                );
1367
            }
1368
        }
1369
        // update cron time
1370
        DB::update(
1371
            prefixTable('misc'),
1372
            array(
1373
                'valeur' => time(),
1374
                'updated_at' => time(),
1375
            ),
1376
            'intitule = %s AND type = %s',
1377
            'sending_emails',
1378
            'cron'
1379
        );
1380
    }
1381
}
1382
1383
1384
function refreshUserItemsSeenList(
1385
    array $SETTINGS
1386
): string
1387
{
1388
    $session = SessionManager::getSession();
1389
1390
    // get list of last items seen
1391
    $arr_html = array();
1392
    $rows = DB::query(
1393
        'SELECT i.id AS id, i.label AS label, i.id_tree AS id_tree, l.date, i.perso AS perso, i.restricted_to AS restricted
1394
        FROM ' . prefixTable('log_items') . ' AS l
1395
        RIGHT JOIN ' . prefixTable('items') . ' AS i ON (l.id_item = i.id)
1396
        WHERE l.action = %s AND l.id_user = %i
1397
        ORDER BY l.date DESC
1398
        LIMIT 0, 100',
1399
        'at_shown',
1400
        $session->get('user-id')
1401
    );
1402
    if (DB::count() > 0) {
1403
        foreach ($rows as $record) {
1404
            if (in_array($record['id']->id, array_column($arr_html, 'id')) === false) {
1405
                array_push(
1406
                    $arr_html,
1407
                    array(
1408
                        'id' => $record['id'],
1409
                        'label' => htmlspecialchars(stripslashes(htmlspecialchars_decode($record['label'], ENT_QUOTES)), ENT_QUOTES),
1410
                        'tree_id' => $record['id_tree'],
1411
                        'perso' => $record['perso'],
1412
                        'restricted' => $record['restricted'],
1413
                    )
1414
                );
1415
                if (count($arr_html) >= (int) $SETTINGS['max_latest_items']) {
1416
                    break;
1417
                }
1418
            }
1419
        }
1420
    }
1421
1422
    // get wainting suggestions
1423
    $nb_suggestions_waiting = 0;
1424
    if (isKeyExistingAndEqual('enable_suggestion', 1, $SETTINGS) === true
1425
        && ((int) $session->get('user-admin') === 1 || (int) $session->get('user-manager') === 1)
1426
    ) {
1427
        DB::query('SELECT * FROM ' . prefixTable('suggestion'));
1428
        $nb_suggestions_waiting = DB::count();
1429
    }
1430
1431
    return json_encode(
1432
        array(
1433
            'error' => '',
1434
            'existing_suggestions' => $nb_suggestions_waiting,
1435
            'html_json' => $arr_html,
1436
        ),
1437
        JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1438
    );
1439
}
1440
1441
function sendingStatistics(
1442
    array $SETTINGS
1443
): void
1444
{
1445
    $session = SessionManager::getSession();
1446
    if (
1447
        isSetArrayOfValues([$SETTINGS['send_statistics_items'], $SETTINGS['send_stats_time']]) === true
1448
        && isKeyExistingAndEqual('send_stats', 1, $SETTINGS) === true
1449
        && (int) ($SETTINGS['send_stats_time'] + TP_ONE_DAY_SECONDS) > time()
1450
    ) {
1451
        // get statistics data
1452
        $stats_data = getStatisticsData($SETTINGS);
1453
1454
        // get statistics items to share
1455
        $statsToSend = [];
1456
        $statsToSend['ip'] = $_SERVER['SERVER_ADDR'];
1457
        $statsToSend['timestamp'] = time();
1458
        foreach (array_filter(explode(';', $SETTINGS['send_statistics_items'])) as $data) {
1459
            if ($data === 'stat_languages') {
1460
                $tmp = '';
1461
                foreach ($stats_data[$data] as $key => $value) {
1462
                    $tmp .= $tmp === '' ? $key . '-' . $value : ',' . $key . '-' . $value;
1463
                }
1464
                $statsToSend[$data] = $tmp;
1465
            } elseif ($data === 'stat_country') {
1466
                $tmp = '';
1467
                foreach ($stats_data[$data] as $key => $value) {
1468
                    $tmp .= $tmp === '' ? $key . '-' . $value : ',' . $key . '-' . $value;
1469
                }
1470
                $statsToSend[$data] = $tmp;
1471
            } else {
1472
                $statsToSend[$data] = $stats_data[$data];
1473
            }
1474
        }
1475
1476
        // connect to Teampass Statistics database
1477
        $link2 = new MeekroDB(
1478
            'teampass.pw',
1479
            'teampass_user',
1480
            'ZMlEfRzKzFLZNzie',
1481
            'teampass_followup',
1482
            '3306',
1483
            'utf8'
1484
        );
1485
1486
        $link2->insert(
1487
            'statistics',
1488
            $statsToSend
1489
        );
1490
1491
        // update table misc with current timestamp
1492
        DB::update(
1493
            prefixTable('misc'),
1494
            array(
1495
                'valeur' => time(),
1496
                'updated_at' => time(),
1497
            ),
1498
            'type = %s AND intitule = %s',
1499
            'admin',
1500
            'send_stats_time'
1501
        );
1502
1503
        //permits to test only once by session
1504
        $session->set('system-send_stats_done', 1);
1505
        $SETTINGS['send_stats_time'] = time();
1506
    }
1507
}
1508
1509
function generateBugReport(
1510
    array $data,
1511
    array $SETTINGS
1512
): string
1513
{
1514
    $config_exclude_vars = array(
1515
        'bck_script_passkey',
1516
        'email_smtp_server',
1517
        'email_auth_username',
1518
        'email_auth_pwd',
1519
        'email_from',
1520
        'onthefly-restore-key',
1521
        'onthefly-backup-key',
1522
        'ldap_password',
1523
        'ldap_hosts',
1524
        'proxy_ip',
1525
        'ldap_bind_passwd',
1526
        'syslog_host',
1527
        'duo_akey',
1528
        'duo_ikey',
1529
        'duo_skey',
1530
        'duo_host',
1531
        'oauth2_client_id',
1532
        'oauth2_tenant_id',
1533
        'oauth2_client_secret',
1534
    );
1535
1536
    // Load user's language
1537
    $session = SessionManager::getSession();
1538
    $lang = new Language($session->get('user-language') ?? 'english');
1539
1540
    // Read config file
1541
    $list_of_options = '';
1542
    $url_found = '';
1543
    $anonym_url = '';
1544
    $sortedSettings = $SETTINGS;
1545
    ksort($sortedSettings);
1546
1547
    foreach ($sortedSettings as $key => $value) {
1548
        // Identify url to anonymize it
1549
        if ($key === 'cpassman_url' && empty($url_found) === true) {
1550
            $url_found = $value;
1551
            if (empty($url_found) === false) {
1552
                $tmp = parse_url($url_found);
1553
                $anonym_url = $tmp['scheme'] . '://<anonym_url>' . (isset($tmp['path']) === true ? $tmp['path'] : '');
1554
                $value = $anonym_url;
1555
            } else {
1556
                $value = '';
1557
            }
1558
        }
1559
1560
        // Anonymize all urls
1561
        if (empty($anonym_url) === false) {
1562
            $value = str_replace($url_found, $anonym_url, (string) $value);
1563
        }
1564
1565
        // Clear some vars
1566
        foreach ($config_exclude_vars as $var) {
1567
            if ($key === $var) {
1568
                $value = '<removed>';
1569
            }
1570
        }
1571
1572
        // Complete line to display
1573
        $list_of_options .= "'$key' => '$value'\n";
1574
    }
1575
1576
    // Get error
1577
    $err = error_get_last();
1578
1579
    // Get 10 latest errors in Teampass
1580
    $teampass_errors = '';
1581
    $rows = DB::query(
1582
        'SELECT label, date AS error_date
1583
        FROM ' . prefixTable('log_system') . "
1584
        WHERE `type` LIKE 'error'
1585
        ORDER BY `date` DESC
1586
        LIMIT 0, 10"
1587
    );
1588
    if (DB::count() > 0) {
1589
        foreach ($rows as $record) {
1590
            if (empty($teampass_errors) === true) {
1591
                $teampass_errors = ' * ' . date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['error_date']) . ' - ' . $record['label'];
1592
            } else {
1593
                $teampass_errors .= ' * ' . date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], (int) $record['error_date']) . ' - ' . $record['label'];
1594
            }
1595
        }
1596
    }
1597
1598
    $link = mysqli_connect(DB_HOST, DB_USER, DB_PASSWD_CLEAR, DB_NAME, (int) DB_PORT, null);
1599
1600
    // Now prepare text
1601
    $txt = '### Page on which it happened
1602
' . $data['current_page'] . '
1603
1604
### Steps to reproduce
1605
1.
1606
2.
1607
3.
1608
1609
### Expected behaviour
1610
Tell us what should happen
1611
1612
1613
### Actual behaviour
1614
Tell us what happens instead
1615
1616
### Server configuration
1617
**Operating system**: ' . php_uname() . '
1618
1619
**Web server:** ' . $_SERVER['SERVER_SOFTWARE'] . '
1620
1621
**Database:** ' . ($link === false ? $lang->get('undefined') : mysqli_get_server_info($link)) . '
1622
1623
**PHP version:** ' . PHP_VERSION . '
1624
1625
**Teampass version:** ' . TP_VERSION . '
1626
1627
**Teampass configuration file:**
1628
```
1629
' . $list_of_options . '
1630
```
1631
1632
**Updated from an older Teampass or fresh install:**
1633
1634
### Client configuration
1635
1636
**Browser:** ' . $data['browser_name'] . ' - ' . $data['browser_version'] . '
1637
1638
**Operating system:** ' . $data['os'] . ' - ' . $data['os_archi'] . 'bits
1639
1640
### Logs
1641
1642
#### Web server error log
1643
```
1644
' . $err['message'] . ' - ' . $err['file'] . ' (' . $err['line'] . ')
1645
```
1646
1647
#### Teampass 10 last system errors
1648
```
1649
' . $teampass_errors . '
1650
```
1651
1652
#### Log from the web-browser developer console (CTRL + SHIFT + i)
1653
```
1654
Insert the log here and especially the answer of the query that failed.
1655
```
1656
';
1657
1658
    return prepareExchangedData(
1659
        array(
1660
            'html' => $txt,
1661
            'error' => '',
1662
        ),
1663
        'encode'
1664
    );
1665
}
1666
1667
/**
1668
 * Check that the user password is valid
1669
 *
1670
 * @param integer $post_user_id
1671
 * @param string $post_user_password
1672
 * @param array $SETTINGS
1673
 * @return string
1674
 */
1675
function isUserPasswordCorrect(
1676
    int $post_user_id,
1677
    string $post_user_password,
1678
    array $SETTINGS
1679
): string
1680
{
1681
    $session = SessionManager::getSession();
1682
    // Load user's language
1683
    $lang = new Language($session->get('user-language') ?? 'english');
1684
    
1685
    if (isUserIdValid($post_user_id) === true) {
1686
        // Check if user exists
1687
        $userInfo = DB::queryFirstRow(
1688
            'SELECT public_key, private_key, pw, auth_type
1689
            FROM ' . prefixTable('users') . '
1690
            WHERE id = %i',
1691
            $post_user_id
1692
        );
1693
        if (DB::count() > 0 && empty($userInfo['private_key']) === false) {
1694
            // Get itemKey from current user
1695
            // Get one item
1696
            $currentUserKey = DB::queryFirstRow(
1697
                'SELECT object_id, share_key, increment_id
1698
                FROM ' . prefixTable('sharekeys_items') . ' AS si
1699
                INNER JOIN ' . prefixTable('items') . ' AS i ON  (i.id = si.object_id)
1700
                INNER JOIN ' . prefixTable('nested_tree') . ' AS nt ON  (i.id_tree = nt.id)
1701
                WHERE user_id = %i AND nt.personal_folder = %i',
1702
                $post_user_id,
1703
                0
1704
            );
1705
            
1706
            if (DB::count() === 0) {
1707
                // This user has no items
1708
                // let's consider no items in DB
1709
                return prepareExchangedData(
1710
                    array(
1711
                        'error' => false,
1712
                        'message' => '',
1713
                        'debug' => '',
1714
                    ),
1715
                    'encode'
1716
                );
1717
            }
1718
1719
            if ($currentUserKey !== null) {
1720
                // Decrypt itemkey with user key
1721
                // use old password to decrypt private_key
1722
                $session->set('user-private_key', decryptPrivateKey($post_user_password, $userInfo['private_key']));
1723
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
1724
1725
                //echo $post_user_password."  --  ".$userInfo['private_key']. ";;";
1726
1727
                if (empty(base64_decode($itemKey)) === false) {
1728
                    // GOOD password
1729
                    return prepareExchangedData(
1730
                        array(
1731
                            'error' => false,
1732
                            'message' => '',
1733
                            'debug' => '',
1734
                        ),
1735
                        'encode'
1736
                    );
1737
                }
1738
            }
1739
1740
            // use the password check
1741
            $passwordManager = new PasswordManager();
1742
            
1743
            if ($passwordManager->verifyPassword($userInfo['pw'], htmlspecialchars_decode($post_user_password)) === true) {
1744
                // GOOD password
1745
                return prepareExchangedData(
1746
                    array(
1747
                        'error' => false,
1748
                        'message' => '',
1749
                        'debug' => '',
1750
                    ),
1751
                    'encode'
1752
                );
1753
            }
1754
        }
1755
    }
1756
1757
    return prepareExchangedData(
1758
        array(
1759
            'error' => true,
1760
            'message' => $lang->get('password_is_not_correct'),
1761
        ),
1762
        'encode'
1763
    );
1764
}
1765
1766
function changePrivateKeyEncryptionPassword(
1767
    int $post_user_id,
1768
    string $post_current_code,
1769
    string $post_new_code,
1770
    string $post_action_type,
1771
    array $SETTINGS
1772
): string
1773
{
1774
    $session = SessionManager::getSession();
1775
    // Load user's language
1776
    $lang = new Language($session->get('user-language') ?? 'english');
1777
    
1778
    if (empty($post_new_code) === true) {
1779
        if (empty($session->get('user-password')) === false) {
1780
            $post_new_code = $session->get('user-password');
1781
        } else {
1782
            // no user password???
1783
            return prepareExchangedData(
1784
                array(
1785
                    'error' => true,
1786
                    'message' => $lang->get('error_no_user_password_exists'),
1787
                    'debug' => '',
1788
                ),
1789
                'encode'
1790
            );
1791
        }
1792
    }
1793
1794
    if (isUserIdValid($post_user_id) === true) {
1795
        // Get user info
1796
        $userData = DB::queryFirstRow(
1797
            'SELECT private_key
1798
            FROM ' . prefixTable('users') . '
1799
            WHERE id = %i',
1800
            $post_user_id
1801
        );
1802
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
1803
            if ($post_action_type === 'encrypt_privkey_with_user_password') {
1804
                // Here the user has his private key encrypted with an OTC.
1805
                // We need to encrypt it with his real password
1806
                $privateKey = decryptPrivateKey($post_new_code, $userData['private_key']);
1807
                $hashedPrivateKey = encryptPrivateKey($post_current_code, $privateKey);
1808
            } else {
1809
                $privateKey = decryptPrivateKey($post_current_code, $userData['private_key']);
1810
                $hashedPrivateKey = encryptPrivateKey($post_new_code, $privateKey);
1811
            }
1812
1813
            // Should fail here to avoid break user private key.
1814
            if (strlen($privateKey) === 0 || strlen($hashedPrivateKey) < 30) {
1815
                error_log("Error reencrypt user private key. User ID: {$post_user_id}, Given OTP: '{$post_current_code}'");
1816
                return prepareExchangedData(
1817
                    array(
1818
                        'error' => true,
1819
                        'message' => $lang->get('error_otp_secret'),
1820
                        'debug' => '',
1821
                    ),
1822
                    'encode'
1823
                );
1824
            }
1825
1826
            // Update user account
1827
            DB::update(
1828
                prefixTable('users'),
1829
                array(
1830
                    'private_key' => $hashedPrivateKey,
1831
                    'special' => 'none',
1832
                    'otp_provided' => 1,
1833
                ),
1834
                'id = %i',
1835
                $post_user_id
1836
            );
1837
1838
            $session->set('user-private_key', $privateKey);
1839
        }
1840
1841
        // Return
1842
        return prepareExchangedData(
1843
            array(
1844
                'error' => false,
1845
                'message' => '',
1846
            ),
1847
            'encode'
1848
        );
1849
    }
1850
    
1851
    return prepareExchangedData(
1852
        array(
1853
            'error' => true,
1854
            'message' => $lang->get('error_no_user'),
1855
            'debug' => '',
1856
        ),
1857
        'encode'
1858
    );
1859
}
1860
1861
function initializeUserPassword(
1862
    int $post_user_id,
1863
    string $post_special,
1864
    string $post_user_password,
1865
    bool $post_self_change,
1866
    array $SETTINGS
1867
): string
1868
{
1869
    // Load user's language
1870
    $session = SessionManager::getSession();
1871
    $lang = new Language($session->get('user-language') ?? 'english');
1872
    
1873
    if (isUserIdValid($post_user_id) === true) {
1874
        // Get user info
1875
        $userData = DB::queryFirstRow(
1876
            'SELECT email, auth_type, login
1877
            FROM ' . prefixTable('users') . '
1878
            WHERE id = %i',
1879
            $post_user_id
1880
        );
1881
        if (DB::count() > 0 && empty($userData['email']) === false) {
1882
            // If user pwd is empty then generate a new one and send it to user
1883
            if (isset($post_user_password) === false || empty($post_user_password) === true) {
1884
                // Generate new password
1885
                $post_user_password = generateQuickPassword();
1886
            }
1887
1888
            // If LDAP enabled, then
1889
            // check that this password is correct
1890
            $continue = true;
1891
            if ($userData['auth_type'] === 'ldap' && (int) $SETTINGS['ldap_mode'] === 1) {
1892
                $continue = ldapCheckUserPassword(
1893
                    $userData['login'],
1894
                    $post_user_password,
1895
                    $SETTINGS
1896
                );
1897
            }
1898
1899
            if ($continue === true) {
1900
                // Only change if email is successfull
1901
                $passwordManager = new PasswordManager();
1902
                // GEnerate new keys
1903
                $userKeys = generateUserKeys($post_user_password);
1904
1905
                // Update user account
1906
                DB::update(
1907
                    prefixTable('users'),
1908
                    array(
1909
                        'special' => $post_special,
1910
                        'pw' => $passwordManager->hashPassword($post_user_password),
1911
                        'public_key' => $userKeys['public_key'],
1912
                        'private_key' => $userKeys['private_key'],
1913
                        'last_pw_change' => time(),
1914
                    ),
1915
                    'id = %i',
1916
                    $post_user_id
1917
                );
1918
1919
                // Return
1920
                return prepareExchangedData(
1921
                    array(
1922
                        'error' => false,
1923
                        'message' => '',
1924
                        'user_pwd' => $post_user_password,
1925
                        'user_email' => $userData['email'],
1926
                    ),
1927
                    'encode'
1928
                );
1929
            }
1930
            // Return error
1931
            return prepareExchangedData(
1932
                array(
1933
                    'error' => true,
1934
                    'message' => $lang->get('no_email_set'),
1935
                    'debug' => '',
1936
                    'self_change' => $post_self_change,
1937
                ),
1938
                'encode'
1939
            );
1940
        }
1941
1942
        // Error
1943
        return prepareExchangedData(
1944
            array(
1945
                'error' => true,
1946
                'message' => $lang->get('no_email_set'),
1947
                'debug' => '',
1948
            ),
1949
            'encode'
1950
        );
1951
    }
1952
    
1953
    return prepareExchangedData(
1954
        array(
1955
            'error' => true,
1956
            'message' => $lang->get('error_no_user'),
1957
            'debug' => '',
1958
        ),
1959
        'encode'
1960
    );
1961
}
1962
1963
function generateOneTimeCode(
1964
    int $post_user_id
1965
): string
1966
{
1967
    // Load user's language
1968
    $session = SessionManager::getSession();
1969
    $lang = new Language($session->get('user-language') ?? 'english');
1970
    
1971
    if (isUserIdValid($post_user_id) === true) {
1972
        // Get user info
1973
        $userData = DB::queryFirstRow(
1974
            'SELECT email, auth_type, login
1975
            FROM ' . prefixTable('users') . '
1976
            WHERE id = %i',
1977
            $post_user_id
1978
        );
1979
        if (DB::count() > 0 && empty($userData['email']) === false) {
1980
            // Generate pwd
1981
            $password = generateQuickPassword();
1982
1983
            // GEnerate new keys
1984
            $userKeys = generateUserKeys($password);
1985
1986
            // Save in DB
1987
            DB::update(
1988
                prefixTable('users'),
1989
                array(
1990
                    'public_key' => $userKeys['public_key'],
1991
                    'private_key' => $userKeys['private_key'],
1992
                    'special' => 'generate-keys',
1993
                ),
1994
                'id=%i',
1995
                $post_user_id
1996
            );
1997
1998
            return prepareExchangedData(
1999
                array(
2000
                    'error' => false,
2001
                    'message' => '',
2002
                    'code' => $password,
2003
                    'visible_otp' => ADMIN_VISIBLE_OTP_ON_LDAP_IMPORT,
2004
                ),
2005
                'encode'
2006
            );
2007
        }
2008
        
2009
        return prepareExchangedData(
2010
            array(
2011
                'error' => true,
2012
                'message' => $lang->get('no_email_set'),
2013
            ),
2014
            'encode'
2015
        );
2016
    }
2017
        
2018
    return prepareExchangedData(
2019
        array(
2020
            'error' => true,
2021
            'message' => $lang->get('error_no_user'),
2022
        ),
2023
        'encode'
2024
    );
2025
}
2026
2027
function startReEncryptingUserSharekeys(
2028
    int $post_user_id,
2029
    bool $post_self_change,
2030
    array $SETTINGS
2031
): string
2032
{
2033
    // Load user's language
2034
    $session = SessionManager::getSession();
2035
    $lang = new Language($session->get('user-language') ?? 'english');
2036
    
2037
    if (isUserIdValid($post_user_id) === true) {
2038
        // Check if user exists
2039
        DB::queryFirstRow(
2040
            'SELECT *
2041
            FROM ' . prefixTable('users') . '
2042
            WHERE id = %i',
2043
            $post_user_id
2044
        );
2045
        if (DB::count() > 0) {
2046
            // CLear old sharekeys
2047
            if ($post_self_change === false) {
2048
                deleteUserObjetsKeys($post_user_id, $SETTINGS);
2049
            }
2050
2051
            // Continu with next step
2052
            return prepareExchangedData(
2053
                array(
2054
                    'error' => false,
2055
                    'message' => '',
2056
                    'step' => 'step1',
2057
                    'userId' => $post_user_id,
2058
                    'start' => 0,
2059
                    'self_change' => $post_self_change,
2060
                ),
2061
                'encode'
2062
            );
2063
        }
2064
        // Nothing to do
2065
        return prepareExchangedData(
2066
            array(
2067
                'error' => true,
2068
                'message' => $lang->get('error_no_user'),
2069
            ),
2070
            'encode'
2071
        );
2072
    }
2073
2074
    return prepareExchangedData(
2075
        array(
2076
            'error' => true,
2077
            'message' => $lang->get('error_no_user'),
2078
        ),
2079
        'encode'
2080
    );
2081
}
2082
2083
/**
2084
 * Permits to encrypt user's keys
2085
 *
2086
 * @param integer $post_user_id
2087
 * @param boolean $post_self_change
2088
 * @param string $post_action
2089
 * @param integer $post_start
2090
 * @param integer $post_length
2091
 * @param array $SETTINGS
2092
 * @return string
2093
 */
2094
function continueReEncryptingUserSharekeys(
2095
    int     $post_user_id,
2096
    bool    $post_self_change,
2097
    string  $post_action,
2098
    int     $post_start,
2099
    int     $post_length,
2100
    array   $SETTINGS
2101
): string
2102
{
2103
    // Load user's language
2104
    $session = SessionManager::getSession();
2105
    $lang = new Language($session->get('user-language') ?? 'english');
2106
    
2107
    if (isUserIdValid($post_user_id) === true) {
2108
        // Check if user exists
2109
        $userInfo = DB::queryFirstRow(
2110
            'SELECT public_key
2111
            FROM ' . prefixTable('users') . '
2112
            WHERE id = %i',
2113
            $post_user_id
2114
        );
2115
        if (isset($userInfo['public_key']) === true) {
2116
            $return = [];
2117
2118
            // WHAT STEP TO PERFORM?
2119
            if ($post_action === 'step0') {
2120
                // CLear old sharekeys
2121
                if ($post_self_change === false) {
2122
                    deleteUserObjetsKeys($post_user_id, $SETTINGS);
2123
                }
2124
2125
                $return['post_action'] = 'step10';
2126
            }
2127
            
2128
            // STEP 1 - ITEMS
2129
            elseif ($post_action === 'step10') {
2130
                $return = continueReEncryptingUserSharekeysStep10(
2131
                    $post_user_id,
2132
                    $post_self_change,
2133
                    $post_action,
2134
                    $post_start,
2135
                    $post_length,
2136
                    $userInfo['public_key'],
2137
                    $SETTINGS
2138
                );
2139
            }
2140
2141
            // STEP 2 - LOGS
2142
            elseif ($post_action === 'step20') {
2143
                $return = continueReEncryptingUserSharekeysStep20(
2144
                    $post_user_id,
2145
                    $post_self_change,
2146
                    $post_action,
2147
                    $post_start,
2148
                    $post_length,
2149
                    $userInfo['public_key'],
2150
                    $SETTINGS
2151
                );
2152
            }
2153
2154
            // STEP 3 - FIELDS
2155
            elseif ($post_action === 'step30') {
2156
                $return = continueReEncryptingUserSharekeysStep30(
2157
                    $post_user_id,
2158
                    $post_self_change,
2159
                    $post_action,
2160
                    $post_start,
2161
                    $post_length,
2162
                    $userInfo['public_key'],
2163
                    $SETTINGS
2164
                );
2165
            }
2166
            
2167
            // STEP 4 - SUGGESTIONS
2168
            elseif ($post_action === 'step40') {
2169
                $return = continueReEncryptingUserSharekeysStep40(
2170
                    $post_user_id,
2171
                    $post_self_change,
2172
                    $post_action,
2173
                    $post_start,
2174
                    $post_length,
2175
                    $userInfo['public_key'],
2176
                    $SETTINGS
2177
                );
2178
            }
2179
            
2180
            // STEP 5 - FILES
2181
            elseif ($post_action === 'step50') {
2182
                $return = continueReEncryptingUserSharekeysStep50(
2183
                    $post_user_id,
2184
                    $post_self_change,
2185
                    $post_action,
2186
                    $post_start,
2187
                    $post_length,
2188
                    $userInfo['public_key'],
2189
                    $SETTINGS
2190
                );
2191
            }
2192
            
2193
            // STEP 6 - PERSONAL ITEMS
2194
            elseif ($post_action === 'step60') {
2195
                $return = continueReEncryptingUserSharekeysStep60(
2196
                    $post_user_id,
2197
                    $post_self_change,
2198
                    $post_action,
2199
                    $post_start,
2200
                    $post_length,
2201
                    $userInfo['public_key'],
2202
                    $SETTINGS
2203
                );
2204
            }
2205
            
2206
            // Continu with next step
2207
            return prepareExchangedData(
2208
                array(
2209
                    'error' => false,
2210
                    'message' => '',
2211
                    'step' => isset($return['post_action']) === true ? $return['post_action'] : '',
2212
                    'start' => isset($return['next_start']) === true ? $return['next_start'] : 0,
2213
                    'userId' => $post_user_id,
2214
                    'self_change' => $post_self_change,
2215
                ),
2216
                'encode'
2217
            );
2218
        }
2219
        
2220
        // Nothing to do
2221
        return prepareExchangedData(
2222
            array(
2223
                'error' => false,
2224
                'message' => '',
2225
                'step' => 'finished',
2226
                'start' => 0,
2227
                'userId' => $post_user_id,
2228
                'self_change' => $post_self_change,
2229
            ),
2230
            'encode'
2231
        );
2232
    }
2233
    
2234
    // Nothing to do
2235
    return prepareExchangedData(
2236
        array(
2237
            'error' => true,
2238
            'message' => $lang->get('error_no_user'),
2239
            'extra' => $post_user_id,
2240
        ),
2241
        'encode'
2242
    );
2243
}
2244
2245
function continueReEncryptingUserSharekeysStep10(
2246
    int $post_user_id,
2247
    bool $post_self_change,
2248
    string $post_action,
2249
    int $post_start,
2250
    int $post_length,
2251
    string $user_public_key,
2252
    array $SETTINGS
2253
): array 
2254
{
2255
    $session = SessionManager::getSession();
2256
    // Loop on items
2257
    $rows = DB::query(
2258
        'SELECT id, pw
2259
        FROM ' . prefixTable('items') . '
2260
        WHERE perso = 0
2261
        LIMIT ' . $post_start . ', ' . $post_length
2262
    );
2263
    foreach ($rows as $record) {
2264
        // Get itemKey from current user
2265
        $currentUserKey = DB::queryFirstRow(
2266
            'SELECT share_key, increment_id
2267
            FROM ' . prefixTable('sharekeys_items') . '
2268
            WHERE object_id = %i AND user_id = %i',
2269
            $record['id'],
2270
            $session->get('user-id')
2271
        );
2272
2273
        // do we have any input? (#3481)
2274
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2275
            continue;
2276
        }
2277
2278
        // Decrypt itemkey with admin key
2279
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2280
        
2281
        // Encrypt Item key
2282
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2283
        
2284
        // Save the key in DB
2285
        if ($post_self_change === false) {
2286
            DB::insert(
2287
                prefixTable('sharekeys_items'),
2288
                array(
2289
                    'object_id' => (int) $record['id'],
2290
                    'user_id' => (int) $post_user_id,
2291
                    'share_key' => $share_key_for_item,
2292
                )
2293
            );
2294
        } else {
2295
            // Get itemIncrement from selected user
2296
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2297
                $currentUserKey = DB::queryFirstRow(
2298
                    'SELECT increment_id
2299
                    FROM ' . prefixTable('sharekeys_items') . '
2300
                    WHERE object_id = %i AND user_id = %i',
2301
                    $record['id'],
2302
                    $post_user_id
2303
                );
2304
2305
                if (DB::count() > 0) {
2306
                    // NOw update
2307
                    DB::update(
2308
                        prefixTable('sharekeys_items'),
2309
                        array(
2310
                            'share_key' => $share_key_for_item,
2311
                        ),
2312
                        'increment_id = %i',
2313
                        $currentUserKey['increment_id']
2314
                    );
2315
                } else {
2316
                    DB::insert(
2317
                        prefixTable('sharekeys_items'),
2318
                        array(
2319
                            'object_id' => (int) $record['id'],
2320
                            'user_id' => (int) $post_user_id,
2321
                            'share_key' => $share_key_for_item,
2322
                        )
2323
                    );
2324
                }
2325
            }
2326
        }
2327
    }
2328
2329
    // SHould we change step?
2330
    DB::query(
2331
        'SELECT *
2332
        FROM ' . prefixTable('items') . '
2333
        WHERE perso = 0'
2334
    );
2335
2336
    $next_start = (int) $post_start + (int) $post_length;
2337
    return [
2338
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2339
        'post_action' => $next_start > DB::count() ? 'step20' : 'step10',
2340
    ];
2341
}
2342
2343
function continueReEncryptingUserSharekeysStep20(
2344
    int $post_user_id,
2345
    bool $post_self_change,
2346
    string $post_action,
2347
    int $post_start,
2348
    int $post_length,
2349
    string $user_public_key,
2350
    array $SETTINGS
2351
): array
2352
{
2353
    $session = SessionManager::getSession();
2354
    // Loop on logs
2355
    $rows = DB::query(
2356
        'SELECT increment_id
2357
        FROM ' . prefixTable('log_items') . '
2358
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"
2359
        LIMIT ' . $post_start . ', ' . $post_length
2360
    );
2361
    foreach ($rows as $record) {
2362
        // Get itemKey from current user
2363
        $currentUserKey = DB::queryFirstRow(
2364
            'SELECT share_key
2365
            FROM ' . prefixTable('sharekeys_logs') . '
2366
            WHERE object_id = %i AND user_id = %i',
2367
            $record['increment_id'],
2368
            $session->get('user-id')
2369
        );
2370
2371
        // do we have any input? (#3481)
2372
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2373
            continue;
2374
        }
2375
2376
        // Decrypt itemkey with admin key
2377
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2378
2379
        // Encrypt Item key
2380
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2381
2382
        // Save the key in DB
2383
        if ($post_self_change === false) {
2384
            DB::insert(
2385
                prefixTable('sharekeys_logs'),
2386
                array(
2387
                    'object_id' => (int) $record['increment_id'],
2388
                    'user_id' => (int) $post_user_id,
2389
                    'share_key' => $share_key_for_item,
2390
                )
2391
            );
2392
        } else {
2393
            // Get itemIncrement from selected user
2394
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2395
                $currentUserKey = DB::queryFirstRow(
2396
                    'SELECT increment_id
2397
                    FROM ' . prefixTable('sharekeys_items') . '
2398
                    WHERE object_id = %i AND user_id = %i',
2399
                    $record['id'],
2400
                    $post_user_id
2401
                );
2402
            }
2403
2404
            // NOw update
2405
            DB::update(
2406
                prefixTable('sharekeys_logs'),
2407
                array(
2408
                    'share_key' => $share_key_for_item,
2409
                ),
2410
                'increment_id = %i',
2411
                $currentUserKey['increment_id']
2412
            );
2413
        }
2414
    }
2415
2416
    // SHould we change step?
2417
    DB::query(
2418
        'SELECT increment_id
2419
        FROM ' . prefixTable('log_items') . '
2420
        WHERE raison LIKE "at_pw :%" AND encryption_type = "teampass_aes"'
2421
    );
2422
2423
    $next_start = (int) $post_start + (int) $post_length;
2424
    return [
2425
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2426
        'post_action' => $next_start > DB::count() ? 'step30' : 'step20',
2427
    ];
2428
}
2429
2430
function continueReEncryptingUserSharekeysStep30(
2431
    int $post_user_id,
2432
    bool $post_self_change,
2433
    string $post_action,
2434
    int $post_start,
2435
    int $post_length,
2436
    string $user_public_key,
2437
    array $SETTINGS
2438
): array
2439
{
2440
    $session = SessionManager::getSession();
2441
    // Loop on fields
2442
    $rows = DB::query(
2443
        'SELECT id
2444
        FROM ' . prefixTable('categories_items') . '
2445
        WHERE encryption_type = "teampass_aes"
2446
        LIMIT ' . $post_start . ', ' . $post_length
2447
    );
2448
    foreach ($rows as $record) {
2449
        // Get itemKey from current user
2450
        $currentUserKey = DB::queryFirstRow(
2451
            'SELECT share_key
2452
            FROM ' . prefixTable('sharekeys_fields') . '
2453
            WHERE object_id = %i AND user_id = %i',
2454
            $record['id'],
2455
            $session->get('user-id')
2456
        );
2457
2458
        // do we have any input? (#3481)
2459
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2460
            continue;
2461
        }
2462
2463
        // Decrypt itemkey with admin key
2464
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2465
2466
        // Encrypt Item key
2467
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2468
2469
        // Save the key in DB
2470
        if ($post_self_change === false) {
2471
            DB::insert(
2472
                prefixTable('sharekeys_fields'),
2473
                array(
2474
                    'object_id' => (int) $record['id'],
2475
                    'user_id' => (int) $post_user_id,
2476
                    'share_key' => $share_key_for_item,
2477
                )
2478
            );
2479
        } else {
2480
            // Get itemIncrement from selected user
2481
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2482
                $currentUserKey = DB::queryFirstRow(
2483
                    'SELECT increment_id
2484
                    FROM ' . prefixTable('sharekeys_items') . '
2485
                    WHERE object_id = %i AND user_id = %i',
2486
                    $record['id'],
2487
                    $post_user_id
2488
                );
2489
            }
2490
2491
            // NOw update
2492
            DB::update(
2493
                prefixTable('sharekeys_fields'),
2494
                array(
2495
                    'share_key' => $share_key_for_item,
2496
                ),
2497
                'increment_id = %i',
2498
                $currentUserKey['increment_id']
2499
            );
2500
        }
2501
    }
2502
2503
    // SHould we change step?
2504
    DB::query(
2505
        'SELECT *
2506
        FROM ' . prefixTable('categories_items') . '
2507
        WHERE encryption_type = "teampass_aes"'
2508
    );
2509
2510
    $next_start = (int) $post_start + (int) $post_length;
2511
    return [
2512
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2513
        'post_action' => $next_start > DB::count() ? 'step40' : 'step30',
2514
    ];
2515
}
2516
2517
function continueReEncryptingUserSharekeysStep40(
2518
    int $post_user_id,
2519
    bool $post_self_change,
2520
    string $post_action,
2521
    int $post_start,
2522
    int $post_length,
2523
    string $user_public_key,
2524
    array $SETTINGS
2525
): array
2526
{
2527
    $session = SessionManager::getSession();
2528
    // Loop on suggestions
2529
    $rows = DB::query(
2530
        'SELECT id
2531
        FROM ' . prefixTable('suggestion') . '
2532
        LIMIT ' . $post_start . ', ' . $post_length
2533
    );
2534
    foreach ($rows as $record) {
2535
        // Get itemKey from current user
2536
        $currentUserKey = DB::queryFirstRow(
2537
            'SELECT share_key
2538
            FROM ' . prefixTable('sharekeys_suggestions') . '
2539
            WHERE object_id = %i AND user_id = %i',
2540
            $record['id'],
2541
            $session->get('user-id')
2542
        );
2543
2544
        // do we have any input? (#3481)
2545
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2546
            continue;
2547
        }
2548
2549
        // Decrypt itemkey with admin key
2550
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2551
2552
        // Encrypt Item key
2553
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2554
2555
        // Save the key in DB
2556
        if ($post_self_change === false) {
2557
            DB::insert(
2558
                prefixTable('sharekeys_suggestions'),
2559
                array(
2560
                    'object_id' => (int) $record['id'],
2561
                    'user_id' => (int) $post_user_id,
2562
                    'share_key' => $share_key_for_item,
2563
                )
2564
            );
2565
        } else {
2566
            // Get itemIncrement from selected user
2567
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2568
                $currentUserKey = DB::queryFirstRow(
2569
                    'SELECT increment_id
2570
                    FROM ' . prefixTable('sharekeys_items') . '
2571
                    WHERE object_id = %i AND user_id = %i',
2572
                    $record['id'],
2573
                    $post_user_id
2574
                );
2575
            }
2576
2577
            // NOw update
2578
            DB::update(
2579
                prefixTable('sharekeys_suggestions'),
2580
                array(
2581
                    'share_key' => $share_key_for_item,
2582
                ),
2583
                'increment_id = %i',
2584
                $currentUserKey['increment_id']
2585
            );
2586
        }
2587
    }
2588
2589
    // SHould we change step?
2590
    DB::query(
2591
        'SELECT *
2592
        FROM ' . prefixTable('suggestion')
2593
    );
2594
2595
    $next_start = (int) $post_start + (int) $post_length;
2596
    return [
2597
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2598
        'post_action' => $next_start > DB::count() ? 'step50' : 'step40',
2599
    ];
2600
}
2601
2602
function continueReEncryptingUserSharekeysStep50(
2603
    int $post_user_id,
2604
    bool $post_self_change,
2605
    string $post_action,
2606
    int $post_start,
2607
    int $post_length,
2608
    string $user_public_key,
2609
    array $SETTINGS
2610
): array
2611
{
2612
    $session = SessionManager::getSession();
2613
    // Loop on files
2614
    $rows = DB::query(
2615
        'SELECT id
2616
        FROM ' . prefixTable('files') . '
2617
        WHERE status = "' . TP_ENCRYPTION_NAME . '"
2618
        LIMIT ' . $post_start . ', ' . $post_length
2619
    ); //aes_encryption
2620
    foreach ($rows as $record) {
2621
        // Get itemKey from current user
2622
        $currentUserKey = DB::queryFirstRow(
2623
            'SELECT share_key
2624
            FROM ' . prefixTable('sharekeys_files') . '
2625
            WHERE object_id = %i AND user_id = %i',
2626
            $record['id'],
2627
            $session->get('user-id')
2628
        );
2629
2630
        // do we have any input? (#3481)
2631
        if ($currentUserKey === null || count($currentUserKey) === 0) {
2632
            continue;
2633
        }
2634
2635
        // Decrypt itemkey with admin key
2636
        $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2637
2638
        // Encrypt Item key
2639
        $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2640
2641
        // Save the key in DB
2642
        if ($post_self_change === false) {
2643
            DB::insert(
2644
                prefixTable('sharekeys_files'),
2645
                array(
2646
                    'object_id' => (int) $record['id'],
2647
                    'user_id' => (int) $post_user_id,
2648
                    'share_key' => $share_key_for_item,
2649
                )
2650
            );
2651
        } else {
2652
            // Get itemIncrement from selected user
2653
            if ((int) $post_user_id !== (int) $session->get('user-id')) {
2654
                $currentUserKey = DB::queryFirstRow(
2655
                    'SELECT increment_id
2656
                    FROM ' . prefixTable('sharekeys_items') . '
2657
                    WHERE object_id = %i AND user_id = %i',
2658
                    $record['id'],
2659
                    $post_user_id
2660
                );
2661
            }
2662
2663
            // NOw update
2664
            DB::update(
2665
                prefixTable('sharekeys_files'),
2666
                array(
2667
                    'share_key' => $share_key_for_item,
2668
                ),
2669
                'increment_id = %i',
2670
                $currentUserKey['increment_id']
2671
            );
2672
        }
2673
    }
2674
2675
    // SHould we change step?
2676
    DB::query(
2677
        'SELECT *
2678
        FROM ' . prefixTable('files') . '
2679
        WHERE status = "' . TP_ENCRYPTION_NAME . '"'
2680
    );
2681
2682
    $next_start = (int) $post_start + (int) $post_length;
2683
    return [
2684
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2685
        'post_action' => $next_start > DB::count() ? 'step60' : 'step50',
2686
    ];
2687
}
2688
2689
function continueReEncryptingUserSharekeysStep60(
2690
    int $post_user_id,
2691
    bool $post_self_change,
2692
    string $post_action,
2693
    int $post_start,
2694
    int $post_length,
2695
    string $user_public_key,
2696
    array $SETTINGS
2697
): array
2698
{
2699
    $session = SessionManager::getSession();
2700
    // IF USER IS NOT THE SAME
2701
    if ((int) $post_user_id === (int) $session->get('user-id')) {
2702
        return [
2703
            'next_start' => 0,
2704
            'post_action' => 'finished',
2705
        ];
2706
    }
2707
    
2708
    // Loop on persoanl items
2709
    if (count($session->get('user-personal_folders')) > 0) {
2710
        $rows = DB::query(
2711
            'SELECT id, pw
2712
            FROM ' . prefixTable('items') . '
2713
            WHERE perso = 1 AND id_tree IN %ls AND encryption_type = %s
2714
            LIMIT ' . $post_start . ', ' . $post_length,
2715
            $session->get('user-personal_folders'),
2716
            "defuse"
2717
        );
2718
        foreach ($rows as $record) {
2719
            // Get itemKey from current user
2720
            $currentUserKey = DB::queryFirstRow(
2721
                'SELECT share_key, increment_id
2722
                FROM ' . prefixTable('sharekeys_items') . '
2723
                WHERE object_id = %i AND user_id = %i',
2724
                $record['id'],
2725
                $session->get('user-id')
2726
            );
2727
2728
            // Decrypt itemkey with admin key
2729
            $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $session->get('user-private_key'));
2730
2731
            // Encrypt Item key
2732
            $share_key_for_item = encryptUserObjectKey($itemKey, $user_public_key);
2733
2734
            // Save the key in DB
2735
            if ($post_self_change === false) {
2736
                DB::insert(
2737
                    prefixTable('sharekeys_items'),
2738
                    array(
2739
                        'object_id' => (int) $record['id'],
2740
                        'user_id' => (int) $post_user_id,
2741
                        'share_key' => $share_key_for_item,
2742
                    )
2743
                );
2744
            } else {
2745
                // Get itemIncrement from selected user
2746
                if ((int) $post_user_id !== (int) $session->get('user-id')) {
2747
                    $currentUserKey = DB::queryFirstRow(
2748
                        'SELECT increment_id
2749
                        FROM ' . prefixTable('sharekeys_items') . '
2750
                        WHERE object_id = %i AND user_id = %i',
2751
                        $record['id'],
2752
                        $post_user_id
2753
                    );
2754
                }
2755
2756
                // NOw update
2757
                DB::update(
2758
                    prefixTable('sharekeys_items'),
2759
                    array(
2760
                        'share_key' => $share_key_for_item,
2761
                    ),
2762
                    'increment_id = %i',
2763
                    $currentUserKey['increment_id']
2764
                );
2765
            }
2766
        }
2767
    }
2768
2769
    // SHould we change step?
2770
    DB::query(
2771
        'SELECT *
2772
        FROM ' . prefixTable('items') . '
2773
        WHERE perso = 0'
2774
    );
2775
2776
    $next_start = (int) $post_start + (int) $post_length;
2777
    return [
2778
        'next_start' => $next_start > DB::count() ? 0 : $next_start,
2779
        'post_action' => $next_start > DB::count() ? 'finished' : 'step60',
2780
    ];
2781
}
2782
2783
function migrateTo3_DoUserPersonalItemsEncryption(
2784
    int $post_user_id,
2785
    int $post_start,
2786
    int $post_length,
2787
    int $post_counterItemsToTreat,
2788
    string $post_user_psk,
2789
    array $SETTINGS
2790
) {
2791
    $next_step = 'psk';
2792
    
2793
    $session = SessionManager::getSession();
2794
    $lang = new Language($session->get('user-language') ?? 'english');
2795
    
2796
    if (isUserIdValid($post_user_id) === true) {
2797
        // Check if user exists
2798
        $userInfo = DB::queryFirstRow(
2799
            'SELECT public_key, encrypted_psk
2800
            FROM ' . prefixTable('users') . '
2801
            WHERE id = %i',
2802
            $post_user_id
2803
        );
2804
        if (DB::count() > 0) {
2805
            // check if psk is correct.
2806
            if (empty($userInfo['encrypted_psk']) === false) {//echo $post_user_psk." ;; ".$userInfo['encrypted_psk']." ;; ";
2807
                $user_key_encoded = defuse_validate_personal_key(
2808
                    html_entity_decode($post_user_psk), // convert tspecial string back to their original characters due to FILTER_SANITIZE_FULL_SPECIAL_CHARS
2809
                    $userInfo['encrypted_psk']
2810
                );
2811
2812
                if (strpos($user_key_encoded, "Error ") !== false) {
2813
                    return prepareExchangedData(
2814
                        array(
2815
                            'error' => true,
2816
                            'message' => $lang->get('bad_psk'),
2817
                        ),
2818
                        'encode'
2819
                    );
2820
                }
2821
2822
                // Get number of user's personal items with no AES encryption
2823
                if ($post_counterItemsToTreat === -1) {
2824
                    DB::query(
2825
                        'SELECT id
2826
                        FROM ' . prefixTable('items') . '
2827
                        WHERE perso = 1 AND id_tree IN %ls AND encryption_type != %s',
2828
                        $session->get('user-personal_folders'),
2829
                        'teampass_aes'
2830
                    );
2831
                    $countUserPersonalItems = DB::count();
2832
                } else {
2833
                    $countUserPersonalItems = $post_counterItemsToTreat;
2834
                }
2835
2836
                // Loop on persoanl items
2837
                $rows = DB::query(
2838
                    'SELECT id, pw
2839
                    FROM ' . prefixTable('items') . '
2840
                    WHERE perso = 1 AND id_tree IN %ls AND encryption_type != %s
2841
                    LIMIT ' . $post_length,
2842
                    $session->get('user-personal_folders'),
2843
                    'teampass_aes'
2844
                );
2845
                foreach ($rows as $record) {
2846
                    // Decrypt with Defuse
2847
                    $passwd = cryption(
2848
                        $record['pw'],
2849
                        $user_key_encoded,
2850
                        'decrypt',
2851
                        $SETTINGS
2852
                    );
2853
2854
                    // Encrypt with Object Key
2855
                    $cryptedStuff = doDataEncryption(html_entity_decode($passwd['string']));
2856
2857
                    // Store new password in DB
2858
                    DB::update(
2859
                        prefixTable('items'),
2860
                        array(
2861
                            'pw' => $cryptedStuff['encrypted'],
2862
                            'encryption_type' => 'teampass_aes',
2863
                        ),
2864
                        'id = %i',
2865
                        $record['id']
2866
                    );
2867
2868
                    // Insert in DB the new object key for this item by user
2869
                    DB::insert(
2870
                        prefixTable('sharekeys_items'),
2871
                        array(
2872
                            'object_id' => (int) $record['id'],
2873
                            'user_id' => (int) $post_user_id,
2874
                            'share_key' => encryptUserObjectKey($cryptedStuff['objectKey'], $userInfo['public_key']),
2875
                        )
2876
                    );
2877
2878
2879
                    // Does this item has Files?
2880
                    // Loop on files
2881
                    $rows = DB::query(
2882
                        'SELECT id, file
2883
                        FROM ' . prefixTable('files') . '
2884
                        WHERE status != %s
2885
                        AND id_item = %i',
2886
                        TP_ENCRYPTION_NAME,
2887
                        $record['id']
2888
                    );
2889
                    //aes_encryption
2890
                    foreach ($rows as $record2) {
2891
                        // Now decrypt the file
2892
                        prepareFileWithDefuse(
2893
                            'decrypt',
2894
                            $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'],
2895
                            $SETTINGS['path_to_upload_folder'] . '/' . $record2['file'] . '.delete',
2896
                            $SETTINGS,
2897
                            $post_user_psk
2898
                        );
2899
2900
                        // Encrypt the file
2901
                        $encryptedFile = encryptFile($record2['file'] . '.delete', $SETTINGS['path_to_upload_folder']);
2902
2903
                        DB::update(
2904
                            prefixTable('files'),
2905
                            array(
2906
                                'file' => $encryptedFile['fileHash'],
2907
                                'status' => TP_ENCRYPTION_NAME,
2908
                            ),
2909
                            'id = %i',
2910
                            $record2['id']
2911
                        );
2912
2913
                        // Save key
2914
                        DB::insert(
2915
                            prefixTable('sharekeys_files'),
2916
                            array(
2917
                                'object_id' => (int) $record2['id'],
2918
                                'user_id' => (int) $session->get('user-id'),
2919
                                'share_key' => encryptUserObjectKey($encryptedFile['objectKey'], $session->get('user-public_key')),
2920
                            )
2921
                        );
2922
2923
                        // Unlink original file
2924
                        unlink($SETTINGS['path_to_upload_folder'] . '/' . $record2['file']);
2925
                    }
2926
                }
2927
2928
                // SHould we change step?
2929
                $next_start = (int) $post_start + (int) $post_length;
2930
                DB::query(
2931
                    'SELECT id
2932
                    FROM ' . prefixTable('items') . '
2933
                    WHERE perso = 1 AND id_tree IN %ls AND encryption_type != %s',
2934
                    $session->get('user-personal_folders'),
2935
                    'teampass_aes'
2936
                );
2937
                if (DB::count() === 0 || ($next_start - $post_length) >= $countUserPersonalItems) {
2938
                    // Now update user
2939
                    DB::update(
2940
                        prefixTable('users'),
2941
                        array(
2942
                            'special' => 'none',
2943
                            'upgrade_needed' => 0,
2944
                            'encrypted_psk' => '',
2945
                        ),
2946
                        'id = %i',
2947
                        $post_user_id
2948
                    );
2949
2950
                    $next_step = 'finished';
2951
                    $next_start = 0;
2952
                }
2953
2954
                // Continu with next step
2955
                return prepareExchangedData(
2956
                    array(
2957
                        'error' => false,
2958
                        'message' => '',
2959
                        'step' => $next_step,
2960
                        'start' => $next_start,
2961
                        'userId' => $post_user_id
2962
                    ),
2963
                    'encode'
2964
                );
2965
            }
2966
        }
2967
        
2968
        // Nothing to do
2969
        return prepareExchangedData(
2970
            array(
2971
                'error' => true,
2972
                'message' => $lang->get('error_no_user'),
2973
            ),
2974
            'encode'
2975
        );
2976
    }
2977
    
2978
    // Nothing to do
2979
    return prepareExchangedData(
2980
        array(
2981
            'error' => true,
2982
            'message' => $lang->get('error_no_user'),
2983
        ),
2984
        'encode'
2985
    );
2986
}
2987
2988
2989
function getUserInfo(
2990
    int $post_user_id,
2991
    string $post_fields,
2992
    array $SETTINGS
2993
)
2994
{
2995
    // Load user's language
2996
    $session = SessionManager::getSession();
2997
    $lang = new Language($session->get('user-language') ?? 'english');
2998
    
2999
    if (isUserIdValid($post_user_id) === true) {
3000
        // Get user info
3001
        $userData = DB::queryFirstRow(
3002
            'SELECT '.$post_fields.'
3003
            FROM ' . prefixTable('users') . '
3004
            WHERE id = %i',
3005
            $post_user_id
3006
        );
3007
        if (DB::count() > 0) {
3008
            return prepareExchangedData(
3009
                array(
3010
                    'error' => false,
3011
                    'message' => '',
3012
                    'queryResults' => $userData,
3013
                ),
3014
                'encode'
3015
            );
3016
        }
3017
    }
3018
    return prepareExchangedData(
3019
        array(
3020
            'error' => true,
3021
            'message' => $lang->get('error_no_user'),
3022
        ),
3023
        'encode'
3024
    );
3025
}
3026
3027
/**
3028
 * Change user auth password
3029
 *
3030
 * @param integer $post_user_id
3031
 * @param string $post_current_pwd
3032
 * @param string $post_new_pwd
3033
 * @param array $SETTINGS
3034
 * @return string
3035
 */
3036
function changeUserAuthenticationPassword(
3037
    int $post_user_id,
3038
    string $post_current_pwd,
3039
    string $post_new_pwd,
3040
    array $SETTINGS
3041
)
3042
{
3043
    $session = SessionManager::getSession();
3044
    $lang = new Language($session->get('user-language') ?? 'english');
3045
    
3046
    if (isUserIdValid($post_user_id) === true) {
3047
        // Get user info
3048
        $userData = DB::queryFirstRow(
3049
            'SELECT auth_type, login, private_key
3050
            FROM ' . prefixTable('users') . '
3051
            WHERE id = %i',
3052
            $post_user_id
3053
        );
3054
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
3055
            // Now check if current password is correct
3056
            // For this, just check if it is possible to decrypt the privatekey
3057
            // And compare it to the one in session
3058
            try {
3059
                $privateKey = decryptPrivateKey($post_current_pwd, $userData['private_key']);
3060
            } catch (Exception $e) {
3061
                return prepareExchangedData(
3062
                    array(
3063
                        'error' => true,
3064
                        'message' => $lang->get('bad_password'),
3065
                    ),
3066
                    'encode'
3067
                );
3068
            }
3069
3070
            $lang = new Language($session->get('user-language') ?? 'english');
3071
3072
            if ($session->get('user-private_key') === $privateKey) {
3073
                // Encrypt it with new password
3074
                $hashedPrivateKey = encryptPrivateKey($post_new_pwd, $privateKey);
3075
3076
                // Generate new hash for auth password
3077
                $passwordManager = new PasswordManager();
3078
3079
                // Prepare variables
3080
                $newPw = $passwordManager->hashPassword($post_new_pwd);
3081
3082
                // Update user account
3083
                DB::update(
3084
                    prefixTable('users'),
3085
                    array(
3086
                        'private_key' => $hashedPrivateKey,
3087
                        'pw' => $newPw,
3088
                        'special' => 'none',
3089
                    ),
3090
                    'id = %i',
3091
                    $post_user_id
3092
                );
3093
3094
                $session->set('user-private_key', $privateKey);
3095
3096
                return prepareExchangedData(
3097
                    array(
3098
                        'error' => false,
3099
                        'message' => $lang->get('done'),'',
3100
                    ),
3101
                    'encode'
3102
                );
3103
            }
3104
            
3105
            // ERROR
3106
            return prepareExchangedData(
3107
                array(
3108
                    'error' => true,
3109
                    'message' => $lang->get('bad_password'),
3110
                ),
3111
                'encode'
3112
            );
3113
        }
3114
    }
3115
        
3116
    return prepareExchangedData(
3117
        array(
3118
            'error' => true,
3119
            'message' => $lang->get('error_no_user'),
3120
        ),
3121
        'encode'
3122
    );
3123
}
3124
3125
/**
3126
 * Change user LDAP auth password
3127
 *
3128
 * @param integer $post_user_id
3129
 * @param string $post_previous_pwd
3130
 * @param string $post_current_pwd
3131
 * @param array $SETTINGS
3132
 * @return string
3133
 */            
3134
function changeUserLDAPAuthenticationPassword(
3135
    int $post_user_id,
3136
    string $post_previous_pwd,
3137
    string $post_current_pwd,
3138
    array $SETTINGS
3139
)
3140
{
3141
    $session = SessionManager::getSession();
3142
    // Load user's language
3143
    $lang = new Language($session->get('user-language') ?? 'english');
3144
    
3145
    if (isUserIdValid($post_user_id) === true) {
3146
        // Get user info
3147
        $userData = DB::queryFirstRow(
3148
            'SELECT auth_type, login, private_key, special
3149
            FROM ' . prefixTable('users') . '
3150
            WHERE id = %i',
3151
            $post_user_id
3152
        );
3153
        
3154
        if (DB::count() > 0 && empty($userData['private_key']) === false) {
3155
            // Now check if current password is correct (only if not ldap)
3156
            if ($userData['auth_type'] === 'ldap' && $userData['special'] === 'auth-pwd-change') {
3157
                // As it is a change for an LDAP user
3158
                
3159
                // Now check if current password is correct
3160
                // For this, just check if it is possible to decrypt the privatekey
3161
                // And compare it to the one in session
3162
                $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
3163
3164
                // Encrypt it with new password
3165
                $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
3166
3167
                // Update user account
3168
                DB::update(
3169
                    prefixTable('users'),
3170
                    array(
3171
                        'private_key' => $hashedPrivateKey,
3172
                        'special' => 'none',
3173
                    ),
3174
                    'id = %i',
3175
                    $post_user_id
3176
                );
3177
3178
                $session->set('user-private_key', $privateKey);
3179
3180
                return prepareExchangedData(
3181
                    array(
3182
                        'error' => false,
3183
                        'message' => $lang->get('done'),'',
3184
                    ),
3185
                    'encode'
3186
                );
3187
            }
3188
3189
            // For this, just check if it is possible to decrypt the privatekey
3190
            // And try to decrypt one existing key
3191
            $privateKey = decryptPrivateKey($post_previous_pwd, $userData['private_key']);
3192
3193
            if (empty($privateKey) === true) {
3194
                return prepareExchangedData(
3195
                    array(
3196
                        'error' => true,
3197
                        'message' => $lang->get('password_is_not_correct'),
3198
                    ),
3199
                    'encode'
3200
                );
3201
            }
3202
3203
            // Get one itemKey from current user
3204
            $currentUserKey = DB::queryFirstRow(
3205
                'SELECT share_key, increment_id
3206
                FROM ' . prefixTable('sharekeys_items') . '
3207
                WHERE user_id = %i
3208
                LIMIT 1',
3209
                $post_user_id
3210
            );
3211
3212
            if (is_countable($currentUserKey) && count($currentUserKey) > 0) {
3213
                // Decrypt itemkey with user key
3214
                // use old password to decrypt private_key
3215
                $itemKey = decryptUserObjectKey($currentUserKey['share_key'], $privateKey);
3216
                
3217
                if (empty(base64_decode($itemKey)) === false) {
3218
                    // GOOD password
3219
                    // Encrypt it with current password
3220
                    $hashedPrivateKey = encryptPrivateKey($post_current_pwd, $privateKey);
3221
                    
3222
                    // Update user account
3223
                    DB::update(
3224
                        prefixTable('users'),
3225
                        array(
3226
                            'private_key' => $hashedPrivateKey,
3227
                            'special' => 'none',
3228
                        ),
3229
                        'id = %i',
3230
                        $post_user_id
3231
                    );
3232
                    
3233
                    $lang = new Language($session->get('user-language') ?? 'english');
3234
                    $session->set('user-private_key', $privateKey);
3235
3236
                    return prepareExchangedData(
3237
                        array(
3238
                            'error' => false,
3239
                            'message' => $lang->get('done'),
3240
                        ),
3241
                        'encode'
3242
                    );
3243
                }
3244
            }
3245
            
3246
            // ERROR
3247
            return prepareExchangedData(
3248
                array(
3249
                    'error' => true,
3250
                    'message' => $lang->get('bad_password'),
3251
                ),
3252
                'encode'
3253
            );
3254
        }
3255
    }
3256
3257
    // ERROR
3258
    return prepareExchangedData(
3259
        array(
3260
            'error' => true,
3261
            'message' => $lang->get('error_no_user'),
3262
        ),
3263
        'encode'
3264
    );
3265
}
3266
3267
/**
3268
 * Change user LDAP auth password
3269
 *
3270
 * @param integer $post_user_id
3271
 * @param string $post_current_pwd
3272
 * @param string $post_new_pwd
3273
 * @param array $SETTINGS
3274
 * @return string
3275
 */
3276
function increaseSessionDuration(
3277
    int $duration
3278
): string
3279
{
3280
    $session = SessionManager::getSession();
3281
    // check if session is not already expired.
3282
    if ($session->get('user-session_duration') > time()) {
3283
        // Calculate end of session
3284
        $session->set('user-session_duration', (int) $session->get('user-session_duration') + $duration);
3285
        // Update table
3286
        DB::update(
3287
            prefixTable('users'),
3288
            array(
3289
                'session_end' => $session->get('user-session_duration'),
3290
            ),
3291
            'id = %i',
3292
            $session->get('user-id')
3293
        );
3294
        // Return data
3295
        return '[{"new_value":"' . $session->get('user-session_duration') . '"}]';
3296
    }
3297
    
3298
    return '[{"new_value":"expired"}]';
3299
}
3300
3301
function generateAnOTP(string $label, bool $with_qrcode = false, string $secretKey = ''): string
3302
{
3303
    // generate new secret
3304
    $tfa = new TwoFactorAuth();
3305
    if ($secretKey === '') {
3306
        $secretKey = $tfa->createSecret();
3307
    }
3308
3309
    // generate new QR
3310
    if ($with_qrcode === true) {
3311
        $qrcode = $tfa->getQRCodeImageAsDataUri(
3312
            $label,
3313
            $secretKey
3314
        );
3315
    }
3316
3317
    // ERROR
3318
    return prepareExchangedData(
3319
        array(
3320
            'error' => false,
3321
            'message' => '',
3322
            'secret' => $secretKey,
3323
            'qrcode' => $qrcode ?? '',
3324
        ),
3325
        'encode'
3326
    );
3327
}