Passed
Push — teampass_3.0 ( 7063cf...c56083 )
by Nils
06:28
created

logItems()   C

Complexity

Conditions 12
Paths 20

Size

Total Lines 79
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 10
Bugs 0 Features 0
Metric Value
cc 12
eloc 45
c 10
b 0
f 0
nc 20
nop 8
dl 0
loc 79
rs 6.9666

How to fix   Long Method    Complexity    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

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

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/**
4
 * Teampass - a collaborative passwords manager.
5
 * ---
6
 * This library is distributed in the hope that it will be useful,
7
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
9
 * ---
10
 * @project   Teampass
11
 * @file      main.functions.php
12
 * ---
13
 * @author    Nils Laumaillé ([email protected])
14
 * @copyright 2009-2019 Teampass.net
15
 * @license   https://spdx.org/licenses/GPL-3.0-only.html#licenseText GPL-3.0
16
 * ---
17
 * @see       https://www.teampass.net
18
 */
19
20
use LdapRecord\Connection;
21
22
23
if (isset($_SESSION['CPM']) === false || (int) $_SESSION['CPM'] !== 1) {
24
    die('Hacking attempt...');
25
}
26
27
// Load config if $SETTINGS not defined
28
if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
29
    if (file_exists('../includes/config/tp.config.php')) {
30
        include_once '../includes/config/tp.config.php';
31
    } elseif (file_exists('./includes/config/tp.config.php')) {
32
        include_once './includes/config/tp.config.php';
33
    } elseif (file_exists('../../includes/config/tp.config.php')) {
34
        include_once '../../includes/config/tp.config.php';
35
    } else {
36
        throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
37
    }
38
}
39
40
header('Content-type: text/html; charset=utf-8');
41
header('Cache-Control: no-cache, must-revalidate');
42
43
/**
44
 * Convert language code to string.
45
 *
46
 * @param string $string String to get
47
 *
48
 * @return string
49
 */
50
function langHdl($string)
51
{
52
    if (empty($string) === true) {
53
        // Manage error
54
        return 'ERROR in language strings!';
55
    }
56
57
    // Load superglobal
58
    if (file_exists('../includes/libraries/protect/SuperGlobal/SuperGlobal.php')) {
59
        include_once '../includes/libraries/protect/SuperGlobal/SuperGlobal.php';
60
    } elseif (file_exists('./includes/libraries/protect/SuperGlobal/SuperGlobal.php')) {
61
        include_once './includes/libraries/protect/SuperGlobal/SuperGlobal.php';
62
    } elseif (file_exists('../../includes/libraries/protect/SuperGlobal/SuperGlobal.php')) {
63
        include_once '../../includes/libraries/protect/SuperGlobal/SuperGlobal.php';
64
    } else {
65
        throw new Exception("Error file '/includes/libraries/protect/SuperGlobal/SuperGlobal.php' not exists", 1);
66
    }
67
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
68
69
    // Get language string
70
    $session_language = $superGlobal->get(trim($string), 'SESSION', 'lang');
71
72
    if (isset($session_language) === false) {
73
        // Manage error
74
        return 'ERROR in language strings!';
75
    } else {
76
        return str_replace(
77
            array('"', "'"),
78
            array('&quot;', '&apos;'),
79
            $session_language
80
        );
81
    }
82
}
83
84
//Generate N# of random bits for use as salt
85
/**
86
 * Undocumented function.
87
 *
88
 * @param int $size Length
89
 *
90
 * @return string
91
 */
92
function getBits($size)
93
{
94
    $str = '';
95
    $var_x = $size + 10;
96
    for ($var_i = 0; $var_i < $var_x; ++$var_i) {
97
        $str .= base_convert(mt_rand(1, 36), 10, 36);
98
    }
99
100
    return substr($str, 0, $size);
101
}
102
103
//generate pbkdf2 compliant hash
104
function strHashPbkdf2($var_p, $var_s, $var_c, $var_kl, $var_a = 'sha256', $var_st = 0)
105
{
106
    $var_kb = $var_st + $var_kl; // Key blocks to compute
107
    $var_dk = ''; // Derived key
108
109
    for ($block = 1; $block <= $var_kb; ++$block) { // Create key
110
        $var_ib = $var_h = hash_hmac($var_a, $var_s . pack('N', $block), $var_p, true); // Initial hash for this block
111
        for ($var_i = 1; $var_i < $var_c; ++$var_i) { // Perform block iterations
112
            $var_ib ^= ($var_h = hash_hmac($var_a, $var_h, $var_p, true)); // XOR each iterate
113
        }
114
        $var_dk .= $var_ib; // Append iterated block
115
    }
116
117
    return substr($var_dk, $var_st, $var_kl); // Return derived key of correct length
118
}
119
120
/**
121
 * stringUtf8Decode().
122
 *
123
 * utf8_decode
124
 */
125
function stringUtf8Decode($string)
126
{
127
    return str_replace(' ', '+', utf8_decode($string));
128
}
129
130
/**
131
 * encryptOld().
132
 *
133
 * crypt a string
134
 *
135
 * @param string $text
136
 */
137
function encryptOld($text, $personalSalt = '')
138
{
139
    if (empty($personalSalt) === false) {
140
        return trim(
141
            base64_encode(
142
                /** @scrutinizer ignore-deprecated */ mcrypt_encrypt(
143
                    MCRYPT_RIJNDAEL_256,
144
                    $personalSalt,
145
                    $text,
146
                    MCRYPT_MODE_ECB,
147
                    /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(
148
                        /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
149
                        MCRYPT_RAND
150
                    )
151
                )
152
            )
153
        );
154
    }
155
156
    // If $personalSalt is not empty
157
    return trim(
158
        base64_encode(
159
            /** @scrutinizer ignore-deprecated */ mcrypt_encrypt(
160
                MCRYPT_RIJNDAEL_256,
161
                SALT,
162
                $text,
163
                MCRYPT_MODE_ECB,
164
                /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(
165
                    /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
166
                    MCRYPT_RAND
167
                )
168
            )
169
        )
170
    );
171
}
172
173
/**
174
 * decryptOld().
175
 *
176
 * decrypt a crypted string
177
 */
178
function decryptOld($text, $personalSalt = '')
179
{
180
    if (!empty($personalSalt)) {
181
        return trim(
182
            /** @scrutinizer ignore-deprecated */ mcrypt_decrypt(
183
                MCRYPT_RIJNDAEL_256,
184
                $personalSalt,
185
                base64_decode($text),
186
                MCRYPT_MODE_ECB,
187
                /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(
188
                    /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
189
                    MCRYPT_RAND
190
                )
191
            )
192
        );
193
    }
194
195
    // No personal SK
196
    return trim(
197
        /** @scrutinizer ignore-deprecated */ mcrypt_decrypt(
198
            MCRYPT_RIJNDAEL_256,
199
            SALT,
200
            base64_decode($text),
201
            MCRYPT_MODE_ECB,
202
            /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(
203
                /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
204
                MCRYPT_RAND
205
            )
206
        )
207
    );
208
}
209
210
/**
211
 * encrypt().
212
 *
213
 * crypt a string
214
 *
215
 * @param string $decrypted
216
 */
217
/*function encrypt($decrypted, $personalSalt = '')
218
{
219
    global $SETTINGS;
220
221
    if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
222
        require_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
223
    } else {
224
        require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
225
    }
226
227
    if (!empty($personalSalt)) {
228
        $staticSalt = $personalSalt;
229
    } else {
230
        $staticSalt = SALT;
231
    }
232
233
    //set our salt to a variable
234
    // Get 64 random bits for the salt for pbkdf2
235
    $pbkdf2Salt = getBits(64);
236
    // generate a pbkdf2 key to use for the encryption.
237
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
238
    // Build $init_vect and $ivBase64.  We use a block size of 256 bits (AES compliant)
239
    // and CTR mode.  (Note: ECB mode is inadequate as IV is not used.)
240
    $init_vect = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, 'ctr'), MCRYPT_RAND);
241
242
    //base64 trim
243
    if (strlen($ivBase64 = rtrim(base64_encode($init_vect), '=')) != 43) {
244
        return false;
245
    }
246
    // Encrypt $decrypted
247
    $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $decrypted, 'ctr', $init_vect);
248
    // MAC the encrypted text
249
    $mac = hash_hmac('sha256', $encrypted, $staticSalt);
250
    // We're done!
251
    return base64_encode($ivBase64 . $encrypted . $mac . $pbkdf2Salt);
252
}
253
/*
254
255
/**
256
 * decrypt().
257
 *
258
 * decrypt a crypted string
259
 */
260
/*function decrypt($encrypted, $personalSalt = '')
261
{
262
    global $SETTINGS;
263
264
    if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
265
        include_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
266
    } else {
267
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
268
    }
269
270
    if (!empty($personalSalt)) {
271
        $staticSalt = $personalSalt;
272
    } else {
273
        $staticSalt = file_get_contents(SECUREPATH . '/teampass-seckey.txt');
274
    }
275
    //base64 decode the entire payload
276
    $encrypted = base64_decode($encrypted);
277
    // get the salt
278
    $pbkdf2Salt = substr($encrypted, -64);
279
    //remove the salt from the string
280
    $encrypted = substr($encrypted, 0, -64);
281
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
282
    // Retrieve $init_vect which is the first 22 characters plus ==, base64_decoded.
283
    $init_vect = base64_decode(substr($encrypted, 0, 43) . '==');
284
    // Remove $init_vect from $encrypted.
285
    $encrypted = substr($encrypted, 43);
286
    // Retrieve $mac which is the last 64 characters of $encrypted.
287
    $mac = substr($encrypted, -64);
288
    // Remove the last 64 chars from encrypted (remove MAC)
289
    $encrypted = substr($encrypted, 0, -64);
290
    //verify the sha256hmac from the encrypted data before even trying to decrypt it
291
    if (hash_hmac('sha256', $encrypted, $staticSalt) != $mac) {
292
        return false;
293
    }
294
    // Decrypt the data.
295
    $decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, 'ctr', $init_vect), "\0\4");
296
    // Yay!
297
    return $decrypted;
298
}
299
*/
300
301
/**
302
 * genHash().
303
 *
304
 * Generate a hash for user login
305
 *
306
 * @param string $password
307
 */
308
function bCrypt($password, $cost)
309
{
310
    $salt = sprintf('$2y$%02d$', $cost);
311
    if (function_exists('openssl_random_pseudo_bytes')) {
312
        $salt .= bin2hex(openssl_random_pseudo_bytes(11));
313
    } else {
314
        $chars = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
315
        for ($i = 0; $i < 22; ++$i) {
316
            $salt .= $chars[mt_rand(0, 63)];
317
        }
318
    }
319
320
    return crypt($password, $salt);
321
}
322
323
function testHex2Bin($val)
324
{
325
    if (!@hex2bin($val)) {
326
        throw new Exception('ERROR');
327
    }
328
329
    return hex2bin($val);
330
}
331
332
/**
333
 * Defuse cryption function.
334
 *
335
 * @param string $message   what to de/crypt
336
 * @param string $ascii_key key to use
337
 * @param string $type      operation to perform
338
 * @param array  $SETTINGS  Teampass settings
339
 *
340
 * @return array
341
 */
342
function cryption($message, $ascii_key, $type, $SETTINGS)
343
{
344
    $ascii_key = (empty($ascii_key) === true) ? file_get_contents(SECUREPATH . '/teampass-seckey.txt') : $ascii_key;
345
    $err = false;
346
347
    // load PhpEncryption library
348
    if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
349
        $path = '../includes/libraries/Encryption/Encryption/';
350
    } else {
351
        $path = $SETTINGS['cpassman_dir'] . '/includes/libraries/Encryption/Encryption/';
352
    }
353
354
    include_once $path . 'Crypto.php';
355
    include_once $path . 'Encoding.php';
356
    include_once $path . 'DerivedKeys.php';
357
    include_once $path . 'Key.php';
358
    include_once $path . 'KeyOrPassword.php';
359
    include_once $path . 'File.php';
360
    include_once $path . 'RuntimeTests.php';
361
    include_once $path . 'KeyProtectedByPassword.php';
362
    include_once $path . 'Core.php';
363
364
    // convert KEY
365
    $key = \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key);
366
367
    try {
368
        if ($type === 'encrypt') {
369
            $text = \Defuse\Crypto\Crypto::encrypt($message, $key);
370
        } else if ($type === 'decrypt') {
371
            $text = \Defuse\Crypto\Crypto::decrypt($message, $key);
372
        }
373
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
374
        $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.';
375
    } catch (Defuse\Crypto\Exception\BadFormatException $ex) {
376
        $err = $ex;
377
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
378
        $err = $ex;
379
    } catch (Defuse\Crypto\Exception\CryptoException $ex) {
380
        $err = $ex;
381
    } catch (Defuse\Crypto\Exception\IOException $ex) {
382
        $err = $ex;
383
    }
384
    //echo \Defuse\Crypto\Crypto::decrypt($message, $key).' ## ';
385
386
    return array(
387
        'string' => isset($text) ? $text : '',
388
        'error' => $err,
389
    );
390
}
391
392
/**
393
 * Generating a defuse key.
394
 *
395
 * @return string
396
 */
397
function defuse_generate_key()
398
{
399
    // load PhpEncryption library
400
    if (file_exists('../includes/config/tp.config.php') === true) {
401
        $path = '../includes/libraries/Encryption/Encryption/';
402
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
403
        $path = './includes/libraries/Encryption/Encryption/';
404
    } else {
405
        $path = '../includes/libraries/Encryption/Encryption/';
406
    }
407
408
    include_once $path . 'Crypto.php';
409
    include_once $path . 'Encoding.php';
410
    include_once $path . 'DerivedKeys.php';
411
    include_once $path . 'Key.php';
412
    include_once $path . 'KeyOrPassword.php';
413
    include_once $path . 'File.php';
414
    include_once $path . 'RuntimeTests.php';
415
    include_once $path . 'KeyProtectedByPassword.php';
416
    include_once $path . 'Core.php';
417
418
    $key = \Defuse\Crypto\Key::createNewRandomKey();
419
    $key = $key->saveToAsciiSafeString();
420
421
    return $key;
422
}
423
424
/**
425
 * Generate a Defuse personal key.
426
 *
427
 * @param string $psk psk used
428
 *
429
 * @return string
430
 */
