Passed
Push — master ( 08a4fb...fb9f5a )
by Nils
24:11 queued 19:15
created

handleUserKeys()   F

Complexity

Conditions 20
Paths 141

Size

Total Lines 165
Code Lines 89

Duplication

Lines 0
Ratio 0 %

Importance

Changes 6
Bugs 0 Features 0
Metric Value
cc 20
eloc 89
c 6
b 0
f 0
nc 141
nop 12
dl 0
loc 165
rs 3.825

How to fix   Long Method    Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Teampass - a collaborative passwords manager.
7
 * ---
8
 * This file is part of the TeamPass project.
9
 * 
10
 * TeamPass is free software: you can redistribute it and/or modify it
11
 * under the terms of the GNU General Public License as published by
12
 * the Free Software Foundation, version 3 of the License.
13
 * 
14
 * TeamPass is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
 * GNU General Public License for more details.
18
 * 
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program. If not, see <https://www.gnu.org/licenses/>.
21
 * 
22
 * Certain components of this file may be under different licenses. For
23
 * details, see the `licenses` directory or individual file headers.
24
 * ---
25
 * @file      main.functions.php
26
 * @author    Nils Laumaillé ([email protected])
27
 * @copyright 2009-2024 Teampass.net
28
 * @license   GPL-3.0
29
 * @see       https://www.teampass.net
30
 */
31
32
use LdapRecord\Connection;
33
use ForceUTF8\Encoding;
34
use Elegant\Sanitizer\Sanitizer;
35
use voku\helper\AntiXSS;
36
use Hackzilla\PasswordGenerator\Generator\ComputerPasswordGenerator;
37
use Hackzilla\PasswordGenerator\RandomGenerator\Php7RandomGenerator;
38
use TeampassClasses\SessionManager\SessionManager;
39
use Symfony\Component\HttpFoundation\Request as SymfonyRequest;
40
use TeampassClasses\Language\Language;
41
use TeampassClasses\NestedTree\NestedTree;
42
use Defuse\Crypto\Key;
43
use Defuse\Crypto\Crypto;
44
use Defuse\Crypto\KeyProtectedByPassword;
45
use Defuse\Crypto\File as CryptoFile;
46
use Defuse\Crypto\Exception as CryptoException;
47
use Elegant\Sanitizer\Filters\Uppercase;
48
use PHPMailer\PHPMailer\PHPMailer;
49
use TeampassClasses\PasswordManager\PasswordManager;
50
use Symfony\Component\Process\Exception\ProcessFailedException;
51
use Symfony\Component\Process\Process;
52
use Symfony\Component\Process\PhpExecutableFinder;
53
use TeampassClasses\Encryption\Encryption;
54
use TeampassClasses\ConfigManager\ConfigManager;
55
56
header('Content-type: text/html; charset=utf-8');
57
header('Cache-Control: no-cache, must-revalidate');
58
59
loadClasses('DB');
60
$session = SessionManager::getSession();
61
62
// Load config if $SETTINGS not defined
63
$configManager = new ConfigManager();
64
$SETTINGS = $configManager->getAllSettings();
65
/*if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
66
    include_once __DIR__ . '/../includes/config/tp.config.php';
67
}
68
*/
69
70
/**
71
 * genHash().
72
 *
73
 * Generate a hash for user login
74
 *
75
 * @param string $password What password
76
 * @param string $cost     What cost
77
 *
78
 * @return string|void
79
 */
80
function bCrypt(
81
    string $password,
82
    string $cost
83
): ?string
84
{
85
    $salt = sprintf('$2y$%02d$', $cost);
86
    if (function_exists('openssl_random_pseudo_bytes')) {
87
        $salt .= bin2hex(openssl_random_pseudo_bytes(11));
88
    } else {
89
        $chars = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
90
        for ($i = 0; $i < 22; ++$i) {
91
            $salt .= $chars[mt_rand(0, 63)];
92
        }
93
    }
94
95
    return crypt($password, $salt);
96
}
97
98
/**
99
 * Checks if a string is hex encoded
100
 *
101
 * @param string $str
102
 * @return boolean
103
 */
104
function isHex(string $str): bool
105
{
106
    if (str_starts_with(strtolower($str), '0x')) {
107
        $str = substr($str, 2);
108
    }
109
110
    return ctype_xdigit($str);
111
}
112
113
/**
114
 * Defuse cryption function.
115
 *
116
 * @param string $message   what to de/crypt
117
 * @param string $ascii_key key to use
118
 * @param string $type      operation to perform
119
 * @param array  $SETTINGS  Teampass settings
120
 *
121
 * @return array
122
 */
123
function cryption(string $message, string $ascii_key, string $type, ?array $SETTINGS = []): array
124
{
125
    $ascii_key = empty($ascii_key) === true ? file_get_contents(SECUREPATH.'/'.SECUREFILE) : $ascii_key;
126
    $err = false;
127
    
128
    // convert KEY
129
    $key = Key::loadFromAsciiSafeString($ascii_key);
130
    try {
131
        if ($type === 'encrypt') {
132
            $text = Crypto::encrypt($message, $key);
133
        } elseif ($type === 'decrypt') {
134
            $text = Crypto::decrypt($message, $key);
135
        }
136
    } catch (CryptoException\WrongKeyOrModifiedCiphertextException $ex) {
137
        $err = 'an attack! either the wrong key was loaded, or the ciphertext has changed since it was created either corrupted in the database or intentionally modified by someone trying to carry out an attack.';
138
    } catch (CryptoException\BadFormatException $ex) {
139
        $err = $ex;
140
    } catch (CryptoException\EnvironmentIsBrokenException $ex) {
141
        $err = $ex;
142
    } catch (CryptoException\CryptoException $ex) {
143
        $err = $ex;
144
    } catch (CryptoException\IOException $ex) {
145
        $err = $ex;
146
    }
147
148
    return [
149
        'string' => $text ?? '',
150
        'error' => $err,
151
    ];
152
}
153
154
/**
155
 * Generating a defuse key.
156
 *
157
 * @return string
158
 */
159
function defuse_generate_key()
160
{
161
    $key = Key::createNewRandomKey();
162
    $key = $key->saveToAsciiSafeString();
163
    return $key;
164
}
165
166
/**
167
 * Generate a Defuse personal key.
168
 *
169
 * @param string $psk psk used
170
 *
171
 * @return string
172
 */
173
function defuse_generate_personal_key(string $psk): string
174
{
175
    $protected_key = KeyProtectedByPassword::createRandomPasswordProtectedKey($psk);
176
    return $protected_key->saveToAsciiSafeString(); // save this in user table
177
}
178
179
/**
180
 * Validate persoanl key with defuse.
181
 *
182
 * @param string $psk                   the user's psk
183
 * @param string $protected_key_encoded special key
184
 *
185
 * @return string
186
 */
187
function defuse_validate_personal_key(string $psk, string $protected_key_encoded): string
188
{
189
    try {
190
        $protected_key_encoded = KeyProtectedByPassword::loadFromAsciiSafeString($protected_key_encoded);
191
        $user_key = $protected_key_encoded->unlockKey($psk);
192
        $user_key_encoded = $user_key->saveToAsciiSafeString();
193
    } catch (CryptoException\EnvironmentIsBrokenException $ex) {
194
        return 'Error - Major issue as the encryption is broken.';
195
    } catch (CryptoException\WrongKeyOrModifiedCiphertextException $ex) {
196
        return 'Error - The saltkey is not the correct one.';
197
    }
198
199
    return $user_key_encoded;
200
    // store it in session once user has entered his psk
201
}
202
203
/**
204
 * Decrypt a defuse string if encrypted.
205
 *
206
 * @param string $value Encrypted string
207
 *
208
 * @return string Decrypted string
209
 */
210
function defuseReturnDecrypted(string $value, $SETTINGS): string
211
{
212
    if (substr($value, 0, 3) === 'def') {
213
        $value = cryption($value, '', 'decrypt', $SETTINGS)['string'];
214
    }
215
216
    return $value;
217
}
218
219
/**
220
 * Trims a string depending on a specific string.
221
 *
222
 * @param string|array $chaine  what to trim
223
 * @param string       $element trim on what
224
 *
225
 * @return string
226
 */
227
function trimElement($chaine, string $element): string
228
{
229
    if (! empty($chaine)) {
230
        if (is_array($chaine) === true) {
231
            $chaine = implode(';', $chaine);
232
        }
233
        $chaine = trim($chaine);
234
        if (substr($chaine, 0, 1) === $element) {
235
            $chaine = substr($chaine, 1);
236
        }
237
        if (substr($chaine, strlen($chaine) - 1, 1) === $element) {
238
            $chaine = substr($chaine, 0, strlen($chaine) - 1);
239
        }
240
    }
241
242
    return $chaine;
243
}
244
245
/**
246
 * Permits to suppress all "special" characters from string.
247
 *
248
 * @param string $string  what to clean
249
 * @param bool   $special use of special chars?
250
 *
251
 * @return string
252
 */
253
function cleanString(string $string, bool $special = false): string
254
{
255
    // Create temporary table for special characters escape
256
    $tabSpecialChar = [];
257
    for ($i = 0; $i <= 31; ++$i) {
258
        $tabSpecialChar[] = chr($i);
259
    }
260
    array_push($tabSpecialChar, '<br />');
261
    if ((int) $special === 1) {
262
        $tabSpecialChar = array_merge($tabSpecialChar, ['</li>', '<ul>', '<ol>']);
263
    }
264
265
    return str_replace($tabSpecialChar, "\n", $string);
266
}
267
268
/**
269
 * Erro manager for DB.
270
 *
271
 * @param array $params output from query
272
 *
273
 * @return void
274
 */
275
function db_error_handler(array $params): void
276
{
277
    echo 'Error: ' . $params['error'] . "<br>\n";
278
    echo 'Query: ' . $params['query'] . "<br>\n";
279
    throw new Exception('Error - Query', 1);
280
}
281
282
/**
283
 * Identify user's rights
284
 *
285
 * @param string|array $groupesVisiblesUser  [description]
286
 * @param string|array $groupesInterditsUser [description]
287
 * @param string       $isAdmin              [description]
288
 * @param string       $idFonctions          [description]
289
 *
290
 * @return bool
291
 */
292
function identifyUserRights(
293
    $groupesVisiblesUser,
294
    $groupesInterditsUser,
295
    $isAdmin,
296
    $idFonctions,
297
    $SETTINGS
298
) {
299
    $session = SessionManager::getSession();
300
    $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
301
302
    // Check if user is ADMINISTRATOR    
303
    (int) $isAdmin === 1 ?
304
        identAdmin(
305
            $idFonctions,
306
            $SETTINGS, /** @scrutinizer ignore-type */
307
            $tree
308
        )
309
        :
310
        identUser(
311
            $groupesVisiblesUser,
312
            $groupesInterditsUser,
313
            $idFonctions,
314
            $SETTINGS, /** @scrutinizer ignore-type */
315
            $tree
316
        );
317
318
    // update user's timestamp
319
    DB::update(
320
        prefixTable('users'),
321
        [
322
            'timestamp' => time(),
323
        ],
324
        'id=%i',
325
        $session->get('user-id')
326
    );
327
328
    return true;
329
}
330
331
/**
332
 * Identify administrator.
333
 *
334
 * @param string $idFonctions Roles of user
335
 * @param array  $SETTINGS    Teampass settings
336
 * @param object $tree        Tree of folders
337
 *
338
 * @return bool
339
 */
340
function identAdmin($idFonctions, $SETTINGS, $tree)
341
{
342
    
343
    $session = SessionManager::getSession();
344
    $groupesVisibles = [];
345
    $session->set('user-personal_folders', []);
346
    $session->set('user-accessible_folders', []);
347
    $session->set('user-no_access_folders', []);
348
    $session->set('user-personal_visible_folders', []);
349
    $session->set('user-read_only_folders', []);
350
    $session->set('system-list_restricted_folders_for_items', []);
351
    $session->set('system-list_folders_editable_by_role', []);
352
    $session->set('user-list_folders_limited', []);
353
    $session->set('user-forbiden_personal_folders', []);
354
    $globalsUserId = $session->get('user-id');
355
    $globalsVisibleFolders = $session->get('user-accessible_folders');
356
    $globalsPersonalVisibleFolders = $session->get('user-personal_visible_folders');
357
    // Get list of Folders
358
    $rows = DB::query('SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i', 0);
359
    foreach ($rows as $record) {
360
        array_push($groupesVisibles, $record['id']);
361
    }
362
    $session->set('user-accessible_folders', $groupesVisibles);
363
    $session->set('user-all_non_personal_folders', $groupesVisibles);
364
    // Exclude all PF
365
    $where = new WhereClause('and');
366
    // create a WHERE statement of pieces joined by ANDs
367
    $where->add('personal_folder=%i', 1);
368
    if (
369
        isset($SETTINGS['enable_pf_feature']) === true
370
        && (int) $SETTINGS['enable_pf_feature'] === 1
371
    ) {
372
        $where->add('title=%s', $globalsUserId);
373
        $where->negateLast();
374
    }
375
    // Get ID of personal folder
376
    $persfld = DB::queryfirstrow(
377
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE title = %s',
378
        $globalsUserId
379
    );
380
    if (empty($persfld['id']) === false) {
381
        if (in_array($persfld['id'], $globalsVisibleFolders) === false) {
382
            array_push($globalsVisibleFolders, $persfld['id']);
383
            array_push($globalsPersonalVisibleFolders, $persfld['id']);
384
            // get all descendants
385
            $tree->rebuild();
386
            $tst = $tree->getDescendants($persfld['id']);
387
            foreach ($tst as $t) {
388
                array_push($globalsVisibleFolders, $t->id);
389
                array_push($globalsPersonalVisibleFolders, $t->id);
390
            }
391
        }
392
    }
393
394
    // get complete list of ROLES
395
    $tmp = explode(';', $idFonctions);
396
    $rows = DB::query(
397
        'SELECT * FROM ' . prefixTable('roles_title') . '
398
        ORDER BY title ASC'
399
    );
400
    foreach ($rows as $record) {
401
        if (! empty($record['id']) && ! in_array($record['id'], $tmp)) {
402
            array_push($tmp, $record['id']);
403
        }
404
    }
405
    $session->set('user-roles', implode(';', $tmp));
406
    $session->set('user-admin', 1);
407
    // Check if admin has created Folders and Roles
408
    DB::query('SELECT * FROM ' . prefixTable('nested_tree') . '');
409
    $session->set('user-nb_folders', DB::count());
410
    DB::query('SELECT * FROM ' . prefixTable('roles_title'));
411
    $session->set('user-nb_roles', DB::count());
412
413
    return true;
414
}
415
416
/**
417
 * Permits to convert an element to array.
418
 *
419
 * @param string|array $element Any value to be returned as array
420
 *
421
 * @return array
422
 */
423
function convertToArray($element): array
424
{
425
    if (is_string($element) === true) {
426
        if (empty($element) === true) {
427
            return [];
428
        }
429
        return explode(
430
            ';',
431
            trimElement($element, ';')
432
        );
433
    }
434
    return $element;
435
}
436
437
/**
438
 * Defines the rights the user has.
439
 *
440
 * @param string|array $allowedFolders  Allowed folders
441
 * @param string|array $noAccessFolders Not allowed folders
442
 * @param string|array $userRoles       Roles of user
443
 * @param array        $SETTINGS        Teampass settings
444
 * @param object       $tree            Tree of folders
445
 * 
446
 * @return bool
447
 */
448
function identUser(
449
    $allowedFolders,
450
    $noAccessFolders,
451
    $userRoles,
452
    array $SETTINGS,
453
    object $tree
454
) {
455
    
456
    $session = SessionManager::getSession();
457
    // Init
458
    $session->set('user-accessible_folders', []);
459
    $session->set('user-personal_folders', []);
460
    $session->set('user-no_access_folders', []);
461
    $session->set('user-personal_visible_folders', []);
462
    $session->set('user-read_only_folders', []);
463
    $session->set('user-user-roles', $userRoles);
464
    $session->set('user-admin', 0);
465
    // init
466
    $personalFolders = [];
467
    $readOnlyFolders = [];
468
    $noAccessPersonalFolders = [];
469
    $restrictedFoldersForItems = [];
470
    $foldersLimited = [];
471
    $foldersLimitedFull = [];
472
    $allowedFoldersByRoles = [];
473
    $globalsUserId = $session->get('user-id');
474
    $globalsPersonalFolders = $session->get('user-personal_folder_enabled');
475
    // Ensure consistency in array format
476
    $noAccessFolders = convertToArray($noAccessFolders);
477
    $userRoles = convertToArray($userRoles);
478
    $allowedFolders = convertToArray($allowedFolders);
479
    
480
    // Get list of folders depending on Roles
481
    $arrays = identUserGetFoldersFromRoles(
482
        $userRoles,
483
        $allowedFoldersByRoles,
484
        $readOnlyFolders,
485
        $allowedFolders
486
    );
487
    $allowedFoldersByRoles = $arrays['allowedFoldersByRoles'];
488
    $readOnlyFolders = $arrays['readOnlyFolders'];
489
490
    // Does this user is allowed to see other items
491
    $inc = 0;
492
    $rows = DB::query(
493
        'SELECT id, id_tree FROM ' . prefixTable('items') . '
494
            WHERE restricted_to LIKE %ss AND inactif = %s'.
495
            (count($allowedFolders) > 0 ? ' AND id_tree NOT IN ('.implode(',', $allowedFolders).')' : ''),
496
        $globalsUserId,
497
        '0'
498
    );
499
    foreach ($rows as $record) {
500
        // Exclude restriction on item if folder is fully accessible
501
        //if (in_array($record['id_tree'], $allowedFolders) === false) {
502
            $restrictedFoldersForItems[$record['id_tree']][$inc] = $record['id'];
503
            ++$inc;
504
        //}
505
    }
506
507
    // Check for the users roles if some specific rights exist on items
508
    $rows = DB::query(
509
        'SELECT i.id_tree, r.item_id
510
        FROM ' . prefixTable('items') . ' as i
511
        INNER JOIN ' . prefixTable('restriction_to_roles') . ' as r ON (r.item_id=i.id)
512
        WHERE i.id_tree <> "" '.
513
        (count($userRoles) > 0 ? 'AND r.role_id IN %li ' : '').
514
        'ORDER BY i.id_tree ASC',
515
        $userRoles
516
    );
517
    $inc = 0;
518
    foreach ($rows as $record) {
519
        //if (isset($record['id_tree'])) {
520
            $foldersLimited[$record['id_tree']][$inc] = $record['item_id'];
521
            array_push($foldersLimitedFull, $record['id_tree']);
522
            ++$inc;
523
        //}
524
    }
525
526
    // Get list of Personal Folders
527
    $arrays = identUserGetPFList(
528
        $globalsPersonalFolders,
529
        $allowedFolders,
530
        $globalsUserId,
531
        $personalFolders,
532
        $noAccessPersonalFolders,
533
        $foldersLimitedFull,
534
        $allowedFoldersByRoles,
535
        array_keys($restrictedFoldersForItems),
536
        $readOnlyFolders,
537
        $noAccessFolders,
538
        isset($SETTINGS['enable_pf_feature']) === true ? $SETTINGS['enable_pf_feature'] : 0,
539
        $tree
540
    );
541
    $allowedFolders = $arrays['allowedFolders'];
542
    $personalFolders = $arrays['personalFolders'];
543
    $noAccessPersonalFolders = $arrays['noAccessPersonalFolders'];
544
545
    // Return data
546
    $session->set('user-all_non_personal_folders', $allowedFolders);
547
    $session->set('user-accessible_folders', array_unique(array_merge($allowedFolders, $personalFolders), SORT_NUMERIC));
548
    $session->set('user-read_only_folders', $readOnlyFolders);
549
    $session->set('user-no_access_folders', $noAccessFolders);
550
    $session->set('user-personal_folders', $personalFolders);
551
    $session->set('user-list_folders_limited', $foldersLimited);
552
    $session->set('system-list_folders_editable_by_role', $allowedFoldersByRoles, 'SESSION');
553
    $session->set('system-list_restricted_folders_for_items', $restrictedFoldersForItems);
554
    $session->set('user-forbiden_personal_folders', $noAccessPersonalFolders);
555
    $session->set(
556
        'all_folders_including_no_access',
557
        array_unique(array_merge(
558
            $allowedFolders,
559
            $personalFolders,
560
            $noAccessFolders,
561
            $readOnlyFolders
562
        ), SORT_NUMERIC)
563
    );
564
    // Folders and Roles numbers
565
    DB::queryfirstrow('SELECT id FROM ' . prefixTable('nested_tree') . '');
566
    $session->set('user-nb_folders', DB::count());
567
    DB::queryfirstrow('SELECT id FROM ' . prefixTable('roles_title'));
568
    $session->set('user-nb_roles', DB::count());
569
    // check if change proposals on User's items
570
    if (isset($SETTINGS['enable_suggestion']) === true && (int) $SETTINGS['enable_suggestion'] === 1) {
571
        $countNewItems = DB::query(
572
            'SELECT COUNT(*)
573
            FROM ' . prefixTable('items_change') . ' AS c
574
            LEFT JOIN ' . prefixTable('log_items') . ' AS i ON (c.item_id = i.id_item)
575
            WHERE i.action = %s AND i.id_user = %i',
576
            'at_creation',
577
            $globalsUserId
578
        );
579
        $session->set('user-nb_item_change_proposals', $countNewItems);
580
    } else {
581
        $session->set('user-nb_item_change_proposals', 0);
582
    }
583
584
    return true;
585
}
586
587
/**
588
 * Get list of folders depending on Roles
589
 * 
590
 * @param array $userRoles
591
 * @param array $allowedFoldersByRoles
592
 * @param array $readOnlyFolders
593
 * @param array $allowedFolders
594
 * 
595
 * @return array
596
 */
