Passed
Push — dev ( 3ad29f...97f4bd )
by Nils
08:12
created

userHandler()   C

Complexity

Conditions 11
Paths 10

Size

Total Lines 102
Code Lines 55

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 11
eloc 55
c 4
b 0
f 0
nc 10
nop 3
dl 0
loc 102
rs 6.8351

How to fix   Long Method    Complexity   

Long Method

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

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

Commonly applied refactorings include:

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