Passed
Pull Request — master (#4237)
by
unknown
08:33 queued 03:38
created

keyHandler()   C

Complexity

Conditions 12
Paths 9

Size

Total Lines 108
Code Lines 61

Duplication

Lines 0
Ratio 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
cc 12
eloc 61
c 5
b 0
f 0
nc 9
nop 3
dl 0
loc 108
rs 6.4242

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
43
// Load functions
44
require_once 'main.functions.php';
45
46
loadClasses('DB');
47
$session = SessionManager::getSession();
48
$request = SymfonyRequest::createFromGlobals();
49
$lang = new Language($session->get('user-language') ?? 'english');
50
51
// TODO : ajouter un check sue l'envoi de la key
52
53
// Load config
54
$configManager = new ConfigManager();
55
$SETTINGS = $configManager->getAllSettings();
56
57
// Do checks
58
// Instantiate the class with posted data
59
$checkUserAccess = new PerformChecks(
60
    dataSanitizer(
61
        [
62
            'type' => $request->request->get('type', '') !== '' ? htmlspecialchars($request->request->get('type')) : '',
63
        ],
64
        [
65
            'type' => 'trim|escape',
66
        ],
67
    ),
68
    [
69
        'user_id' => returnIfSet($session->get('user-id'), null),
70
        'user_key' => returnIfSet($session->get('key'), null),
71
    ]
72
);
73
// Handle the case
74
echo $checkUserAccess->caseHandler();
75
if (
76
    ($checkUserAccess->userAccessPage('home') === false ||
77
    $checkUserAccess->checkSession() === false)
78
    && in_array(filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS), ['get_teampass_settings', 'ga_generate_qr']) === false
79
) {
80
    // Not allowed page
81
    $session->set('system-error_code', ERR_NOT_ALLOWED);
82
    include $SETTINGS['cpassman_dir'] . '/error.php';
83
    exit;
84
}
85
86
// Define Timezone
87
date_default_timezone_set(isset($SETTINGS['timezone']) === true ? $SETTINGS['timezone'] : 'UTC');
88
set_time_limit(600);
89
90
// DO CHECKS
91
$post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
92
if (
93
    isset($post_type) === true
94
    && ($post_type === 'ga_generate_qr'
95
        || $post_type === 'get_teampass_settings')
96
) {
97
    // continue
98
    mainQuery($SETTINGS);
99
} elseif (
100
    $session->has('user-id') && null !== $session->get('user-id')
101
    && $checkUserAccess->userAccessPage('home') === false
102
) {
103
    $session->set('system-error_code', ERR_NOT_ALLOWED); //not allowed page
104
    include __DIR__.'/../error.php';
105
    exit();
106
} elseif (($session->has('user-id') && null !== $session->get('user-id')
107
        && $session->get('key') !== null)
108
    || (isset($post_type) === true
109
        && null !== filter_input(INPUT_POST, 'data', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES))
110
) {
111
    // continue
112
    mainQuery($SETTINGS);
113
} else {
114
    $session->set('system-error_code', ERR_NOT_ALLOWED); //not allowed page
115
    include __DIR__.'/../error.php';
116
    exit();
117
}
118
119
// Includes
120
include_once __DIR__.'/../sources/main.functions.php';
121
122
/**
123
 * Undocumented function.
124
 */