597
function identUserGetFoldersFromRoles($userRoles, $allowedFoldersByRoles, $readOnlyFolders, $allowedFolders) : array
598
{
599
    $rows = DB::query(
600
        'SELECT *
601
        FROM ' . prefixTable('roles_values') . '
602
        WHERE type IN %ls'.(count($userRoles) > 0 ? ' AND role_id IN %li' : ''),
603
        ['W', 'ND', 'NE', 'NDNE', 'R'],
604
        $userRoles,
605
    );
606
    foreach ($rows as $record) {
607
        if ($record['type'] === 'R') {
608
            array_push($readOnlyFolders, $record['folder_id']);
609
        } elseif (in_array($record['folder_id'], $allowedFolders) === false) {
610
            array_push($allowedFoldersByRoles, $record['folder_id']);
611
        }
612
    }
613
    $allowedFoldersByRoles = array_unique($allowedFoldersByRoles);
614
    $readOnlyFolders = array_unique($readOnlyFolders);
615
    
616
    // Clean arrays
617
    foreach ($allowedFoldersByRoles as $value) {
618
        $key = array_search($value, $readOnlyFolders);
619
        if ($key !== false) {
620
            unset($readOnlyFolders[$key]);
621
        }
622
    }
623
    return [
624
        'readOnlyFolders' => $readOnlyFolders,
625
        'allowedFoldersByRoles' => $allowedFoldersByRoles
626
    ];
627
}
628
629
/**
630
 * Get list of Personal Folders
631
 * 
632
 * @param int $globalsPersonalFolders
633
 * @param array $allowedFolders
634
 * @param int $globalsUserId
635
 * @param array $personalFolders
636
 * @param array $noAccessPersonalFolders
637
 * @param array $foldersLimitedFull
638
 * @param array $allowedFoldersByRoles
639
 * @param array $restrictedFoldersForItems
640
 * @param array $readOnlyFolders
641
 * @param array $noAccessFolders
642
 * @param int $enablePfFeature
643
 * @param object $tree
644
 * 
645
 * @return array
646
 */
647
function identUserGetPFList(
648
    $globalsPersonalFolders,
649
    $allowedFolders,
650
    $globalsUserId,
651
    $personalFolders,
652
    $noAccessPersonalFolders,
653
    $foldersLimitedFull,
654
    $allowedFoldersByRoles,
655
    $restrictedFoldersForItems,
656
    $readOnlyFolders,
657
    $noAccessFolders,
658
    $enablePfFeature,
659
    $tree
660
)
661
{
662
    if (
663
        (int) $enablePfFeature === 1
664
        && (int) $globalsPersonalFolders === 1
665
    ) {
666
        $persoFld = DB::queryfirstrow(
667
            'SELECT id
668
            FROM ' . prefixTable('nested_tree') . '
669
            WHERE title = %s AND personal_folder = %i'.
670
            (count($allowedFolders) > 0 ? ' AND id NOT IN ('.implode(',', $allowedFolders).')' : ''),
671
            $globalsUserId,
672
            1
673
        );
674
        if (empty($persoFld['id']) === false) {
675
            array_push($personalFolders, $persoFld['id']);
676
            array_push($allowedFolders, $persoFld['id']);
677
            // get all descendants
678
            $ids = $tree->getDescendants($persoFld['id'], false, false, true);
679
            foreach ($ids as $id) {
680
                //array_push($allowedFolders, $id);
681
                array_push($personalFolders, $id);
682
            }
683
        }
684
    }
685
    
686
    // Exclude all other PF
687
    $where = new WhereClause('and');
688
    $where->add('personal_folder=%i', 1);
689
    if (count($personalFolders) > 0) {
690
        $where->add('id NOT IN ('.implode(',', $personalFolders).')');
691
    }
692
    if (
693
        (int) $enablePfFeature === 1
694
        && (int) $globalsPersonalFolders === 1
695
    ) {
696
        $where->add('title=%s', $globalsUserId);
697
        $where->negateLast();
698
    }
699
    $persoFlds = DB::query(
700
        'SELECT id
701
        FROM ' . prefixTable('nested_tree') . '
702
        WHERE %l',
703
        $where
704
    );
705
    foreach ($persoFlds as $persoFldId) {
706
        array_push($noAccessPersonalFolders, $persoFldId['id']);
707
    }
708
709
    // All folders visibles
710
    $allowedFolders = array_unique(array_merge(
711
        $allowedFolders,
712
        $foldersLimitedFull,
713
        $allowedFoldersByRoles,
714
        $restrictedFoldersForItems,
715
        $readOnlyFolders
716
    ), SORT_NUMERIC);
717
    // Exclude from allowed folders all the specific user forbidden folders
718
    if (count($noAccessFolders) > 0) {
719
        $allowedFolders = array_diff($allowedFolders, $noAccessFolders);
720
    }
721
722
    return [
723
        'allowedFolders' => array_diff(array_diff($allowedFolders, $noAccessPersonalFolders), $personalFolders),
724
        'personalFolders' => $personalFolders,
725
        'noAccessPersonalFolders' => $noAccessPersonalFolders
726
    ];
727
}
728
729
730
/**
731
 * Update the CACHE table.
732
 *
733
 * @param string $action   What to do
734
 * @param array  $SETTINGS Teampass settings
735
 * @param int    $ident    Ident format
736
 * 
737
 * @return void
738
 */
739
function updateCacheTable(string $action, ?int $ident = null): void
740
{
741
    if ($action === 'reload') {
742
        // Rebuild full cache table
743
        cacheTableRefresh();
744
    } elseif ($action === 'update_value' && is_null($ident) === false) {
745
        // UPDATE an item
746
        cacheTableUpdate($ident);
747
    } elseif ($action === 'add_value' && is_null($ident) === false) {
748
        // ADD an item
749
        cacheTableAdd($ident);
750
    } elseif ($action === 'delete_value' && is_null($ident) === false) {
751
        // DELETE an item
752
        DB::delete(prefixTable('cache'), 'id = %i', $ident);
753
    }
754
}
755
756
/**
757
 * Cache table - refresh.
758
 *
759
 * @return void
760
 */
761
function cacheTableRefresh(): void
762
{
763
    // Load class DB
764
    loadClasses('DB');
765
766
    //Load Tree
767
    $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
768
    // truncate table
769
    DB::query('TRUNCATE TABLE ' . prefixTable('cache'));
770
    // reload date
771
    $rows = DB::query(
772
        'SELECT *
773
        FROM ' . prefixTable('items') . ' as i
774
        INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
775
        AND l.action = %s
776
        AND i.inactif = %i',
777
        'at_creation',
778
        0
779
    );
780
    foreach ($rows as $record) {
781
        if (empty($record['id_tree']) === false) {
782
            // Get all TAGS
783
            $tags = '';
784
            $itemTags = DB::query(
785
                'SELECT tag
786
                FROM ' . prefixTable('tags') . '
787
                WHERE item_id = %i AND tag != ""',
788
                $record['id']
789
            );
790
            foreach ($itemTags as $itemTag) {
791
                $tags .= $itemTag['tag'] . ' ';
792
            }
793
794
            // Get renewal period
795
            $resNT = DB::queryfirstrow(
796
                'SELECT renewal_period
797
                FROM ' . prefixTable('nested_tree') . '
798
                WHERE id = %i',
799
                $record['id_tree']
800
            );
801
            // form id_tree to full foldername
802
            $folder = [];
803
            $arbo = $tree->getPath($record['id_tree'], true);
804
            foreach ($arbo as $elem) {
805
                // Check if title is the ID of a user
806
                if (is_numeric($elem->title) === true) {
807
                    // Is this a User id?
808
                    $user = DB::queryfirstrow(
809
                        'SELECT id, login
810
                        FROM ' . prefixTable('users') . '
811
                        WHERE id = %i',
812
                        $elem->title
813
                    );
814
                    if (count($user) > 0) {
815
                        $elem->title = $user['login'];
816
                    }
817
                }
818
                // Build path
819
                array_push($folder, stripslashes($elem->title));
820
            }
821
            // store data
822
            DB::insert(
823
                prefixTable('cache'),
824
                [
825
                    'id' => $record['id'],
826
                    'label' => $record['label'],
827
                    'description' => $record['description'] ?? '',
828
                    'url' => isset($record['url']) && ! empty($record['url']) ? $record['url'] : '0',
829
                    'tags' => $tags,
830
                    'id_tree' => $record['id_tree'],
831
                    'perso' => $record['perso'],
832
                    'restricted_to' => isset($record['restricted_to']) && ! empty($record['restricted_to']) ? $record['restricted_to'] : '0',
833
                    'login' => $record['login'] ?? '',
834
                    'folder' => implode(' > ', $folder),
835
                    'author' => $record['id_user'],
836
                    'renewal_period' => $resNT['renewal_period'] ?? '0',
837
                    'timestamp' => $record['date'],
838
                ]
839
            );
840
        }
841
    }
842
}
843
844
/**
845
 * Cache table - update existing value.
846
 *
847
 * @param int    $ident    Ident format
848
 * 
849
 * @return void
850
 */
851
function cacheTableUpdate(?int $ident = null): void
852
{
853
    $session = SessionManager::getSession();
854
    loadClasses('DB');
855
856
    //Load Tree
857
    $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
858
    // get new value from db
859
    $data = DB::queryfirstrow(
860
        'SELECT label, description, id_tree, perso, restricted_to, login, url
861
        FROM ' . prefixTable('items') . '
862
        WHERE id=%i',
863
        $ident
864
    );
865
    // Get all TAGS
866
    $tags = '';
867
    $itemTags = DB::query(
868
        'SELECT tag
869
            FROM ' . prefixTable('tags') . '
870
            WHERE item_id = %i AND tag != ""',
871
        $ident
872
    );
873
    foreach ($itemTags as $itemTag) {
874
        $tags .= $itemTag['tag'] . ' ';
875
    }
876
    // form id_tree to full foldername
877
    $folder = [];
878
    $arbo = $tree->getPath($data['id_tree'], true);
879
    foreach ($arbo as $elem) {
880
        // Check if title is the ID of a user
881
        if (is_numeric($elem->title) === true) {
882
            // Is this a User id?
883
            $user = DB::queryfirstrow(
884
                'SELECT id, login
885
                FROM ' . prefixTable('users') . '
886
                WHERE id = %i',
887
                $elem->title
888
            );
889
            if (count($user) > 0) {
890
                $elem->title = $user['login'];
891
            }
892
        }
893
        // Build path
894
        array_push($folder, stripslashes($elem->title));
895
    }
896
    // finaly update
897
    DB::update(
898
        prefixTable('cache'),
899
        [
900
            'label' => $data['label'],
901
            'description' => $data['description'],
902
            'tags' => $tags,
903
            'url' => isset($data['url']) && ! empty($data['url']) ? $data['url'] : '0',
904
            'id_tree' => $data['id_tree'],
905
            'perso' => $data['perso'],
906
            'restricted_to' => isset($data['restricted_to']) && ! empty($data['restricted_to']) ? $data['restricted_to'] : '0',
907
            'login' => $data['login'] ?? '',
908
            'folder' => implode(' » ', $folder),
909
            'author' => $session->get('user-id'),
910
        ],
911
        'id = %i',
912
        $ident
913
    );
914
}
915
916
/**
917
 * Cache table - add new value.
918
 *
919
 * @param int    $ident    Ident format
920
 * 
921
 * @return void
922
 */
923
function cacheTableAdd(?int $ident = null): void
924
{
925
    $session = SessionManager::getSession();
926
    $globalsUserId = $session->get('user-id');
927
928
    // Load class DB
929
    loadClasses('DB');
930
931
    //Load Tree
932
    $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
933
    // get new value from db
934
    $data = DB::queryFirstRow(
935
        'SELECT i.label, i.description, i.id_tree as id_tree, i.perso, i.restricted_to, i.id, i.login, i.url, l.date
936
        FROM ' . prefixTable('items') . ' as i
937
        INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
938
        WHERE i.id = %i
939
        AND l.action = %s',
940
        $ident,
941
        'at_creation'
942
    );
943
    // Get all TAGS
944
    $tags = '';
945
    $itemTags = DB::query(
946
        'SELECT tag
947
            FROM ' . prefixTable('tags') . '
948
            WHERE item_id = %i AND tag != ""',
949
        $ident
950
    );
951
    foreach ($itemTags as $itemTag) {
952
        $tags .= $itemTag['tag'] . ' ';
953
    }
954
    // form id_tree to full foldername
955
    $folder = [];
956
    $arbo = $tree->getPath($data['id_tree'], true);
957
    foreach ($arbo as $elem) {
958
        // Check if title is the ID of a user
959
        if (is_numeric($elem->title) === true) {
960
            // Is this a User id?
961
            $user = DB::queryfirstrow(
962
                'SELECT id, login
963
                FROM ' . prefixTable('users') . '
964
                WHERE id = %i',
965
                $elem->title
966
            );
967
            if (count($user) > 0) {
968
                $elem->title = $user['login'];
969
            }
970
        }
971
        // Build path
972
        array_push($folder, stripslashes($elem->title));
973
    }
974
    // finaly update
975
    DB::insert(
976
        prefixTable('cache'),
977
        [
978
            'id' => $data['id'],
979
            'label' => $data['label'],
980
            'description' => $data['description'],
981
            'tags' => isset($tags) && empty($tags) === false ? $tags : 'None',
982
            'url' => isset($data['url']) && ! empty($data['url']) ? $data['url'] : '0',
983
            'id_tree' => $data['id_tree'],
984
            'perso' => isset($data['perso']) && empty($data['perso']) === false && $data['perso'] !== 'None' ? $data['perso'] : '0',
985
            'restricted_to' => isset($data['restricted_to']) && empty($data['restricted_to']) === false ? $data['restricted_to'] : '0',
986
            'login' => $data['login'] ?? '',
987
            'folder' => implode(' » ', $folder),
988
            'author' => $globalsUserId,
989
            'timestamp' => $data['date'],
990
        ]
991
    );
992
}
993
994
/**
995
 * Do statistics.
996
 *
997
 * @param array $SETTINGS Teampass settings
998
 *
999
 * @return array
1000
 */
1001
function getStatisticsData(array $SETTINGS): array
1002
{
1003
    DB::query(
1004
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i',
1005
        0
1006
    );
1007
    $counter_folders = DB::count();
1008
    DB::query(
1009
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i',
1010
        1
1011
    );
1012
    $counter_folders_perso = DB::count();
1013
    DB::query(
1014
        'SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i',
1015
        0
1016
    );
1017
    $counter_items = DB::count();
1018
        DB::query(
1019
        'SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i',
1020
        1
1021
    );
1022
    $counter_items_perso = DB::count();
1023
        DB::query(
1024
        'SELECT id FROM ' . prefixTable('users') . ''
1025
    );
1026
    $counter_users = DB::count();
1027
        DB::query(
1028
        'SELECT id FROM ' . prefixTable('users') . ' WHERE admin = %i',
1029
        1
1030
    );
1031
    $admins = DB::count();
1032
    DB::query(
1033
        'SELECT id FROM ' . prefixTable('users') . ' WHERE gestionnaire = %i',
1034
        1
1035
    );
1036
    $managers = DB::count();
1037
    DB::query(
1038
        'SELECT id FROM ' . prefixTable('users') . ' WHERE read_only = %i',
1039
        1
1040
    );
1041
    $readOnly = DB::count();
1042
    // list the languages
1043
    $usedLang = [];
1044
    $tp_languages = DB::query(
1045
        'SELECT name FROM ' . prefixTable('languages')
1046
    );
1047
    foreach ($tp_languages as $tp_language) {
1048
        DB::query(
1049
            'SELECT * FROM ' . prefixTable('users') . ' WHERE user_language = %s',
1050
            $tp_language['name']
1051
        );
1052
        $usedLang[$tp_language['name']] = round((DB::count() * 100 / $counter_users), 0);
1053
    }
1054
1055
    // get list of ips
1056
    $usedIp = [];
1057
    $tp_ips = DB::query(
1058
        'SELECT user_ip FROM ' . prefixTable('users')
1059
    );
1060
    foreach ($tp_ips as $ip) {
1061
        if (array_key_exists($ip['user_ip'], $usedIp)) {
1062
            $usedIp[$ip['user_ip']] += $usedIp[$ip['user_ip']];
1063
        } elseif (! empty($ip['user_ip']) && $ip['user_ip'] !== 'none') {
1064
            $usedIp[$ip['user_ip']] = 1;
1065
        }
1066
    }
1067
1068
    return [
1069
        'error' => '',
1070
        'stat_phpversion' => phpversion(),
1071
        'stat_folders' => $counter_folders,
1072
        'stat_folders_shared' => intval($counter_folders) - intval($counter_folders_perso),
1073
        'stat_items' => $counter_items,
1074
        'stat_items_shared' => intval($counter_items) - intval($counter_items_perso),
1075
        'stat_users' => $counter_users,
1076
        'stat_admins' => $admins,
1077
        'stat_managers' => $managers,
1078
        'stat_ro' => $readOnly,
1079
        'stat_kb' => $SETTINGS['enable_kb'],
1080
        'stat_pf' => $SETTINGS['enable_pf_feature'],
1081
        'stat_fav' => $SETTINGS['enable_favourites'],
1082
        'stat_teampassversion' => TP_VERSION,
1083
        'stat_ldap' => $SETTINGS['ldap_mode'],
1084
        'stat_agses' => $SETTINGS['agses_authentication_enabled'],
1085
        'stat_duo' => $SETTINGS['duo'],
1086
        'stat_suggestion' => $SETTINGS['enable_suggestion'],
1087
        'stat_api' => $SETTINGS['api'],
1088
        'stat_customfields' => $SETTINGS['item_extra_fields'],
1089
        'stat_syslog' => $SETTINGS['syslog_enable'],
1090
        'stat_2fa' => $SETTINGS['google_authentication'],
1091
        'stat_stricthttps' => $SETTINGS['enable_sts'],
1092
        'stat_mysqlversion' => DB::serverVersion(),
1093
        'stat_languages' => $usedLang,
1094
        'stat_country' => $usedIp,
1095
    ];
1096
}
1097
1098
/**
1099
 * Permits to prepare the way to send the email
1100
 * 
1101
 * @param string $subject       email subject
1102
 * @param string $body          email message
1103
 * @param string $email         email
1104
 * @param string $receiverName  Receiver name
1105
 * @param array  $SETTINGS      settings
1106
 *
1107
 * @return void
1108
 */