431
function defuse_generate_personal_key($psk)
432
{
433
    // load PhpEncryption library
434
    if (file_exists('../includes/config/tp.config.php') === true) {
435
        $path = '../includes/libraries/Encryption/Encryption/';
436
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
437
        $path = './includes/libraries/Encryption/Encryption/';
438
    } else {
439
        $path = '../includes/libraries/Encryption/Encryption/';
440
    }
441
442
    include_once $path . 'Crypto.php';
443
    include_once $path . 'Encoding.php';
444
    include_once $path . 'DerivedKeys.php';
445
    include_once $path . 'Key.php';
446
    include_once $path . 'KeyOrPassword.php';
447
    include_once $path . 'File.php';
448
    include_once $path . 'RuntimeTests.php';
449
    include_once $path . 'KeyProtectedByPassword.php';
450
    include_once $path . 'Core.php';
451
452
    $protected_key = \Defuse\Crypto\KeyProtectedByPassword::createRandomPasswordProtectedKey($psk);
453
    $protected_key_encoded = $protected_key->saveToAsciiSafeString();
454
455
    return $protected_key_encoded; // save this in user table
456
}
457
458
/**
459
 * Validate persoanl key with defuse.
460
 *
461
 * @param string $psk                   the user's psk
462
 * @param string $protected_key_encoded special key
463
 *
464
 * @return string
465
 */
466
function defuse_validate_personal_key($psk, $protected_key_encoded)
467
{
468
    // load PhpEncryption library
469
    if (file_exists('../includes/config/tp.config.php') === true) {
470
        $path = '../includes/libraries/Encryption/Encryption/';
471
    } elseif (file_exists('./includes/config/tp.config.php') === true) {
472
        $path = './includes/libraries/Encryption/Encryption/';
473
    } else {
474
        $path = '../includes/libraries/Encryption/Encryption/';
475
    }
476
477
    include_once $path . 'Crypto.php';
478
    include_once $path . 'Encoding.php';
479
    include_once $path . 'DerivedKeys.php';
480
    include_once $path . 'Key.php';
481
    include_once $path . 'KeyOrPassword.php';
482
    include_once $path . 'File.php';
483
    include_once $path . 'RuntimeTests.php';
484
    include_once $path . 'KeyProtectedByPassword.php';
485
    include_once $path . 'Core.php';
486
487
    try {
488
        $protected_key = \Defuse\Crypto\KeyProtectedByPassword::loadFromAsciiSafeString($protected_key_encoded);
489
        $user_key = $protected_key->unlockKey($psk);
490
        $user_key_encoded = $user_key->saveToAsciiSafeString();
491
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
492
        return 'Error - Major issue as the encryption is broken.';
493
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
494
        return 'Error - The saltkey is not the correct one.';
495
    }
496
497
    return $user_key_encoded; // store it in session once user has entered his psk
498
}
499
500
/**
501
 * Decrypt a defuse string if encrypted.
502
 *
503
 * @param string $value Encrypted string
504
 *
505
 * @return string Decrypted string
506
 */
507
function defuseReturnDecrypted($value, $SETTINGS)
508
{
509
    if (substr($value, 0, 3) === 'def') {
510
        $value = cryption($value, '', 'decrypt', $SETTINGS)['string'];
511
    }
512
513
    return $value;
514
}
515
516
/**
517
 * Trims a string depending on a specific string.
518
 *
519
 * @param string|array $chaine  what to trim
520
 * @param string       $element trim on what
521
 *
522
 * @return string
523
 */
524
function trimElement($chaine, $element)
525
{
526
    if (!empty($chaine)) {
527
        if (is_array($chaine) === true) {
528
            $chaine = implode(';', $chaine);
529
        }
530
        $chaine = trim($chaine);
531
        if (substr($chaine, 0, 1) === $element) {
532
            $chaine = substr($chaine, 1);
533
        }
534
        if (substr($chaine, strlen($chaine) - 1, 1) === $element) {
535
            $chaine = substr($chaine, 0, strlen($chaine) - 1);
536
        }
537
    }
538
539
    return $chaine;
540
}
541
542
/**
543
 * Permits to suppress all "special" characters from string.
544
 *
545
 * @param string $string  what to clean
546
 * @param bool   $special use of special chars?
547
 *
548
 * @return string
549
 */
550
function cleanString($string, $special = false)
551
{
552
    // Create temporary table for special characters escape
553
    $tabSpecialChar = array();
554
    for ($i = 0; $i <= 31; ++$i) {
555
        $tabSpecialChar[] = chr($i);
556
    }
557
    array_push($tabSpecialChar, '<br />');
558
    if ((int) $special === 1) {
559
        $tabSpecialChar = array_merge($tabSpecialChar, array('</li>', '<ul>', '<ol>'));
560
    }
561
562
    return str_replace($tabSpecialChar, "\n", $string);
563
}
564
565
/**
566
 * Erro manager for DB.
567
 *
568
 * @param array $params output from query
569
 */
570
function db_error_handler($params)
571
{
572
    echo 'Error: ' . $params['error'] . "<br>\n";
573
    echo 'Query: ' . $params['query'] . "<br>\n";
574
    throw new Exception('Error - Query', 1);
575
}
576
577
/**
578
 * [identifyUserRights description].
579
 *
580
 * @param string $groupesVisiblesUser  [description]
581
 * @param string $groupesInterditsUser [description]
582
 * @param string $isAdmin              [description]
583
 * @param string $idFonctions          [description]
584
 *
585
 * @return string [description]
586
 */
587
function identifyUserRights(
588
    $groupesVisiblesUser,
589
    $groupesInterditsUser,
590
    $isAdmin,
591
    $idFonctions,
592
    $SETTINGS
593
) {
594
    //load ClassLoader
595
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
596
597
    // Load superglobal
598
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
599
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
600
601
    //Connect to DB
602
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
603
    if (defined('DB_PASSWD_CLEAR') === false) {
604
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
605
    }
606
    DB::$host = DB_HOST;
607
    DB::$user = DB_USER;
608
    DB::$password = DB_PASSWD_CLEAR;
609
    DB::$dbName = DB_NAME;
610
    DB::$port = DB_PORT;
611
    DB::$encoding = DB_ENCODING;
612
613
    //Build tree
614
    $tree = new SplClassLoader('Tree\NestedTree', $SETTINGS['cpassman_dir'] . '/includes/libraries');
615
    $tree->register();
616
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
617
618
    // Check if user is ADMINISTRATOR
619
    if ((int) $isAdmin === 1) {
620
        identAdmin(
621
            $idFonctions,
622
            $SETTINGS,
623
            /** @scrutinizer ignore-type */ $tree
624
        );
625
    } else {
626
        identUser(
627
            $groupesVisiblesUser,
628
            $groupesInterditsUser,
629
            $idFonctions,
630
            $SETTINGS,
631
            /** @scrutinizer ignore-type */ $tree
632
        );
633
    }
634
635
    // update user's timestamp
636
    DB::update(
637
        prefixTable('users'),
638
        array(
639
            'timestamp' => time(),
640
        ),
641
        'id=%i',
642
        $superGlobal->get('user_id', 'SESSION')
643
    );
644
}
645
646
/**
647
 * Identify administrator.
648
 *
649
 * @param string $idFonctions Roles of user
650
 * @param array  $SETTINGS    Teampass settings
651
 * @param array  $tree        Tree of folders
652
 */
653
function identAdmin($idFonctions, $SETTINGS, $tree)
654
{
655
    // Load superglobal
656
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
657
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
658
659
    // Init
660
    $groupesVisibles = array();
661
    $superGlobal->put('personal_folders', array(), 'SESSION');
662
    $superGlobal->put('groupes_visibles', array(), 'SESSION');
663
    $superGlobal->put('no_access_folders', array(), 'SESSION');
664
    $superGlobal->put('personal_visible_groups', array(), 'SESSION');
665
    $superGlobal->put('read_only_folders', array(), 'SESSION');
666
    $superGlobal->put('list_restricted_folders_for_items', array(), 'SESSION');
667
    $superGlobal->put('list_folders_editable_by_role', array(), 'SESSION');
668
    $superGlobal->put('list_folders_limited', array(), 'SESSION');
669
    $superGlobal->put('no_access_folders', array(), 'SESSION');
670
    $superGlobal->put('forbiden_pfs', array(), 'SESSION');
671
672
    // Get superglobals
673
    $globalsUserId = $superGlobal->get('user_id', 'SESSION');
674
    $globalsVisibleFolders = $superGlobal->get('groupes_visibles', 'SESSION');
675
    $globalsPersonalVisibleFolders = $superGlobal->get('personal_visible_groups', 'SESSION');
676
677
    // Get list of Folders
678
    $rows = DB::query('SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i', 0);
679
    foreach ($rows as $record) {
680
        array_push($groupesVisibles, $record['id']);
681
    }
682
    $superGlobal->put('groupes_visibles', $groupesVisibles, 'SESSION');
683
    $superGlobal->put('all_non_personal_folders', $groupesVisibles, 'SESSION');
684
685
    // Exclude all PF
686
    $where = new WhereClause('and'); // create a WHERE statement of pieces joined by ANDs
687
    $where->add('personal_folder=%i', 1);
688
    if (
689
        isset($SETTINGS['enable_pf_feature']) === true
690
        && (int) $SETTINGS['enable_pf_feature'] === 1
691
    ) {
692
        $where->add('title=%s', $globalsUserId);
693
        $where->negateLast();
694
    }
695
    // Get ID of personal folder
696
    $persfld = DB::queryfirstrow(
697
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE title = %s',
698
        $globalsUserId
699
    );
700
    if (empty($persfld['id']) === false) {
701
        if (in_array($persfld['id'], $globalsVisibleFolders) === false) {
702
            array_push($globalsVisibleFolders, $persfld['id']);
703
            array_push($globalsPersonalVisibleFolders, $persfld['id']);
704
            // get all descendants
705
            $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
706
            $tree->rebuild();
707
            $tst = $tree->getDescendants($persfld['id']);
708
            foreach ($tst as $t) {
709
                array_push($globalsVisibleFolders, $t->id);
710
                array_push($globalsPersonalVisibleFolders, $t->id);
711
            }
712
        }
713
    }
714
715
    // get complete list of ROLES
716
    $tmp = explode(';', $idFonctions);
717
    $rows = DB::query(
718
        'SELECT * FROM ' . prefixTable('roles_title') . '
719
        ORDER BY title ASC'
720
    );
721
    foreach ($rows as $record) {
722
        if (!empty($record['id']) && !in_array($record['id'], $tmp)) {
723
            array_push($tmp, $record['id']);
724
        }
725
    }
726
    $superGlobal->put('fonction_id', implode(';', $tmp), 'SESSION');
727
    $superGlobal->put('is_admin', 1, 'SESSION');
728
729
    // Check if admin has created Folders and Roles
730
    DB::query('SELECT * FROM ' . prefixTable('nested_tree') . '');
731
    $superGlobal->put('nb_folders', DB::count(), 'SESSION');
732
    DB::query('SELECT * FROM ' . prefixTable('roles_title'));
733
    $superGlobal->put('nb_roles', DB::count(), 'SESSION');
734
}
735
736
/**
737
 * Permits to convert an element to array.
738
 *
739
 * @param string|array $element Any value to be returned as array
740
 *
741
 * @return array
742
 */
743
function convertToArray($element)
744
{
745
    if (is_string($element) === true) {
746
        if (empty($element) === true) {
747
            return array();
748
        } else {
749
            return explode(
750
                ';',
751
                trimElement($element, ';')
752
            );
753
        }
754
    } else {
755
        return $element;
756
    }
757
}
758
759
/**
760
 * Defines the rights the user has.
761
 *
762
 * @param string|array $allowedFolders  Allowed folders
763
 * @param string|array $noAccessFolders Not allowed folders
764
 * @param string|array $userRoles       Roles of user
765
 * @param array        $SETTINGS        Teampass settings
766
 * @param array        $tree            Tree of folders
767
 */