125
function mainQuery(array $SETTINGS)
126
{
127
    header('Content-type: text/html; charset=utf-8');
128
    header('Cache-Control: no-cache');
129
    error_reporting(E_ERROR);
130
131
    // Load libraries
132
    loadClasses('DB');
133
134
    // User's language loading
135
    $session = SessionManager::getSession();
136
    $lang = new Language($session->get('user-language') ?? 'english');
137
138
    // Prepare post variables
139
    $post_key = filter_input(INPUT_POST, 'key', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
140
    $post_type = filter_input(INPUT_POST, 'type', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
141
    $post_type_category = filter_input(INPUT_POST, 'type_category', FILTER_SANITIZE_FULL_SPECIAL_CHARS);
142
    $post_data = filter_input(INPUT_POST, 'data', FILTER_SANITIZE_FULL_SPECIAL_CHARS, FILTER_FLAG_NO_ENCODE_QUOTES);
143
144
    // Check KEY
145
    if (isValueSetNullEmpty($post_key) === true) {
146
        echo prepareExchangedData(
147
            array(
148
                'error' => true,
149
                'message' => $lang->get('key_is_not_correct'),
150
            ),
151
            'encode',
152
            $post_key
153
        );
154
        return false;
155
    }
156
    // decrypt and retreive data in JSON format
157
    $dataReceived = empty($post_data) === false ? prepareExchangedData(
158
        $post_data,
159
        'decode'
160
    ) : '';
161
    
162
    switch ($post_type_category) {
163
        case 'action_password':
164
            echo passwordHandler($post_type, $dataReceived, $SETTINGS);
165
            break;
166
167
        case 'action_user':
168
            echo userHandler($post_type, $dataReceived, $SETTINGS, $post_key);
169
            break;
170
171
        case 'action_mail':
172
            echo mailHandler($post_type, $dataReceived, $SETTINGS);
173
            break;
174
175
        case 'action_key':
176
            // deepcode ignore ServerLeak: All cases handled by keyHandler return an encrypted string that is sent back to the client
177
            echo keyHandler($post_type, $dataReceived, $SETTINGS);
178
            break;
179
180
        case 'action_system':
181
            echo systemHandler($post_type, $dataReceived, $SETTINGS);
182
            break;
183
184
        case 'action_utils':
185
            echo utilsHandler($post_type, $dataReceived, $SETTINGS);
186
            break;
187
    }
188
    
189
    // Manage type of action asked
190
    //switch ($post_type) {
191
        /*
192
         * TODO Check if suggestions are existing
193
         */
194
        /*
195
        case 'is_existings_suggestions':
196
            if ($_SESSION['user_manager'] === '1' || $_SESSION['is_admin'] === '1') {
197
                $count = 0;
198
                DB::query('SELECT * FROM ' . prefixTable('items_change'));
199
                $count += DB::count();
200
                DB::query('SELECT * FROM ' . prefixTable('suggestion'));
201
                $count += DB::count();
202
203
                echo '[ { "error" : "" , "count" : "' . $count . '" , "show_sug_in_menu" : "0"} ]';
204
                break;
205
            }
206
            
207
            if (isset($_SESSION['nb_item_change_proposals']) && $_SESSION['nb_item_change_proposals'] > 0) {
208
                echo '[ { "error" : "" , "count" : "' . $_SESSION['nb_item_change_proposals'] . '" , "show_sug_in_menu" : "1"} ]';
209
                break;
210
            }
211
            
212
            echo '[ { "error" : "" , "count" : "" , "show_sug_in_menu" : "0"} ]';
213
214
            break;
215
        */
216
    //}
217
}
218
219
/**
220
 * Handler for all password tasks
221
 *
222
 * @param string $post_type
223
 * @param array|null|string $dataReceived
224
 * @param array $SETTINGS
225
 * @return string
226
 */
227
function passwordHandler(string $post_type, /*php8 array|null|string*/ $dataReceived, array $SETTINGS): string
228
{
229
    switch ($post_type) {
230
        case 'change_pw'://action_password
231
            return changePassword(
232
                (string) filter_var($dataReceived['new_pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
233
                isset($dataReceived['current_pw']) === true ? (string) filter_var($dataReceived['current_pw'], FILTER_SANITIZE_FULL_SPECIAL_CHARS) : '',
234
                (int) filter_var($dataReceived['complexity'], FILTER_SANITIZE_NUMBER_INT),
235
                (string) filter_var($dataReceived['change_request'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
236
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
237
                $SETTINGS
238
            );
239
240
        /*
241
        * Change user's authenticataion password
242
        */
243
        case 'change_user_auth_password'://action_password
244
            return changeUserAuthenticationPassword(
245
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
246
                (string) filter_var($dataReceived['old_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
247
                (string) filter_var($dataReceived['new_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
248
                $SETTINGS
249
            );
250
251
        /*
252
        * User's authenticataion password in LDAP has changed
253
        */
254
        case 'change_user_ldap_auth_password'://action_password
255
            return /** @scrutinizer ignore-call */ changeUserLDAPAuthenticationPassword(
256
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
257
                filter_var($dataReceived['previous_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
258
                filter_var($dataReceived['current_password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
259
                $SETTINGS
260
            );
261
262
        /*
263
        * test_current_user_password_is_correct
264
        */
265
        case 'test_current_user_password_is_correct'://action_password
266
            return isUserPasswordCorrect(
267
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
268
                (string) filter_var($dataReceived['password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
269
                $SETTINGS
270
            );
271
272
        /*
273
        * User's password has to be initialized
274
        */
275
        case 'initialize_user_password'://action_password
276
            return initializeUserPassword(
277
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
278
                (string) filter_var($dataReceived['special'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
279
                (string) filter_var($dataReceived['password'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
280
                (bool) filter_var($dataReceived['self_change'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
281
                $SETTINGS
282
            );
283
284
        /*
285
        * Default case
286
        */
287
        default :
288
            return prepareExchangedData(
289
                array(
290
                    'error' => true,
291
                ),
292
                'encode'
293
            );
294
    }
295
}
296
297
/**
298
 * Handler for all user tasks
299
 *
300
 * @param string $post_type
301
 * @param array|null|string $dataReceived
302
 * @param array $SETTINGS
303
 * @param string $post_key
304
 * @return string
305
 */
306
function userHandler(string $post_type, array|null|string $dataReceived, array $SETTINGS, string $post_key): string
307
{
308
    $session = SessionManager::getSession();
309
310
    switch ($post_type) {
311
        /*
312
        * Get info 
313
        */
314
        case 'get_user_info'://action_user
315
            return getUserInfo(
316
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
317
                (string) filter_var($dataReceived['fields'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
318
                $SETTINGS
319
            );
320
321
        /*
322
        * Increase the session time of User
323
        */
324
        case 'increase_session_time'://action_user
325
            return increaseSessionDuration(
326
                (int) filter_input(INPUT_POST, 'duration', FILTER_SANITIZE_NUMBER_INT)
327
            );
328
329
        /*
330
        * Generate a password generic
331
        */
332
        case 'generate_password'://action_user
333
            return generateGenericPassword(
334
                (int) filter_input(INPUT_POST, 'size', FILTER_SANITIZE_NUMBER_INT),
335
                (bool) filter_input(INPUT_POST, 'secure_pwd', FILTER_VALIDATE_BOOLEAN),
336
                (bool) filter_input(INPUT_POST, 'lowercase', FILTER_VALIDATE_BOOLEAN),
337
                (bool) filter_input(INPUT_POST, 'capitalize', FILTER_VALIDATE_BOOLEAN),
338
                (bool) filter_input(INPUT_POST, 'numerals', FILTER_VALIDATE_BOOLEAN),
339
                (bool) filter_input(INPUT_POST, 'symbols', FILTER_VALIDATE_BOOLEAN),
340
                $SETTINGS
341
            );
342
343
        /*
344
        * Refresh list of last items seen
345
        */
346
        case 'refresh_list_items_seen'://action_user
347
            if ($session->has('user-id') || (int) $session->get('user-id') && null !== $session->get('user-id') || (int) $session->get('user-id') > 0) {
348
                return refreshUserItemsSeenList(
349
                    $SETTINGS
350
                );
351
352
            } else {
353
                return json_encode(
354
                    array(
355
                        'error' => '',
356
                        'existing_suggestions' => 0,
357
                        'html_json' => '',
358
                    ),
359
                    JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
360
                );
361
            }
362
363
        /*
364
        * This will generate the QR Google Authenticator
365
        */
366
        case 'ga_generate_qr'://action_user
367
            return generateQRCode(
368
                (int) filter_var($dataReceived['user_id'], FILTER_SANITIZE_NUMBER_INT),
369
                (string) filter_var($dataReceived['demand_origin'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
370
                (string) filter_var($dataReceived['send_email'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
371
                (string) filter_var($dataReceived['login'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
372
                (string) filter_var($dataReceived['pwd'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
373
                (string) filter_var($dataReceived['token'], FILTER_SANITIZE_FULL_SPECIAL_CHARS),
374
                $SETTINGS,
375
                (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

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