1109
function prepareSendingEmail(
1110
    $subject,
1111
    $body,
1112
    $email,
1113
    $receiverName = ''
1114
): void 
1115
{
1116
    DB::insert(
1117
        prefixTable('background_tasks'),
1118
        array(
1119
            'created_at' => time(),
1120
            'process_type' => 'send_email',
1121
            'arguments' => json_encode([
1122
                'subject' => $subject,
1123
                'receivers' => $email,
1124
                'body' => $body,
1125
                'receiver_name' => $receiverName,
1126
            ], JSON_HEX_QUOT | JSON_HEX_TAG),
1127
        )
1128
    );
1129
}
1130
1131
/**
1132
 * Permits to send an email.
1133
 *
1134
 * @param string $subject     email subject
1135
 * @param string $textMail    email message
1136
 * @param string $email       email
1137
 * @param array  $SETTINGS    settings
1138
 * @param string $textMailAlt email message alt
1139
 * @param bool   $silent      no errors
1140
 *
1141
 * @return string some json info
1142
 */
1143
function sendEmail(
1144
    $subject,
1145
    $textMail,
1146
    $email,
1147
    $SETTINGS,
1148
    $textMailAlt = null,
1149
    $silent = true,
1150
    $cron = false
1151
) {
1152
    $session = SessionManager::getSession();
1153
    $lang = new Language($session->get('user-language') ?? 'english');
1154
1155
    // CAse where email not defined
1156
    if ($email === 'none' || empty($email) === true) {
1157
        return json_encode(
1158
            [
1159
                'error' => true,
1160
                'message' => $lang->get('forgot_my_pw_email_sent'),
1161
            ]
1162
        );
1163
    }
1164
1165
    // Build and send email
1166
    $email = buildEmail(
1167
        $subject,
1168
        $textMail,
1169
        $email,
1170
        $SETTINGS,
1171
        $textMailAlt = null,
1172
        $silent = true,
1173
        $cron
1174
    );
1175
1176
    if ($silent === false) {
0 ignored issues
show
introduced by
The condition $silent === false is always false.
Loading history...
1177
        return json_encode(
1178
            [
1179
                'error' => false,
1180
                'message' => $lang->get('forgot_my_pw_email_sent'),
1181
            ]
1182
        );
1183
    }
1184
    // Debug purpose
1185
    if ((int) $SETTINGS['email_debug_level'] !== 0 && $cron === false) {
1186
        return json_encode(
1187
            [
1188
                'error' => true,
1189
                'message' => isset($email['ErrorInfo']) === true ? $email['ErrorInfo'] : '',
1190
            ]
1191
        );
1192
    }
1193
    return json_encode(
1194
        [
1195
            'error' => false,
1196
            'message' => $lang->get('share_sent_ok'),
1197
        ]
1198
    );
1199
}
1200
1201
1202
function buildEmail(
1203
    $subject,
1204
    $textMail,
1205
    $email,
1206
    $SETTINGS,
1207
    $textMailAlt = null,
1208
    $silent = true,
1209
    $cron = false
1210
)
1211
{
1212
    // Load PHPMailer
1213
    $mail = new PHPMailer(true);
1214
    $languageDir = $SETTINGS['cpassman_dir'] . '/vendor/phpmailer/phpmailer/language/';
1215
1216
    try {
1217
        // Set language and SMTPDebug
1218
        $mail->setLanguage('en', $languageDir);
1219
        $mail->SMTPDebug = ($cron || $silent) ? 0 : $SETTINGS['email_debug_level'];
1220
        
1221
        /*
1222
        // Define custom Debug output function
1223
        $mail->Debugoutput = function($str, $level) {
1224
            // Path to your log file
1225
            $logFilePath = '/var/log/phpmailer.log';
1226
            file_put_contents($logFilePath, gmdate('Y-m-d H:i:s'). "\t$level\t$str\n", FILE_APPEND | LOCK_EX);
1227
        };
1228
        */
1229
1230
        // Configure SMTP
1231
        $mail->isSMTP();
1232
        $mail->Host = $SETTINGS['email_smtp_server'];
1233
        $mail->SMTPAuth = (int) $SETTINGS['email_smtp_auth'] === 1;
1234
        $mail->Username = $SETTINGS['email_auth_username'];
1235
        $mail->Password = $SETTINGS['email_auth_pwd'];
1236
        $mail->Port = (int) $SETTINGS['email_port'];
1237
        $mail->SMTPSecure = $SETTINGS['email_security'] !== 'none' ? $SETTINGS['email_security'] : '';
1238
        $mail->SMTPAutoTLS = $SETTINGS['email_security'] !== 'none';
1239
        $mail->CharSet = 'utf-8';   //#4143
1240
        $mail->SMTPOptions = [
1241
            'ssl' => [
1242
                'verify_peer' => false,
1243
                'verify_peer_name' => false,
1244
                'allow_self_signed' => true,
1245
            ],
1246
        ];
1247
1248
        // Set From and FromName
1249
        $mail->From = $SETTINGS['email_from'];
1250
        $mail->FromName = $SETTINGS['email_from_name'];
1251
1252
        // Prepare recipients
1253
        foreach (array_filter(explode(',', $email)) as $dest) {
1254
            $mail->addAddress($dest);
1255
        }
1256
        
1257
        // Prepare HTML and AltBody
1258
        $text_html = emailBody($textMail);
1259
        $mail->WordWrap = 80;
1260
        $mail->isHtml(true);
1261
        $mail->Subject = $subject;
1262
        $mail->Body = $text_html;
1 ignored issue
show
Security File Manipulation introduced by
$Body can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

6 paths for user data to reach this point

  1. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 69
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 69
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  2. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 70
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 70
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  3. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 65
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 65
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  4. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 66
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 66
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  5. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 67
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 67
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  6. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 68
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 68
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258

Used in path-write context

  1. $text_html is assigned to property PHPMailer::$Body
    in sources/main.functions.php on line 1262
  2. Read from property PHPMailer::$Body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  3. Data is passed through encodeString()
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  4. $this->encodeString($this->Body, $this->Encoding) is assigned to $body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  5. $body is returned
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3135
  6. $this->createBody() is assigned to property PHPMailer::$MIMEBody
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1622
  7. Read from property PHPMailer::$MIMEBody
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1687
  8. PHPMailer::sendmailSend() is called
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1687
  9. Enters via parameter $body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1726
  10. fwrite() is called
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1803

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security File Manipulation introduced by
$Body can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

6 paths for user data to reach this point

  1. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 67
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 67
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  2. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 69
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 69
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  3. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 65
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 65
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  4. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 66
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 66
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  5. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 68
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 68
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  6. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 70
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 70
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258

Used in path-write context

  1. $text_html is assigned to property PHPMailer::$Body
    in sources/main.functions.php on line 1262
  2. Read from property PHPMailer::$Body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  3. Data is passed through encodeString()
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  4. $this->encodeString($this->Body, $this->Encoding) is assigned to $body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  5. file_put_contents() is called
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3092

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
Security Header Injection introduced by
$Body can contain request data and is used in request header context(s) leading to a potential security vulnerability.

6 paths for user data to reach this point

  1. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 69
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 69
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  2. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 68
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 68
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  3. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 70
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 70
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  4. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 67
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 67
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  5. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 65
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 65
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258
  6. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 66
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 66
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 870
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4422
  10. sendEmail() is called
    in sources/main.functions.php on line 4439
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1258

Used in request-header context

  1. $text_html is assigned to property PHPMailer::$Body
    in sources/main.functions.php on line 1262
  2. Read from property PHPMailer::$Body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  3. Data is passed through encodeString()
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  4. $this->encodeString($this->Body, $this->Encoding) is assigned to $body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  5. $body is returned
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3135
  6. $this->createBody() is assigned to property PHPMailer::$MIMEBody
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1622
  7. Read from property PHPMailer::$MIMEBody
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1656
  8. Data is passed through DKIM_Add()
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1653
  9. $this->DKIM_Add($this->MIMEHeader . $this->mailHeader, $this->encodeHeader($this->secureHeader($this->Subject)), $this->MIMEBody) is assigned to $header_dkim
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1653
  10. Data is passed through normalizeBreaks()
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1659
  11. static::stripTrailingWSP($this->MIMEHeader) . static::LE . static::normalizeBreaks($header_dkim) . static::LE is assigned to property PHPMailer::$MIMEHeader
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1658
  12. Read from property PHPMailer::$MIMEHeader
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1698
  13. PHPMailer::mailSend() is called
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1698
  14. Enters via parameter $header
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1913
  15. Data is passed through stripTrailingWSP()
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1915
  16. static::stripTrailingWSP($header) . static::LE . static::LE is assigned to $header
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1915
  17. PHPMailer::mailPassthru() is called
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1956
  18. Enters via parameter $header
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 865
  19. mail() is called
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 881

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
1263
        $mail->AltBody = is_null($textMailAlt) ? '' : $textMailAlt;
1264
        
1265
        // Send email
1266
        $mail->send();
1267
        $mail->smtpClose();
1268
        
1269
        return '';
1270
    } catch (Exception $e) {
1271
        error_log('Error sending email: ' . $e->getMessage());
1272
        if (!$silent || (int) $SETTINGS['email_debug_level'] !== 0) {
1273
            return json_encode([
1274
                'error' => true,
1275
                'errorInfo' => str_replace(["\n", "\t", "\r"], '', $mail->ErrorInfo),
1276
            ]);
1277
        }
1278
        return '';
1279
    }
1280
}
1281
1282
/**
1283
 * Returns the email body.
1284
 *
1285
 * @param string $textMail Text for the email
1286
 */
1287
function emailBody(string $textMail): string
1288
{
1289
    return '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.=
1290
    w3.org/TR/html4/loose.dtd"><html>
1291
    <head><title>Email Template</title>
1292
    <style type="text/css">
1293
    body { background-color: #f0f0f0; padding: 10px 0; margin:0 0 10px =0; }
1294
    </style></head>
1295
    <body style="-ms-text-size-adjust: none; size-adjust: none; margin: 0; padding: 10px 0; background-color: #f0f0f0;" bgcolor="#f0f0f0" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
1296
    <table border="0" width="100%" height="100%" cellpadding="0" cellspacing="0" bgcolor="#f0f0f0" style="border-spacing: 0;">
1297
    <tr><td style="border-collapse: collapse;"><br>
1298
        <table border="0" width="100%" cellpadding="0" cellspacing="0" bgcolor="#17357c" style="border-spacing: 0; margin-bottom: 25px;">
1299
        <tr><td style="border-collapse: collapse; padding: 11px 20px;">
1300
            <div style="max-width:150px; max-height:34px; color:#f0f0f0; font-weight:bold;">Teampass</div>
1301
        </td></tr></table></td>
1302
    </tr>
1303
    <tr><td align="center" valign="top" bgcolor="#f0f0f0" style="border-collapse: collapse; background-color: #f0f0f0;">
1304
        <table width="600" cellpadding="0" cellspacing="0" border="0" class="container" bgcolor="#ffffff" style="border-spacing: 0; border-bottom: 1px solid #e0e0e0; box-shadow: 0 0 3px #ddd; color: #434343; font-family: Helvetica, Verdana, sans-serif;">
1305
        <tr><td class="container-padding" bgcolor="#ffffff" style="border-collapse: collapse; border-left: 1px solid #e0e0e0; background-color: #ffffff; padding-left: 30px; padding-right: 30px;">
1306
        <br><div style="float:right;">' .
1307
        $textMail .
1308
        '<br><br></td></tr></table>
1309
    </td></tr></table>
1310
    <br></body></html>';
1311
}
1312
1313
/**
1314
 * Convert date to timestamp.
1315
 *
1316
 * @param string $date        The date
1317
 * @param string $date_format Date format
1318
 *
1319
 * @return int
1320
 */
1321
function dateToStamp(string $date, string $date_format): int
1322
{
1323
    $date = date_parse_from_format($date_format, $date);
1324
    if ((int) $date['warning_count'] === 0 && (int) $date['error_count'] === 0) {
1325
        return mktime(
1326
            empty($date['hour']) === false ? $date['hour'] : 23,
1327
            empty($date['minute']) === false ? $date['minute'] : 59,
1328
            empty($date['second']) === false ? $date['second'] : 59,
1329
            $date['month'],
1330
            $date['day'],
1331
            $date['year']
1332
        );
1333
    }
1334
    return 0;
1335
}
1336
1337
/**
1338
 * Is this a date.
1339
 *
1340
 * @param string $date Date
1341
 *
1342
 * @return bool
1343
 */
1344
function isDate(string $date): bool
1345
{
1346
    return strtotime($date) !== false;
1347
}
1348
1349
/**
1350
 * Check if isUTF8().
1351
 *
1352
 * @param string|array $string Is the string
1353
 *
1354
 * @return int is the string in UTF8 format
1355
 */
1356
function isUTF8($string): int
1357
{
1358
    if (is_array($string) === true) {
1359
        $string = $string['string'];
1360
    }
1361
1362
    return preg_match(
1363
        '%^(?:
1364
        [\x09\x0A\x0D\x20-\x7E] # ASCII
1365
        | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
1366
        | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
1367
        | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
1368
        | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
1369
        | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
1370
        | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
1371
        | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
1372
        )*$%xs',
1373
        $string
1374
    );
1375
}
1376
1377
/**
1378
 * Prepare an array to UTF8 format before JSON_encode.
1379
 *
1380
 * @param array $array Array of values
1381
 *
1382
 * @return array
1383
 */
1384
function utf8Converter(array $array): array
1385
{
1386
    array_walk_recursive(
1387
        $array,
1388
        static function (&$item): void {
1389
            if (mb_detect_encoding((string) $item, 'utf-8', true) === false) {
1390
                $item = mb_convert_encoding($item, 'ISO-8859-1', 'UTF-8');
1391
            }
1392
        }
1393
    );
1394
    return $array;
1395
}
1396
1397
/**
1398
 * Permits to prepare data to be exchanged.
1399
 *
1400
 * @param array|string $data Text
1401
 * @param string       $type Parameter
1402
 * @param string       $key  Optional key
1403
 *
1404
 * @return string|array
1405
 */
1406
function prepareExchangedData($data, string $type, ?string $key = null)
1407
{
1408
    $session = SessionManager::getSession();
1409
    
1410
    // Perform
1411
    if ($type === 'encode' && is_array($data) === true) {
1412
        // Now encode
1413
        return Encryption::encrypt(
1414
            json_encode(
1415
                $data,
1416
                JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1417
            ),
1418
            $session->get('key')
1419
        );
1420
    }
1421
    if ($type === 'decode' && is_array($data) === false) {
1422
        // check if key exists
1423
        return json_decode(
1424
            (string) Encryption::decrypt(
1425
                (string) $data,
1426
                $session->get('key')
1427
            ),
1428
            true
1429
        );
1430
    }
1431
    return '';
1432
}
1433
1434
1435
/**
1436
 * Create a thumbnail.
1437
 *
1438
 * @param string  $src           Source
1439
 * @param string  $dest          Destination
1440
 * @param int $desired_width Size of width
1441
 * 
1442
 * @return void|string|bool
1443
 */
1444
function makeThumbnail(string $src, string $dest, int $desired_width)
1445
{
1446
    /* read the source image */
1447
    if (is_file($src) === true && mime_content_type($src) === 'image/png') {
1448
        $source_image = imagecreatefrompng($src);
1449
        if ($source_image === false) {
1450
            return "Error: Not a valid PNG file! It's type is ".mime_content_type($src);
1451
        }
1452
    } else {
1453
        return "Error: Not a valid PNG file! It's type is ".mime_content_type($src);
1454
    }
1455
1456
    // Get height and width
1457
    $width = imagesx($source_image);
1458
    $height = imagesy($source_image);
1459
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1460
    $desired_height = (int) floor($height * $desired_width / $width);
1461
    /* create a new, "virtual" image */
1462
    $virtual_image = imagecreatetruecolor($desired_width, $desired_height);
1463
    if ($virtual_image === false) {
1464
        return false;
1465
    }
1466
    /* copy source image at a resized size */
1467
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
1468
    /* create the physical thumbnail image to its destination */
1469
    imagejpeg($virtual_image, $dest);
1470
}
1471
1472
/**
1473
 * Check table prefix in SQL query.
1474
 *
1475
 * @param string $table Table name
1476
 * 
1477
 * @return string
1478
 */
1479
function prefixTable(string $table): string
1480
{
1481
    $safeTable = htmlspecialchars(DB_PREFIX . $table);
1482
    if (empty($safeTable) === false) {
1483
        // sanitize string
1484
        return $safeTable;
1485
    }
1486
    // stop error no table
1487
    return 'table_not_exists';
1488
}
1489
1490
/**
1491
 * GenerateCryptKey
1492
 *
1493
 * @param int     $size      Length
1494
 * @param bool $secure Secure
1495
 * @param bool $numerals Numerics
1496
 * @param bool $uppercase Uppercase letters
1497
 * @param bool $symbols Symbols
1498
 * @param bool $lowercase Lowercase
1499
 * @param array   $SETTINGS  SETTINGS
1500
 * 
1501
 * @return string
1502
 */
1503
function GenerateCryptKey(
1504
    int $size = 20,
1505
    bool $secure = false,
1506
    bool $numerals = false,
1507
    bool $uppercase = false,
1508
    bool $symbols = false,
1509
    bool $lowercase = false,
1510
    array $SETTINGS = []
1511
): string {
1512
    $generator = new ComputerPasswordGenerator();
1513
    $generator->setRandomGenerator(new Php7RandomGenerator());
1514
    
1515
    // Manage size
1516
    $generator->setLength((int) $size);
1517
    if ($secure === true) {
1518
        $generator->setSymbols(true);
1519
        $generator->setLowercase(true);
1520
        $generator->setUppercase(true);
1521
        $generator->setNumbers(true);
1522
    } else {
1523
        $generator->setLowercase($lowercase);
1524
        $generator->setUppercase($uppercase);
1525
        $generator->setNumbers($numerals);
1526
        $generator->setSymbols($symbols);
1527
    }
1528
1529
    return $generator->generatePasswords()[0];
1530
}
1531
1532
/**
1533
 * Send sysLOG message
1534
 *
1535
 * @param string    $message
1536
 * @param string    $host
1537
 * @param int       $port
1538
 * @param string    $component
1539
 * 
1540
 * @return void
1541
*/
1542
function send_syslog($message, $host, $port, $component = 'teampass'): void
1543
{
1544
    $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
1545
    $syslog_message = '<123>' . date('M d H:i:s ') . $component . ': ' . $message;
1546
    socket_sendto($sock, (string) $syslog_message, strlen($syslog_message), 0, (string) $host, (int) $port);
1547
    socket_close($sock);
1548
}
1549
1550
/**
1551
 * Permits to log events into DB
1552
 *
1553
 * @param array  $SETTINGS Teampass settings
1554
 * @param string $type     Type
1555
 * @param string $label    Label
1556
 * @param string $who      Who
1557
 * @param string $login    Login
1558
 * @param string|int $field_1  Field
1559
 * 
1560
 * @return void
1561
 */
