Passed
Push — master ( c7269a...782261 )
by Nils
04:57
created

buildEmail()   D

Complexity

Conditions 9
Paths 500

Size

Total Lines 81
Code Lines 42

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 9
eloc 42
c 2
b 0
f 0
nc 500
nop 7
dl 0
loc 81
rs 4.9146

How to fix   Long Method   

Long Method

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

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

Commonly applied refactorings include:

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

6 paths for user data to reach this point

  1. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 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 4399
  10. sendEmail() is called
    in sources/main.functions.php on line 4416
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1260
  15. Data is passed through xss_clean(), and $antiXss->xss_clean($text_html) is assigned to $text_html
    in sources/main.functions.php on line 1261
  16. Data is passed through htmlspecialchars(), and htmlspecialchars($text_html, ENT_QUOTES, 'UTF-8') is assigned to $text_html
    in sources/main.functions.php on line 1262
  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 4399
  10. sendEmail() is called
    in sources/main.functions.php on line 4416
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1260
  15. Data is passed through xss_clean(), and $antiXss->xss_clean($text_html) is assigned to $text_html
    in sources/main.functions.php on line 1261
  16. Data is passed through htmlspecialchars(), and htmlspecialchars($text_html, ENT_QUOTES, 'UTF-8') is assigned to $text_html
    in sources/main.functions.php on line 1262
  3. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 70
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 70
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4399
  10. sendEmail() is called
    in sources/main.functions.php on line 4416
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1260
  15. Data is passed through xss_clean(), and $antiXss->xss_clean($text_html) is assigned to $text_html
    in sources/main.functions.php on line 1261
  16. Data is passed through htmlspecialchars(), and htmlspecialchars($text_html, ENT_QUOTES, 'UTF-8') is assigned to $text_html
    in sources/main.functions.php on line 1262
  4. 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 4399
  10. sendEmail() is called
    in sources/main.functions.php on line 4416
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1260
  15. Data is passed through xss_clean(), and $antiXss->xss_clean($text_html) is assigned to $text_html
    in sources/main.functions.php on line 1261
  16. Data is passed through htmlspecialchars(), and htmlspecialchars($text_html, ENT_QUOTES, 'UTF-8') is assigned to $text_html
    in sources/main.functions.php on line 1262
  5. 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 4399
  10. sendEmail() is called
    in sources/main.functions.php on line 4416
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1260
  15. Data is passed through xss_clean(), and $antiXss->xss_clean($text_html) is assigned to $text_html
    in sources/main.functions.php on line 1261
  16. Data is passed through htmlspecialchars(), and htmlspecialchars($text_html, ENT_QUOTES, 'UTF-8') is assigned to $text_html
    in sources/main.functions.php on line 1262
  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 4399
  10. sendEmail() is called
    in sources/main.functions.php on line 4416
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1260
  15. Data is passed through xss_clean(), and $antiXss->xss_clean($text_html) is assigned to $text_html
    in sources/main.functions.php on line 1261
  16. Data is passed through htmlspecialchars(), and htmlspecialchars($text_html, ENT_QUOTES, 'UTF-8') is assigned to $text_html
    in sources/main.functions.php on line 1262

Used in path-write context

  1. $text_html is assigned to property PHPMailer::$Body
    in sources/main.functions.php on line 1266
  2. Read from property PHPMailer::$Body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  3. Data is passed through encodeString()
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  4. $this->encodeString($this->Body, $this->Encoding) is assigned to $body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3075
  5. $body is returned
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 3135
  6. $this->createBody() is assigned to property PHPMailer::$MIMEBody
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1622
  7. Read from property PHPMailer::$MIMEBody
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1687
  8. PHPMailer::sendmailSend() is called
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1687
  9. Enters via parameter $body
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 1726
  10. fwrite() is called
    in vendor/phpmailer/phpmailer/src/PHPMailer.php on line 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
