Passed
Branch wip_sessions (2e0cc8)
by Nils
04:59
created

generateQRCode()   D

Complexity

Conditions 15
Paths 17

Size

Total Lines 192
Code Lines 110

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 15
eloc 110
c 2
b 0
f 0
nc 17
nop 7
dl 0
loc 192
rs 4.7333

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

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