1562
function logEvents(
1563
    array $SETTINGS, 
1564
    string $type, 
1565
    string $label, 
1566
    string $who, 
1567
    ?string $login = null, 
1568
    $field_1 = null
1569
): void
1570
{
1571
    if (empty($who)) {
1572
        $who = getClientIpServer();
1573
    }
1574
1575
    // Load class DB
1576
    loadClasses('DB');
1577
1578
    DB::insert(
1579
        prefixTable('log_system'),
1580
        [
1581
            'type' => $type,
1582
            'date' => time(),
1583
            'label' => $label,
1584
            'qui' => $who,
1585
            'field_1' => $field_1 === null ? '' : $field_1,
1586
        ]
1587
    );
1588
    // If SYSLOG
1589
    if (isset($SETTINGS['syslog_enable']) === true && (int) $SETTINGS['syslog_enable'] === 1) {
1590
        if ($type === 'user_mngt') {
1591
            send_syslog(
1592
                'action=' . str_replace('at_', '', $label) . ' attribute=user user=' . $who . ' userid="' . $login . '" change="' . $field_1 . '" ',
1593
                $SETTINGS['syslog_host'],
1594
                $SETTINGS['syslog_port'],
1595
                'teampass'
1596
            );
1597
        } else {
1598
            send_syslog(
1599
                'action=' . $type . ' attribute=' . $label . ' user=' . $who . ' userid="' . $login . '" ',
1600
                $SETTINGS['syslog_host'],
1601
                $SETTINGS['syslog_port'],
1602
                'teampass'
1603
            );
1604
        }
1605
    }
1606
}
1607
1608
/**
1609
 * Log events.
1610
 *
1611
 * @param array  $SETTINGS        Teampass settings
1612
 * @param int    $item_id         Item id
1613
 * @param string $item_label      Item label
1614
 * @param int    $id_user         User id
1615
 * @param string $action          Code for reason
1616
 * @param string $login           User login
1617
 * @param string $raison          Code for reason
1618
 * @param string $encryption_type Encryption on
1619
 * @param string $time Encryption Time
1620
 * @param string $old_value       Old value
1621
 * 
1622
 * @return void
1623
 */
1624
function logItems(
1625
    array $SETTINGS,
1626
    int $item_id,
1627
    string $item_label,
1628
    int $id_user,
1629
    string $action,
1630
    ?string $login = null,
1631
    ?string $raison = null,
1632
    ?string $encryption_type = null,
1633
    ?string $time = null,
1634
    ?string $old_value = null
1635
): void {
1636
    // Load class DB
1637
    loadClasses('DB');
1638
1639
    // Insert log in DB
1640
    DB::insert(
1641
        prefixTable('log_items'),
1642
        [
1643
            'id_item' => $item_id,
1644
            'date' => is_null($time) === true ? time() : $time,
1645
            'id_user' => $id_user,
1646
            'action' => $action,
1647
            'raison' => $raison,
1648
            'old_value' => $old_value,
1649
            'encryption_type' => is_null($encryption_type) === true ? TP_ENCRYPTION_NAME : $encryption_type,
1650
        ]
1651
    );
1652
    // Timestamp the last change
1653
    if ($action === 'at_creation' || $action === 'at_modifiation' || $action === 'at_delete' || $action === 'at_import') {
1654
        DB::update(
1655
            prefixTable('misc'),
1656
            [
1657
                'valeur' => time(),
1658
                'updated_at' => time(),
1659
            ],
1660
            'type = %s AND intitule = %s',
1661
            'timestamp',
1662
            'last_item_change'
1663
        );
1664
    }
1665
1666
    // SYSLOG
1667
    if (isset($SETTINGS['syslog_enable']) === true && $SETTINGS['syslog_enable'] === '1') {
1668
        // Extract reason
1669
        $attribute = is_null($raison) === true ? Array('') : explode(' : ', $raison);
1670
        // Get item info if not known
1671
        if (empty($item_label) === true) {
1672
            $dataItem = DB::queryfirstrow(
1673
                'SELECT id, id_tree, label
1674
                FROM ' . prefixTable('items') . '
1675
                WHERE id = %i',
1676
                $item_id
1677
            );
1678
            $item_label = $dataItem['label'];
1679
        }
1680
1681
        send_syslog(
1682
            'action=' . str_replace('at_', '', $action) .
1683
                ' attribute=' . str_replace('at_', '', $attribute[0]) .
1684
                ' itemno=' . $item_id .
1685
                ' user=' . (is_null($login) === true ? '' : addslashes((string) $login)) .
1686
                ' itemname="' . addslashes($item_label) . '"',
1687
            $SETTINGS['syslog_host'],
1688
            $SETTINGS['syslog_port'],
1689
            'teampass'
1690
        );
1691
    }
1692
1693
    // send notification if enabled
1694
    //notifyOnChange($item_id, $action, $SETTINGS);
1695
}
1696
1697
/**
1698
 * Prepare notification email to subscribers.
1699
 *
1700
 * @param int    $item_id  Item id
1701
 * @param string $label    Item label
1702
 * @param array  $changes  List of changes
1703
 * @param array  $SETTINGS Teampass settings
1704
 * 
1705
 * @return void
1706
 */
1707
function notifyChangesToSubscribers(int $item_id, string $label, array $changes, array $SETTINGS): void
1708
{
1709
    $session = SessionManager::getSession();
1710
    $lang = new Language($session->get('user-language') ?? 'english');
1711
    $globalsUserId = $session->get('user-id');
1712
    $globalsLastname = $session->get('user-lastname');
1713
    $globalsName = $session->get('user-name');
1714
    // send email to user that what to be notified
1715
    $notification = DB::queryOneColumn(
1716
        'email',
1717
        'SELECT *
1718
        FROM ' . prefixTable('notification') . ' AS n
1719
        INNER JOIN ' . prefixTable('users') . ' AS u ON (n.user_id = u.id)
1720
        WHERE n.item_id = %i AND n.user_id != %i',
1721
        $item_id,
1722
        $globalsUserId
1723
    );
1724
    if (DB::count() > 0) {
1725
        // Prepare path
1726
        $path = geItemReadablePath($item_id, '', $SETTINGS);
1727
        // Get list of changes
1728
        $htmlChanges = '<ul>';
1729
        foreach ($changes as $change) {
1730
            $htmlChanges .= '<li>' . $change . '</li>';
1731
        }
1732
        $htmlChanges .= '</ul>';
1733
        // send email
1734
        DB::insert(
1735
            prefixTable('emails'),
1736
            [
1737
                'timestamp' => time(),
1738
                'subject' => $lang->get('email_subject_item_updated'),
1739
                'body' => str_replace(
1740
                    ['#item_label#', '#folder_name#', '#item_id#', '#url#', '#name#', '#lastname#', '#changes#'],
1741
                    [$label, $path, $item_id, $SETTINGS['cpassman_url'], $globalsName, $globalsLastname, $htmlChanges],
1742
                    $lang->get('email_body_item_updated')
1743
                ),
1744
                'receivers' => implode(',', $notification),
1745
                'status' => '',
1746
            ]
1747
        );
1748
    }
1749
}
1750
1751
/**
1752
 * Returns the Item + path.
1753
 *
1754
 * @param int    $id_tree  Node id
1755
 * @param string $label    Label
1756
 * @param array  $SETTINGS TP settings
1757
 * 
1758
 * @return string
1759
 */
1760
function geItemReadablePath(int $id_tree, string $label, array $SETTINGS): string
1761
{
1762
    $tree = new NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
1763
    $arbo = $tree->getPath($id_tree, true);
1764
    $path = '';
1765
    foreach ($arbo as $elem) {
1766
        if (empty($path) === true) {
1767
            $path = htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES) . ' ';
1768
        } else {
1769
            $path .= '&#8594; ' . htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES);
1770
        }
1771
    }
1772
1773
    // Build text to show user
1774
    if (empty($label) === false) {
1775
        return empty($path) === true ? addslashes($label) : addslashes($label) . ' (' . $path . ')';
1776
    }
1777
    return empty($path) === true ? '' : $path;
1778
}
1779
1780
/**
1781
 * Get the client ip address.
1782
 *
1783
 * @return string IP address
1784
 */
1785
function getClientIpServer(): string
1786
{
1787
    if (getenv('HTTP_CLIENT_IP')) {
1788
        $ipaddress = getenv('HTTP_CLIENT_IP');
1789
    } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
1790
        $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
1791
    } elseif (getenv('HTTP_X_FORWARDED')) {
1792
        $ipaddress = getenv('HTTP_X_FORWARDED');
1793
    } elseif (getenv('HTTP_FORWARDED_FOR')) {
1794
        $ipaddress = getenv('HTTP_FORWARDED_FOR');
1795
    } elseif (getenv('HTTP_FORWARDED')) {
1796
        $ipaddress = getenv('HTTP_FORWARDED');
1797
    } elseif (getenv('REMOTE_ADDR')) {
1798
        $ipaddress = getenv('REMOTE_ADDR');
1799
    } else {
1800
        $ipaddress = 'UNKNOWN';
1801
    }
1802
1803
    return $ipaddress;
1804
}
1805
1806
/**
1807
 * Escape all HTML, JavaScript, and CSS.
1808
 *
1809
 * @param string $input    The input string
1810
 * @param string $encoding Which character encoding are we using?
1811
 * 
1812
 * @return string
1813
 */
1814
function noHTML(string $input, string $encoding = 'UTF-8'): string
1815
{
1816
    return htmlspecialchars($input, ENT_QUOTES | ENT_XHTML, $encoding, false);
1817
}
1818
1819
/**
1820
 * Permits to handle the Teampass config file
1821
 * $action accepts "rebuild" and "update"
1822
 *
1823
 * @param string $action   Action to perform
1824
 * @param array  $SETTINGS Teampass settings
1825
 * @param string $field    Field to refresh
1826
 * @param string $value    Value to set
1827
 *
1828
 * @return string|bool
1829
 */
1830
function handleConfigFile($action, $SETTINGS, $field = null, $value = null)
1831
{
1832
    $tp_config_file = $SETTINGS['cpassman_dir'] . '/includes/config/tp.config.php';
1833
1834
    // Load class DB
1835
    loadClasses('DB');
1836
1837
    if (file_exists($tp_config_file) === false || $action === 'rebuild') {
1838
        // perform a copy
1839
        if (file_exists($tp_config_file)) {
1840
            if (! copy($tp_config_file, $tp_config_file . '.' . date('Y_m_d_His', time()))) {
1841
                return "ERROR: Could not copy file '" . $tp_config_file . "'";
1842
            }
1843
        }
1844
1845
        // regenerate
1846
        $data = [];
1847
        $data[0] = "<?php\n";
1848
        $data[1] = "global \$SETTINGS;\n";
1849
        $data[2] = "\$SETTINGS = array (\n";
1850
        $rows = DB::query(
1851
            'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s',
1852
            'admin'
1853
        );
1854
        foreach ($rows as $record) {
1855
            array_push($data, "    '" . $record['intitule'] . "' => '" . htmlspecialchars_decode($record['valeur'], ENT_COMPAT) . "',\n");
1856
        }
1857
        array_push($data, ");\n");
1858
        $data = array_unique($data);
1859
    // ---
1860
    } elseif ($action === 'update' && empty($field) === false) {
1861
        $data = file($tp_config_file);
1862
        $inc = 0;
1863
        $bFound = false;
1864
        foreach ($data as $line) {
1865
            if (stristr($line, ');')) {
1866
                break;
1867
            }
1868
1869
            if (stristr($line, "'" . $field . "' => '")) {
1870
                $data[$inc] = "    '" . $field . "' => '" . htmlspecialchars_decode($value ?? '', ENT_COMPAT) . "',\n";
1871
                $bFound = true;
1872
                break;
1873
            }
1874
            ++$inc;
1875
        }
1876
        if ($bFound === false) {
1877
            $data[$inc] = "    '" . $field . "' => '" . htmlspecialchars_decode($value ?? '', ENT_COMPAT). "',\n);\n";
1878
        }
1879
    }
1880
1881
    // update file
1882
    file_put_contents($tp_config_file, implode('', $data ?? []));
1883
    return true;
1884
}
1885
1886
/**
1887
 * Permits to replace &#92; to permit correct display
1888
 *
1889
 * @param string $input Some text
1890
 * 
1891
 * @return string
1892
 */
1893
function handleBackslash(string $input): string
1894
{
1895
    return str_replace('&amp;#92;', '&#92;', $input);
1896
}
1897
1898
/**
1899
 * Permits to load settings
1900
 * 
1901
 * @return void
1902
*/
1903
function loadSettings(): void
1904
{
1905
    global $SETTINGS;
1906
    /* LOAD CPASSMAN SETTINGS */
1907
    if (! isset($SETTINGS['loaded']) || $SETTINGS['loaded'] !== 1) {
1908
        $SETTINGS = [];
1909
        $SETTINGS['duplicate_folder'] = 0;
1910
        //by default, this is set to 0;
1911
        $SETTINGS['duplicate_item'] = 0;
1912
        //by default, this is set to 0;
1913
        $SETTINGS['number_of_used_pw'] = 5;
1914
        //by default, this value is set to 5;
1915
        $settings = [];
1916
        $rows = DB::query(
1917
            'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s_type OR type=%s_type2',
1918
            [
1919
                'type' => 'admin',
1920
                'type2' => 'settings',
1921
            ]
1922
        );
1923
        foreach ($rows as $record) {
1924
            if ($record['type'] === 'admin') {
1925
                $SETTINGS[$record['intitule']] = $record['valeur'];
1926
            } else {
1927
                $settings[$record['intitule']] = $record['valeur'];
1928
            }
1929
        }
1930
        $SETTINGS['loaded'] = 1;
1931
        $SETTINGS['default_session_expiration_time'] = 5;
1932
    }
1933
}
1934
1935
/**
1936
 * check if folder has custom fields.
1937
 * Ensure that target one also has same custom fields
1938
 * 
1939
 * @param int $source_id
1940
 * @param int $target_id 
1941
 * 
1942
 * @return bool
1943
*/
1944
function checkCFconsistency(int $source_id, int $target_id): bool
1945
{
1946
    $source_cf = [];
1947
    $rows = DB::QUERY(
1948
        'SELECT id_category
1949
            FROM ' . prefixTable('categories_folders') . '
1950
            WHERE id_folder = %i',
1951
        $source_id
1952
    );
1953
    foreach ($rows as $record) {
1954
        array_push($source_cf, $record['id_category']);
1955
    }
1956
1957
    $target_cf = [];
1958
    $rows = DB::QUERY(
1959
        'SELECT id_category
1960
            FROM ' . prefixTable('categories_folders') . '
1961
            WHERE id_folder = %i',
1962
        $target_id
1963
    );
1964
    foreach ($rows as $record) {
1965
        array_push($target_cf, $record['id_category']);
1966
    }
1967
1968
    $cf_diff = array_diff($source_cf, $target_cf);
1969
    if (count($cf_diff) > 0) {
1970
        return false;
1971
    }
1972
1973
    return true;
1974
}
1975
1976
/**
1977
 * Will encrypte/decrypt a fil eusing Defuse.
1978
 *
1979
 * @param string $type        can be either encrypt or decrypt
1980
 * @param string $source_file path to source file
1981
 * @param string $target_file path to target file
1982
 * @param array  $SETTINGS    Settings
1983
 * @param string $password    A password
1984
 *
1985
 * @return string|bool
1986
 */
1987
function prepareFileWithDefuse(
1988
    string $type,
1989
    string $source_file,
1990
    string $target_file,
1991
    array $SETTINGS,
1992
    string $password = null
1993
) {
1994
    // Load AntiXSS
1995
    $antiXss = new AntiXSS();
1996
    // Protect against bad inputs
1997
    if (is_array($source_file) === true || is_array($target_file) === true) {
1998
        return 'error_cannot_be_array';
1999
    }
2000
2001
    // Sanitize
2002
    $source_file = $antiXss->xss_clean($source_file);
2003
    $target_file = $antiXss->xss_clean($target_file);
2004
    if (empty($password) === true || is_null($password) === true) {
2005
        // get KEY to define password
2006
        $ascii_key = file_get_contents(SECUREPATH.'/'.SECUREFILE);
2007
        $password = Key::loadFromAsciiSafeString($ascii_key);
2008
    }
2009
2010
    $err = '';
2011
    if ($type === 'decrypt') {
2012
        // Decrypt file
2013
        $err = defuseFileDecrypt(
2014
            $source_file,
2015
            $target_file,
2016
            $SETTINGS, /** @scrutinizer ignore-type */
2017
            $password
2018
        );
2019
    } elseif ($type === 'encrypt') {
2020
        // Encrypt file
2021
        $err = defuseFileEncrypt(
2022
            $source_file,
2023
            $target_file,
2024
            $SETTINGS, /** @scrutinizer ignore-type */
2025
            $password
2026
        );
2027
    }
2028
2029
    // return error
2030
    return $err === true ? $err : '';
2031
}
2032
2033
/**
2034
 * Encrypt a file with Defuse.
2035
 *
2036
 * @param string $source_file path to source file
2037
 * @param string $target_file path to target file
2038
 * @param array  $SETTINGS    Settings
2039
 * @param string $password    A password
2040
 *
2041
 * @return string|bool
2042
 */
2043
function defuseFileEncrypt(
2044
    string $source_file,
2045
    string $target_file,
2046
    array $SETTINGS,
2047
    string $password = null
2048
) {
2049
    try {
2050
        CryptoFile::encryptFileWithPassword(
2051
            $source_file,
2052
            $target_file,
2053
            $password
2054
        );
2055
    } catch (CryptoException\WrongKeyOrModifiedCiphertextException $ex) {
2056
        $err = 'wrong_key';
2057
    } catch (CryptoException\EnvironmentIsBrokenException $ex) {
2058
        $err = $ex;
2059
    } catch (CryptoException\IOException $ex) {
2060
        $err = $ex;
2061
    }
2062
2063
    // return error
2064
    return empty($err) === false ? $err : true;
2065
}
2066
2067
/**
2068
 * Decrypt a file with Defuse.
2069
 *
2070
 * @param string $source_file path to source file
2071
 * @param string $target_file path to target file
2072
 * @param array  $SETTINGS    Settings
2073
 * @param string $password    A password
2074
 *
2075
 * @return string|bool
2076
 */
2077
function defuseFileDecrypt(
2078
    string $source_file,
2079
    string $target_file,
2080
    array $SETTINGS,
2081
    string $password = null
2082
) {
2083
    try {
2084
        CryptoFile::decryptFileWithPassword(
2085
            $source_file,
2086
            $target_file,
2087
            $password
2088
        );
2089
    } catch (CryptoException\WrongKeyOrModifiedCiphertextException $ex) {
2090
        $err = 'wrong_key';
2091
    } catch (CryptoException\EnvironmentIsBrokenException $ex) {
2092
        $err = $ex;
2093
    } catch (CryptoException\IOException $ex) {
2094
        $err = $ex;
2095
    }
2096
2097
    // return error
2098
    return empty($err) === false ? $err : true;
2099
}
2100
2101
/*
2102
* NOT TO BE USED
2103
*/
2104
/**
2105
 * Undocumented function.
2106
 *
2107
 * @param string $text Text to debug
2108
 */
2109
function debugTeampass(string $text): void
2110
{
2111
    $debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+');
2112
    if ($debugFile !== false) {
2113
        fputs($debugFile, $text);
2114
        fclose($debugFile);
2115
    }
2116
}
2117
2118
/**
2119
 * DELETE the file with expected command depending on server type.
2120
 *
2121
 * @param string $file     Path to file
2122
 * @param array  $SETTINGS Teampass settings
2123
 *
2124
 * @return void
2125
 */
2126
function fileDelete(string $file, array $SETTINGS): void
2127
{
2128
    // Load AntiXSS
2129
    $antiXss = new AntiXSS();
2130
    $file = $antiXss->xss_clean($file);
2131
    if (is_file($file)) {
2132
        unlink($file);
2133
    }
2134
}
2135
2136
/**
2137
 * Permits to extract the file extension.
2138
 *
2139
 * @param string $file File name
2140
 *
2141
 * @return string
2142
 */