$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 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 4399
  10. sendEmail() is called
    in sources/main.functions.php on line 4416
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1260
  15. Data is passed through xss_clean(), and $antiXss->xss_clean($text_html) is assigned to $text_html
    in sources/main.functions.php on line 1261
  16. Data is passed through htmlspecialchars(), and htmlspecialchars($text_html, ENT_QUOTES, 'UTF-8') is assigned to $text_html
    in sources/main.functions.php on line 1262
  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 4399
  10. sendEmail() is called
    in sources/main.functions.php on line 4416
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1260
  15. Data is passed through xss_clean(), and $antiXss->xss_clean($text_html) is assigned to $text_html
    in sources/main.functions.php on line 1261
  16. Data is passed through htmlspecialchars(), and htmlspecialchars($text_html, ENT_QUOTES, 'UTF-8') is assigned to $text_html
    in sources/main.functions.php on line 1262
  3. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 70
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 70
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4399
  10. sendEmail() is called
    in sources/main.functions.php on line 4416
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1260
  15. Data is passed through xss_clean(), and $antiXss->xss_clean($text_html) is assigned to $text_html
    in sources/main.functions.php on line 1261
  16. Data is passed through htmlspecialchars(), and htmlspecialchars($text_html, ENT_QUOTES, 'UTF-8') is assigned to $text_html
    in sources/main.functions.php on line 1262
  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 4399
  10. sendEmail() is called
    in sources/main.functions.php on line 4416
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1260
  15. Data is passed through xss_clean(), and $antiXss->xss_clean($text_html) is assigned to $text_html
    in sources/main.functions.php on line 1261
  16. Data is passed through htmlspecialchars(), and htmlspecialchars($text_html, ENT_QUOTES, 'UTF-8') is assigned to $text_html
    in sources/main.functions.php on line 1262
  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 4399
  10. sendEmail() is called
    in sources/main.functions.php on line 4416
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1260
  15. Data is passed through xss_clean(), and $antiXss->xss_clean($text_html) is assigned to $text_html
    in sources/main.functions.php on line 1261
  16. Data is passed through htmlspecialchars(), and htmlspecialchars($text_html, ENT_QUOTES, 'UTF-8') is assigned to $text_html
    in sources/main.functions.php on line 1262
  6. Path: Read from $_SERVER in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 66
  1. Read from $_SERVER
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 66
  2. array('subTaskId' => $_SERVER['argv'][1], 'index' => $_SERVER['argv'][2], 'nb' => $_SERVER['argv'][3], 'step' => $_SERVER['argv'][4], 'taskArguments' => $_SERVER['argv'][5], 'taskId' => $_SERVER['argv'][6]) is assigned to $inputData
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 64
  3. Data is passed through json_decode(), and json_decode($inputData['taskArguments'], true) is assigned to $taskArgs
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 82
  4. performUserCreationKeys() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 107
  5. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 141
  6. cronContinueReEncryptingUserSharekeysStep10() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 174
  7. Enters via parameter $extra_arguments
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 830
  8. sendMailToUser() is called
    in scripts/background_tasks___userKeysCreation_subtaskHdl.php on line 871
  9. Enters via parameter $post_body
    in sources/main.functions.php on line 4399
  10. sendEmail() is called
    in sources/main.functions.php on line 4416
  11. Enters via parameter $textMail
    in sources/main.functions.php on line 1145
  12. buildEmail() is called
    in sources/main.functions.php on line 1168
  13. Enters via parameter $textMail
    in sources/main.functions.php on line 1204
  14. Data is passed through emailBody(), and emailBody($textMail) is assigned to $text_html
    in sources/main.functions.php on line 1260
  15. Data is passed through xss_clean(), and $antiXss->xss_clean($text_html) is assigned to $text_html
    in sources/main.functions.php on line 1261
  16. Data is passed through htmlspecialchars(), and htmlspecialchars($text_html, ENT_QUOTES, 'UTF-8') is assigned to $text_html
    in sources/main.functions.php on line 1262

Used in path-write context

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

General Strategies to prevent injection

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

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

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

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

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

4408
            array_values(/** @scrutinizer ignore-type */ $post_replace),
Loading history...
4409
            $post_body
4410
        );
4411
    }
4412
4413
    if ($immediate_email === true) {
4414
        $ret = sendEmail(
4415
            $post_subject,
4416
            $post_body,
4417
            $post_receipt,
4418
            $SETTINGS,
4419
            '',
4420
            false
4421
        );
4422
    
4423
        $ret = json_decode($ret, true);
4424
    
4425
        return prepareExchangedData(
4426
            array(
4427
                'error' => empty($ret['error']) === true ? false : true,
4428
                'message' => $ret['message'],
4429
            ),
4430
            'encode'
4431
        );
4432
    } else {
4433
        // Send through task handler
4434
        prepareSendingEmail(
4435
            $post_subject,
4436
            $post_body,
4437
            $post_receipt,
4438
            ""
4439
        );
4440
    }
4441
4442
    return null;
4443
}
4444
4445
/**
4446
 * Converts a password strengh value to zxcvbn level
4447
 * 
4448
 * @param integer $passwordStrength
4449
 * 
4450
 * @return integer
4451
 */
4452
function convertPasswordStrength($passwordStrength): int
4453
{
4454
    if ($passwordStrength === 0) {
4455
        return TP_PW_STRENGTH_1;
4456
    } else if ($passwordStrength === 1) {
4457
        return TP_PW_STRENGTH_2;
4458
    } else if ($passwordStrength === 2) {
4459
        return TP_PW_STRENGTH_3;
4460
    } else if ($passwordStrength === 3) {
4461
        return TP_PW_STRENGTH_4;
4462
    } else {
4463
        return TP_PW_STRENGTH_5;
4464
    }
4465
}