Passed
Push — master ( cea058...bb409d )
by Nils
04:10
created

generateOneTimeCode()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 61
Code Lines 36

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
cc 4
eloc 36
c 2
b 1
f 0
nc 3
nop 2
dl 0
loc 61
rs 9.344

How to fix   Long Method   

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