2143
function getFileExtension(string $file): string
2144
{
2145
    if (strpos($file, '.') === false) {
2146
        return $file;
2147
    }
2148
2149
    return substr($file, strrpos($file, '.') + 1);
2150
}
2151
2152
/**
2153
 * Chmods files and folders with different permissions.
2154
 *
2155
 * This is an all-PHP alternative to using: \n
2156
 * <tt>exec("find ".$path." -type f -exec chmod 644 {} \;");</tt> \n
2157
 * <tt>exec("find ".$path." -type d -exec chmod 755 {} \;");</tt>
2158
 *
2159
 * @author Jeppe Toustrup (tenzer at tenzer dot dk)
2160
  *
2161
 * @param string $path      An either relative or absolute path to a file or directory which should be processed.
2162
 * @param int    $filePerm The permissions any found files should get.
2163
 * @param int    $dirPerm  The permissions any found folder should get.
2164
 *
2165
 * @return bool Returns TRUE if the path if found and FALSE if not.
2166
 *
2167
 * @warning The permission levels has to be entered in octal format, which
2168
 * normally means adding a zero ("0") in front of the permission level. \n
2169
 * More info at: http://php.net/chmod.
2170
*/
2171
2172
function recursiveChmod(
2173
    string $path,
2174
    int $filePerm = 0644,
2175
    int  $dirPerm = 0755
2176
) {
2177
    // Check if the path exists
2178
    $path = basename($path);
2179
    if (! file_exists($path)) {
2180
        return false;
2181
    }
2182
2183
    // See whether this is a file
2184
    if (is_file($path)) {
2185
        // Chmod the file with our given filepermissions
2186
        try {
2187
            chmod($path, $filePerm);
2188
        } catch (Exception $e) {
2189
            return false;
2190
        }
2191
    // If this is a directory...
2192
    } elseif (is_dir($path)) {
2193
        // Then get an array of the contents
2194
        $foldersAndFiles = scandir($path);
2195
        // Remove "." and ".." from the list
2196
        $entries = array_slice($foldersAndFiles, 2);
2197
        // Parse every result...
2198
        foreach ($entries as $entry) {
2199
            // And call this function again recursively, with the same permissions
2200
            recursiveChmod($path.'/'.$entry, $filePerm, $dirPerm);
2201
        }
2202
2203
        // When we are done with the contents of the directory, we chmod the directory itself
2204
        try {
2205
            chmod($path, $filePerm);
2206
        } catch (Exception $e) {
2207
            return false;
2208
        }
2209
    }
2210
2211
    // Everything seemed to work out well, return true
2212
    return true;
2213
}
2214
2215
/**
2216
 * Check if user can access to this item.
2217
 *
2218
 * @param int   $item_id ID of item
2219
 * @param array $SETTINGS
2220
 *
2221
 * @return bool|string
2222
 */
2223
function accessToItemIsGranted(int $item_id, array $SETTINGS)
2224
{
2225
    
2226
    $session = SessionManager::getSession();
2227
    $session_groupes_visibles = $session->get('user-accessible_folders');
2228
    $session_list_restricted_folders_for_items = $session->get('system-list_restricted_folders_for_items');
2229
    // Load item data
2230
    $data = DB::queryFirstRow(
2231
        'SELECT id_tree
2232
        FROM ' . prefixTable('items') . '
2233
        WHERE id = %i',
2234
        $item_id
2235
    );
2236
    // Check if user can access this folder
2237
    if (in_array($data['id_tree'], $session_groupes_visibles) === false) {
2238
        // Now check if this folder is restricted to user
2239
        if (isset($session_list_restricted_folders_for_items[$data['id_tree']]) === true
2240
            && in_array($item_id, $session_list_restricted_folders_for_items[$data['id_tree']]) === false
2241
        ) {
2242
            return 'ERR_FOLDER_NOT_ALLOWED';
2243
        }
2244
    }
2245
2246
    return true;
2247
}
2248
2249
/**
2250
 * Creates a unique key.
2251
 *
2252
 * @param int $lenght Key lenght
2253
 *
2254
 * @return string
2255
 */
2256
function uniqidReal(int $lenght = 13): string
2257
{
2258
    if (function_exists('random_bytes')) {
2259
        $bytes = random_bytes(intval(ceil($lenght / 2)));
2260
    } elseif (function_exists('openssl_random_pseudo_bytes')) {
2261
        $bytes = openssl_random_pseudo_bytes(intval(ceil($lenght / 2)));
2262
    } else {
2263
        throw new Exception('no cryptographically secure random function available');
2264
    }
2265
2266
    return substr(bin2hex($bytes), 0, $lenght);
2267
}
2268
2269
/**
2270
 * Obfuscate an email.
2271
 *
2272
 * @param string $email Email address
2273
 *
2274
 * @return string
2275
 */
2276
function obfuscateEmail(string $email): string
2277
{
2278
    $email = explode("@", $email);
2279
    $name = $email[0];
2280
    if (strlen($name) > 3) {
2281
        $name = substr($name, 0, 2);
2282
        for ($i = 0; $i < strlen($email[0]) - 3; $i++) {
2283
            $name .= "*";
2284
        }
2285
        $name .= substr($email[0], -1, 1);
2286
    }
2287
    $host = explode(".", $email[1])[0];
2288
    if (strlen($host) > 3) {
2289
        $host = substr($host, 0, 1);
2290
        for ($i = 0; $i < strlen(explode(".", $email[1])[0]) - 2; $i++) {
2291
            $host .= "*";
2292
        }
2293
        $host .= substr(explode(".", $email[1])[0], -1, 1);
2294
    }
2295
    $email = $name . "@" . $host . "." . explode(".", $email[1])[1];
2296
    return $email;
2297
}
2298
2299
/**
2300
 * Perform a Query.
2301
 *
2302
 * @param array  $SETTINGS Teamapss settings
2303
 * @param string $fields   Fields to use
2304
 * @param string $table    Table to use
2305
 *
2306
 * @return array
2307
 */
2308
function performDBQuery(array $SETTINGS, string $fields, string $table): array
2309
{
2310
    // include librairies & connect to DB
2311
    //include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
2312
2313
    // Load class DB
2314
    loadClasses('DB');
2315
    
2316
    // Insert log in DB
2317
    return DB::query(
2318
        'SELECT ' . $fields . '
2319
        FROM ' . prefixTable($table)
2320
    );
2321
}
2322
2323
/**
2324
 * Undocumented function.
2325
 *
2326
 * @param int $bytes Size of file
2327
 *
2328
 * @return string
2329
 */
2330
function formatSizeUnits(int $bytes): string
2331
{
2332
    if ($bytes >= 1073741824) {
2333
        $bytes = number_format($bytes / 1073741824, 2) . ' GB';
2334
    } elseif ($bytes >= 1048576) {
2335
        $bytes = number_format($bytes / 1048576, 2) . ' MB';
2336
    } elseif ($bytes >= 1024) {
2337
        $bytes = number_format($bytes / 1024, 2) . ' KB';
2338
    } elseif ($bytes > 1) {
2339
        $bytes .= ' bytes';
2340
    } elseif ($bytes === 1) {
2341
        $bytes .= ' byte';
2342
    } else {
2343
        $bytes = '0 bytes';
2344
    }
2345
2346
    return $bytes;
2347
}
2348
2349
/**
2350
 * Generate user pair of keys.
2351
 *
2352
 * @param string $userPwd User password
2353
 *
2354
 * @return array
2355
 */
2356
function generateUserKeys(string $userPwd): array
2357
{
2358
    // Sanitize
2359
    $antiXss = new AntiXSS();
2360
    $userPwd = $antiXss->xss_clean($userPwd);
2361
    // Load classes
2362
    $rsa = new Crypt_RSA();
2363
    $cipher = new Crypt_AES();
2364
    // Create the private and public key
2365
    $res = $rsa->createKey(4096);
2366
    // Encrypt the privatekey
2367
    $cipher->setPassword($userPwd);
2368
    $privatekey = $cipher->encrypt($res['privatekey']);
2369
    return [
2370
        'private_key' => base64_encode($privatekey),
2371
        'public_key' => base64_encode($res['publickey']),
2372
        'private_key_clear' => base64_encode($res['privatekey']),
2373
    ];
2374
}
2375
2376
/**
2377
 * Permits to decrypt the user's privatekey.
2378
 *
2379
 * @param string $userPwd        User password
2380
 * @param string $userPrivateKey User private key
2381
 *
2382
 * @return string|object
2383
 */
2384
function decryptPrivateKey(string $userPwd, string $userPrivateKey)
2385
{
2386
    // Sanitize
2387
    $antiXss = new AntiXSS();
2388
    $userPwd = $antiXss->xss_clean($userPwd);
2389
    $userPrivateKey = $antiXss->xss_clean($userPrivateKey);
2390
2391
    if (empty($userPwd) === false) {
2392
        // Load classes
2393
        $cipher = new Crypt_AES();
2394
        // Encrypt the privatekey
2395
        $cipher->setPassword($userPwd);
2396
        try {
2397
            return base64_encode((string) $cipher->decrypt(base64_decode($userPrivateKey)));
2398
        } catch (Exception $e) {
2399
            return $e;
2400
        }
2401
    }
2402
    return '';
2403
}
2404
2405
/**
2406
 * Permits to encrypt the user's privatekey.
2407
 *
2408
 * @param string $userPwd        User password
2409
 * @param string $userPrivateKey User private key
2410
 *
2411
 * @return string
2412
 */
2413
function encryptPrivateKey(string $userPwd, string $userPrivateKey): string
2414
{
2415
    // Sanitize
2416
    $antiXss = new AntiXSS();
2417
    $userPwd = $antiXss->xss_clean($userPwd);
2418
    $userPrivateKey = $antiXss->xss_clean($userPrivateKey);
2419
2420
    if (empty($userPwd) === false) {
2421
        // Load classes
2422
        $cipher = new Crypt_AES();
2423
        // Encrypt the privatekey
2424
        $cipher->setPassword($userPwd);        
2425
        try {
2426
            return base64_encode($cipher->encrypt(base64_decode($userPrivateKey)));
2427
        } catch (Exception $e) {
2428
            return $e;
2429
        }
2430
    }
2431
    return '';
2432
}
2433
2434
/**
2435
 * Encrypts a string using AES.
2436
 *
2437
 * @param string $data String to encrypt
2438
 * @param string $key
2439
 *
2440
 * @return array
2441
 */
2442
function doDataEncryption(string $data, string $key = NULL): array
2443
{
2444
    // Sanitize
2445
    $antiXss = new AntiXSS();
2446
    $data = $antiXss->xss_clean($data);
2447
    
2448
    // Load classes
2449
    $cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
2450
    // Generate an object key
2451
    $objectKey = is_null($key) === true ? uniqidReal(KEY_LENGTH) : $antiXss->xss_clean($key);
2452
    // Set it as password
2453
    $cipher->setPassword($objectKey);
2454
    return [
2455
        'encrypted' => base64_encode($cipher->encrypt($data)),
2456
        'objectKey' => base64_encode($objectKey),
2457
    ];
2458
}
2459
2460
/**
2461
 * Decrypts a string using AES.
2462
 *
2463
 * @param string $data Encrypted data
2464
 * @param string $key  Key to uncrypt
2465
 *
2466
 * @return string
2467
 */
2468
function doDataDecryption(string $data, string $key): string
2469
{
2470
    // Sanitize
2471
    $antiXss = new AntiXSS();
2472
    $data = $antiXss->xss_clean($data);
2473
    $key = $antiXss->xss_clean($key);
2474
2475
    // Load classes
2476
    $cipher = new Crypt_AES();
2477
    // Set the object key
2478
    $cipher->setPassword(base64_decode($key));
2479
    return base64_encode((string) $cipher->decrypt(base64_decode($data)));
2480
}
2481
2482
/**
2483
 * Encrypts using RSA a string using a public key.
2484
 *
2485
 * @param string $key       Key to be encrypted
2486
 * @param string $publicKey User public key
2487
 *
2488
 * @return string
2489
 */
2490
function encryptUserObjectKey(string $key, string $publicKey): string
2491
{
2492
    // Sanitize
2493
    $antiXss = new AntiXSS();
2494
    $publicKey = $antiXss->xss_clean($publicKey);
2495
    // Load classes
2496
    $rsa = new Crypt_RSA();
2497
    // Load the public key
2498
    $decodedPublicKey = base64_decode($publicKey, true);
2499
    if ($decodedPublicKey === false) {
2500
        throw new InvalidArgumentException("Error while decoding key.");
2501
    }
2502
    $rsa->loadKey($decodedPublicKey);
2503
    // Encrypt
2504
    $encrypted = $rsa->encrypt(base64_decode($key));
2505
    if ($encrypted === false) {
0 ignored issues
show
introduced by
The condition $encrypted === false is always false.
Loading history...
2506
        throw new RuntimeException("Error while encrypting key.");
2507
    }
2508
    // Return
2509
    return base64_encode($encrypted);
2510
}
2511
2512
/**
2513
 * Decrypts using RSA an encrypted string using a private key.
2514
 *
2515
 * @param string $key        Encrypted key
2516
 * @param string $privateKey User private key
2517
 *
2518
 * @return string
2519
 */
2520
function decryptUserObjectKey(string $key, string $privateKey): string
2521
{
2522
    // Sanitize
2523
    $antiXss = new AntiXSS();
2524
    $privateKey = $antiXss->xss_clean($privateKey);
2525
2526
    // Load classes
2527
    $rsa = new Crypt_RSA();
2528
    // Load the private key
2529
    $decodedPrivateKey = base64_decode($privateKey, true);
2530
    if ($decodedPrivateKey === false) {
2531
        throw new InvalidArgumentException("Error while decoding private key.");
2532
    }
2533
2534
    $rsa->loadKey($decodedPrivateKey);
2535
2536
    // Decrypt
2537
    try {
2538
        $decodedKey = base64_decode($key, true);
2539
        if ($decodedKey === false) {
2540
            throw new InvalidArgumentException("Error while decoding key.");
2541
        }
2542
2543
        $tmpValue = $rsa->decrypt($decodedKey);
2544
        if ($tmpValue !== false) {
0 ignored issues
show
introduced by
The condition $tmpValue !== false is always true.
Loading history...
2545
            return base64_encode($tmpValue);
2546
        } else {
2547
            return '';
2548
        }
2549
    } catch (Exception $e) {
2550
        error_log('TEAMPASS Error - ldap - '.$e->getMessage());
2551
        return 'Exception: could not decrypt object';
2552
    }
2553
}
2554
2555
/**
2556
 * Encrypts a file.
2557
 *
2558
 * @param string $fileInName File name
2559
 * @param string $fileInPath Path to file
2560
 *
2561
 * @return array
2562
 */
2563
function encryptFile(string $fileInName, string $fileInPath): array
2564
{
2565
    if (defined('FILE_BUFFER_SIZE') === false) {
2566
        define('FILE_BUFFER_SIZE', 128 * 1024);
2567
    }
2568
2569
    // Load classes
2570
    $cipher = new Crypt_AES();
2571
2572
    // Generate an object key
2573
    $objectKey = uniqidReal(32);
2574
    // Set it as password
2575
    $cipher->setPassword($objectKey);
2576
    // Prevent against out of memory
2577
    $cipher->enableContinuousBuffer();
2578
2579
    // Encrypt the file content
2580
    $filePath = filter_var($fileInPath . '/' . $fileInName, FILTER_SANITIZE_URL);
2581
    $fileContent = file_get_contents($filePath);
2582
    $plaintext = $fileContent;
2583
    $ciphertext = $cipher->encrypt($plaintext);
2584
2585
    // Save new file
2586
    // deepcode ignore InsecureHash: is simply used to get a unique name
2587
    $hash = md5($plaintext);
2588
    $fileOut = $fileInPath . '/' . TP_FILE_PREFIX . $hash;
2589
    file_put_contents($fileOut, $ciphertext);
2590
    unlink($fileInPath . '/' . $fileInName);
2591
    return [
2592
        'fileHash' => base64_encode($hash),
2593
        'objectKey' => base64_encode($objectKey),
2594
    ];
2595
}
2596
2597
/**
2598
 * Decrypt a file.
2599
 *
2600
 * @param string $fileName File name
2601
 * @param string $filePath Path to file
2602
 * @param string $key      Key to use
2603
 *
2604
 * @return string
2605
 */
2606
function decryptFile(string $fileName, string $filePath, string $key): string
2607
{
2608
    if (! defined('FILE_BUFFER_SIZE')) {
2609
        define('FILE_BUFFER_SIZE', 128 * 1024);
2610
    }
2611
    
2612
    // Load classes
2613
    $cipher = new Crypt_AES();
2614
    $antiXSS = new AntiXSS();
2615
    
2616
    // Get file name
2617
    $safeFileName = $antiXSS->xss_clean(base64_decode($fileName));
2618
2619
    // Set the object key
2620
    $cipher->setPassword(base64_decode($key));
2621
    // Prevent against out of memory
2622
    $cipher->enableContinuousBuffer();
2623
    $cipher->disablePadding();
2624
    // Get file content
2625
    $safeFilePath = realpath($filePath . '/' . TP_FILE_PREFIX . $safeFileName);
2626
    $ciphertext = file_get_contents(filter_var($safeFilePath, FILTER_SANITIZE_URL));
2627
2628
    if (WIP) error_log('DEBUG: File image url -> '.filter_var($safeFilePath, FILTER_SANITIZE_URL));
2629
2630
    // Decrypt file content and return
2631
    return base64_encode($cipher->decrypt($ciphertext));
2632
}
2633
2634
/**
2635
 * Generate a simple password
2636
 *
2637
 * @param int $length Length of string
2638
 * @param bool $symbolsincluded Allow symbols
2639
 *
2640
 * @return string
2641
 */
2642
function generateQuickPassword(int $length = 16, bool $symbolsincluded = true): string
2643
{
2644
    // Generate new user password
2645
    $small_letters = range('a', 'z');
2646
    $big_letters = range('A', 'Z');
2647
    $digits = range(0, 9);
2648
    $symbols = $symbolsincluded === true ?
2649
        ['#', '_', '-', '@', '$', '+', '!'] : [];
2650
    $res = array_merge($small_letters, $big_letters, $digits, $symbols);
2651
    $count = count($res);
2652
    // first variant
2653
2654
    $random_string = '';
2655
    for ($i = 0; $i < $length; ++$i) {
2656
        $random_string .= $res[random_int(0, $count - 1)];
2657
    }
2658
2659
    return $random_string;
2660
}
2661
2662
/**
2663
 * Permit to store the sharekey of an object for users.
2664
 *
2665
 * @param string $object_name             Type for table selection
2666
 * @param int    $post_folder_is_personal Personal
2667
 * @param int    $post_folder_id          Folder
2668
 * @param int    $post_object_id          Object
2669
 * @param string $objectKey               Object key
2670
 * @param array  $SETTINGS                Teampass settings
2671
 * @param int    $user_id                 User ID if needed
2672
 * @param bool   $onlyForUser                 User ID if needed
2673
 * @param bool   $deleteAll                 User ID if needed
2674
 * @param array  $objectKeyArray                 User ID if needed
2675
 * @param int    $all_users_except_id                 User ID if needed
2676
 * @param int   $apiUserId                 User ID if needed
2677
 *
2678
 * @return void
2679
 */