768
function identUser(
769
    $allowedFolders,
770
    $noAccessFolders,
771
    $userRoles,
772
    $SETTINGS,
773
    $tree
774
) {
775
    // Load superglobal
776
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
777
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
778
779
    // Init
780
    $superGlobal->put('groupes_visibles', array(), 'SESSION');
781
    $superGlobal->put('personal_folders', array(), 'SESSION');
782
    $superGlobal->put('no_access_folders', array(), 'SESSION');
783
    $superGlobal->put('personal_visible_groups', array(), 'SESSION');
784
    $superGlobal->put('read_only_folders', array(), 'SESSION');
785
    $superGlobal->put('fonction_id', $userRoles, 'SESSION');
786
    $superGlobal->put('is_admin', 0, 'SESSION');
787
788
    // init
789
    $personalFolders = array();
790
    $readOnlyFolders = array();
791
    $noAccessPersonalFolders = array();
792
    $restrictedFoldersForItems = array();
793
    $foldersLimited = array();
794
    $foldersLimitedFull = array();
795
    $allowedFoldersByRoles = array();
796
797
    // Get superglobals
798
    $globalsUserId = $superGlobal->get('user_id', 'SESSION');
799
    $globalsPersonalFolders = $superGlobal->get('personal_folder', 'SESSION');
800
801
    // Ensure consistency in array format
802
    $noAccessFolders = convertToArray($noAccessFolders);
803
    $userRoles = convertToArray($userRoles);
804
    $allowedFolders = convertToArray($allowedFolders);
805
806
    // Get list of folders depending on Roles
807
    $rows = DB::query(
808
        'SELECT *
809
        FROM ' . prefixTable('roles_values') . '
810
        WHERE role_id IN %li AND type IN %ls',
811
        $userRoles,
812
        array('W', 'ND', 'NE', 'NDNE', 'R')
813
    );
814
    foreach ($rows as $record) {
815
        if ($record['type'] === 'R') {
816
            array_push($readOnlyFolders, $record['folder_id']);
817
        } elseif (in_array($record['folder_id'], $allowedFolders) === false) {
818
            array_push($allowedFoldersByRoles, $record['folder_id']);
819
        }
820
    }
821
    $allowedFoldersByRoles = array_unique($allowedFoldersByRoles);
822
    $readOnlyFolders = array_unique($readOnlyFolders);
823
824
    // Clean arrays
825
    foreach ($allowedFoldersByRoles as $value) {
826
        if (($key = array_search($value, $readOnlyFolders)) !== false) {
827
            unset($readOnlyFolders[$key]);
828
        }
829
    }
830
831
    // Does this user is allowed to see other items
832
    $inc = 0;
833
    $rows = DB::query(
834
        'SELECT id, id_tree FROM ' . prefixTable('items') . '
835
        WHERE restricted_to LIKE %ss AND inactif = %s',
836
        $globalsUserId . ';',
837
        '0'
838
    );
839
    foreach ($rows as $record) {
840
        // Exclude restriction on item if folder is fully accessible
841
        if (in_array($record['id_tree'], $allowedFolders) === false) {
842
            $restrictedFoldersForItems[$record['id_tree']][$inc] = $record['id'];
843
            ++$inc;
844
        }
845
    }
846
847
    // Check for the users roles if some specific rights exist on items
848
    $rows = DB::query(
849
        'SELECT i.id_tree, r.item_id
850
        FROM ' . prefixTable('items') . ' as i
851
        INNER JOIN ' . prefixTable('restriction_to_roles') . ' as r ON (r.item_id=i.id)
852
        WHERE r.role_id IN %li
853
        ORDER BY i.id_tree ASC',
854
        $userRoles
855
    );
856
    $inc = 0;
857
    foreach ($rows as $record) {
858
        if (isset($record['id_tree'])) {
859
            $foldersLimited[$record['id_tree']][$inc] = $record['item_id'];
860
            array_push($foldersLimitedFull, $record['item_id']);
861
            ++$inc;
862
        }
863
    }
864
865
    // Get list of Personal Folders
866
    if (
867
        isset($SETTINGS['enable_pf_feature']) === true && (int) $SETTINGS['enable_pf_feature'] === 1
868
        && isset($globalsPersonalFolders) === true && (int) $globalsPersonalFolders === 1
869
    ) {
870
        $persoFld = DB::queryfirstrow(
871
            'SELECT id
872
            FROM ' . prefixTable('nested_tree') . '
873
            WHERE title = %s AND personal_folder = %i',
874
            $globalsUserId,
875
            1
876
        );
877
        if (empty($persoFld['id']) === false) {
878
            if (in_array($persoFld['id'], $allowedFolders) === false) {
879
                array_push($personalFolders, $persoFld['id']);
880
                array_push($allowedFolders, $persoFld['id']);
881
882
                // get all descendants
883
                $ids = $tree->getChildren($persoFld['id'], false);
884
                foreach ($ids as $ident) {
885
                    if ((int) $ident->personal_folder === 1) {
886
                        array_push($allowedFolders, $ident->id);
887
                        array_push($personalFolders, $ident->id);
888
                    }
889
                }
890
            }
891
        }
892
    }
893
894
    // Exclude all other PF
895
    $where = new WhereClause('and');
896
    $where->add('personal_folder=%i', 1);
897
    if (
898
        isset($SETTINGS['enable_pf_feature']) === true && (int) $SETTINGS['enable_pf_feature'] === 1
899
        && isset($globalsPersonalFolders) === true && (int) $globalsPersonalFolders === 1
900
    ) {
901
        $where->add('title=%s', $globalsUserId);
902
        $where->negateLast();
903
    }
904
    $persoFlds = DB::query(
905
        'SELECT id
906
        FROM ' . prefixTable('nested_tree') . '
907
        WHERE %l',
908
        $where
909
    );
910
    foreach ($persoFlds as $persoFldId) {
911
        array_push($noAccessPersonalFolders, $persoFldId['id']);
912
    }
913
914
    // All folders visibles
915
    $allowedFolders = array_merge(
916
        $foldersLimitedFull,
917
        $allowedFoldersByRoles,
918
        $restrictedFoldersForItems,
919
        $readOnlyFolders
920
    );
921
922
    // Exclude from allowed folders all the specific user forbidden folders
923
    if (count($noAccessFolders) > 0) {
924
        $allowedFolders = array_diff($allowedFolders, $noAccessFolders);
925
    }
926
927
    // Return data
928
    $superGlobal->put('all_non_personal_folders', $allowedFolders, 'SESSION');
929
    $superGlobal->put('groupes_visibles', array_merge($allowedFolders, $personalFolders), 'SESSION');
930
    $superGlobal->put('read_only_folders', $readOnlyFolders, 'SESSION');
931
    $superGlobal->put('no_access_folders', $noAccessFolders, 'SESSION');
932
    $superGlobal->put('personal_folders', $personalFolders, 'SESSION');
933
    $superGlobal->put('list_folders_limited', $foldersLimited, 'SESSION');
934
    $superGlobal->put('list_folders_editable_by_role', $allowedFoldersByRoles, 'SESSION');
935
    $superGlobal->put('list_restricted_folders_for_items', $restrictedFoldersForItems, 'SESSION');
936
    $superGlobal->put('forbiden_pfs', $noAccessPersonalFolders, 'SESSION');
937
    $superGlobal->put(
938
        'all_folders_including_no_access',
939
        array_merge(
940
            $allowedFolders,
941
            $personalFolders,
942
            $noAccessFolders,
943
            $readOnlyFolders
944
        ),
945
        'SESSION'
946
    );
947
948
    // Folders and Roles numbers
949
    DB::queryfirstrow('SELECT id FROM ' . prefixTable('nested_tree') . '');
950
    $superGlobal->put('nb_folders', DB::count(), 'SESSION');
951
    DB::queryfirstrow('SELECT id FROM ' . prefixTable('roles_title'));
952
    $superGlobal->put('nb_roles', DB::count(), 'SESSION');
953
954
    // check if change proposals on User's items
955
    if (isset($SETTINGS['enable_suggestion']) === true && (int) $SETTINGS['enable_suggestion'] === 1) {
956
        DB::query(
957
            'SELECT *
958
            FROM ' . prefixTable('items_change') . ' AS c
959
            LEFT JOIN ' . prefixTable('log_items') . ' AS i ON (c.item_id = i.id_item)
960
            WHERE i.action = %s AND i.id_user = %i',
961
            'at_creation',
962
            $globalsUserId
963
        );
964
        $superGlobal->put('nb_item_change_proposals', DB::count(), 'SESSION');
965
    } else {
966
        $superGlobal->put('nb_item_change_proposals', 0, 'SESSION');
967
    }
968
969
    return true;
970
}
971
972
/**
973
 * Update the CACHE table.
974
 *
975
 * @param string $action   What to do
976
 * @param array  $SETTINGS Teampass settings
977
 * @param string $ident    Ident format
978
 */
979
function updateCacheTable($action, $SETTINGS, $ident = null)
980
{
981
    if ($action === 'reload') {
982
        // Rebuild full cache table
983
        cacheTableRefresh($SETTINGS);
984
    } elseif ($action === 'update_value' && is_null($ident) === false) {
985
        // UPDATE an item
986
        cacheTableUpdate($SETTINGS, $ident);
987
    } elseif ($action === 'add_value' && is_null($ident) === false) {
988
        // ADD an item
989
        cacheTableAdd($SETTINGS, $ident);
990
    } elseif ($action === 'delete_value' && is_null($ident) === false) {
991
        // DELETE an item
992
        DB::delete(prefixTable('cache'), 'id = %i', $ident);
993
    }
994
}
995
996
/**
997
 * Cache table - refresh.
998
 *
999
 * @param array $SETTINGS Teampass settings
1000
 */
1001
function cacheTableRefresh($SETTINGS)
1002
{
1003
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1004
1005
    //Connect to DB
1006
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1007
    if (defined('DB_PASSWD_CLEAR') === false) {
1008
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
1009
    }
1010
    DB::$host = DB_HOST;
1011
    DB::$user = DB_USER;
1012
    DB::$password = DB_PASSWD_CLEAR;
1013
    DB::$dbName = DB_NAME;
1014
    DB::$port = DB_PORT;
1015
    DB::$encoding = DB_ENCODING;
1016
1017
    //Load Tree
1018
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
1019
    $tree->register();
1020
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
1021
1022
    // truncate table
1023
    DB::query('TRUNCATE TABLE ' . prefixTable('cache'));
1024
1025
    // reload date
1026
    $rows = DB::query(
1027
        'SELECT *
1028
        FROM ' . prefixTable('items') . ' as i
1029
        INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
1030
        AND l.action = %s
1031
        AND i.inactif = %i',
1032
        'at_creation',
1033
        0
1034
    );
1035
    foreach ($rows as $record) {
1036
        if (empty($record['id_tree']) === false) {
1037
            // Get all TAGS
1038
            $tags = '';
1039
            $itemTags = DB::query(
1040
                'SELECT tag
1041
                FROM ' . prefixTable('tags') . '
1042
                WHERE item_id = %i AND tag != ""',
1043
                $record['id']
1044
            );
1045
            foreach ($itemTags as $itemTag) {
1046
                $tags .= $itemTag['tag'] . ' ';
1047
            }
1048
1049
            // Get renewal period
1050
            $resNT = DB::queryfirstrow(
1051
                'SELECT renewal_period
1052
                FROM ' . prefixTable('nested_tree') . '
1053
                WHERE id = %i',
1054
                $record['id_tree']
1055
            );
1056
1057
            // form id_tree to full foldername
1058
            $folder = array();
1059
            $arbo = $tree->getPath($record['id_tree'], true);
1060
            foreach ($arbo as $elem) {
1061
                // Check if title is the ID of a user
1062
                if (is_numeric($elem->title) === true) {
1063
                    // Is this a User id?
1064
                    $user = DB::queryfirstrow(
1065
                        'SELECT id, login
1066
                        FROM ' . prefixTable('users') . '
1067
                        WHERE id = %i',
1068
                        $elem->title
1069
                    );
1070
                    if (count($user) > 0) {
1071
                        $elem->title = $user['login'];
1072
                    }
1073
                }
1074
                // Build path
1075
                array_push($folder, stripslashes($elem->title));
1076
            }
1077
            // store data
1078
            DB::insert(
1079
                prefixTable('cache'),
1080
                array(
1081
                    'id' => $record['id'],
1082
                    'label' => $record['label'],
1083
                    'description' => isset($record['description']) ? $record['description'] : '',
1084
                    'url' => (isset($record['url']) && !empty($record['url'])) ? $record['url'] : '0',
1085
                    'tags' => $tags,
1086
                    'id_tree' => $record['id_tree'],
1087
                    'perso' => $record['perso'],
1088
                    'restricted_to' => (isset($record['restricted_to']) && !empty($record['restricted_to'])) ? $record['restricted_to'] : '0',
1089
                    'login' => isset($record['login']) ? $record['login'] : '',
1090
                    'folder' => implode(' > ', $folder),
1091
                    'author' => $record['id_user'],
1092
                    'renewal_period' => isset($resNT['renewal_period']) ? $resNT['renewal_period'] : '0',
1093
                    'timestamp' => $record['date'],
1094
                )
1095
            );
1096
        }
1097
    }
1098
}
1099
1100
/**
1101
 * Cache table - update existing value.
1102
 *
1103
 * @param array  $SETTINGS Teampass settings
1104
 * @param string $ident    Ident format
1105
 */
1106
function cacheTableUpdate($SETTINGS, $ident = null)
1107
{
1108
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1109
1110
    // Load superglobal
1111
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1112
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1113
1114
    //Connect to DB
1115
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1116
    if (defined('DB_PASSWD_CLEAR') === false) {
1117
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
1118
    }
1119
    DB::$host = DB_HOST;
1120
    DB::$user = DB_USER;
1121
    DB::$password = DB_PASSWD_CLEAR;
1122
    DB::$dbName = DB_NAME;
1123
    DB::$port = DB_PORT;
1124
    DB::$encoding = DB_ENCODING;
1125
1126
    //Load Tree
1127
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
1128
    $tree->register();
1129
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
1130
1131
    // get new value from db
1132
    $data = DB::queryfirstrow(
1133
        'SELECT label, description, id_tree, perso, restricted_to, login, url
1134
        FROM ' . prefixTable('items') . '
1135
        WHERE id=%i',
1136
        $ident
1137
    );
1138
    // Get all TAGS
1139
    $tags = '';
1140
    $itemTags = DB::query(
1141
        'SELECT tag
1142
        FROM ' . prefixTable('tags') . '
1143
        WHERE item_id = %i AND tag != ""',
1144
        $ident
1145
    );
1146
    foreach ($itemTags as $itemTag) {
1147
        $tags .= $itemTag['tag'] . ' ';
1148
    }
1149
    // form id_tree to full foldername
1150
    $folder = array();
1151
    $arbo = $tree->getPath($data['id_tree'], true);
1152
    foreach ($arbo as $elem) {
1153
        // Check if title is the ID of a user
1154
        if (is_numeric($elem->title) === true) {
1155
            // Is this a User id?
1156
            $user = DB::queryfirstrow(
1157
                'SELECT id, login
1158
                FROM ' . prefixTable('users') . '
1159
                WHERE id = %i',
1160
                $elem->title
1161
            );
1162
            if (count($user) > 0) {
1163
                $elem->title = $user['login'];
1164
            }
1165
        }
1166
        // Build path
1167
        array_push($folder, stripslashes($elem->title));
1168
    }
1169
    // finaly update
1170
    DB::update(
1171
        prefixTable('cache'),
1172
        array(
1173
            'label' => $data['label'],
1174
            'description' => $data['description'],
1175
            'tags' => $tags,
1176
            'url' => (isset($data['url']) && !empty($data['url'])) ? $data['url'] : '0',
1177
            'id_tree' => $data['id_tree'],
1178
            'perso' => $data['perso'],
1179
            'restricted_to' => (isset($data['restricted_to']) && !empty($data['restricted_to'])) ? $data['restricted_to'] : '0',
1180
            'login' => isset($data['login']) ? $data['login'] : '',
1181
            'folder' => implode(' » ', $folder),
1182
            'author' => $superGlobal->get('user_id', 'SESSION'),
1183
        ),
1184
        'id = %i',
1185
        $ident
1186
    );
1187
}
1188
1189
/**
1190
 * Cache table - add new value.
1191
 *
1192
 * @param array  $SETTINGS Teampass settings
1193
 * @param string $ident    Ident format
1194
 */
