Passed
Pull Request — master (#4359)
by Nils
04:59
created

logItems()   B

Complexity

Conditions 9
Paths 10

Size

Total Lines 66
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 9
eloc 35
c 3
b 0
f 0
nc 10
nop 10
dl 0
loc 66
rs 8.0555

How to fix   Long Method    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
use TeampassClasses\EmailService\EmailService;
56
use TeampassClasses\EmailService\EmailSettings;
57
58
header('Content-type: text/html; charset=utf-8');
59
header('Cache-Control: no-cache, must-revalidate');
60
61
loadClasses('DB');
62
$session = SessionManager::getSession();
63
64
// Load config if $SETTINGS not defined
65
$configManager = new ConfigManager($session);
66
$SETTINGS = $configManager->getAllSettings();
67
68
/**
69
 * genHash().
70
 *
71
 * Generate a hash for user login
72
 *
73
 * @param string $password What password
74
 * @param string $cost     What cost
75
 *
76
 * @return string|void
77
 */
78
/* TODO - Remove this function
79
function bCrypt(
80
    string $password,
81
    string $cost
82
): ?string
83
{
84
    $salt = sprintf('$2y$%02d$', $cost);
85
    if (function_exists('openssl_random_pseudo_bytes')) {
86
        $salt .= bin2hex(openssl_random_pseudo_bytes(11));
87
    } else {
88
        $chars = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
89
        for ($i = 0; $i < 22; ++$i) {
90
            $salt .= $chars[mt_rand(0, 63)];
91
        }
92
    }
93
94
    return crypt($password, $salt);
95
}
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
 * Returns the email body.
1133
 *
1134
 * @param string $textMail Text for the email
1135
 */
1136
function emailBody(string $textMail): string
1137
{
1138
    return '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.=
1139
    w3.org/TR/html4/loose.dtd"><html>
1140
    <head><title>Email Template</title>
1141
    <style type="text/css">
1142
    body { background-color: #f0f0f0; padding: 10px 0; margin:0 0 10px =0; }
1143
    </style></head>
1144
    <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">
1145
    <table border="0" width="100%" height="100%" cellpadding="0" cellspacing="0" bgcolor="#f0f0f0" style="border-spacing: 0;">
1146
    <tr><td style="border-collapse: collapse;"><br>
1147
        <table border="0" width="100%" cellpadding="0" cellspacing="0" bgcolor="#17357c" style="border-spacing: 0; margin-bottom: 25px;">
1148
        <tr><td style="border-collapse: collapse; padding: 11px 20px;">
1149
            <div style="max-width:150px; max-height:34px; color:#f0f0f0; font-weight:bold;">Teampass</div>
1150
        </td></tr></table></td>
1151
    </tr>
1152
    <tr><td align="center" valign="top" bgcolor="#f0f0f0" style="border-collapse: collapse; background-color: #f0f0f0;">
1153
        <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;">
1154
        <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;">
1155
        <br><div style="float:right;">' .
1156
        $textMail .
1157
        '<br><br></td></tr></table>
1158
    </td></tr></table>
1159
    <br></body></html>';
1160
}
1161
1162
/**
1163
 * Convert date to timestamp.
1164
 *
1165
 * @param string $date        The date
1166
 * @param string $date_format Date format
1167
 *
1168
 * @return int
1169
 */
1170
function dateToStamp(string $date, string $date_format): int
1171
{
1172
    $date = date_parse_from_format($date_format, $date);
1173
    if ((int) $date['warning_count'] === 0 && (int) $date['error_count'] === 0) {
1174
        return mktime(
1175
            empty($date['hour']) === false ? $date['hour'] : 23,
1176
            empty($date['minute']) === false ? $date['minute'] : 59,
1177
            empty($date['second']) === false ? $date['second'] : 59,
1178
            $date['month'],
1179
            $date['day'],
1180
            $date['year']
1181
        );
1182
    }
1183
    return 0;
1184
}
1185
1186
/**
1187
 * Is this a date.
1188
 *
1189
 * @param string $date Date
1190
 *
1191
 * @return bool
1192
 */
1193
function isDate(string $date): bool
1194
{
1195
    return strtotime($date) !== false;
1196
}
1197
1198
/**
1199
 * Check if isUTF8().
1200
 *
1201
 * @param string|array $string Is the string
1202
 *
1203
 * @return int is the string in UTF8 format
1204
 */
1205
function isUTF8($string): int
1206
{
1207
    if (is_array($string) === true) {
1208
        $string = $string['string'];
1209
    }
1210
1211
    return preg_match(
1212
        '%^(?:
1213
        [\x09\x0A\x0D\x20-\x7E] # ASCII
1214
        | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
1215
        | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
1216
        | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
1217
        | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
1218
        | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
1219
        | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
1220
        | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
1221
        )*$%xs',
1222
        $string
1223
    );
1224
}
1225
1226
/**
1227
 * Prepare an array to UTF8 format before JSON_encode.
1228
 *
1229
 * @param array $array Array of values
1230
 *
1231
 * @return array
1232
 */
1233
function utf8Converter(array $array): array
1234
{
1235
    array_walk_recursive(
1236
        $array,
1237
        static function (&$item): void {
1238
            if (mb_detect_encoding((string) $item, 'utf-8', true) === false) {
1239
                $item = mb_convert_encoding($item, 'ISO-8859-1', 'UTF-8');
1240
            }
1241
        }
1242
    );
1243
    return $array;
1244
}
1245
1246
/**
1247
 * Permits to prepare data to be exchanged.
1248
 *
1249
 * @param array|string $data Text
1250
 * @param string       $type Parameter
1251
 * @param string       $key  Optional key
1252
 *
1253
 * @return string|array
1254
 */
1255
function prepareExchangedData($data, string $type, ?string $key = null)
1256
{
1257
    $session = SessionManager::getSession();
1258
    
1259
    // Perform
1260
    if ($type === 'encode' && is_array($data) === true) {
1261
        // json encoding
1262
        $data = json_encode(
1263
            $data,
1264
            JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1265
        );
1266
1267
        // Now encrypt
1268
        if ($session->get('encryptClientServer') === 1) {
1269
            $data = Encryption::encrypt(
1270
                $data,
1271
                $session->get('key')
1272
            );
1273
        }
1274
1275
        return $data;
1276
    }
1277
    if ($type === 'decode' && is_array($data) === false) {
1278
        // Decrypt if needed
1279
        if ($session->get('encryptClientServer') === 1) {
1280
            $data = (string) Encryption::decrypt(
1281
                (string) $data,
1282
                $session->get('key')
1283
            );
1284
        } else {
1285
            // Double html encoding received
1286
            $data = html_entity_decode(html_entity_decode($data));
0 ignored issues
show
Bug introduced by
$data of type array is incompatible with the type string expected by parameter $string of html_entity_decode(). ( Ignorable by Annotation )

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

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

4399
            array_keys(/** @scrutinizer ignore-type */ $post_replace),
Loading history...
4400
            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

4400
            array_values(/** @scrutinizer ignore-type */ $post_replace),
Loading history...
4401
            $post_body
4402
        );
4403
    }
4404
4405
    if ($immediate_email === true) {
4406
        
4407
        $ret = $emailService->sendMail(
4408
            $post_subject,
4409
            $post_body,
0 ignored issues
show
Security File Manipulation introduced by
$post_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 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  4. 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388

Used in path-write context

  1. EmailService::sendMail() is called
    in sources/main.functions.php on line 4554
  2. Enters via parameter $textMail
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 94
  3. Data is passed through sanitizeEmailBody()
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 109
  4. $this->sanitizeEmailBody($textMail) is assigned to $textMail
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 109
  5. Data is passed through emailBody()
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 115
  6. emailBody($textMail) is assigned to property PHPMailer::$Body
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 115
  7. Read from property PHPMailer::$Body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  8. Data is passed through encodeString()
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  9. $this->encodeString($this->Body, $this->Encoding) is assigned to $body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  10. $body is returned
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3135
  11. $this->createBody() is assigned to property PHPMailer::$MIMEBody
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1622
  12. Read from property PHPMailer::$MIMEBody
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1687
  13. PHPMailer::sendmailSend() is called
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1687
  14. Enters via parameter $body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1726
  15. fwrite() is called
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1779

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
$post_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 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  2. 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  3. 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  4. 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  6. 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388

Used in path-write context

  1. EmailService::sendMail() is called
    in sources/main.functions.php on line 4554
  2. Enters via parameter $textMail
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 94
  3. Data is passed through sanitizeEmailBody()
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 109
  4. $this->sanitizeEmailBody($textMail) is assigned to $textMail
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 109
  5. Data is passed through emailBody()
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 115
  6. emailBody($textMail) is assigned to property PHPMailer::$Body
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 115
  7. Read from property PHPMailer::$Body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  8. Data is passed through encodeString()
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  9. $this->encodeString($this->Body, $this->Encoding) is assigned to $body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  10. 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 File Manipulation introduced by
$post_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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  2. 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  3. 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  4. 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  5. 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  6. 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388

Used in path-write context

  1. EmailService::sendMail() is called
    in sources/main.functions.php on line 4554
  2. Enters via parameter $textMail
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 94
  3. Data is passed through sanitizeEmailBody()
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 109
  4. $this->sanitizeEmailBody($textMail) is assigned to $textMail
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 109
  5. Data is passed through emailBody()
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 115
  6. emailBody($textMail) is assigned to property PHPMailer::$Body
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 115
  7. Read from property PHPMailer::$Body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  8. Data is passed through encodeString()
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  9. $this->encodeString($this->Body, $this->Encoding) is assigned to $body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  10. $body is returned
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3135
  11. $this->createBody() is assigned to property PHPMailer::$MIMEBody
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1622
  12. Read from property PHPMailer::$MIMEBody
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1689
  13. PHPMailer::smtpSend() is called
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1689
  14. Enters via parameter $body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 2058
  15. SMTP::data() is called
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 2096
  16. Enters via parameter $msg_data
    in vendor/phpmailer/phpmailer/src/SMTP.php on line 728
  17. Data is passed through str_replace()
    in vendor/phpmailer/phpmailer/src/SMTP.php on line 744
  18. Data is passed through explode()
    in vendor/phpmailer/phpmailer/src/SMTP.php on line 744
  19. ``explode(' ', str_replace(array(' ', ' '), ' ', $msg_data))`` is assigned to $lines
    in vendor/phpmailer/phpmailer/src/SMTP.php on line 744
  20. $lines is assigned to $line
    in vendor/phpmailer/phpmailer/src/SMTP.php on line 757
  21. $line is assigned to $lines_out
    in vendor/phpmailer/phpmailer/src/SMTP.php on line 785
  22. $lines_out is assigned to $line_out
    in vendor/phpmailer/phpmailer/src/SMTP.php on line 788
  23. SMTP::client_send() is called
    in vendor/phpmailer/phpmailer/src/SMTP.php on line 794
  24. Enters via parameter $data
    in vendor/phpmailer/phpmailer/src/SMTP.php on line 1153
  25. fwrite() is called
    in vendor/phpmailer/phpmailer/src/SMTP.php on line 1166

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
$post_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 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  4. 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  5. 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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388
  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 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4388

Used in request-header context

  1. EmailService::sendMail() is called
    in sources/main.functions.php on line 4323
  2. Enters via parameter $textMail
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 98
  3. Data is passed through sanitizeEmailBody()
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 113
  4. $this->sanitizeEmailBody($textMail) is assigned to $textMail
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 113
  5. Data is passed through emailBody()
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 119
  6. emailBody($textMail) is assigned to property PHPMailer::$Body
    in vendor/teampassclasses/emailservice/src/EmailService.php on line 119
  7. Read from property PHPMailer::$Body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  8. Data is passed through encodeString()
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  9. $this->encodeString($this->Body, $this->Encoding) is assigned to $body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  10. $body is returned
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3135
  11. $this->createBody() is assigned to property PHPMailer::$MIMEBody
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1622
  12. Read from property PHPMailer::$MIMEBody
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1656
  13. Data is passed through DKIM_Add()
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1653
  14. $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
  15. Data is passed through normalizeBreaks()
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1659
  16. 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
  17. Read from property PHPMailer::$MIMEHeader
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1698
  18. PHPMailer::mailSend() is called
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1698
  19. Enters via parameter $header
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1913
  20. Data is passed through stripTrailingWSP()
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1915
  21. static::stripTrailingWSP($header) . static::LE . static::LE is assigned to $header
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1915
  22. PHPMailer::mailPassthru() is called
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1956
  23. Enters via parameter $header
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 865
  24. 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...
4410
            $post_receipt,
4411
            $emailSettings,
4412
            '',
4413
            false
4414
        );
4415
    
4416
        $ret = json_decode($ret, true);
4417
    
4418
        return prepareExchangedData(
4419
            array(
4420
                'error' => empty($ret['error']) === true ? false : true,
4421
                'message' => $ret['message'],
4422
            ),
4423
            'encode'
4424
        );
4425
    } else {
4426
        // Send through task handler
4427
        prepareSendingEmail(
4428
            $post_subject,
4429
            $post_body,
4430
            $post_receipt,
4431
            ""
4432
        );
4433
    }
4434
4435
    return null;
4436
}
4437
4438
/**
4439
 * Converts a password strengh value to zxcvbn level
4440
 * 
4441
 * @param integer $passwordStrength
4442
 * 
4443
 * @return integer
4444
 */
4445
function convertPasswordStrength($passwordStrength): int
4446
{
4447
    if ($passwordStrength === 0) {
4448
        return TP_PW_STRENGTH_1;
4449
    } else if ($passwordStrength === 1) {
4450
        return TP_PW_STRENGTH_2;
4451
    } else if ($passwordStrength === 2) {
4452
        return TP_PW_STRENGTH_3;
4453
    } else if ($passwordStrength === 3) {
4454
        return TP_PW_STRENGTH_4;
4455
    } else {
4456
        return TP_PW_STRENGTH_5;
4457
    }
4458
}
4459
4460
/**
4461
 * Vérifie si les IDs d'un tableau existent bien dans la table.
4462
 *
4463
 * @param array $ids - Tableau d'IDs à vérifier
4464
 * @param string $tableName - Nom de la table dans laquelle vérifier les IDs
4465
 * @param string $fieldName - Nom du champ dans lequel vérifier les IDs
4466
 * @return array - IDs qui n'existent pas dans la table
4467
 */
4468
function checkIdsExist(array $ids, string $tableName, string $fieldName) : array
4469
{
4470
    // Assure-toi que le tableau d'IDs n'est pas vide
4471
    if (empty($ids)) {
4472
        return [];
4473
    }
4474
4475
    // Nettoyage des IDs pour éviter les injections SQL
4476
    $ids = array_map('intval', $ids);  // Assure que chaque ID est un entier
4477
4478
    // Construction de la requête SQL pour vérifier les IDs dans la table
4479
    $result = DB::query('SELECT id FROM ' . prefixTable($tableName) . ' WHERE ' . $fieldName . ' IN %li', $ids);
4480
4481
    // Extraire les IDs existants de la table
4482
    $existingIds = array_column($result, 'id');
4483
4484
    // Trouver les IDs manquants en comparant les deux tableaux
4485
    $missingIds = array_diff($ids, $existingIds);
4486
4487
    return $missingIds; // Renvoie les IDs qui n'existent pas dans la table
4488
}
4489