Passed
Pull Request — master (#4323)
by
unknown
06:32
created

changeUserLDAPAuthenticationPassword()   C

Complexity

Conditions 10
Paths 7

Size

Total Lines 130
Code Lines 63

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 63
c 1
b 0
f 0
nc 7
nop 4
dl 0
loc 130
rs 6.9406

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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

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