1195
function cacheTableAdd($SETTINGS, $ident = null)
1196
{
1197
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1198
1199
    // Load superglobal
1200
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1201
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1202
1203
    // Get superglobals
1204
    $globalsUserId = $superGlobal->get('user_id', 'SESSION');
1205
1206
    //Connect to DB
1207
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1208
    if (defined('DB_PASSWD_CLEAR') === false) {
1209
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
1210
    }
1211
    DB::$host = DB_HOST;
1212
    DB::$user = DB_USER;
1213
    DB::$password = DB_PASSWD_CLEAR;
1214
    DB::$dbName = DB_NAME;
1215
    DB::$port = DB_PORT;
1216
    DB::$encoding = DB_ENCODING;
1217
1218
    //Load Tree
1219
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
1220
    $tree->register();
1221
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
1222
1223
    // get new value from db
1224
    $data = DB::queryFirstRow(
1225
        'SELECT i.label, i.description, i.id_tree as id_tree, i.perso, i.restricted_to, i.id, i.login, i.url, l.date
1226
        FROM ' . prefixTable('items') . ' as i
1227
        INNER JOIN ' . prefixTable('log_items') . ' as l ON (l.id_item = i.id)
1228
        WHERE i.id = %i
1229
        AND l.action = %s',
1230
        $ident,
1231
        'at_creation'
1232
    );
1233
    // Get all TAGS
1234
    $tags = '';
1235
    $itemTags = DB::query(
1236
        'SELECT tag
1237
        FROM ' . prefixTable('tags') . '
1238
        WHERE item_id = %i AND tag != ""',
1239
        $ident
1240
    );
1241
    foreach ($itemTags as $itemTag) {
1242
        $tags .= $itemTag['tag'] . ' ';
1243
    }
1244
    // form id_tree to full foldername
1245
    $folder = array();
1246
    $arbo = $tree->getPath($data['id_tree'], true);
1247
    foreach ($arbo as $elem) {
1248
        // Check if title is the ID of a user
1249
        if (is_numeric($elem->title) === true) {
1250
            // Is this a User id?
1251
            $user = DB::queryfirstrow(
1252
                'SELECT id, login
1253
                FROM ' . prefixTable('users') . '
1254
                WHERE id = %i',
1255
                $elem->title
1256
            );
1257
            if (count($user) > 0) {
1258
                $elem->title = $user['login'];
1259
            }
1260
        }
1261
        // Build path
1262
        array_push($folder, stripslashes($elem->title));
1263
    }
1264
    // finaly update
1265
    DB::insert(
1266
        prefixTable('cache'),
1267
        array(
1268
            'id' => $data['id'],
1269
            'label' => $data['label'],
1270
            'description' => $data['description'],
1271
            'tags' => (isset($tags) && empty($tags) === false) ? $tags : 'None',
1272
            'url' => (isset($data['url']) && !empty($data['url'])) ? $data['url'] : '0',
1273
            'id_tree' => $data['id_tree'],
1274
            'perso' => (isset($data['perso']) && empty($data['perso']) === false && $data['perso'] !== 'None') ? $data['perso'] : '0',
1275
            'restricted_to' => (isset($data['restricted_to']) && empty($data['restricted_to']) === false) ? $data['restricted_to'] : '0',
1276
            'login' => isset($data['login']) ? $data['login'] : '',
1277
            'folder' => implode(' » ', $folder),
1278
            'author' => $globalsUserId,
1279
            'timestamp' => $data['date'],
1280
        )
1281
    );
1282
}
1283
1284
/**
1285
 * Do statistics.
1286
 *
1287
 * @param array $SETTINGS Teampass settings
1288
 *
1289
 * @return array
1290
 */
1291
function getStatisticsData($SETTINGS)
1292
{
1293
    DB::query(
1294
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i',
1295
        0
1296
    );
1297
    $counter_folders = DB::count();
1298
1299
    DB::query(
1300
        'SELECT id FROM ' . prefixTable('nested_tree') . ' WHERE personal_folder = %i',
1301
        1
1302
    );
1303
    $counter_folders_perso = DB::count();
1304
1305
    DB::query(
1306
        'SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i',
1307
        0
1308
    );
1309
    $counter_items = DB::count();
1310
1311
    DB::query(
1312
        'SELECT id FROM ' . prefixTable('items') . ' WHERE perso = %i',
1313
        1
1314
    );
1315
    $counter_items_perso = DB::count();
1316
1317
    DB::query(
1318
        'SELECT id FROM ' . prefixTable('users') . ''
1319
    );
1320
    $counter_users = DB::count();
1321
1322
    DB::query(
1323
        'SELECT id FROM ' . prefixTable('users') . ' WHERE admin = %i',
1324
        1
1325
    );
1326
    $admins = DB::count();
1327
1328
    DB::query(
1329
        'SELECT id FROM ' . prefixTable('users') . ' WHERE gestionnaire = %i',
1330
        1
1331
    );
1332
    $managers = DB::count();
1333
1334
    DB::query(
1335
        'SELECT id FROM ' . prefixTable('users') . ' WHERE read_only = %i',
1336
        1
1337
    );
1338
    $readOnly = DB::count();
1339
1340
    // list the languages
1341
    $usedLang = [];
1342
    $tp_languages = DB::query(
1343
        'SELECT name FROM ' . prefixTable('languages')
1344
    );
1345
    foreach ($tp_languages as $tp_language) {
1346
        DB::query(
1347
            'SELECT * FROM ' . prefixTable('users') . ' WHERE user_language = %s',
1348
            $tp_language['name']
1349
        );
1350
        $usedLang[$tp_language['name']] = round((DB::count() * 100 / $counter_users), 0);
1351
    }
1352
1353
    // get list of ips
1354
    $usedIp = [];
1355
    $tp_ips = DB::query(
1356
        'SELECT user_ip FROM ' . prefixTable('users')
1357
    );
1358
    foreach ($tp_ips as $ip) {
1359
        if (array_key_exists($ip['user_ip'], $usedIp)) {
1360
            $usedIp[$ip['user_ip']] = $usedIp[$ip['user_ip']] + 1;
1361
        } elseif (!empty($ip['user_ip']) && $ip['user_ip'] !== 'none') {
1362
            $usedIp[$ip['user_ip']] = 1;
1363
        }
1364
    }
1365
1366
    return array(
1367
        'error' => '',
1368
        'stat_phpversion' => phpversion(),
1369
        'stat_folders' => $counter_folders,
1370
        'stat_folders_shared' => intval($counter_folders) - intval($counter_folders_perso),
1371
        'stat_items' => $counter_items,
1372
        'stat_items_shared' => intval($counter_items) - intval($counter_items_perso),
1373
        'stat_users' => $counter_users,
1374
        'stat_admins' => $admins,
1375
        'stat_managers' => $managers,
1376
        'stat_ro' => $readOnly,
1377
        'stat_kb' => $SETTINGS['enable_kb'],
1378
        'stat_pf' => $SETTINGS['enable_pf_feature'],
1379
        'stat_fav' => $SETTINGS['enable_favourites'],
1380
        'stat_teampassversion' => TP_VERSION_FULL,
1381
        'stat_ldap' => $SETTINGS['ldap_mode'],
1382
        'stat_agses' => $SETTINGS['agses_authentication_enabled'],
1383
        'stat_duo' => $SETTINGS['duo'],
1384
        'stat_suggestion' => $SETTINGS['enable_suggestion'],
1385
        'stat_api' => $SETTINGS['api'],
1386
        'stat_customfields' => $SETTINGS['item_extra_fields'],
1387
        'stat_syslog' => $SETTINGS['syslog_enable'],
1388
        'stat_2fa' => $SETTINGS['google_authentication'],
1389
        'stat_stricthttps' => $SETTINGS['enable_sts'],
1390
        'stat_mysqlversion' => DB::serverVersion(),
1391
        'stat_languages' => $usedLang,
1392
        'stat_country' => $usedIp,
1393
    );
1394
}
1395
1396
/**
1397
 * Permits to send an email.
1398
 *
1399
 * @param string $subject     email subject
1400
 * @param string $textMail    email message
1401
 * @param string $email       email
1402
 * @param array  $SETTINGS    settings
1403
 * @param string $textMailAlt email message alt
1404
 * @param bool   $silent      no errors
1405
 *
1406
 * @return string some json info
1407
 */
1408
function sendEmail(
1409
    $subject,
1410
    $textMail,
1411
    $email,
1412
    $SETTINGS,
1413
    $textMailAlt = null,
1414
    $silent = true
1415
) {
1416
    // CAse where email not defined
1417
    if ($email === 'none' || empty($email) === true) {
1418
        return '"error":"" , "message":"' . langHdl('forgot_my_pw_email_sent') . '"';
1419
    }
1420
1421
    // Load settings
1422
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
1423
1424
    // Load superglobal
1425
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1426
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1427
1428
    // Get user language
1429
    include_once $SETTINGS['cpassman_dir'] . '/includes/language/' . $superGlobal->get('user_language', 'SESSION') . '.php';
1430
1431
    // Load library
1432
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1433
1434
    // load PHPMailer
1435
    $mail = new SplClassLoader('PHPMailer\PHPMailer', '../includes/libraries');
1436
    $mail->register();
1437
    $mail = new PHPMailer\PHPMailer\PHPMailer(true);
1438
    try {
1439
        // send to user
1440
        $mail->setLanguage('en', $SETTINGS['cpassman_dir'] . '/includes/libraries/PHPMailer/PHPMailer/language/');
1441
        $mail->SMTPDebug = 0; //value 1 can be used to debug - 4 for debuging connections
1442
        $mail->Port = $SETTINGS['email_port']; //COULD BE USED
1443
        $mail->CharSet = 'utf-8';
1444
        $mail->SMTPSecure = ($SETTINGS['email_security'] === 'tls'
1445
            || $SETTINGS['email_security'] === 'ssl') ? $SETTINGS['email_security'] : '';
1446
        $mail->SMTPAutoTLS = ($SETTINGS['email_security'] === 'tls'
1447
            || $SETTINGS['email_security'] === 'ssl') ? true : false;
1448
        $mail->SMTPOptions = array(
1449
            'ssl' => array(
1450
                'verify_peer' => false,
1451
                'verify_peer_name' => false,
1452
                'allow_self_signed' => true,
1453
            ),
1454
        );
1455
        $mail->isSmtp(); // send via SMTP
1456
        $mail->Host = $SETTINGS['email_smtp_server']; // SMTP servers
1457
        $mail->SMTPAuth = (int) $SETTINGS['email_smtp_auth'] === 1 ? true : false; // turn on SMTP authentication
1458
        $mail->Username = $SETTINGS['email_auth_username']; // SMTP username
1459
        $mail->Password = $SETTINGS['email_auth_pwd']; // SMTP password
1460
        $mail->From = $SETTINGS['email_from'];
1461
        $mail->FromName = $SETTINGS['email_from_name'];
1462
1463
        // Prepare for each person
1464
        foreach (array_filter(explode(',', $email)) as $dest) {
1465
            $mail->addAddress($dest);
1466
        }
1467
1468
        // Prepare HTML
1469
        $text_html = emailBody($textMail);
1470
1471
        $mail->WordWrap = 80; // set word wrap
1472
        $mail->isHtml(true); // send as HTML
1473
        $mail->Subject = $subject;
1474
        $mail->Body = $text_html;
1475
        $mail->AltBody = (is_null($textMailAlt) === false) ? $textMailAlt : '';
1476
        // send email
1477
        $mail->send();
1478
        $mail->smtpClose();
1479
        if ($silent === false) {
1480
            return json_encode(
1481
                array(
1482
                    'error' => false,
1483
                    'message' => langHdl('forgot_my_pw_email_sent'),
1484
                )
1485
            );
1486
        }
1487
    } catch (Exception $e) {
1488
        if ($silent === false) {
1489
            return json_encode(
1490
                array(
1491
                    'error' => true,
1492
                    'message' => str_replace(array("\n", "\t", "\r"), '', $mail->ErrorInfo),
1493
                )
1494
            );
1495
        }
1496
    }
1497
}
1498
1499
/**
1500
 * Returns the email body.
1501
 *
1502
 * @param string $textMail Text for the email
1503
 *
1504
 * @return string
1505
 */
1506
function emailBody($textMail)
1507
{
1508
    return '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.=
1509
    w3.org/TR/html4/loose.dtd"><html>
1510
    <head><title>Email Template</title>
1511
    <style type="text/css">
1512
    body { background-color: #f0f0f0; padding: 10px 0; margin:0 0 10px =0; }
1513
    </style></head>
1514
    <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">
1515
    <table border="0" width="100%" height="100%" cellpadding="0" cellspacing="0" bgcolor="#f0f0f0" style="border-spacing: 0;">
1516
    <tr><td style="border-collapse: collapse;"><br>
1517
        <table border="0" width="100%" cellpadding="0" cellspacing="0" bgcolor="#17357c" style="border-spacing: 0; margin-bottom: 25px;">
1518
        <tr><td style="border-collapse: collapse; padding: 11px 20px;">
1519
            <div style="max-width:150px; max-height:34px; color:#f0f0f0; font-weight:bold;">Teampass</div>
1520
        </td></tr></table></td>
1521
    </tr>
1522
    <tr><td align="center" valign="top" bgcolor="#f0f0f0" style="border-collapse: collapse; background-color: #f0f0f0;">
1523
        <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;">
1524
        <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;">
1525
        <br><div style="float:right;">' .
1526
        $textMail .
1527
        '<br><br></td></tr></table>
1528
    </td></tr></table>
1529
    <br></body></html>';
1530
}
1531
1532
/**
1533
 * Generate a Key.
1534
 *
1535
 * @return string
1536
 */
1537
function generateKey()
1538
{
1539
    return substr(md5(rand() . rand()), 0, 15);
1540
}
1541
1542
/**
1543
 * Convert date to timestamp.
1544
 *
1545
 * @param string $date     The date
1546
 * @param array  $SETTINGS Teampass settings
1547
 *
1548
 * @return string
1549
 */
1550
function dateToStamp($date, $SETTINGS)
1551
{
1552
    $date = date_parse_from_format($SETTINGS['date_format'], $date);
1553
    if ((int) $date['warning_count'] === 0 && (int) $date['error_count'] === 0) {
1554
        return mktime(23, 59, 59, $date['month'], $date['day'], $date['year']);
1555
    } else {
1556
        return '';
1557
    }
1558
}
1559
1560
/**
1561
 * Is this a date.
1562
 *
1563
 * @param string $date Date
1564
 *
1565
 * @return bool
1566
 */
1567
function isDate($date)
1568
{
1569
    return strtotime($date) !== false;
1570
}
1571
1572
/**
1573
 * Check if isUTF8().
1574
 *
1575
 * @return int is the string in UTF8 format
1576
 */
1577
function isUTF8($string)
1578
{
1579
    if (is_array($string) === true) {
1580
        $string = $string['string'];
1581
    }
1582
1583
    return preg_match(
1584
        '%^(?:
1585
        [\x09\x0A\x0D\x20-\x7E] # ASCII
1586
        | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
1587
        | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
1588
        | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
1589
        | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
1590
        | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
1591
        | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
1592
        | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
1593
        )*$%xs',
1594
        $string
1595
    );
1596
}
1597
1598
/**
1599
 * Prepare an array to UTF8 format before JSON_encode.
1600
 *
1601
 * @param array $array Array of values
1602
 *
1603
 * @return array
1604
 */
1605
function utf8Converter($array)
1606
{
1607
    array_walk_recursive(
1608
        $array,
1609
        function (&$item) {
1610
            if (mb_detect_encoding($item, 'utf-8', true) === false) {
1611
                $item = utf8_encode($item);
1612
            }
1613
        }
1614
    );
1615
1616
    return $array;
1617
}
1618
1619
/**
1620
 * Permits to prepare data to be exchanged.
1621
 *
1622
 * @param array|string $data Text
1623
 * @param string       $type Parameter
1624
 * @param string       $key  Optional key
1625
 *
1626
 * @return resource|string|array
1627
 */