2680
function storeUsersShareKey(
2681
    string $object_name,
2682
    int $post_folder_is_personal,
2683
    int $post_folder_id,
2684
    int $post_object_id,
2685
    string $objectKey,
2686
    bool $onlyForUser = false,
2687
    bool $deleteAll = true,
2688
    array $objectKeyArray = [],
2689
    int $all_users_except_id = -1,
2690
    int $apiUserId = -1
2691
): void {
2692
    
2693
    $session = SessionManager::getSession();
2694
    loadClasses('DB');
2695
2696
    // Delete existing entries for this object
2697
    if ($deleteAll === true) {
2698
        DB::delete(
2699
            $object_name,
2700
            'object_id = %i',
2701
            $post_object_id
2702
        );
2703
    }
2704
    
2705
    if (
2706
        $onlyForUser === true || (int) $post_folder_is_personal === 1
2707
    ) {
2708
        // Only create the sharekey for a user
2709
        $user = DB::queryFirstRow(
2710
            'SELECT public_key
2711
            FROM ' . prefixTable('users') . '
2712
            WHERE id = ' . ($apiUserId === -1 ? (int) $session->get('user-id') : $apiUserId) . '
2713
            AND public_key != ""'
2714
        );
2715
2716
        if (empty($objectKey) === false) {
2717
            DB::insert(
2718
                $object_name,
2719
                [
2720
                    'object_id' => (int) $post_object_id,
2721
                    'user_id' => (int) ($apiUserId === -1 ? (int) $session->get('user-id') : $apiUserId),
2722
                    'share_key' => encryptUserObjectKey(
2723
                        $objectKey,
2724
                        $user['public_key']
2725
                    ),
2726
                ]
2727
            );
2728
        } else if (count($objectKeyArray) > 0) {
2729
            foreach ($objectKeyArray as $object) {
2730
                DB::insert(
2731
                    $object_name,
2732
                    [
2733
                        'object_id' => (int) $object['objectId'],
2734
                        'user_id' => (int) ($apiUserId === -1 ? (int) $session->get('user-id') : $apiUserId),
2735
                        'share_key' => encryptUserObjectKey(
2736
                            $object['objectKey'],
2737
                            $user['public_key']
2738
                        ),
2739
                    ]
2740
                );
2741
            }
2742
        }
2743
    } else {
2744
        // Create sharekey for each user
2745
        //error_log('Building QUERY - all_users_except_id: '. $all_users_except_id);
2746
        //DB::debugmode(true);
2747
        $users = DB::query(
2748
            'SELECT id, public_key
2749
            FROM ' . prefixTable('users') . '
2750
            WHERE ' . ($onlyForUser === true ? 
0 ignored issues
show
introduced by
The condition $onlyForUser === true is always false.
Loading history...
2751
                'id IN ("' . TP_USER_ID . '","' . ($apiUserId === -1 ? (int) $session->get('user-id') : $apiUserId) . '") ' : 
2752
                'id NOT IN ("' . OTV_USER_ID . '","' . SSH_USER_ID . '","' . API_USER_ID . '"'.($all_users_except_id === -1 ? '' : ', "'.$all_users_except_id.'"').') ') . '
2753
            AND public_key != ""'
2754
        );
2755
        //DB::debugmode(false);
2756
        foreach ($users as $user) {
2757
            // Insert in DB the new object key for this item by user
2758
            if (count($objectKeyArray) === 0) {
2759
                if (WIP === true) error_log('TEAMPASS Debug - storeUsersShareKey case1 - ' . $object_name . ' - ' . $post_object_id . ' - ' . $user['id'] . ' - ' . $objectKey);
2760
                DB::insert(
2761
                    $object_name,
2762
                    [
2763
                        'object_id' => $post_object_id,
2764
                        'user_id' => (int) $user['id'],
2765
                        'share_key' => encryptUserObjectKey(
2766
                            $objectKey,
2767
                            $user['public_key']
2768
                        ),
2769
                    ]
2770
                );
2771
            } else {
2772
                foreach ($objectKeyArray as $object) {
2773
                    if (WIP === true) error_log('TEAMPASS Debug - storeUsersShareKey case2 - ' . $object_name . ' - ' . $object['objectId'] . ' - ' . $user['id'] . ' - ' . $object['objectKey']);
2774
                    DB::insert(
2775
                        $object_name,
2776
                        [
2777
                            'object_id' => (int) $object['objectId'],
2778
                            'user_id' => (int) $user['id'],
2779
                            'share_key' => encryptUserObjectKey(
2780
                                $object['objectKey'],
2781
                                $user['public_key']
2782
                            ),
2783
                        ]
2784
                    );
2785
                }
2786
            }
2787
        }
2788
    }
2789
}
2790
2791
/**
2792
 * Is this string base64 encoded?
2793
 *
2794
 * @param string $str Encoded string?
2795
 *
2796
 * @return bool
2797
 */
2798
function isBase64(string $str): bool
2799
{
2800
    $str = (string) trim($str);
2801
    if (! isset($str[0])) {
2802
        return false;
2803
    }
2804
2805
    $base64String = (string) base64_decode($str, true);
2806
    if ($base64String && base64_encode($base64String) === $str) {
2807
        return true;
2808
    }
2809
2810
    return false;
2811
}
2812
2813
/**
2814
 * Undocumented function
2815
 *
2816
 * @param string $field Parameter
2817
 *
2818
 * @return array|bool|resource|string
2819
 */
2820
function filterString(string $field)
2821
{
2822
    // Sanitize string
2823
    $field = filter_var(trim($field), FILTER_SANITIZE_FULL_SPECIAL_CHARS);
2824
    if (empty($field) === false) {
2825
        // Load AntiXSS
2826
        $antiXss = new AntiXSS();
2827
        // Return
2828
        return $antiXss->xss_clean($field);
2829
    }
2830
2831
    return false;
2832
}
2833
2834
/**
2835
 * CHeck if provided credentials are allowed on server
2836
 *
2837
 * @param string $login    User Login
2838
 * @param string $password User Pwd
2839
 * @param array  $SETTINGS Teampass settings
2840
 *
2841
 * @return bool
2842
 */
2843
function ldapCheckUserPassword(string $login, string $password, array $SETTINGS): bool
2844
{
2845
    // Build ldap configuration array
2846
    $config = [
2847
        // Mandatory Configuration Options
2848
        'hosts' => [$SETTINGS['ldap_hosts']],
2849
        'base_dn' => $SETTINGS['ldap_bdn'],
2850
        'username' => $SETTINGS['ldap_username'],
2851
        'password' => $SETTINGS['ldap_password'],
2852
2853
        // Optional Configuration Options
2854
        'port' => $SETTINGS['ldap_port'],
2855
        'use_ssl' => (int) $SETTINGS['ldap_ssl'] === 1 ? true : false,
2856
        'use_tls' => (int) $SETTINGS['ldap_tls'] === 1 ? true : false,
2857
        'version' => 3,
2858
        'timeout' => 5,
2859
        'follow_referrals' => false,
2860
2861
        // Custom LDAP Options
2862
        'options' => [
2863
            // See: http://php.net/ldap_set_option
2864
            LDAP_OPT_X_TLS_REQUIRE_CERT => (isset($SETTINGS['ldap_tls_certiface_check']) ? $SETTINGS['ldap_tls_certiface_check'] : LDAP_OPT_X_TLS_HARD),
2865
        ],
2866
    ];
2867
    
2868
    $connection = new Connection($config);
2869
    // Connect to LDAP
2870
    try {
2871
        $connection->connect();
2872
    } catch (\LdapRecord\Auth\BindException $e) {
2873
        $error = $e->getDetailedError();
2874
        if ($error) {
2875
            error_log('TEAMPASS Error - LDAP - '.$error->getErrorCode()." - ".$error->getErrorMessage(). " - ".$error->getDiagnosticMessage());
2876
        } else {
2877
            error_log('TEAMPASS Error - LDAP - Code: '.$e->getCode().' - Message: '.$e->getMessage());
2878
        }
2879
        // deepcode ignore ServerLeak: No important data is sent
2880
        echo 'An error occurred.';
2881
        return false;
2882
    }
2883
2884
    // Authenticate user
2885
    try {
2886
        if ($SETTINGS['ldap_type'] === 'ActiveDirectory') {
2887
            $connection->auth()->attempt($login, $password, $stayAuthenticated = true);
2888
        } else {
2889
            $connection->auth()->attempt($SETTINGS['ldap_user_attribute'].'='.$login.','.(isset($SETTINGS['ldap_dn_additional_user_dn']) && !empty($SETTINGS['ldap_dn_additional_user_dn']) ? $SETTINGS['ldap_dn_additional_user_dn'].',' : '').$SETTINGS['ldap_bdn'], $password, $stayAuthenticated = true);
2890
        }
2891
    } catch (\LdapRecord\Auth\BindException $e) {
2892
        $error = $e->getDetailedError();
2893
        if ($error) {
2894
            error_log('TEAMPASS Error - LDAP - '.$error->getErrorCode()." - ".$error->getErrorMessage(). " - ".$error->getDiagnosticMessage());
2895
        } else {
2896
            error_log('TEAMPASS Error - LDAP - Code: '.$e->getCode().' - Message: '.$e->getMessage());
2897
        }
2898
        // deepcode ignore ServerLeak: No important data is sent
2899
        echo 'An error occurred.';
2900
        return false;
2901
    }
2902
2903
    return true;
2904
}
2905
2906
/**
2907
 * Removes from DB all sharekeys of this user
2908
 *
2909
 * @param int $userId User's id
2910
 * @param array   $SETTINGS Teampass settings
2911
 *
2912
 * @return bool
2913
 */
2914
function deleteUserObjetsKeys(int $userId, array $SETTINGS = []): bool
2915
{
2916
    // Load class DB
2917
    loadClasses('DB');
2918
2919
    // Remove all item sharekeys items
2920
    // expect if personal item
2921
    DB::delete(
2922
        prefixTable('sharekeys_items'),
2923
        'user_id = %i AND object_id NOT IN (SELECT i.id FROM ' . prefixTable('items') . ' AS i WHERE i.perso = 1)',
2924
        $userId
2925
    );
2926
    // Remove all item sharekeys files
2927
    DB::delete(
2928
        prefixTable('sharekeys_files'),
2929
        'user_id = %i AND object_id NOT IN (
2930
            SELECT f.id 
2931
            FROM ' . prefixTable('items') . ' AS i 
2932
            INNER JOIN ' . prefixTable('files') . ' AS f ON f.id_item = i.id
2933
            WHERE i.perso = 1
2934
        )',
2935
        $userId
2936
    );
2937
    // Remove all item sharekeys fields
2938
    DB::delete(
2939
        prefixTable('sharekeys_fields'),
2940
        'user_id = %i AND object_id NOT IN (
2941
            SELECT c.id 
2942
            FROM ' . prefixTable('items') . ' AS i 
2943
            INNER JOIN ' . prefixTable('categories_items') . ' AS c ON c.item_id = i.id
2944
            WHERE i.perso = 1
2945
        )',
2946
        $userId
2947
    );
2948
    // Remove all item sharekeys logs
2949
    DB::delete(
2950
        prefixTable('sharekeys_logs'),
2951
        'user_id = %i AND object_id NOT IN (SELECT i.id FROM ' . prefixTable('items') . ' AS i WHERE i.perso = 1)',
2952
        $userId
2953
    );
2954
    // Remove all item sharekeys suggestions
2955
    DB::delete(
2956
        prefixTable('sharekeys_suggestions'),
2957
        'user_id = %i AND object_id NOT IN (SELECT i.id FROM ' . prefixTable('items') . ' AS i WHERE i.perso = 1)',
2958
        $userId
2959
    );
2960
    return false;
2961
}
2962
2963
/**
2964
 * Manage list of timezones   $SETTINGS Teampass settings
2965
 *
2966
 * @return array
2967
 */
2968
function timezone_list()
2969
{
2970
    static $timezones = null;
2971
    if ($timezones === null) {
2972
        $timezones = [];
2973
        $offsets = [];
2974
        $now = new DateTime('now', new DateTimeZone('UTC'));
2975
        foreach (DateTimeZone::listIdentifiers() as $timezone) {
2976
            $now->setTimezone(new DateTimeZone($timezone));
2977
            $offsets[] = $offset = $now->getOffset();
2978
            $timezones[$timezone] = '(' . format_GMT_offset($offset) . ') ' . format_timezone_name($timezone);
2979
        }
2980
2981
        array_multisort($offsets, $timezones);
2982
    }
2983
2984
    return $timezones;
2985
}
2986
2987
/**
2988
 * Provide timezone offset
2989
 *
2990
 * @param int $offset Timezone offset
2991
 *
2992
 * @return string
2993
 */
2994
function format_GMT_offset($offset): string
2995
{
2996
    $hours = intval($offset / 3600);
2997
    $minutes = abs(intval($offset % 3600 / 60));
2998
    return 'GMT' . ($offset ? sprintf('%+03d:%02d', $hours, $minutes) : '');
2999
}
3000
3001
/**
3002
 * Provides timezone name
3003
 *
3004
 * @param string $name Timezone name
3005
 *
3006
 * @return string
3007
 */
3008
function format_timezone_name($name): string
3009
{
3010
    $name = str_replace('/', ', ', $name);
3011
    $name = str_replace('_', ' ', $name);
3012
3013
    return str_replace('St ', 'St. ', $name);
3014
}
3015
3016
/**
3017
 * Provides info if user should use MFA based on roles
3018
 *
3019
 * @param string $userRolesIds  User roles ids
3020
 * @param string $mfaRoles      Roles for which MFA is requested
3021
 *
3022
 * @return bool
3023
 */
3024
function mfa_auth_requested_roles(string $userRolesIds, string $mfaRoles): bool
3025
{
3026
    if (empty($mfaRoles) === true) {
3027
        return true;
3028
    }
3029
3030
    $mfaRoles = array_values(json_decode($mfaRoles, true));
3031
    $userRolesIds = array_filter(explode(';', $userRolesIds));
3032
    if (count($mfaRoles) === 0 || count(array_intersect($mfaRoles, $userRolesIds)) > 0) {
3033
        return true;
3034
    }
3035
3036
    return false;
3037
}
3038
3039
/**
3040
 * Permits to clean a string for export purpose
3041
 *
3042
 * @param string $text
3043
 * @param bool $emptyCheckOnly
3044
 * 
3045
 * @return string
3046
 */
3047
function cleanStringForExport(string $text, bool $emptyCheckOnly = false): string
3048
{
3049
    if (is_null($text) === true || empty($text) === true) {
3050
        return '';
3051
    }
3052
    // only expected to check if $text was empty
3053
    elseif ($emptyCheckOnly === true) {
3054
        return $text;
3055
    }
3056
3057
    return strip_tags(
3058
        cleanString(
3059
            html_entity_decode($text, ENT_QUOTES | ENT_XHTML, 'UTF-8'),
3060
            true)
3061
        );
3062
}
3063
3064
/**
3065
 * Permits to check if user ID is valid
3066
 *
3067
 * @param integer $post_user_id
3068
 * @return bool
3069
 */
3070
function isUserIdValid($userId): bool
3071
{
3072
    if (is_null($userId) === false
3073
        && isset($userId) === true
3074
        && empty($userId) === false
3075
    ) {
3076
        return true;
3077
    }
3078
    return false;
3079
}
3080
3081
/**
3082
 * Check if a key exists and if its value equal the one expected
3083
 *
3084
 * @param string $key
3085
 * @param integer|string $value
3086
 * @param array $array
3087
 * 
3088
 * @return boolean
3089
 */
3090
function isKeyExistingAndEqual(
3091
    string $key,
3092
    /*PHP8 - integer|string*/$value,
3093
    array $array
3094
): bool
3095
{
3096
    if (isset($array[$key]) === true
3097
        && (is_int($value) === true ?
3098
            (int) $array[$key] === $value :
3099
            (string) $array[$key] === $value)
3100
    ) {
3101
        return true;
3102
    }
3103
    return false;
3104
}
3105
3106
/**
3107
 * Check if a variable is not set or equal to a value
3108
 *
3109
 * @param string|null $var
3110
 * @param integer|string $value
3111
 * 
3112
 * @return boolean
3113
 */
3114
function isKeyNotSetOrEqual(
3115
    /*PHP8 - string|null*/$var,
3116
    /*PHP8 - integer|string*/$value
3117
): bool
3118
{
3119
    if (isset($var) === false
3120
        || (is_int($value) === true ?
3121
            (int) $var === $value :
3122
            (string) $var === $value)
3123
    ) {
3124
        return true;
3125
    }
3126
    return false;
3127
}
3128
3129
/**
3130
 * Check if a key exists and if its value < to the one expected
3131
 *
3132
 * @param string $key
3133
 * @param integer $value
3134
 * @param array $array
3135
 * 
3136
 * @return boolean
3137
 */
3138
function isKeyExistingAndInferior(string $key, int $value, array $array): bool
3139
{
3140
    if (isset($array[$key]) === true && (int) $array[$key] < $value) {
3141
        return true;
3142
    }
3143
    return false;
3144
}
3145
3146
/**
3147
 * Check if a key exists and if its value > to the one expected
3148
 *
3149
 * @param string $key
3150
 * @param integer $value
3151
 * @param array $array
3152
 * 
3153
 * @return boolean
3154
 */
3155
function isKeyExistingAndSuperior(string $key, int $value, array $array): bool
3156
{
3157
    if (isset($array[$key]) === true && (int) $array[$key] > $value) {
3158
        return true;
3159
    }
3160
    return false;
3161
}
3162
3163
/**
3164
 * Check if values in array are set
3165
 * Return true if all set
3166
 * Return false if one of them is not set
3167
 *
3168
 * @param array $arrayOfValues
3169
 * @return boolean
3170
 */
3171
function isSetArrayOfValues(array $arrayOfValues): bool
3172
{
3173
    foreach($arrayOfValues as $value) {
3174
        if (isset($value) === false) {
3175
            return false;
3176
        }
3177
    }
3178
    return true;
3179
}
3180
3181
/**
3182
 * Check if values in array are set
3183
 * Return true if all set
3184
 * Return false if one of them is not set
3185
 *
3186
 * @param array $arrayOfValues
3187
 * @param integer|string $value
3188
 * @return boolean
3189
 */
3190
function isArrayOfVarsEqualToValue(
3191
    array $arrayOfVars,
3192
    /*PHP8 - integer|string*/$value
3193
) : bool
3194
{
3195
    foreach($arrayOfVars as $variable) {
3196
        if ($variable !== $value) {
3197
            return false;
3198
        }
3199
    }
3200
    return true;
3201
}
3202
3203
/**
3204
 * Checks if at least one variable in array is equal to value
3205
 *
3206
 * @param array $arrayOfValues
3207
 * @param integer|string $value
3208
 * @return boolean
3209
 */
3210
function isOneVarOfArrayEqualToValue(
3211
    array $arrayOfVars,
3212
    /*PHP8 - integer|string*/$value
3213
) : bool
3214
{
3215
    foreach($arrayOfVars as $variable) {
3216
        if ($variable === $value) {
3217
            return true;
3218
        }
3219
    }
3220
    return false;
3221
}
3222
3223
/**
3224
 * Checks is value is null, not set OR empty
3225
 *
3226
 * @param string|int|null $value
3227
 * @return boolean
3228
 */
3229
function isValueSetNullEmpty(/*PHP8 - string|int|null*/ $value) : bool
3230
{
3231
    if (is_null($value) === true || isset($value) === false || empty($value) === true) {
3232
        return true;
3233
    }
3234
    return false;
3235
}
3236
3237
/**
3238
 * Checks if value is set and if empty is equal to passed boolean
3239
 *
3240
 * @param string|int $value
3241
 * @param boolean $boolean
3242
 * @return boolean
3243
 */
3244
function isValueSetEmpty($value, $boolean = true) : bool
3245
{
3246
    if (isset($value) === true && empty($value) === $boolean) {
3247
        return true;
3248
    }
3249
    return false;
3250
}
3251
3252
/**
3253
 * Ensure Complexity is translated
3254
 *
3255
 * @return void
3256
 */
3257
function defineComplexity() : void
3258
{
3259
    // Load user's language
3260
    $session = SessionManager::getSession();
3261
    $lang = new Language($session->get('user-language') ?? 'english');
3262
    
3263
    if (defined('TP_PW_COMPLEXITY') === false) {
3264
        define(
3265
            'TP_PW_COMPLEXITY',
3266
            [
3267
                TP_PW_STRENGTH_1 => array(TP_PW_STRENGTH_1, $lang->get('complex_level1'), 'fas fa-thermometer-empty text-danger'),
3268
                TP_PW_STRENGTH_2 => array(TP_PW_STRENGTH_2, $lang->get('complex_level2'), 'fas fa-thermometer-quarter text-warning'),
3269
                TP_PW_STRENGTH_3 => array(TP_PW_STRENGTH_3, $lang->get('complex_level3'), 'fas fa-thermometer-half text-warning'),
3270
                TP_PW_STRENGTH_4 => array(TP_PW_STRENGTH_4, $lang->get('complex_level4'), 'fas fa-thermometer-three-quarters text-success'),
3271
                TP_PW_STRENGTH_5 => array(TP_PW_STRENGTH_5, $lang->get('complex_level5'), 'fas fa-thermometer-full text-success'),
3272
            ]
3273
        );
3274
    }
3275
}
3276
3277
/**
3278
 * Uses Sanitizer to perform data sanitization
3279
 *
3280
 * @param array     $data
3281
 * @param array     $filters
3282
 * @return array|string
3283
 */