1628
function prepareExchangedData($data, $type, $key = null)
1629
{
1630
    if (file_exists('../includes/config/tp.config.php')) {
1631
        include '../includes/config/tp.config.php';
1632
    } elseif (file_exists('./includes/config/tp.config.php')) {
1633
        include './includes/config/tp.config.php';
1634
    } elseif (file_exists('../../includes/config/tp.config.php')) {
1635
        include '../../includes/config/tp.config.php';
1636
    } else {
1637
        throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
1638
    }
1639
1640
    if (isset($SETTINGS) === false) {
1641
        return 'ERROR';
1642
    }
1643
1644
    // Load superglobal
1645
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1646
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1647
1648
    // Get superglobals
1649
    if ($key !== null) {
1650
        $superGlobal->put('key', $key, 'SESSION');
1651
        $globalsKey = $key;
1652
    } else {
1653
        $globalsKey = $superGlobal->get('key', 'SESSION');
1654
    }
1655
1656
    //load ClassLoader
1657
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1658
    //Load AES
1659
    $aes = new SplClassLoader('Encryption\Crypt', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1660
    $aes->register();
1661
1662
    if ($type === 'encode' && is_array($data) === true) {
1663
        // Ensure UTF8 format
1664
        $data = utf8Converter($data);
1665
        // Now encode
1666
        if (isset($SETTINGS['encryptClientServer'])
1667
            && $SETTINGS['encryptClientServer'] === '0'
1668
        ) {
1669
            return json_encode(
1670
                $data,
1671
                JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1672
            );
1673
        } else {
1674
            return Encryption\Crypt\aesctr::encrypt(
1675
                json_encode(
1676
                    $data,
1677
                    JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1678
                ),
1679
                $globalsKey,
1680
                256
1681
            );
1682
        }
1683
    } elseif ($type === 'decode' && is_array($data) === false) {
1684
        if (isset($SETTINGS['encryptClientServer'])
1685
            && $SETTINGS['encryptClientServer'] === '0'
1686
        ) {
1687
            return json_decode(
1688
                /** @scrutinizer ignore-type */ (string) $data,
1689
                true
1690
            );
1691
        } else {
1692
            return json_decode(
1693
                Encryption\Crypt\aesctr::decrypt(
1694
                    /** @scrutinizer ignore-type */ (string) $data,
1695
                    $globalsKey,
1696
                    256
1697
                ),
1698
                true
1699
            );
1700
        }
1701
    }
1702
}
1703
1704
/**
1705
 * Create a thumbnail.
1706
 *
1707
 * @param string  $src           Source
1708
 * @param string  $dest          Destination
1709
 * @param integer $desired_width Size of width
1710
 */
1711
function makeThumbnail($src, $dest, $desired_width)
1712
{
1713
    /* read the source image */
1714
    if(is_file($src) === true && mime_content_type($src) === 'image/png'){
1715
        $source_image = imagecreatefrompng($src);
1716
        if ($source_image === false) {
1717
            return "Error: Not a valid PNG file! It's type is ".mime_content_type($src);
1718
        }
1719
    } else {
1720
        return "Error: Not a valid PNG file! It's type is ".mime_content_type($src);
1721
    }
1722
    
1723
    // Get height and width
1724
    $width = imagesx($source_image);
1725
    $height = imagesy($source_image);
1726
1727
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1728
    $desired_height = (int) floor($height * ($desired_width / $width));
1729
1730
    /* create a new, "virtual" image */
1731
    $virtual_image = imagecreatetruecolor($desired_width, $desired_height);
1732
    if ($virtual_image === false) {
1733
        return false;
1734
    }
1735
    /* copy source image at a resized size */
1736
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
1737
1738
    /* create the physical thumbnail image to its destination */
1739
    imagejpeg($virtual_image, $dest);
1740
}
1741
1742
/**
1743
 * Check table prefix in SQL query.
1744
 *
1745
 * @param string $table Table name
1746
 *
1747
 * @return string
1748
 */
1749
function prefixTable($table)
1750
{
1751
    $safeTable = htmlspecialchars(DB_PREFIX . $table);
1752
    if (!empty($safeTable)) {
1753
        // sanitize string
1754
        return $safeTable;
1755
    } else {
1756
        // stop error no table
1757
        return 'table_not_exists';
1758
    }
1759
}
1760
1761
1762
/**
1763
 * GenerateCryptKey
1764
 *
1765
 * @param int     $size      Length
1766
 * @param boolean $secure    Secure
1767
 * @param boolean $numerals  Numerics
1768
 * @param boolean $uppercase Uppercase letters
1769
 * @param boolean $symbols   Symbols
1770
 * @param boolean $lowercase Lowercase
1771
 * @param array   $SETTINGS  SETTINGS
1772
 * @return string
1773
 */
1774
function GenerateCryptKey(
1775
    $size = 10,
1776
    $secure = false,
1777
    $numerals = false,
1778
    $uppercase = false,
1779
    $symbols = false,
1780
    $lowercase = false,
1781
    $SETTINGS
1782
) {
1783
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
1784
    $generator = new SplClassLoader('PasswordGenerator\Generator', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1785
    $generator->register();
1786
    $generator = new PasswordGenerator\Generator\ComputerPasswordGenerator();
1787
1788
    // Is PHP7 being used?
1789
    if (version_compare(PHP_VERSION, '7.0.0', '>=')) {
1790
        $php7generator = new SplClassLoader('PasswordGenerator\RandomGenerator', $SETTINGS['cpassman_dir'] . '/includes/libraries');
1791
        $php7generator->register();
1792
        $generator->setRandomGenerator(new PasswordGenerator\RandomGenerator\Php7RandomGenerator());
1793
    }
1794
1795
    // Manage size
1796
    $generator->setLength((int) $size);
1797
1798
    if ($secure === true) {
1799
        $generator->setSymbols(true);
1800
        $generator->setLowercase(true);
1801
        $generator->setUppercase(true);
1802
        $generator->setNumbers(true);
1803
    } else {
1804
        $generator->setLowercase($lowercase);
1805
        $generator->setUppercase($uppercase);
1806
        $generator->setNumbers($numerals);
1807
        $generator->setSymbols($symbols);
1808
    }
1809
1810
    return $generator->generatePasswords()[0];
1811
}
1812
1813
/*
1814
* Send sysLOG message
1815
* @param string $message
1816
* @param string $host
1817
*/
1818
function send_syslog($message, $host, $port, $component = 'teampass')
1819
{
1820
    $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
1821
    $syslog_message = '<123>' . date('M d H:i:s ') . $component . ': ' . $message;
1822
    socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, $host, $port);
1823
    socket_close($sock);
1824
}
1825
1826
/**
1827
 * Permits to log events into DB
1828
 *
1829
 * @param array  $SETTINGS Teampass settings
1830
 * @param string $type     Type
1831
 * @param string $label    Label
1832
 * @param string $who      Who
1833
 * @param string $login    Login
1834
 * @param string $field_1  Field
1835
 *
1836
 * @return void
1837
 */
1838
function logEvents($SETTINGS, $type, $label, $who, $login = null, $field_1 = null)
1839
{
1840
    if (empty($who)) {
1841
        $who = getClientIpServer();
1842
    }
1843
1844
    // include librairies & connect to DB
1845
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1846
    if (defined('DB_PASSWD_CLEAR') === false) {
1847
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
1848
    }
1849
    DB::$host = DB_HOST;
1850
    DB::$user = DB_USER;
1851
    DB::$password = DB_PASSWD_CLEAR;
1852
    DB::$dbName = DB_NAME;
1853
    DB::$port = DB_PORT;
1854
    DB::$encoding = DB_ENCODING;
1855
1856
    DB::insert(
1857
        prefixTable('log_system'),
1858
        array(
1859
            'type' => $type,
1860
            'date' => time(),
1861
            'label' => $label,
1862
            'qui' => $who,
1863
            'field_1' => $field_1 === null ? '' : $field_1,
1864
        )
1865
    );
1866
1867
    // If SYSLOG
1868
    if (isset($SETTINGS['syslog_enable']) === true && (int) $SETTINGS['syslog_enable'] === 1) {
1869
        if ($type === 'user_mngt') {
1870
            send_syslog(
1871
                'action=' . str_replace('at_', '', $label) . ' attribute=user user=' . $who . ' userid="' . $login . '" change="' . $field_1 . '" ',
1872
                $SETTINGS['syslog_host'],
1873
                $SETTINGS['syslog_port'],
1874
                'teampass'
1875
            );
1876
        } else {
1877
            send_syslog(
1878
                'action=' . $type . ' attribute=' . $label . ' user=' . $who . ' userid="' . $login . '" ',
1879
                $SETTINGS['syslog_host'],
1880
                $SETTINGS['syslog_port'],
1881
                'teampass'
1882
            );
1883
        }
1884
    }
1885
}
1886
1887
/**
1888
 * Log events.
1889
 *
1890
 * @param array  $SETTINGS        Teampass settings
1891
 * @param int    $item_id         Item id
1892
 * @param string $item_label      Item label
1893
 * @param int    $id_user         User id
1894
 * @param string $action          Code for reason
1895
 * @param string $login           User login
1896
 * @param string $raison          Code for reason
1897
 * @param string $encryption_type Encryption on
1898
 */
1899
function logItems(
1900
    $SETTINGS,
1901
    $item_id,
1902
    $item_label,
1903
    $id_user,
1904
    $action,
1905
    $login = null,
1906
    $raison = null,
1907
    $encryption_type = null
1908
) {
1909
    // include librairies & connect to DB
1910
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
1911
    if (defined('DB_PASSWD_CLEAR') === false) {
1912
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
1913
    }
1914
    DB::$host = DB_HOST;
1915
    DB::$user = DB_USER;
1916
    DB::$password = DB_PASSWD_CLEAR;
1917
    DB::$dbName = DB_NAME;
1918
    DB::$port = DB_PORT;
1919
    DB::$encoding = DB_ENCODING;
1920
1921
    // Insert log in DB
1922
    DB::insert(
1923
        prefixTable('log_items'),
1924
        array(
1925
            'id_item' => $item_id,
1926
            'date' => time(),
1927
            'id_user' => $id_user,
1928
            'action' => $action,
1929
            'raison' => $raison,
1930
            'raison_iv' => '',
1931
            'encryption_type' => is_null($encryption_type) === true ? TP_ENCRYPTION_NAME : $encryption_type,
1932
        )
1933
    );
1934
    // Timestamp the last change
1935
    if ($action === 'at_creation' || $action === 'at_modifiation' || $action === 'at_delete' || $action === 'at_import') {
1936
        DB::update(
1937
            prefixTable('misc'),
1938
            array(
1939
                'valeur' => time(),
1940
            ),
1941
            'type = %s AND intitule = %s',
1942
            'timestamp',
1943
            'last_item_change'
1944
        );
1945
    }
1946
1947
    // SYSLOG
1948
    if (isset($SETTINGS['syslog_enable']) === true && $SETTINGS['syslog_enable'] === '1') {
1949
        // Extract reason
1950
        $attribute = isnull($raison) === true ? '' : explode(' : ', $raison);
0 ignored issues
show
Bug introduced by
It seems like $raison can also be of type null; however, parameter $string of explode() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

1950
        $attribute = isnull($raison) === true ? '' : explode(' : ', /** @scrutinizer ignore-type */ $raison);
Loading history...
Bug introduced by
The function isnull was not found. Maybe you did not declare it correctly or list all dependencies? ( Ignorable by Annotation )

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

1950
        $attribute = /** @scrutinizer ignore-call */ isnull($raison) === true ? '' : explode(' : ', $raison);
Loading history...
1951
1952
        // Get item info if not known
1953
        if (empty($item_label) === true) {
1954
            $dataItem = DB::queryfirstrow(
1955
                'SELECT id, id_tree, label
1956
                FROM ' . prefixTable('items') . '
1957
                WHERE id = %i',
1958
                $item_id
1959
            );
1960
1961
            $item_label = $dataItem['label'];
1962
        }
1963
1964
        send_syslog(
1965
            'action=' . str_replace('at_', '', $action) .
0 ignored issues
show
introduced by
The condition 'action=' . str_replace(...isnull($login) === true is always false.
Loading history...
1966
                ' attribute=' . str_replace('at_', '', $attribute[0]) .
1967
                ' itemno=' . $item_id .
1968
                ' user=' . isnull($login) === true ? '' : addslashes($login) .
1 ignored issue
show
Bug introduced by
It seems like $login can also be of type null; however, parameter $string of addslashes() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

1968
                ' user=' . isnull($login) === true ? '' : addslashes(/** @scrutinizer ignore-type */ $login) .
Loading history...
1969
                ' itemname="' . addslashes($item_label) . '"',
1970
            $SETTINGS['syslog_host'],
1971
            $SETTINGS['syslog_port'],
1972
            'teampass'
1973
        );
1974
    }
1975
1976
    // send notification if enabled
1977
    notifyOnChange($item_id, $action, $SETTINGS);
1978
}
1979
1980
/**
1981
 * If enabled, then notify admin/manager.
1982
 *
1983
 * @param int    $item_id  Item id
1984
 * @param string $action   Action to do
1985
 * @param array  $SETTINGS Teampass settings
1986
 */
1987
function notifyOnChange($item_id, $action, $SETTINGS)
1988
{
1989
    if (
1990
        isset($SETTINGS['enable_email_notification_on_item_shown']) === true
1991
        && (int) $SETTINGS['enable_email_notification_on_item_shown'] === 1
1992
        && $action === 'at_shown'
1993
    ) {
1994
        // Load superglobal
1995
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1996
        $superGlobal = new protect\SuperGlobal\SuperGlobal();
1997
1998
        // Get superglobals
1999
        $globalsLastname = $superGlobal->get('lastname', 'SESSION');
2000
        $globalsName = $superGlobal->get('name', 'SESSION');
2001
        $globalsNotifiedEmails = $superGlobal->get('listNotificationEmails', 'SESSION');
2002
2003
        // Get info about item
2004
        $dataItem = DB::queryfirstrow(
2005
            'SELECT id, id_tree, label
2006
            FROM ' . prefixTable('items') . '
2007
            WHERE id = %i',
2008
            $item_id
2009
        );
2010
        $item_label = $dataItem['label'];
2011
2012
        // send back infos
2013
        DB::insert(
2014
            prefixTable('emails'),
2015
            array(
2016
                'timestamp' => time(),
2017
                'subject' => langHdl('email_on_open_notification_subject'),
2018
                'body' => str_replace(
2019
                    array('#tp_user#', '#tp_item#', '#tp_link#'),
2020
                    array(
2021
                        addslashes($globalsName . ' ' . $globalsLastname),
2022
                        addslashes($item_label),
2023
                        $SETTINGS['cpassman_url'] . '/index.php?page=items&group=' . $dataItem['id_tree'] . '&id=' . $item_id,
2024
                    ),
2025
                    langHdl('email_on_open_notification_mail')
2026
                ),
2027
                'receivers' => $globalsNotifiedEmails,
2028
                'status' => '',
2029
            )
2030
        );
2031
    }
2032
}
2033
2034
/**
2035
 * Prepare notification email to subscribers.
2036
 *
2037
 * @param int    $item_id  Item id
2038
 * @param string $label    Item label
2039
 * @param array  $changes  List of changes
2040
 * @param array  $SETTINGS Teampass settings
2041
 */
2042
function notifyChangesToSubscribers($item_id, $label, $changes, $SETTINGS)
2043
{
2044
    // Load superglobal
2045
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2046
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2047
2048
    // Get superglobals
2049
    $globalsUserId = $superGlobal->get('user_id', 'SESSION');
2050
    $globalsLastname = $superGlobal->get('lastname', 'SESSION');
2051
    $globalsName = $superGlobal->get('name', 'SESSION');
2052
2053
    // send email to user that what to be notified
2054
    $notification = DB::queryOneColumn(
2055
        'email',
2056
        'SELECT *
2057
        FROM ' . prefixTable('notification') . ' AS n
2058
        INNER JOIN ' . prefixTable('users') . ' AS u ON (n.user_id = u.id)
2059
        WHERE n.item_id = %i AND n.user_id != %i',
2060
        $item_id,
2061
        $globalsUserId
2062
    );
2063
2064
    if (DB::count() > 0) {
2065
        // Prepare path
2066
        $path = geItemReadablePath($item_id, '', $SETTINGS);
2067
2068
        // Get list of changes
2069
        $htmlChanges = '<ul>';
2070
        foreach ($changes as $change) {
2071
            $htmlChanges .= '<li>' . $change . '</li>';
2072
        }
2073
        $htmlChanges .= '</ul>';
2074
2075
        // send email
2076
        DB::insert(
2077
            prefixTable('emails'),
2078
            array(
2079
                'timestamp' => time(),
2080
                'subject' => langHdl('email_subject_item_updated'),
2081
                'body' => str_replace(
2082
                    array('#item_label#', '#folder_name#', '#item_id#', '#url#', '#name#', '#lastname#', '#changes#'),
2083
                    array($label, $path, $item_id, $SETTINGS['cpassman_url'], $globalsName, $globalsLastname, $htmlChanges),
2084
                    langHdl('email_body_item_updated')
2085
                ),
2086
                'receivers' => implode(',', $notification),
2087
                'status' => '',
2088
            )
2089
        );
2090
    }
2091
}
2092
2093
/**
2094
 * Returns the Item + path.
2095
 *
2096
 * @param int    $id_tree  Node id
2097
 * @param string $label    Label
2098
 * @param array  $SETTINGS TP settings
2099
 *
2100
 * @return string
2101
 */
2102
function geItemReadablePath($id_tree, $label, $SETTINGS)
2103
{
2104
    // Class loader
2105
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
2106
2107
    //Load Tree
2108
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
2109
    $tree->register();
2110
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
2111
2112
    $arbo = $tree->getPath($id_tree, true);
2113
    $path = '';
2114
    foreach ($arbo as $elem) {
2115
        if (empty($path) === true) {
2116
            $path = htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES) . ' ';
2117
        } else {
2118
            $path .= '&#8594; ' . htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES);
2119
        }
2120
    }
2121
2122
    // Build text to show user
2123
    if (empty($label) === false) {
2124
        return empty($path) === true ? addslashes($label) : addslashes($label) . ' (' . $path . ')';
2125
    } else {
2126
        return empty($path) === true ? '' : $path;
2127
    }
2128
}
2129
2130
/**
2131
 * Get the client ip address.
2132
 *
2133
 * @return string IP address
2134
 */
2135
function getClientIpServer()
2136
{
2137
    if (getenv('HTTP_CLIENT_IP')) {
2138
        $ipaddress = getenv('HTTP_CLIENT_IP');
2139
    } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
2140
        $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
2141
    } elseif (getenv('HTTP_X_FORWARDED')) {
2142
        $ipaddress = getenv('HTTP_X_FORWARDED');
2143
    } elseif (getenv('HTTP_FORWARDED_FOR')) {
2144
        $ipaddress = getenv('HTTP_FORWARDED_FOR');
2145
    } elseif (getenv('HTTP_FORWARDED')) {
2146
        $ipaddress = getenv('HTTP_FORWARDED');
2147
    } elseif (getenv('REMOTE_ADDR')) {
2148
        $ipaddress = getenv('REMOTE_ADDR');
2149
    } else {
2150
        $ipaddress = 'UNKNOWN';
2151
    }
2152
2153
    return $ipaddress;
2154
}
2155
2156
/**
2157
 * Escape all HTML, JavaScript, and CSS.
2158
 *
2159
 * @param string $input    The input string
2160
 * @param string $encoding Which character encoding are we using?
2161
 *
2162
 * @return string
2163
 */
2164
function noHTML($input, $encoding = 'UTF-8')
2165
{
2166
    return htmlspecialchars($input, ENT_QUOTES | ENT_XHTML, $encoding, false);
2167
}
2168
2169
/**
2170
 * 
2171
 * Permits to handle the Teampass config file
2172
 * $action accepts "rebuild" and "update"
2173
 *
2174
 * @param string $action   Action to perform
2175
 * @param array  $SETTINGS Teampass settings
2176
 * @param string $field    Field to refresh
2177
 * @param string $value    Value to set
2178
 *
2179
 * @return string|boolean
2180
 */
2181
function handleConfigFile($action, $SETTINGS, $field = null, $value = null)
2182
{
2183
    $tp_config_file = $SETTINGS['cpassman_dir'] . '/includes/config/tp.config.php';
2184
2185
    // include librairies & connect to DB
2186
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
2187
    if (defined('DB_PASSWD_CLEAR') === false) {
2188
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
2189
    }
2190
    DB::$host = DB_HOST;
2191
    DB::$user = DB_USER;
2192
    DB::$password = DB_PASSWD_CLEAR;
2193
    DB::$dbName = DB_NAME;
2194
    DB::$port = DB_PORT;
2195
    DB::$encoding = DB_ENCODING;
2196
2197
    if (file_exists($tp_config_file) === false || $action === 'rebuild') {
2198
        // perform a copy
2199
        if (file_exists($tp_config_file)) {
2200
            if (!copy($tp_config_file, $tp_config_file . '.' . date('Y_m_d_His', time()))) {
2201
                return "ERROR: Could not copy file '" . $tp_config_file . "'";
2202
            }
2203
        }
2204
2205
2206
        // regenerate
2207
        $data = array();
2208
        $data[0] = "<?php\n";
2209
        $data[1] = "global \$SETTINGS;\n";
2210
        $data[2] = "\$SETTINGS = array (\n";
2211
        $rows = DB::query(
2212
            'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s',
2213
            'admin'
2214
        );
2215
        foreach ($rows as $record) {
2216
            array_push($data, "    '" . $record['intitule'] . "' => '" . $record['valeur'] . "',\n");
2217
        }
2218
        array_push($data, ");\n");
2219
        $data = array_unique($data);
2220
        // ---
2221
    } elseif ($action === 'update' && empty($field) === false) {
2222
        $data = file($tp_config_file);
2223
        $inc = 0;
2224
        $bFound = false;
2225
        foreach ($data as $line) {
2226
            if (stristr($line, ');')) {
2227
                break;
2228
            }
2229
2230
            if (stristr($line, "'" . $field . "' => '")) {
2231
                $data[$inc] = "    '" . $field . "' => '" . filter_var($value, FILTER_SANITIZE_STRING) . "',\n";
2232
                $bFound = true;
2233
                break;
2234
            }
2235
            ++$inc;
2236
        }
2237
        if ($bFound === false) {
2238
            $data[($inc)] = "    '" . $field . "' => '" . filter_var($value, FILTER_SANITIZE_STRING) . "',\n);\n";
2239
        }
2240
    }
2241
2242
    // update file
2243
    file_put_contents($tp_config_file, implode('', isset($data) ? $data : array()));
2244
2245
    return true;
2246
}
2247
2248
2249
/**
2250
 * Permits to replace &#92; to permit correct display
2251
 *
2252
 * @param string $input Some text
2253
 *
2254
 * @return string
2255
 */
2256
function handleBackslash($input)
2257
{
2258
    return str_replace('&amp;#92;', '&#92;', $input);
2259
}
2260
2261
/*
2262
** Permits to loas settings
2263
*/
2264
function loadSettings()
2265
{
2266
    global $SETTINGS;
2267
2268
    /* LOAD CPASSMAN SETTINGS */
2269
    if (!isset($SETTINGS['loaded']) || $SETTINGS['loaded'] != 1) {
2270
        $SETTINGS['duplicate_folder'] = 0; //by default, this is set to 0;
2271
        $SETTINGS['duplicate_item'] = 0; //by default, this is set to 0;
2272
        $SETTINGS['number_of_used_pw'] = 5; //by default, this value is set to 5;
2273
        $settings = array();
2274
2275
        $rows = DB::query(
2276
            'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s_type OR type=%s_type2',
2277
            array(
2278
                'type' => 'admin',
2279
                'type2' => 'settings',
2280
            )
2281
        );
2282
        foreach ($rows as $record) {
2283
            if ($record['type'] === 'admin') {
2284
                $SETTINGS[$record['intitule']] = $record['valeur'];
2285
            } else {
2286
                $settings[$record['intitule']] = $record['valeur'];
2287
            }
2288
        }
2289
        $SETTINGS['loaded'] = 1;
2290
        $SETTINGS['default_session_expiration_time'] = 5;
2291
    }
2292
}
2293
2294
/*
2295
** check if folder has custom fields.
2296
** Ensure that target one also has same custom fields
2297
*/
2298
function checkCFconsistency($source_id, $target_id)
2299
{
2300
    $source_cf = array();
2301
    $rows = DB::QUERY(
2302
        'SELECT id_category
2303
        FROM ' . prefixTable('categories_folders') . '
2304
        WHERE id_folder = %i',
2305
        $source_id
2306
    );
2307
    foreach ($rows as $record) {
2308
        array_push($source_cf, $record['id_category']);
2309
    }
2310
2311
    $target_cf = array();
2312
    $rows = DB::QUERY(
2313
        'SELECT id_category
2314
        FROM ' . prefixTable('categories_folders') . '
2315
        WHERE id_folder = %i',
2316
        $target_id
2317
    );
2318
    foreach ($rows as $record) {
2319
        array_push($target_cf, $record['id_category']);
2320
    }
2321
2322
    $cf_diff = array_diff($source_cf, $target_cf);
2323
    if (count($cf_diff) > 0) {
2324
        return false;
2325
    }
2326
2327
    return true;
2328
}
2329
2330
/**
2331
 * Will encrypte/decrypt a fil eusing Defuse.
2332
 *
2333
 * @param string $type        can be either encrypt or decrypt
2334
 * @param string $source_file path to source file
2335
 * @param string $target_file path to target file
2336
 * @param array  $SETTINGS    Settings
2337
 * @param string $password    A password
2338
 *
2339
 * @return string|boolean
2340
 */
2341
function prepareFileWithDefuse(
2342
    $type,
2343
    $source_file,
2344
    $target_file,
2345
    $SETTINGS,
2346
    $password = null
2347
) {
2348
    // Load AntiXSS
2349
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/AntiXSS.php';
2350
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/ASCII.php';
2351
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/UTF8.php';
2352
    $antiXss = new voku\helper\AntiXSS();
2353
2354
    // Protect against bad inputs
2355
    if (is_array($source_file) === true || is_array($target_file) === true) {
2356
        return 'error_cannot_be_array';
2357
    }
2358
2359
    // Sanitize
2360
    $source_file = $antiXss->xss_clean($source_file);
2361
    $target_file = $antiXss->xss_clean($target_file);
2362
2363
    if (empty($password) === true || is_null($password) === true) {
2364
        // get KEY to define password
2365
        $ascii_key = file_get_contents(SECUREPATH . '/teampass-seckey.txt');
2366
        $password = \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key);
2367
    }
2368
2369
    $err = '';
2370
    if ($type === 'decrypt') {
2371
        // Decrypt file
2372
        $err = defuseFileDecrypt(
2373
            $source_file,
2374
            $target_file,
2375
            $SETTINGS,
2376
            /** @scrutinizer ignore-type */ $password
2377
        );
2378
        // ---
2379
    } elseif ($type === 'encrypt') {
2380
        // Encrypt file
2381
        $err = defuseFileEncrypt(
2382
            $source_file,
2383
            $target_file,
2384
            $SETTINGS,
2385
            /** @scrutinizer ignore-type */ $password
2386
        );
2387
    }
2388
2389
    // return error
2390
    return empty($err) === false ? $err : "";
2391
}
2392
2393
/**
2394
 * Encrypt a file with Defuse.
2395
 *
2396
 * @param string $source_file path to source file
2397
 * @param string $target_file path to target file
2398
 * @param array  $SETTINGS    Settings
2399
 * @param string $password    A password
2400
 *
2401
 * @return string|bool
2402
 */
2403
function defuseFileEncrypt(
2404
    $source_file,
2405
    $target_file,
2406
    $SETTINGS,
2407
    $password = null
2408
) {
2409
    // load PhpEncryption library
2410
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2411
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Crypto.php';
2412
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Encoding.php';
2413
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'DerivedKeys.php';
2414
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Key.php';
2415
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyOrPassword.php';
2416
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'File.php';
2417
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'RuntimeTests.php';
2418
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyProtectedByPassword.php';
2419
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Core.php';
2420
2421
    try {
2422
        \Defuse\Crypto\File::encryptFileWithPassword(
2423
            $source_file,
2424
            $target_file,
2425
            $password
2426
        );
2427
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2428
        $err = 'wrong_key';
2429
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2430
        $err = $ex;
2431
    } catch (Defuse\Crypto\Exception\IOException $ex) {
2432
        $err = $ex;
2433
    }
2434
2435
    // return error
2436
    return empty($err) === false ? $err : true;
2437
}
2438
2439
/**
2440
 * Decrypt a file with Defuse.
2441
 *
2442
 * @param string $source_file path to source file
2443
 * @param string $target_file path to target file
2444
 * @param array  $SETTINGS    Settings
2445
 * @param string $password    A password
2446
 *
2447
 * @return string|bool
2448
 */