3284
function dataSanitizer(array $data, array $filters): array|string
3285
{
3286
    // Load Sanitizer library
3287
    $sanitizer = new Sanitizer($data, $filters);
3288
3289
    // Load AntiXSS
3290
    $antiXss = new AntiXSS();
3291
3292
    // Sanitize post and get variables
3293
    return $antiXss->xss_clean($sanitizer->sanitize());
3294
}
3295
3296
/**
3297
 * Permits to manage the cache tree for a user
3298
 *
3299
 * @param integer $user_id
3300
 * @param string $data
3301
 * @param array $SETTINGS
3302
 * @param string $field_update
3303
 * @return void
3304
 */
3305
function cacheTreeUserHandler(int $user_id, string $data, array $SETTINGS, string $field_update = '')
3306
{
3307
    // Load class DB
3308
    loadClasses('DB');
3309
3310
    // Exists ?
3311
    $userCacheId = DB::queryfirstrow(
3312
        'SELECT increment_id
3313
        FROM ' . prefixTable('cache_tree') . '
3314
        WHERE user_id = %i',
3315
        $user_id
3316
    );
3317
    
3318
    if (is_null($userCacheId) === true || count($userCacheId) === 0) {
3319
        // insert in table
3320
        DB::insert(
3321
            prefixTable('cache_tree'),
3322
            array(
3323
                'data' => $data,
3324
                'timestamp' => time(),
3325
                'user_id' => $user_id,
3326
                'visible_folders' => '',
3327
            )
3328
        );
3329
    } else {
3330
        if (empty($field_update) === true) {
3331
            DB::update(
3332
                prefixTable('cache_tree'),
3333
                [
3334
                    'timestamp' => time(),
3335
                    'data' => $data,
3336
                ],
3337
                'increment_id = %i',
3338
                $userCacheId['increment_id']
3339
            );
3340
        /* USELESS
3341
        } else {
3342
            DB::update(
3343
                prefixTable('cache_tree'),
3344
                [
3345
                    $field_update => $data,
3346
                ],
3347
                'increment_id = %i',
3348
                $userCacheId['increment_id']
3349
            );*/
3350
        }
3351
    }
3352
}
3353
3354
/**
3355
 * Permits to calculate a %
3356
 *
3357
 * @param float $nombre
3358
 * @param float $total
3359
 * @param float $pourcentage
3360
 * @return float
3361
 */
3362
function pourcentage(float $nombre, float $total, float $pourcentage): float
3363
{ 
3364
    $resultat = ($nombre/$total) * $pourcentage;
3365
    return round($resultat);
3366
}
3367
3368
/**
3369
 * Load the folders list from the cache
3370
 *
3371
 * @param string $fieldName
3372
 * @param string $sessionName
3373
 * @param boolean $forceRefresh
3374
 * @return array
3375
 */
3376
function loadFoldersListByCache(
3377
    string $fieldName,
3378
    string $sessionName,
3379
    bool $forceRefresh = false
3380
): array
3381
{
3382
    // Case when refresh is EXPECTED / MANDATORY
3383
    if ($forceRefresh === true) {
3384
        return [
3385
            'state' => false,
3386
            'data' => [],
3387
        ];
3388
    }
3389
    
3390
    $session = SessionManager::getSession();
3391
3392
    // Get last folder update
3393
    $lastFolderChange = DB::queryfirstrow(
3394
        'SELECT valeur FROM ' . prefixTable('misc') . '
3395
        WHERE type = %s AND intitule = %s',
3396
        'timestamp',
3397
        'last_folder_change'
3398
    );
3399
    if (DB::count() === 0) {
3400
        $lastFolderChange['valeur'] = 0;
3401
    }
3402
3403
    // Case when an update in the tree has been done
3404
    // Refresh is then mandatory
3405
    if ((int) $lastFolderChange['valeur'] > (int) (null !== $session->get('user-tree_last_refresh_timestamp') ? $session->get('user-tree_last_refresh_timestamp') : 0)) {
3406
        return [
3407
            'state' => false,
3408
            'data' => [],
3409
        ];
3410
    }
3411
3412
    // Does this user has the tree structure in session?
3413
    // If yes then use it
3414
    if (count(null !== $session->get('user-folders_list') ? $session->get('user-folders_list') : []) > 0) {
3415
        return [
3416
            'state' => true,
3417
            'data' => json_encode($session->get('user-folders_list')[0]),
3418
            'extra' => 'to_be_parsed',
3419
        ];
3420
    }
3421
    
3422
    // Does this user has a tree cache
3423
    $userCacheTree = DB::queryfirstrow(
3424
        'SELECT '.$fieldName.'
3425
        FROM ' . prefixTable('cache_tree') . '
3426
        WHERE user_id = %i',
3427
        $session->get('user-id')
3428
    );
3429
    if (empty($userCacheTree[$fieldName]) === false && $userCacheTree[$fieldName] !== '[]') {
3430
        SessionManager::addRemoveFromSessionAssociativeArray(
3431
            'user-folders_list',
3432
            [$userCacheTree[$fieldName]],
3433
            'add'
3434
        );
3435
        return [
3436
            'state' => true,
3437
            'data' => $userCacheTree[$fieldName],
3438
            'extra' => '',
3439
        ];
3440
    }
3441
3442
    return [
3443
        'state' => false,
3444
        'data' => [],
3445
    ];
3446
}
3447
3448
3449
/**
3450
 * Permits to refresh the categories of folders
3451
 *
3452
 * @param array $folderIds
3453
 * @return void
3454
 */
3455
function handleFoldersCategories(
3456
    array $folderIds
3457
)
3458
{
3459
    // Load class DB
3460
    loadClasses('DB');
3461
3462
    $arr_data = array();
3463
3464
    // force full list of folders
3465
    if (count($folderIds) === 0) {
3466
        $folderIds = DB::queryFirstColumn(
3467
            'SELECT id
3468
            FROM ' . prefixTable('nested_tree') . '
3469
            WHERE personal_folder=%i',
3470
            0
3471
        );
3472
    }
3473
3474
    // Get complexity
3475
    defineComplexity();
3476
3477
    // update
3478
    foreach ($folderIds as $folder) {
3479
        // Do we have Categories
3480
        // get list of associated Categories
3481
        $arrCatList = array();
3482
        $rows_tmp = DB::query(
3483
            'SELECT c.id, c.title, c.level, c.type, c.masked, c.order, c.encrypted_data, c.role_visibility, c.is_mandatory,
3484
            f.id_category AS category_id
3485
            FROM ' . prefixTable('categories_folders') . ' AS f
3486
            INNER JOIN ' . prefixTable('categories') . ' AS c ON (f.id_category = c.parent_id)
3487
            WHERE id_folder=%i',
3488
            $folder
3489
        );
3490
        if (DB::count() > 0) {
3491
            foreach ($rows_tmp as $row) {
3492
                $arrCatList[$row['id']] = array(
3493
                    'id' => $row['id'],
3494
                    'title' => $row['title'],
3495
                    'level' => $row['level'],
3496
                    'type' => $row['type'],
3497
                    'masked' => $row['masked'],
3498
                    'order' => $row['order'],
3499
                    'encrypted_data' => $row['encrypted_data'],
3500
                    'role_visibility' => $row['role_visibility'],
3501
                    'is_mandatory' => $row['is_mandatory'],
3502
                    'category_id' => $row['category_id'],
3503
                );
3504
            }
3505
        }
3506
        $arr_data['categories'] = $arrCatList;
3507
3508
        // Now get complexity
3509
        $valTemp = '';
3510
        $data = DB::queryFirstRow(
3511
            'SELECT valeur
3512
            FROM ' . prefixTable('misc') . '
3513
            WHERE type = %s AND intitule=%i',
3514
            'complex',
3515
            $folder
3516
        );
3517
        if (DB::count() > 0 && empty($data['valeur']) === false) {
3518
            $valTemp = array(
3519
                'value' => $data['valeur'],
3520
                'text' => TP_PW_COMPLEXITY[$data['valeur']][1],
3521
            );
3522
        }
3523
        $arr_data['complexity'] = $valTemp;
3524
3525
        // Now get Roles
3526
        $valTemp = '';
3527
        $rows_tmp = DB::query(
3528
            'SELECT t.title
3529
            FROM ' . prefixTable('roles_values') . ' as v
3530
            INNER JOIN ' . prefixTable('roles_title') . ' as t ON (v.role_id = t.id)
3531
            WHERE v.folder_id = %i
3532
            GROUP BY title',
3533
            $folder
3534
        );
3535
        foreach ($rows_tmp as $record) {
3536
            $valTemp .= (empty($valTemp) === true ? '' : ' - ') . $record['title'];
3537
        }
3538
        $arr_data['visibilityRoles'] = $valTemp;
3539
3540
        // now save in DB
3541
        DB::update(
3542
            prefixTable('nested_tree'),
3543
            array(
3544
                'categories' => json_encode($arr_data),
3545
            ),
3546
            'id = %i',
3547
            $folder
3548
        );
3549
    }
3550
}
3551
3552
/**
3553
 * List all users that have specific roles
3554
 *
3555
 * @param array $roles
3556
 * @return array
3557
 */
3558
function getUsersWithRoles(
3559
    array $roles
3560
): array
3561
{
3562
    $session = SessionManager::getSession();
3563
    $arrUsers = array();
3564
3565
    foreach ($roles as $role) {
3566
        // loop on users and check if user has this role
3567
        $rows = DB::query(
3568
            'SELECT id, fonction_id
3569
            FROM ' . prefixTable('users') . '
3570
            WHERE id != %i AND admin = 0 AND fonction_id IS NOT NULL AND fonction_id != ""',
3571
            $session->get('user-id')
3572
        );
3573
        foreach ($rows as $user) {
3574
            $userRoles = is_null($user['fonction_id']) === false && empty($user['fonction_id']) === false ? explode(';', $user['fonction_id']) : [];
3575
            if (in_array($role, $userRoles, true) === true) {
3576
                array_push($arrUsers, $user['id']);
3577
            }
3578
        }
3579
    }
3580
3581
    return $arrUsers;
3582
}
3583
3584
3585
/**
3586
 * Get all users informations
3587
 *
3588
 * @param integer $userId
3589
 * @return array
3590
 */
3591
function getFullUserInfos(
3592
    int $userId
3593
): array
3594
{
3595
    if (empty($userId) === true) {
3596
        return array();
3597
    }
3598
3599
    $val = DB::queryfirstrow(
3600
        'SELECT *
3601
        FROM ' . prefixTable('users') . '
3602
        WHERE id = %i',
3603
        $userId
3604
    );
3605
3606
    return $val;
3607
}
3608
3609
/**
3610
 * Is required an upgrade
3611
 *
3612
 * @return boolean
3613
 */
3614
function upgradeRequired(): bool
3615
{
3616
    // Get settings.php
3617
    include_once __DIR__. '/../includes/config/settings.php';
3618
3619
    // Get timestamp in DB
3620
    $val = DB::queryfirstrow(
3621
        'SELECT valeur
3622
        FROM ' . prefixTable('misc') . '
3623
        WHERE type = %s AND intitule = %s',
3624
        'admin',
3625
        'upgrade_timestamp'
3626
    );
3627
    
3628
    // if not exists then error
3629
    if (is_null($val) === true || count($val) === 0 || defined('UPGRADE_MIN_DATE') === false) return true;
3630
3631
    // if empty or too old then error
3632
    if (empty($val['valeur']) === true || (int) $val['valeur'] < (int) UPGRADE_MIN_DATE) {
3633
        return true;
3634
    }
3635
3636
    return false;
3637
}
3638
3639
/**
3640
 * Permits to change the user keys on his demand
3641
 *
3642
 * @param integer $userId
3643
 * @param string $passwordClear
3644
 * @param integer $nbItemsToTreat
3645
 * @param string $encryptionKey
3646
 * @param boolean $deleteExistingKeys
3647
 * @param boolean $sendEmailToUser
3648
 * @param boolean $encryptWithUserPassword
3649
 * @param boolean $generate_user_new_password
3650
 * @param string $emailBody
3651
 * @param boolean $user_self_change
3652
 * @param string $recovery_public_key
3653
 * @param string $recovery_private_key
3654
 * @return string
3655
 */
3656
function handleUserKeys(
3657
    int $userId,
3658
    string $passwordClear,
3659
    int $nbItemsToTreat,
3660
    string $encryptionKey = '',
3661
    bool $deleteExistingKeys = false,
3662
    bool $sendEmailToUser = true,
3663
    bool $encryptWithUserPassword = false,
3664
    bool $generate_user_new_password = false,
3665
    string $emailBody = '',
3666
    bool $user_self_change = false,
3667
    string $recovery_public_key = '',
3668
    string $recovery_private_key = ''
3669
): string
3670
{
3671
    $session = SessionManager::getSession();
3672
    $lang = new Language($session->get('user-language') ?? 'english');
3673
3674
    // prepapre background tasks for item keys generation        
3675
    $userTP = DB::queryFirstRow(
3676
        'SELECT pw, public_key, private_key
3677
        FROM ' . prefixTable('users') . '
3678
        WHERE id = %i',
3679
        TP_USER_ID
3680
    );
3681
    if (DB::count() === 0) {
3682
        return prepareExchangedData(
3683
            array(
3684
                'error' => true,
3685
                'message' => 'User not exists',
3686
            ),
3687
            'encode'
3688
        );
3689
    }
3690
3691
    // Do we need to generate new user password
3692
    if ($generate_user_new_password === true) {
3693
        // Generate a new password
3694
        $passwordClear = GenerateCryptKey(20, false, true, true, false, true);
3695
    }
3696
3697
    // Create password hash
3698
    $passwordManager = new PasswordManager();
3699
    $hashedPassword = $passwordManager->hashPassword($passwordClear);
3700
    if ($passwordManager->verifyPassword($hashedPassword, $passwordClear) === false) {
3701
        return prepareExchangedData(
3702
            array(
3703
                'error' => true,
3704
                'message' => $lang->get('pw_hash_not_correct'),
3705
            ),
3706
            'encode'
3707
        );
3708
    }
3709
3710
    // Check if valid public/private keys
3711
    if ($recovery_public_key !== '' && $recovery_private_key !== '') {
3712
        try {
3713
            // Generate random string
3714
            $random_str = generateQuickPassword(12, false);
3715
            // Encrypt random string with user publick key
3716
            $encrypted = encryptUserObjectKey($random_str, $recovery_public_key);
3717
            // Decrypt $encrypted with private key
3718
            $decrypted = decryptUserObjectKey($encrypted, $recovery_private_key);
3719
            error_log('$random_str='.$random_str.' | $decrypted='.$decrypted);
3720
            // Check if decryptUserObjectKey returns our random string
3721
            if ($decrypted !== $random_str) {
3722
                throw new Exception('Public/Private keypair invalid.');
3723
            }
3724
        } catch (Exception $e) {
3725
            // Show error message to user and log event
3726
            error_log('ERROR: User '.$userId.' - '.$e->getMessage());
3727
    
3728
            return prepareExchangedData([
3729
                    'error' => true,
3730
                    'message' => $lang->get('pw_encryption_error'),
3731
                ],
3732
                'encode'
3733
            );
3734
        }
3735
    }
3736
    
3737
    // Generate new keys
3738
    if ($user_self_change === true && empty($recovery_public_key) === false && empty($recovery_private_key) === false){
3739
        $userKeys = [
3740
            'public_key' => $recovery_public_key,
3741
            'private_key_clear' => $recovery_private_key,
3742
            'private_key' => encryptPrivateKey($passwordClear, $recovery_private_key),
3743
        ];
3744
    } else {
3745
        $userKeys = generateUserKeys($passwordClear);
3746
    }
3747
3748
    // Save in DB
3749
    DB::update(
3750
        prefixTable('users'),
3751
        array(
3752
            'pw' => $hashedPassword,
3753
            'public_key' => $userKeys['public_key'],
3754
            'private_key' => $userKeys['private_key'],
3755
        ),
3756
        'id=%i',
3757
        $userId
3758
    );
3759
3760
    // update session too
3761
    if ($userId === $session->get('user-id')) {
3762
        $session->set('user-private_key', $userKeys['private_key_clear']);
3763
        $session->set('user-public_key', $userKeys['public_key']);
3764
    }
3765
3766
    // Manage empty encryption key
3767
    // Let's take the user's password if asked and if no encryption key provided
3768
    $encryptionKey = $encryptWithUserPassword === true && empty($encryptionKey) === true ? $passwordClear : $encryptionKey;
3769
3770
    // Create process
3771
    DB::insert(
3772
        prefixTable('background_tasks'),
3773
        array(
3774
            'created_at' => time(),
3775
            'process_type' => 'create_user_keys',
3776
            'arguments' => json_encode([
3777
                'new_user_id' => (int) $userId,
3778
                'new_user_pwd' => cryption($passwordClear, '','encrypt')['string'],
3779
                'new_user_code' => cryption(empty($encryptionKey) === true ? uniqidReal(20) : $encryptionKey, '','encrypt')['string'],
3780
                'owner_id' => (int) TP_USER_ID,
3781
                'creator_pwd' => $userTP['pw'],
3782
                'send_email' => $sendEmailToUser === true ? 1 : 0,
3783
                'otp_provided_new_value' => 1,
3784
                'email_body' => empty($emailBody) === true ? '' : $lang->get($emailBody),
3785
                'user_self_change' => $user_self_change === true ? 1 : 0,
3786
            ]),
3787
        )
3788
    );
3789
    $processId = DB::insertId();
3790
3791
    // Delete existing keys
3792
    if ($deleteExistingKeys === true) {
3793
        deleteUserObjetsKeys(
3794
            (int) $userId,
3795
        );
3796
    }
3797
3798
    // Create tasks
3799
    createUserTasks($processId, $nbItemsToTreat);
3800
3801
    // update user's new status
3802
    DB::update(
3803
        prefixTable('users'),
3804
        [
3805
            'is_ready_for_usage' => 0,
3806
            'otp_provided' => 1,
3807
            'ongoing_process_id' => $processId,
3808
            'special' => 'generate-keys',
3809
        ],
3810
        'id=%i',
3811
        $userId
3812
    );
3813
3814
    return prepareExchangedData(
3815
        array(
3816
            'error' => false,
3817
            'message' => '',
3818
            'user_password' => $generate_user_new_password === true ? $passwordClear : '',
3819
        ),
3820
        'encode'
3821
    );
3822
}
3823
3824
/**
3825
 * Permits to generate a new password for a user
3826
 *
3827
 * @param integer $processId
3828
 * @param integer $nbItemsToTreat
3829
 * @return void
3830
 
3831
 */