2449
function defuseFileDecrypt(
2450
    $source_file,
2451
    $target_file,
2452
    $SETTINGS,
2453
    $password = null
2454
) {
2455
    // load PhpEncryption library
2456
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2457
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Crypto.php';
2458
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Encoding.php';
2459
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'DerivedKeys.php';
2460
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Key.php';
2461
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyOrPassword.php';
2462
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'File.php';
2463
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'RuntimeTests.php';
2464
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyProtectedByPassword.php';
2465
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Core.php';
2466
2467
    try {
2468
        \Defuse\Crypto\File::decryptFileWithPassword(
2469
            $source_file,
2470
            $target_file,
2471
            $password
2472
        );
2473
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2474
        $err = 'wrong_key';
2475
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2476
        $err = $ex;
2477
    } catch (Defuse\Crypto\Exception\IOException $ex) {
2478
        $err = $ex;
2479
    }
2480
2481
    // return error
2482
    return empty($err) === false ? $err : true;
2483
}
2484
2485
/*
2486
* NOT TO BE USED
2487
*/
2488
/**
2489
 * Undocumented function.
2490
 *
2491
 * @param string $text Text to debug
2492
 */
2493
function debugTeampass($text)
2494
{
2495
    $debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+');
2496
    if ($debugFile !== false) {
2497
        fputs($debugFile, $text);
2498
        fclose($debugFile);
2499
    }    
2500
}
2501
2502
/**
2503
 * DELETE the file with expected command depending on server type.
2504
 *
2505
 * @param string $file     Path to file
2506
 * @param array  $SETTINGS Teampass settings
2507
 */
2508
function fileDelete($file, $SETTINGS)
2509
{
2510
    // Load AntiXSS
2511
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/ASCII.php';
2512
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/UTF8.php';
2513
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/AntiXSS.php';
2514
    $antiXss = new voku\helper\AntiXSS();
2515
2516
    $file = $antiXss->xss_clean($file);
2517
    if (is_file($file)) {
2518
        unlink($file);
2519
    }
2520
}
2521
2522
/**
2523
 * Permits to extract the file extension.
2524
 *
2525
 * @param string $file File name
2526
 *
2527
 * @return string
2528
 */
2529
function getFileExtension($file)
2530
{
2531
    if (strpos($file, '.') === false) {
2532
        return $file;
2533
    }
2534
2535
    return substr($file, strrpos($file, '.') + 1);
2536
}
2537
2538
/**
2539
 * Performs chmod operation on subfolders.
2540
 *
2541
 * @param string $dir             Parent folder
2542
 * @param int    $dirPermissions  New permission on folders
2543
 * @param int    $filePermissions New permission on files
2544
 *
2545
 * @return bool
2546
 */
2547
function chmodRecursive($dir, $dirPermissions, $filePermissions)
2548
{
2549
    $pointer_dir = opendir($dir);
2550
    if ($pointer_dir === false) {
2551
        return false;
2552
    }  
2553
    $res = true;
2554
    while (false !== ($file = readdir($pointer_dir))) {
2555
        if (($file === '.') || ($file === '..')) {
2556
            continue;
2557
        }
2558
2559
        $fullPath = $dir . '/' . $file;
2560
2561
        if (is_dir($fullPath)) {
2562
            if ($res = @chmod($fullPath, $dirPermissions)) {
2563
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2564
            }
2565
        } else {
2566
            $res = chmod($fullPath, $filePermissions);
2567
        }
2568
        if (!$res) {
2569
            closedir($pointer_dir);
2570
2571
            return false;
2572
        }
2573
    }
2574
    closedir($pointer_dir);
2575
    if (is_dir($dir) && $res) {
2576
        $res = @chmod($dir, $dirPermissions);
2577
    }
2578
2579
    return $res;
2580
}
2581
2582
/**
2583
 * Check if user can access to this item.
2584
 *
2585
 * @param int $item_id ID of item
2586
 */
2587
function accessToItemIsGranted($item_id, $SETTINGS)
2588
{
2589
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2590
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2591
2592
    // Prepare superGlobal variables
2593
    $session_groupes_visibles = $superGlobal->get('groupes_visibles', 'SESSION');
2594
    $session_list_restricted_folders_for_items = $superGlobal->get('list_restricted_folders_for_items', 'SESSION');
2595
2596
    // Load item data
2597
    $data = DB::queryFirstRow(
2598
        'SELECT id_tree
2599
        FROM ' . prefixTable('items') . '
2600
        WHERE id = %i',
2601
        $item_id
2602
    );
2603
2604
    // Check if user can access this folder
2605
    if (in_array($data['id_tree'], $session_groupes_visibles) === false) {
2606
        // Now check if this folder is restricted to user
2607
        if (isset($session_list_restricted_folders_for_items[$data['id_tree']]) === true
2608
            && in_array($item_id, $session_list_restricted_folders_for_items[$data['id_tree']]) === false
2609
        ) {
2610
            return 'ERR_FOLDER_NOT_ALLOWED';
2611
        }
2612
    }
2613
2614
    return true;
2615
}
2616
2617
/**
2618
 * Creates a unique key.
2619
 *
2620
 * @param integer $lenght Key lenght
2621
 *
2622
 * @return string
2623
 */
2624
function uniqidReal($lenght = 13)
2625
{
2626
    if (function_exists('random_bytes')) {
2627
        $bytes = random_bytes(intval(ceil($lenght / 2)));
2628
    } elseif (function_exists('openssl_random_pseudo_bytes')) {
2629
        $bytes = openssl_random_pseudo_bytes(intval(ceil($lenght / 2)));
2630
    } else {
2631
        throw new Exception('no cryptographically secure random function available');
2632
    }
2633
2634
    return substr(bin2hex($bytes), 0, $lenght);
2635
}
2636
2637
/**
2638
 * Obfuscate an email.
2639
 *
2640
 * @param string $email Email address
2641
 *
2642
 * @return string
2643
 */
2644
function obfuscateEmail($email)
2645
{
2646
    $prop = 2;
2647
    $start = '';
2648
    $end = '';
2649
    $domain = substr(strrchr($email, '@'), 1);
2650
    $mailname = str_replace($domain, '', $email);
2651
    $name_l = strlen($mailname);
2652
    $domain_l = strlen($domain);
2653
    for ($i = 0; $i <= $name_l / $prop - 1; ++$i) {
2654
        $start .= 'x';
2655
    }
2656
2657
    for ($i = 0; $i <= $domain_l / $prop - 1; ++$i) {
2658
        $end .= 'x';
2659
    }
2660
2661
    return (string) substr_replace($mailname, $start, 2, $name_l / $prop)
2662
        . (string) substr_replace($domain, $end, 2, $domain_l / $prop);
2663
}
2664
2665
2666
2667
2668
2669
/**
2670
 * Perform a Query.
2671
 *
2672
 * @param array  $SETTINGS Teamapss settings
2673
 * @param string $fields   Fields to use
2674
 * @param string $table    Table to use
2675
 *
2676
 * @return array
2677
 */
2678
function performDBQuery($SETTINGS, $fields, $table)
2679
{
2680
    // include librairies & connect to DB
2681
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
2682
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
2683
    if (defined('DB_PASSWD_CLEAR') === false) {
2684
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
2685
    }
2686
    DB::$host = DB_HOST;
2687
    DB::$user = DB_USER;
2688
    DB::$password = DB_PASSWD_CLEAR;
2689
    DB::$dbName = DB_NAME;
2690
    DB::$port = DB_PORT;
2691
    DB::$encoding = DB_ENCODING;
2692
2693
    // Insert log in DB
2694
    return DB::query(
2695
        'SELECT ' . $fields . '
2696
        FROM ' . prefixTable($table)
2697
    );
2698
}
2699
2700
/**
2701
 * Undocumented function.
2702
 *
2703
 * @param int $bytes Size of file
2704
 *
2705
 * @return string
2706
 */
2707
function formatSizeUnits($bytes)
2708
{
2709
    if ($bytes >= 1073741824) {
2710
        $bytes = number_format($bytes / 1073741824, 2) . ' GB';
2711
    } elseif ($bytes >= 1048576) {
2712
        $bytes = number_format($bytes / 1048576, 2) . ' MB';
2713
    } elseif ($bytes >= 1024) {
2714
        $bytes = number_format($bytes / 1024, 2) . ' KB';
2715
    } elseif ($bytes > 1) {
2716
        $bytes = $bytes . ' bytes';
2717
    } elseif ($bytes == 1) {
2718
        $bytes = $bytes . ' byte';
2719
    } else {
2720
        $bytes = '0 bytes';
2721
    }
2722
2723
    return $bytes;
2724
}
2725
2726
/**
2727
 * Generate user pair of keys.
2728
 *
2729
 * @param string $userPwd User password
2730
 *
2731
 * @return array
2732
 */
2733
function generateUserKeys($userPwd)
2734
{
2735
    // include library
2736
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2737
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2738
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2739
2740
    // Load classes
2741
    $rsa = new Crypt_RSA();
2742
    $cipher = new Crypt_AES();
2743
2744
    // Create the private and public key
2745
    $res = $rsa->createKey(4096);
2746
2747
    // Encrypt the privatekey
2748
    $cipher->setPassword($userPwd);
2749
    $privatekey = $cipher->encrypt($res['privatekey']);
2750
2751
    return array(
2752
        'private_key' => base64_encode($privatekey),
2753
        'public_key' => base64_encode($res['publickey']),
2754
        'private_key_clear' => base64_encode($res['privatekey']),
2755
    );
2756
}
2757
2758
/**
2759
 * Permits to decrypt the user's privatekey.
2760
 *
2761
 * @param string $userPwd        User password
2762
 * @param string $userPrivateKey User private key
2763
 *
2764
 * @return string
2765
 */
2766
function decryptPrivateKey($userPwd, $userPrivateKey)
2767
{
2768
    if (empty($userPwd) === false) {
2769
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2770
2771
        // Load classes
2772
        $cipher = new Crypt_AES();
2773
2774
        // Encrypt the privatekey
2775
        $cipher->setPassword($userPwd);
2776
2777
        return base64_encode($cipher->decrypt(base64_decode($userPrivateKey)));
2778
    }
2779
}
2780
2781
/**
2782
 * Permits to encrypt the user's privatekey.
2783
 *
2784
 * @param string $userPwd        User password
2785
 * @param string $userPrivateKey User private key
2786
 *
2787
 * @return string
2788
 */
2789
function encryptPrivateKey($userPwd, $userPrivateKey)
2790
{
2791
    if (empty($userPwd) === false) {
2792
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2793
2794
        // Load classes
2795
        $cipher = new Crypt_AES();
2796
2797
        // Encrypt the privatekey
2798
        $cipher->setPassword($userPwd);
2799
2800
        return base64_encode($cipher->encrypt(base64_decode($userPrivateKey)));
2801
    }
2802
}
2803
2804
2805
/**
2806
 * Encrypts a string using AES.
2807
 *
2808
 * @param string $data String to encrypt
2809
 *
2810
 * @return array
2811
 */
2812
function doDataEncryption($data)
2813
{
2814
    // Includes
2815
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2816
2817
    // Load classes
2818
    $cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
2819
2820
    // Generate an object key
2821
    $objectKey = uniqidReal(32);
2822
2823
    // Set it as password
2824
    $cipher->setPassword($objectKey);
2825
2826
    return array(
2827
        'encrypted' => base64_encode($cipher->encrypt($data)),
2828
        'objectKey' => base64_encode($objectKey),
2829
    );
2830
}
2831
2832
/**
2833
 * Decrypts a string using AES.
2834
 *
2835
 * @param string $data Encrypted data
2836
 * @param string $key  Key to uncrypt
2837
 *
2838
 * @return string
2839
 */
2840
function doDataDecryption($data, $key)
2841
{
2842
    // Includes
2843
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2844
2845
    // Load classes
2846
    $cipher = new Crypt_AES();
2847
2848
    // Set the object key
2849
    $cipher->setPassword(base64_decode($key));
2850
2851
    return base64_encode($cipher->decrypt(base64_decode($data)));
2852
}
2853
2854
/**
2855
 * Encrypts using RSA a string using a public key.
2856
 *
2857
 * @param string $key       Key to be encrypted
2858
 * @param string $publicKey User public key
2859
 *
2860
 * @return string
2861
 */
2862
function encryptUserObjectKey($key, $publicKey)
2863
{
2864
    // Includes
2865
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2866
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2867
2868
    // Load classes
2869
    $rsa = new Crypt_RSA();
2870
    $rsa->loadKey(base64_decode($publicKey));
2871
2872
    // Encrypt
2873
    return base64_encode($rsa->encrypt(base64_decode($key)));
2874
}
2875
2876
/**
2877
 * Decrypts using RSA an encrypted string using a private key.
2878
 *
2879
 * @param string $key        Encrypted key
2880
 * @param string $privateKey User private key
2881
 *
2882
 * @return string
2883
 */
2884
function decryptUserObjectKey($key, $privateKey)
2885
{
2886
    // Includes
2887
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2888
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2889
2890
    // Load classes
2891
    $rsa = new Crypt_RSA();
2892
    $rsa->loadKey(base64_decode($privateKey));
2893
    
2894
    
2895
    // Decrypt
2896
    try {
2897
        $ret = base64_encode($rsa->decrypt(base64_decode($key)));
2898
    } catch(Exception $e) {
2899
        return $e;
2900
    }
2901
2902
    return $ret;
2903
}
2904
2905
/**
2906
 * Encrypts a file.
2907
 *
2908
 * @param string $fileInName File name
2909
 * @param string $fileInPath Path to file
2910
 *
2911
 * @return array
2912
 */