3832
function createUserTasks($processId, $nbItemsToTreat): void
3833
{
3834
    DB::insert(
3835
        prefixTable('background_subtasks'),
3836
        array(
3837
            'task_id' => $processId,
3838
            'created_at' => time(),
3839
            'task' => json_encode([
3840
                'step' => 'step0',
3841
                'index' => 0,
3842
                'nb' => $nbItemsToTreat,
3843
            ]),
3844
        )
3845
    );
3846
3847
    DB::insert(
3848
        prefixTable('background_subtasks'),
3849
        array(
3850
            'task_id' => $processId,
3851
            'created_at' => time(),
3852
            'task' => json_encode([
3853
                'step' => 'step10',
3854
                'index' => 0,
3855
                'nb' => $nbItemsToTreat,
3856
            ]),
3857
        )
3858
    );
3859
3860
    DB::insert(
3861
        prefixTable('background_subtasks'),
3862
        array(
3863
            'task_id' => $processId,
3864
            'created_at' => time(),
3865
            'task' => json_encode([
3866
                'step' => 'step20',
3867
                'index' => 0,
3868
                'nb' => $nbItemsToTreat,
3869
            ]),
3870
        )
3871
    );
3872
3873
    DB::insert(
3874
        prefixTable('background_subtasks'),
3875
        array(
3876
            'task_id' => $processId,
3877
            'created_at' => time(),
3878
            'task' => json_encode([
3879
                'step' => 'step30',
3880
                'index' => 0,
3881
                'nb' => $nbItemsToTreat,
3882
            ]),
3883
        )
3884
    );
3885
3886
    DB::insert(
3887
        prefixTable('background_subtasks'),
3888
        array(
3889
            'task_id' => $processId,
3890
            'created_at' => time(),
3891
            'task' => json_encode([
3892
                'step' => 'step40',
3893
                'index' => 0,
3894
                'nb' => $nbItemsToTreat,
3895
            ]),
3896
        )
3897
    );
3898
3899
    DB::insert(
3900
        prefixTable('background_subtasks'),
3901
        array(
3902
            'task_id' => $processId,
3903
            'created_at' => time(),
3904
            'task' => json_encode([
3905
                'step' => 'step50',
3906
                'index' => 0,
3907
                'nb' => $nbItemsToTreat,
3908
            ]),
3909
        )
3910
    );
3911
3912
    DB::insert(
3913
        prefixTable('background_subtasks'),
3914
        array(
3915
            'task_id' => $processId,
3916
            'created_at' => time(),
3917
            'task' => json_encode([
3918
                'step' => 'step60',
3919
                'index' => 0,
3920
                'nb' => $nbItemsToTreat,
3921
            ]),
3922
        )
3923
    );
3924
}
3925
3926
/**
3927
 * Permeits to check the consistency of date versus columns definition
3928
 *
3929
 * @param string $table
3930
 * @param array $dataFields
3931
 * @return array
3932
 */
3933
function validateDataFields(
3934
    string $table,
3935
    array $dataFields
3936
): array
3937
{
3938
    // Get table structure
3939
    $result = DB::query(
3940
        "SELECT `COLUMN_NAME`, `CHARACTER_MAXIMUM_LENGTH` FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = '%l' AND TABLE_NAME = '%l';",
3941
        DB_NAME,
3942
        $table
3943
    );
3944
3945
    foreach ($result as $row) {
3946
        $field = $row['COLUMN_NAME'];
3947
        $maxLength = is_null($row['CHARACTER_MAXIMUM_LENGTH']) === false ? (int) $row['CHARACTER_MAXIMUM_LENGTH'] : '';
3948
3949
        if (isset($dataFields[$field]) === true && is_array($dataFields[$field]) === false && empty($maxLength) === false) {
3950
            if (strlen((string) $dataFields[$field]) > $maxLength) {
3951
                return [
3952
                    'state' => false,
3953
                    'field' => $field,
3954
                    'maxLength' => $maxLength,
3955
                    'currentLength' => strlen((string) $dataFields[$field]),
3956
                ];
3957
            }
3958
        }
3959
    }
3960
    
3961
    return [
3962
        'state' => true,
3963
        'message' => '',
3964
    ];
3965
}
3966
3967
/**
3968
 * Adapt special characters sanitized during filter_var with option FILTER_SANITIZE_SPECIAL_CHARS operation
3969
 *
3970
 * @param string $string
3971
 * @return string
3972
 */
3973
function filterVarBack(string $string): string
3974
{
3975
    $arr = [
3976
        '&#060;' => '<',
3977
        '&#062;' => '>',
3978
        '&#034;' => '"',
3979
        '&#039;' => "'",
3980
        '&#038;' => '&',
3981
    ];
3982
3983
    foreach ($arr as $key => $value) {
3984
        $string = str_replace($key, $value, $string);
3985
    }
3986
3987
    return $string;
3988
}
3989
3990
/**
3991
 * 
3992
 */
3993
function storeTask(
3994
    string $taskName,
3995
    int $user_id,
3996
    int $is_personal_folder,
3997
    int $folder_destination_id,
3998
    int $item_id,
3999
    string $object_keys,
4000
    array $fields_keys = [],
4001
    array $files_keys = []
4002
)
4003
{
4004
    if (in_array($taskName, ['item_copy', 'new_item', 'update_item'])) {
4005
        // Create process
4006
        DB::insert(
4007
            prefixTable('background_tasks'),
4008
            array(
4009
                'created_at' => time(),
4010
                'process_type' => $taskName,
4011
                'arguments' => json_encode([
4012
                    'item_id' => $item_id,
4013
                    'object_key' => $object_keys,
4014
                ]),
4015
                'item_id' => $item_id,
4016
            )
4017
        );
4018
        $processId = DB::insertId();
4019
4020
        // Create tasks
4021
        // 1- Create password sharekeys for users of this new ITEM
4022
        DB::insert(
4023
            prefixTable('background_subtasks'),
4024
            array(
4025
                'task_id' => $processId,
4026
                'created_at' => time(),
4027
                'task' => json_encode([
4028
                    'step' => 'create_users_pwd_key',
4029
                    'index' => 0,
4030
                ]),
4031
            )
4032
        );
4033
4034
        // 2- Create fields sharekeys for users of this new ITEM
4035
        DB::insert(
4036
            prefixTable('background_subtasks'),
4037
            array(
4038
                'task_id' => $processId,
4039
                'created_at' => time(),
4040
                'task' => json_encode([
4041
                    'step' => 'create_users_fields_key',
4042
                    'index' => 0,
4043
                    'fields_keys' => $fields_keys,
4044
                ]),
4045
            )
4046
        );
4047
4048
        // 3- Create files sharekeys for users of this new ITEM
4049
        DB::insert(
4050
            prefixTable('background_subtasks'),
4051
            array(
4052
                'task_id' => $processId,
4053
                'created_at' => time(),
4054
                'task' => json_encode([
4055
                    'step' => 'create_users_files_key',
4056
                    'index' => 0,
4057
                    'files_keys' => $files_keys,
4058
                ]),
4059
            )
4060
        );
4061
    }
4062
}
4063
4064
/**
4065
 * 
4066
 */
4067
function createTaskForItem(
4068
    string $processType,
4069
    string|array $taskName,
4070
    int $itemId,
4071
    int $userId,
4072
    string $objectKey,
4073
    int $parentId = -1,
4074
    array $fields_keys = [],
4075
    array $files_keys = []
4076
)
4077
{
4078
    // 1- Create main process
4079
    // ---
4080
    
4081
    // Create process
4082
    DB::insert(
4083
        prefixTable('background_tasks'),
4084
        array(
4085
            'created_at' => time(),
4086
            'process_type' => $processType,
4087
            'arguments' => json_encode([
4088
                'all_users_except_id' => (int) $userId,
4089
                'item_id' => (int) $itemId,
4090
                'object_key' => $objectKey,
4091
                'author' => (int) $userId,
4092
            ]),
4093
            'item_id' => (int) $parentId !== -1 ?  $parentId : null,
4094
        )
4095
    );
4096
    $processId = DB::insertId();
4097
4098
    // 2- Create expected tasks
4099
    // ---
4100
    if (is_array($taskName) === false) {
0 ignored issues
show
introduced by
The condition is_array($taskName) === false is always false.
Loading history...
4101
        $taskName = [$taskName];
4102
    }
4103
    foreach($taskName as $task) {
4104
        error_log('createTaskForItem - task: '.$task);
4105
        switch ($task) {
4106
            case 'item_password':
4107
                
4108
                DB::insert(
4109
                    prefixTable('background_subtasks'),
4110
                    array(
4111
                        'task_id' => $processId,
4112
                        'created_at' => time(),
4113
                        'task' => json_encode([
4114
                            'step' => 'create_users_pwd_key',
4115
                            'index' => 0,
4116
                        ]),
4117
                    )
4118
                );
4119
4120
                break;
4121
            case 'item_field':
4122
                
4123
                DB::insert(
4124
                    prefixTable('background_subtasks'),
4125
                    array(
4126
                        'task_id' => $processId,
4127
                        'created_at' => time(),
4128
                        'task' => json_encode([
4129
                            'step' => 'create_users_fields_key',
4130
                            'index' => 0,
4131
                            'fields_keys' => $fields_keys,
4132
                        ]),
4133
                    )
4134
                );
4135
4136
                break;
4137
            case 'item_file':
4138
4139
                DB::insert(
4140
                    prefixTable('background_subtasks'),
4141
                    array(
4142
                        'task_id' => $processId,
4143
                        'created_at' => time(),
4144
                        'task' => json_encode([
4145
                            'step' => 'create_users_files_key',
4146
                            'index' => 0,
4147
                            'fields_keys' => $files_keys,
4148
                        ]),
4149
                    )
4150
                );
4151
                break;
4152
            default:
4153
                # code...
4154
                break;
4155
        }
4156
    }
4157
}
4158
4159
4160
function deleteProcessAndRelatedTasks(int $processId)
4161
{
4162
    // Delete process
4163
    DB::delete(
4164
        prefixTable('background_tasks'),
4165
        'id=%i',
4166
        $processId
4167
    );
4168
4169
    // Delete tasks
4170
    DB::delete(
4171
        prefixTable('background_subtasks'),
4172
        'task_id=%i',
4173
        $processId
4174
    );
4175
4176
}
4177
4178
/**
4179
 * Return PHP binary path
4180
 *
4181
 * @return string
4182
 */
4183
function getPHPBinary(): string
4184
{
4185
    // Get PHP binary path
4186
    $phpBinaryFinder = new PhpExecutableFinder();
4187
    $phpBinaryPath = $phpBinaryFinder->find();
4188
    return $phpBinaryPath === false ? 'false' : $phpBinaryPath;
4189
}
4190
4191
4192
4193
/**
4194
 * Delete unnecessary keys for personal items
4195
 *
4196
 * @param boolean $allUsers
4197
 * @param integer $user_id
4198
 * @return void
4199
 */
4200
function purgeUnnecessaryKeys(bool $allUsers = true, int $user_id=0)
4201
{
4202
    if ($allUsers === true) {
4203
        // Load class DB
4204
        if (class_exists('DB') === false) {
4205
            loadClasses('DB');
4206
        }
4207
4208
        $users = DB::query(
4209
            'SELECT id
4210
            FROM ' . prefixTable('users') . '
4211
            WHERE id NOT IN ('.OTV_USER_ID.', '.TP_USER_ID.', '.SSH_USER_ID.', '.API_USER_ID.')
4212
            ORDER BY login ASC'
4213
        );
4214
        foreach ($users as $user) {
4215
            purgeUnnecessaryKeysForUser((int) $user['id']);
4216
        }
4217
    } else {
4218
        purgeUnnecessaryKeysForUser((int) $user_id);
4219
    }
4220
}
4221
4222
/**
4223
 * Delete unnecessary keys for personal items
4224
 *
4225
 * @param integer $user_id
4226
 * @return void
4227
 */
4228
function purgeUnnecessaryKeysForUser(int $user_id=0)
4229
{
4230
    if ($user_id === 0) {
4231
        return;
4232
    }
4233
4234
    // Load class DB
4235
    loadClasses('DB');
4236
4237
    $personalItems = DB::queryFirstColumn(
4238
        'SELECT id
4239
        FROM ' . prefixTable('items') . ' AS i
4240
        INNER JOIN ' . prefixTable('log_items') . ' AS li ON li.id_item = i.id
4241
        WHERE i.perso = 1 AND li.action = "at_creation" AND li.id_user IN (%i, '.TP_USER_ID.')',
4242
        $user_id
4243
    );
4244
    if (count($personalItems) > 0) {
4245
        // Item keys
4246
        DB::delete(
4247
            prefixTable('sharekeys_items'),
4248
            'object_id IN %li AND user_id NOT IN (%i, '.TP_USER_ID.')',
4249
            $personalItems,
4250
            $user_id
4251
        );
4252
        // Files keys
4253
        DB::delete(
4254
            prefixTable('sharekeys_files'),
4255
            'object_id IN %li AND user_id NOT IN (%i, '.TP_USER_ID.')',
4256
            $personalItems,
4257
            $user_id
4258
        );
4259
        // Fields keys
4260
        DB::delete(
4261
            prefixTable('sharekeys_fields'),
4262
            'object_id IN %li AND user_id NOT IN (%i, '.TP_USER_ID.')',
4263
            $personalItems,
4264
            $user_id
4265
        );
4266
        // Logs keys
4267
        DB::delete(
4268
            prefixTable('sharekeys_logs'),
4269
            'object_id IN %li AND user_id NOT IN (%i, '.TP_USER_ID.')',
4270
            $personalItems,
4271
            $user_id
4272
        );
4273
    }
4274
}
4275
4276
/**
4277
 * Generate recovery keys file
4278
 *
4279
 * @param integer $userId
4280
 * @param array $SETTINGS
4281
 * @return string
4282
 */
4283
function handleUserRecoveryKeysDownload(int $userId, array $SETTINGS):string
4284
{
4285
    $session = SessionManager::getSession();
4286
    // Check if user exists
4287
    $userInfo = DB::queryFirstRow(
4288
        'SELECT pw, public_key, private_key, login, name
4289
        FROM ' . prefixTable('users') . '
4290
        WHERE id = %i',
4291
        $userId
4292
    );
4293
4294
    if (DB::count() > 0) {
4295
        $now = (int) time();
4296
4297
        // Prepare file content
4298
        $export_value = file_get_contents(__DIR__."/../includes/core/teampass_ascii.txt")."\n".
4299
            "Generation date: ".date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], $now)."\n\n".
4300
            "RECOVERY KEYS - Not to be shared - To be store safely\n\n".
4301
            "Public Key:\n".$userInfo['public_key']."\n\n".
4302
            "Private Key:\n".decryptPrivateKey($session->get('user-password'), $userInfo['private_key'])."\n\n";
4303
4304
        // Update user's keys_recovery_time
4305
        DB::update(
4306
            prefixTable('users'),
4307
            [
4308
                'keys_recovery_time' => $now,
4309
            ],
4310
            'id=%i',
4311
            $userId
4312
        );
4313
        $session->set('user-keys_recovery_time', $now);
4314
4315
        //Log into DB the user's disconnection
4316
        logEvents($SETTINGS, 'user_mngt', 'at_user_keys_download', (string) $userId, $userInfo['login']);
4317
        
4318
        // Return data
4319
        return prepareExchangedData(
4320
            array(
4321
                'error' => false,
4322
                'datetime' => date($SETTINGS['date_format'] . ' ' . $SETTINGS['time_format'], $now),
4323
                'timestamp' => $now,
4324
                'content' => base64_encode($export_value),
4325
                'login' => $userInfo['login'],
4326
            ),
4327
            'encode'
4328
        );
4329
    }
4330
4331
    return prepareExchangedData(
4332
        array(
4333
            'error' => true,
4334
            'datetime' => '',
4335
        ),
4336
        'encode'
4337
    );
4338
}
4339
4340
/**
4341
 * Permits to load expected classes
4342
 *
4343
 * @param string $className
4344
 * @return void
4345
 */
4346
function loadClasses(string $className = ''): void
4347
{
4348
    require_once __DIR__. '/../includes/config/include.php';
4349
    require_once __DIR__. '/../includes/config/settings.php';
4350
    require_once __DIR__.'/../vendor/autoload.php';
4351
4352
    if (defined('DB_PASSWD_CLEAR') === false) {
4353
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, []));
4354
    }
4355
4356
    if (empty($className) === false) {
4357
        // Load class DB
4358
        if ((string) $className === 'DB') {
4359
            //Connect to DB
4360
            DB::$host = DB_HOST;
4361
            DB::$user = DB_USER;
4362
            DB::$password = DB_PASSWD_CLEAR;
4363
            DB::$dbName = DB_NAME;
4364
            DB::$port = DB_PORT;
4365
            DB::$encoding = DB_ENCODING;
4366
            DB::$ssl = DB_SSL;
4367
            DB::$connect_options = DB_CONNECT_OPTIONS;
4368
        }
4369
    }
4370
4371
}
4372
4373
/**
4374
 * Returns the page the user is visiting.
4375
 *
4376
 * @return string The page name
4377
 */
4378
function getCurrectPage($SETTINGS)
4379
{
4380
    
4381
    $request = SymfonyRequest::createFromGlobals();
4382
4383
    // Parse the url
4384
    parse_str(
4385
        substr(
4386
            (string) $request->getRequestUri(),
4387
            strpos((string) $request->getRequestUri(), '?') + 1
4388
        ),
4389
        $result
4390
    );
4391
4392
    return $result['page'];
4393
}
4394
4395
/**
4396
 * Permits to return value if set
4397
 *
4398
 * @param string|int $value
4399
 * @param string|int|null $retFalse
4400
 * @param string|int $retTrue
4401
 * @return mixed
4402
 */
4403
function returnIfSet($value, $retFalse = '', $retTrue = null): mixed
4404
{
4405
4406
    return isset($value) === true ? ($retTrue === null ? $value : $retTrue) : $retFalse;
4407
}
4408
4409
4410
/**
4411
 * SEnd email to user
4412
 *
4413
 * @param string $post_receipt
4414
 * @param string $post_body
4415
 * @param string $post_subject
4416
 * @param array $post_replace
4417
 * @param boolean $immediate_email
4418
 * @return string
4419
 */
4420
function sendMailToUser(
4421
    string $post_receipt,
4422
    string $post_body,
4423
    string $post_subject,
4424
    array $post_replace,
4425
    bool $immediate_email = false
4426
): ?string {
4427
    global $SETTINGS;
4428
    if (count($post_replace) > 0 && is_null($post_replace) === false) {
4429
        $post_body = str_replace(
4430
            array_keys($post_replace),
0 ignored issues
show
Bug introduced by
$post_replace of type null is incompatible with the type array expected by parameter $array of array_keys(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

4430
            array_keys(/** @scrutinizer ignore-type */ $post_replace),
Loading history...
4431
            array_values($post_replace),
0 ignored issues
show
Bug introduced by
$post_replace of type null is incompatible with the type array expected by parameter $array of array_values(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

4431
            array_values(/** @scrutinizer ignore-type */ $post_replace),
Loading history...
4432
            $post_body
4433
        );
4434
    }
4435
4436
    if ($immediate_email === true) {
4437
        $ret = sendEmail(
4438
            $post_subject,
4439
            $post_body,
4440
            $post_receipt,
4441
            $SETTINGS,
4442
            '',
4443
            false
4444
        );
4445
    
4446
        $ret = json_decode($ret, true);
4447
    
4448
        return prepareExchangedData(
4449
            array(
4450
                'error' => empty($ret['error']) === true ? false : true,
4451
                'message' => $ret['message'],
4452
            ),
4453
            'encode'
4454
        );
4455
    } else {
4456
        // Send through task handler
4457
        prepareSendingEmail(
4458
            $post_subject,
4459
            $post_body,
4460
            $post_receipt,
4461
            ""
4462
        );
4463
    }
4464
4465
    return null;
4466
}
4467
4468
/**
4469
 * Converts a password strengh value to zxcvbn level
4470
 * 
4471
 * @param integer $passwordStrength
4472
 * 
4473
 * @return integer
4474
 */
4475
function convertPasswordStrength($passwordStrength): int
4476
{
4477
    if ($passwordStrength === 0) {
4478
        return TP_PW_STRENGTH_1;
4479
    } else if ($passwordStrength === 1) {
4480
        return TP_PW_STRENGTH_2;
4481
    } else if ($passwordStrength === 2) {
4482
        return TP_PW_STRENGTH_3;
4483
    } else if ($passwordStrength === 3) {
4484
        return TP_PW_STRENGTH_4;
4485
    } else {
4486
        return TP_PW_STRENGTH_5;
4487
    }
4488
}