2913
function encryptFile($fileInName, $fileInPath)
2914
{
2915
    if (defined('FILE_BUFFER_SIZE') === false) {
2916
        define('FILE_BUFFER_SIZE', 128 * 1024);
2917
    }
2918
2919
    // Includes
2920
    include_once '../includes/config/include.php';
2921
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2922
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2923
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2924
2925
    // Load classes
2926
    $cipher = new Crypt_AES();
2927
2928
    // Generate an object key
2929
    $objectKey = uniqidReal(32);
2930
2931
    // Set it as password
2932
    $cipher->setPassword($objectKey);
2933
2934
    // Prevent against out of memory
2935
    $cipher->enableContinuousBuffer();
2936
    //$cipher->disablePadding();
2937
2938
    // Encrypt the file content
2939
    $plaintext = file_get_contents(
2940
        filter_var($fileInPath . '/' . $fileInName, FILTER_SANITIZE_URL)
2941
    );
2942
2943
    $ciphertext = $cipher->encrypt($plaintext);
2944
2945
    // Save new file
2946
    $hash = md5($plaintext);
2947
    $fileOut = $fileInPath . '/' . TP_FILE_PREFIX . $hash;
2948
    file_put_contents($fileOut, $ciphertext);
1 ignored issue
show
Security File Manipulation introduced by
$ciphertext can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read tainted data from array, and $_SERVER['PHP_AUTH_PW'] is assigned to $passwordClear
    in sources/identify.php on line 345
  2. generateUserKeys() is called
    in sources/identify.php on line 808
  3. Enters via parameter $userPwd
    in sources/main.functions.php on line 2733
  4. Crypt_Base::setPassword() is called
    in sources/main.functions.php on line 2748
  5. Enters via parameter $password
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 645
  6. $password . $salt is assigned to $t
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 681
  7. Data is passed through substr(), and substr($t, 0, $dkLen) is assigned to $key
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 685
  8. Data is passed through substr(), and Crypt_Base::setIV() is called
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 688
  9. Enters via parameter $iv
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 556
  10. $iv is assigned to property Crypt_AES::$iv
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 562
  11. Read from property Crypt_AES::$iv, and Data is passed through substr(), and Data is passed through str_pad(), and $this->decryptIV = str_pad(substr($this->iv, 0, $this->block_size), $this->block_size, '') is assigned to property Crypt_AES::$encryptIV
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 1944
  12. Read from property Crypt_AES::$encryptIV, and Data is passed through _openssl_ctr_process(), and $this->_openssl_ctr_process($plaintext, $this->encryptIV, $this->enbuffer) is returned
    in includes/libraries/Encryption/phpseclib/Crypt/Base.php on line 769
  13. $cipher->encrypt($plaintext) is assigned to $ciphertext
    in sources/main.functions.php on line 2943

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...
2949
    unlink($fileInPath . '/' . $fileInName);
2950
2951
    return array(
2952
        'fileHash' => base64_encode($hash),
2953
        'objectKey' => base64_encode($objectKey),
2954
    );
2955
}
2956
2957
/**
2958
 * Decrypt a file.
2959
 *
2960
 * @param string $fileName File name
2961
 * @param string $filePath Path to file
2962
 * @param string $key      Key to use
2963
 *
2964
 * @return string
2965
 */
2966
function decryptFile($fileName, $filePath, $key)
2967
{
2968
    if (!defined('FILE_BUFFER_SIZE')) {
2969
        define('FILE_BUFFER_SIZE', 128 * 1024);
2970
    }
2971
2972
    // Includes
2973
    include_once '../includes/config/include.php';
2974
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2975
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2976
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2977
2978
    // Get file name
2979
    $fileName = base64_decode($fileName);
2980
2981
    // Load classes
2982
    $cipher = new Crypt_AES();
2983
2984
    // Set the object key
2985
    $cipher->setPassword(base64_decode($key));
2986
2987
    // Prevent against out of memory
2988
    $cipher->enableContinuousBuffer();
2989
    $cipher->disablePadding();
2990
2991
    // Get file content
2992
    $ciphertext = file_get_contents($filePath . '/' . TP_FILE_PREFIX . $fileName);
2993
2994
    // Decrypt file content and return
2995
    return base64_encode($cipher->decrypt($ciphertext));
2996
}
2997
2998
/**
2999
 * Generate a simple password
3000
 *
3001
 * @param integer $length          Length of string
3002
 * @param boolean $symbolsincluded Allow symbols
3003
 *
3004
 * @return string
3005
 */
3006
function generateQuickPassword($length = 16, $symbolsincluded = true)
3007
{
3008
    // Generate new user password
3009
    $small_letters = range('a', 'z');
3010
    $big_letters = range('A', 'Z');
3011
    $digits = range(0, 9);
3012
    $symbols = $symbolsincluded === true ?
3013
        array('#', '_', '-', '@', '$', '+', '&') : array();
3014
3015
    $res = array_merge($small_letters, $big_letters, $digits, $symbols);
3016
    $count = count($res);
3017
    // first variant
3018
3019
    $random_string = '';
3020
    for ($i = 0; $i < $length; ++$i) {
3021
        $random_string .= $res[random_int(0, $count - 1)];
3022
    }
3023
3024
    return $random_string;
3025
}
3026
3027
/**
3028
 * Permit to store the sharekey of an object for users.
3029
 *
3030
 * @param string $object_name             Type for table selection
3031
 * @param int    $post_folder_is_personal Personal
3032
 * @param int    $post_folder_id          Folder
3033
 * @param int    $post_object_id          Object
3034
 * @param string $objectKey               Object key
3035
 * @param array  $SETTINGS                Teampass settings
3036
 *
3037
 * @return void
3038
 */
3039
function storeUsersShareKey(
3040
    $object_name,
3041
    $post_folder_is_personal,
3042
    $post_folder_id,
3043
    $post_object_id,
3044
    $objectKey,
3045
    $SETTINGS
3046
) {
3047
    // include librairies & connect to DB
3048
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
3049
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
3050
    if (defined('DB_PASSWD_CLEAR') === false) {
3051
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
3052
    }
3053
    DB::$host = DB_HOST;
3054
    DB::$user = DB_USER;
3055
    DB::$password = DB_PASSWD_CLEAR;
3056
    DB::$dbName = DB_NAME;
3057
    DB::$port = DB_PORT;
3058
    DB::$encoding = DB_ENCODING;
3059
3060
    // Delete existing entries for this object
3061
    DB::delete(
3062
        $object_name,
3063
        'object_id = %i',
3064
        $post_object_id
3065
    );
3066
3067
    // Superglobals
3068
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
3069
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
3070
3071
    // Prepare superGlobal variables
3072
    $sessionPpersonaFolders = $superGlobal->get('personal_folders', 'SESSION');
3073
    $sessionUserId = $superGlobal->get('user_id', 'SESSION');
3074
    $sessionUserPublicKey = $superGlobal->get('public_key', 'SESSION', 'user');
3075
3076
    if (
3077
        (int) $post_folder_is_personal === 1
3078
        && in_array($post_folder_id, $sessionPpersonaFolders) === true
3079
    ) {
3080
        // If this is a personal object
3081
        // Only create the sharekey for user
3082
        DB::insert(
3083
            $object_name,
3084
            array(
3085
                'object_id' => $post_object_id,
3086
                'user_id' => $sessionUserId,
3087
                'share_key' => encryptUserObjectKey($objectKey, $sessionUserPublicKey),
3088
            )
3089
        );
3090
    } else {
3091
        // This is a public object
3092
        // Create sharekey for each user
3093
        $users = DB::query(
3094
            'SELECT id, public_key
3095
            FROM ' . prefixTable('users') . '
3096
            WHERE id NOT IN ("' . OTV_USER_ID . '","' . SSH_USER_ID . '","' . API_USER_ID . '")
3097
            AND public_key != ""'
3098
        );
3099
        foreach ($users as $user) {
3100
            // Insert in DB the new object key for this item by user
3101
            DB::insert(
3102
                $object_name,
3103
                array(
3104
                    'object_id' => $post_object_id,
3105
                    'user_id' => $user['id'],
3106
                    'share_key' => encryptUserObjectKey(
3107
                        $objectKey,
3108
                        $user['public_key']
3109
                    ),
3110
                )
3111
            );
3112
        }
3113
    }
3114
}
3115
3116
/**
3117
 * Is this string base64 encoded?
3118
 *
3119
 * @param string $str Encoded string?
3120
 *
3121
 * @return bool
3122
 */
3123
function isBase64($str)
3124
{
3125
    $str = (string) trim($str);
3126
3127
    if (!isset($str[0])) {
3128
        return false;
3129
    }
3130
3131
    $base64String = (string) base64_decode($str, true);
3132
    if ($base64String && base64_encode($base64String) === $str) {
3133
        return true;
3134
    }
3135
3136
    return false;
3137
}
3138
3139
/**
3140
 * Undocumented function
3141
 *
3142
 * @param string $field Parameter
3143
 *
3144
 * @return array|bool|resource|string
3145
 */
3146
function filterString($field)
3147
{
3148
    // Sanitize string
3149
    $field = filter_var(trim($field), FILTER_SANITIZE_STRING);
3150
    if (empty($field) === false) {
3151
        // Load AntiXSS
3152
        include_once '../includes/libraries/voku/helper/AntiXSS.php';
3153
        $antiXss = new voku\helper\AntiXSS();
3154
        // Return
3155
        return $antiXss->xss_clean($field);
3156
    }
3157
3158
    return false;
3159
}
3160
3161
3162
/**
3163
 * CHeck if provided credentials are allowed on server
3164
 *
3165
 * @param string $login    User Login
3166
 * @param string $password User Pwd
3167
 * @param array  $SETTINGS Teampass settings
3168
 *
3169
 * @return bool
3170
 */
3171
function ldapCheckUserPassword($login, $password, $SETTINGS)
3172
{
3173
    // Build ldap configuration array
3174
    $config = [
3175
        // Mandatory Configuration Options
3176
        'hosts'            => [$SETTINGS['ldap_hosts']],
3177
        'base_dn'          => $SETTINGS['ldap_bdn'],
3178
        'username'         => $SETTINGS['ldap_username'],
3179
        'password'         => $SETTINGS['ldap_password'],
3180
3181
        // Optional Configuration Options
3182
        'port'             => $SETTINGS['ldap_port'],
3183
        'use_ssl'          => $SETTINGS['ldap_ssl'] === 1 ? true : false,
3184
        'use_tls'          => $SETTINGS['ldap_tls'] === 1 ? true : false,
3185
        'version'          => 3,
3186
        'timeout'          => 5,
3187
        'follow_referrals' => false,
3188
3189
        // Custom LDAP Options
3190
        'options' => [
3191
            // See: http://php.net/ldap_set_option
3192
            LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD
3193
        ]
3194
    ];
3195
3196
    // Load expected libraries
3197
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Traits/Macroable.php';
3198
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Arr.php';
3199
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/DetectsErrors.php';
3200
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/Connection.php';
3201
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/LdapInterface.php';
3202
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/LdapBase.php';
3203
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/Ldap.php';
3204
3205
    $ad = new SplClassLoader('LdapRecord', '../includes/libraries');
3206
    $ad->register();
3207
    $connection = new Connection($config);
3208
3209
    // COnnect to LDAP
3210
    try {
3211
        $connection->connect();
3212
3213
    } catch (\LdapRecord\Auth\BindException $e) {
3214
        $error = $e->getDetailedError();
3215
3216
        echo "Error : ".$error->getErrorCode()." - ".$error->getErrorMessage(). "<br>".$error->getDiagnosticMessage();
3217
        return false;
3218
    }
3219
3220
    // Authenticate user
3221
    try {
3222
        $connection->auth()->attempt($SETTINGS['ldap_user_attribute']."=".$login.",".$SETTINGS['ldap_bdn'], $password, $stayAuthenticated = true);
3223
3224
    } catch (\LdapRecord\Auth\BindException $e) {
3225
        $error = $e->getDetailedError();
3226
        
3227
        echo "Error : ".$error->getErrorCode()." - ".$error->getErrorMessage(). "<br>".$error->getDiagnosticMessage();
3228
        return false;
3229
    }
3230
3231
    return true;
3232
}
3233
3234
3235
3236
3237
/**
3238
 * Removes from DB all sharekeys of this user
3239
 *
3240
 * @param integer $userId   User's id
3241
 * @param array   $SETTINGS Teampass settings
3242
 *
3243
 * @return void|bool
3244
 */
3245
function deleteUserObjetsKeys($userId, $SETTINGS)
3246
{
3247
    // include librairies & connect to DB
3248
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
3249
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
3250
    if (defined('DB_PASSWD_CLEAR') === false) {
3251
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
3252
    }
3253
    DB::$host = DB_HOST;
3254
    DB::$user = DB_USER;
3255
    DB::$password = DB_PASSWD_CLEAR;
3256
    DB::$dbName = DB_NAME;
3257
    DB::$port = DB_PORT;
3258
    DB::$encoding = DB_ENCODING;
3259
3260
    // Remove all item sharekeys items
3261
    DB::delete(
3262
        prefixTable('sharekeys_items'),
3263
        'user_id = %i',
3264
        $userId
3265
    );
3266
3267
    // Remove all item sharekeys files
3268
    DB::delete(
3269
        prefixTable('sharekeys_files'),
3270
        'user_id = %i',
3271
        $userId
3272
    );
3273
3274
    // Remove all item sharekeys fields
3275
    DB::delete(
3276
        prefixTable('sharekeys_fields'),
3277
        'user_id = %i',
3278
        $userId
3279
    );
3280
3281
    // Remove all item sharekeys logs
3282
    DB::delete(
3283
        prefixTable('sharekeys_logs'),
3284
        'user_id = %i',
3285
        $userId
3286
    );
3287
3288
    // Remove all item sharekeys suggestions
3289
    DB::delete(
3290
        prefixTable('sharekeys_suggestions'),
3291
        'user_id = %i',
3292
        $userId
3293
    );
3294
3295
    return false;
3296
}
3297
3298
// Manage list of timezones
3299
function timezone_list() {
3300
    static $timezones = null;
3301
3302
    if ($timezones === null) {
3303
        $timezones = [];
3304
        $offsets = [];
3305
        $now = new DateTime('now', new DateTimeZone('UTC'));
3306
3307
        foreach (DateTimeZone::listIdentifiers() as $timezone) {
3308
            $now->setTimezone(new DateTimeZone($timezone));
3309
            $offsets[] = $offset = $now->getOffset();
3310
            $timezones[$timezone] = '(' . format_GMT_offset($offset) . ') ' . format_timezone_name($timezone);
3311
        }
3312
3313
        array_multisort($offsets, $timezones);
3314
    }
3315
3316
    return $timezones;
3317
}
3318
3319
function format_GMT_offset($offset) {
3320
    $hours = intval($offset / 3600);
3321
    $minutes = abs(intval($offset % 3600 / 60));
3322
    return 'GMT' . ($offset ? sprintf('%+03d:%02d', $hours, $minutes) : '');
3323
}
3324
3325
function format_timezone_name($name) {
3326
    $name = str_replace('/', ', ', $name);
3327
    $name = str_replace('_', ' ', $name);
3328
    $name = str_replace('St ', 'St. ', $name);
3329
    return $name;
3330
}
3331
3332
3333
/**
3334
 * Provides info about if user should use MFA
3335
 *
3336
 * @param string $userRolesIds  User roles ids
3337
 * @param string $mfaRoles      Roles for which MFA is requested
3338
 *
3339
 * @return bool
3340
 */
3341
function mfa_auth_requested($userRolesIds, $mfaRoles)
3342
{
3343
    $mfaRoles = array_values(json_decode($mfaRoles, true));
3344
    $userRolesIds = array_filter(explode(';' , $userRolesIds));
3345
3346
    if (count($mfaRoles) === 0 || count($mfaRoles) === 0) {
3347
        return true;
3348
    }
3349
3350
    if (count(array_intersect($mfaRoles, $userRolesIds)) > 0) {
3351
        return true;
3352
    } else {
3353
        return false;
3354
    }
3355
}