Passed
Push — teampass_3.0 ( 425d48...b7c627 )
by Nils
06:24
created

timezone_list()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 18
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 11
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 18
rs 9.9
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 = null,
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(($size === null) ? 10 : (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 = 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 = explode(' : ', /** @scrutinizer ignore-type */ $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) . ' attribute=' . str_replace('at_', '', $attribute[0]) . ' itemno=' . $item_id . ' user=' . addslashes($login) . ' itemname="' . addslashes($item_label) . '"',
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

1965
            'action=' . str_replace('at_', '', $action) . ' attribute=' . str_replace('at_', '', $attribute[0]) . ' itemno=' . $item_id . ' user=' . addslashes(/** @scrutinizer ignore-type */ $login) . ' itemname="' . addslashes($item_label) . '"',
Loading history...
1966
            $SETTINGS['syslog_host'],
1967
            $SETTINGS['syslog_port'],
1968
            'teampass'
1969
        );
1970
    }
1971
1972
    // send notification if enabled
1973
    notifyOnChange($item_id, $action, $SETTINGS);
1974
}
1975
1976
/**
1977
 * If enabled, then notify admin/manager.
1978
 *
1979
 * @param int    $item_id  Item id
1980
 * @param string $action   Action to do
1981
 * @param array  $SETTINGS Teampass settings
1982
 */
1983
function notifyOnChange($item_id, $action, $SETTINGS)
1984
{
1985
    if (
1986
        isset($SETTINGS['enable_email_notification_on_item_shown']) === true
1987
        && (int) $SETTINGS['enable_email_notification_on_item_shown'] === 1
1988
        && $action === 'at_shown'
1989
    ) {
1990
        // Load superglobal
1991
        include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1992
        $superGlobal = new protect\SuperGlobal\SuperGlobal();
1993
1994
        // Get superglobals
1995
        $globalsLastname = $superGlobal->get('lastname', 'SESSION');
1996
        $globalsName = $superGlobal->get('name', 'SESSION');
1997
        $globalsNotifiedEmails = $superGlobal->get('listNotificationEmails', 'SESSION');
1998
1999
        // Get info about item
2000
        $dataItem = DB::queryfirstrow(
2001
            'SELECT id, id_tree, label
2002
            FROM ' . prefixTable('items') . '
2003
            WHERE id = %i',
2004
            $item_id
2005
        );
2006
        $item_label = $dataItem['label'];
2007
2008
        // send back infos
2009
        DB::insert(
2010
            prefixTable('emails'),
2011
            array(
2012
                'timestamp' => time(),
2013
                'subject' => langHdl('email_on_open_notification_subject'),
2014
                'body' => str_replace(
2015
                    array('#tp_user#', '#tp_item#', '#tp_link#'),
2016
                    array(
2017
                        addslashes($globalsName . ' ' . $globalsLastname),
2018
                        addslashes($item_label),
2019
                        $SETTINGS['cpassman_url'] . '/index.php?page=items&group=' . $dataItem['id_tree'] . '&id=' . $item_id,
2020
                    ),
2021
                    langHdl('email_on_open_notification_mail')
2022
                ),
2023
                'receivers' => $globalsNotifiedEmails,
2024
                'status' => '',
2025
            )
2026
        );
2027
    }
2028
}
2029
2030
/**
2031
 * Prepare notification email to subscribers.
2032
 *
2033
 * @param int    $item_id  Item id
2034
 * @param string $label    Item label
2035
 * @param array  $changes  List of changes
2036
 * @param array  $SETTINGS Teampass settings
2037
 */
2038
function notifyChangesToSubscribers($item_id, $label, $changes, $SETTINGS)
2039
{
2040
    // Load superglobal
2041
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2042
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2043
2044
    // Get superglobals
2045
    $globalsUserId = $superGlobal->get('user_id', 'SESSION');
2046
    $globalsLastname = $superGlobal->get('lastname', 'SESSION');
2047
    $globalsName = $superGlobal->get('name', 'SESSION');
2048
2049
    // send email to user that what to be notified
2050
    $notification = DB::queryOneColumn(
2051
        'email',
2052
        'SELECT *
2053
        FROM ' . prefixTable('notification') . ' AS n
2054
        INNER JOIN ' . prefixTable('users') . ' AS u ON (n.user_id = u.id)
2055
        WHERE n.item_id = %i AND n.user_id != %i',
2056
        $item_id,
2057
        $globalsUserId
2058
    );
2059
2060
    if (DB::count() > 0) {
2061
        // Prepare path
2062
        $path = geItemReadablePath($item_id, '', $SETTINGS);
2063
2064
        // Get list of changes
2065
        $htmlChanges = '<ul>';
2066
        foreach ($changes as $change) {
2067
            $htmlChanges .= '<li>' . $change . '</li>';
2068
        }
2069
        $htmlChanges .= '</ul>';
2070
2071
        // send email
2072
        DB::insert(
2073
            prefixTable('emails'),
2074
            array(
2075
                'timestamp' => time(),
2076
                'subject' => langHdl('email_subject_item_updated'),
2077
                'body' => str_replace(
2078
                    array('#item_label#', '#folder_name#', '#item_id#', '#url#', '#name#', '#lastname#', '#changes#'),
2079
                    array($label, $path, $item_id, $SETTINGS['cpassman_url'], $globalsName, $globalsLastname, $htmlChanges),
2080
                    langHdl('email_body_item_updated')
2081
                ),
2082
                'receivers' => implode(',', $notification),
2083
                'status' => '',
2084
            )
2085
        );
2086
    }
2087
}
2088
2089
/**
2090
 * Returns the Item + path.
2091
 *
2092
 * @param int    $id_tree  Node id
2093
 * @param string $label    Label
2094
 * @param array  $SETTINGS TP settings
2095
 *
2096
 * @return string
2097
 */
2098
function geItemReadablePath($id_tree, $label, $SETTINGS)
2099
{
2100
    // Class loader
2101
    include_once $SETTINGS['cpassman_dir'] . '/sources/SplClassLoader.php';
2102
2103
    //Load Tree
2104
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
2105
    $tree->register();
2106
    $tree = new Tree\NestedTree\NestedTree(prefixTable('nested_tree'), 'id', 'parent_id', 'title');
2107
2108
    $arbo = $tree->getPath($id_tree, true);
2109
    $path = '';
2110
    foreach ($arbo as $elem) {
2111
        if (empty($path) === true) {
2112
            $path = htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES) . ' ';
2113
        } else {
2114
            $path .= '&#8594; ' . htmlspecialchars(stripslashes(htmlspecialchars_decode($elem->title, ENT_QUOTES)), ENT_QUOTES);
2115
        }
2116
    }
2117
2118
    // Build text to show user
2119
    if (empty($label) === false) {
2120
        return empty($path) === true ? addslashes($label) : addslashes($label) . ' (' . $path . ')';
2121
    } else {
2122
        return empty($path) === true ? '' : $path;
2123
    }
2124
}
2125
2126
/**
2127
 * Get the client ip address.
2128
 *
2129
 * @return string IP address
2130
 */
2131
function getClientIpServer()
2132
{
2133
    if (getenv('HTTP_CLIENT_IP')) {
2134
        $ipaddress = getenv('HTTP_CLIENT_IP');
2135
    } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
2136
        $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
2137
    } elseif (getenv('HTTP_X_FORWARDED')) {
2138
        $ipaddress = getenv('HTTP_X_FORWARDED');
2139
    } elseif (getenv('HTTP_FORWARDED_FOR')) {
2140
        $ipaddress = getenv('HTTP_FORWARDED_FOR');
2141
    } elseif (getenv('HTTP_FORWARDED')) {
2142
        $ipaddress = getenv('HTTP_FORWARDED');
2143
    } elseif (getenv('REMOTE_ADDR')) {
2144
        $ipaddress = getenv('REMOTE_ADDR');
2145
    } else {
2146
        $ipaddress = 'UNKNOWN';
2147
    }
2148
2149
    return $ipaddress;
2150
}
2151
2152
/**
2153
 * Escape all HTML, JavaScript, and CSS.
2154
 *
2155
 * @param string $input    The input string
2156
 * @param string $encoding Which character encoding are we using?
2157
 *
2158
 * @return string
2159
 */
2160
function noHTML($input, $encoding = 'UTF-8')
2161
{
2162
    return htmlspecialchars($input, ENT_QUOTES | ENT_XHTML, $encoding, false);
2163
}
2164
2165
/**
2166
 * 
2167
 * Permits to handle the Teampass config file
2168
 * $action accepts "rebuild" and "update"
2169
 *
2170
 * @param string $action   Action to perform
2171
 * @param array  $SETTINGS Teampass settings
2172
 * @param string $field    Field to refresh
2173
 * @param string $value    Value to set
2174
 *
2175
 * @return string|boolean
2176
 */
2177
function handleConfigFile($action, $SETTINGS, $field = null, $value = null)
2178
{
2179
    $tp_config_file = $SETTINGS['cpassman_dir'] . '/includes/config/tp.config.php';
2180
2181
    // include librairies & connect to DB
2182
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
2183
    if (defined('DB_PASSWD_CLEAR') === false) {
2184
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
2185
    }
2186
    DB::$host = DB_HOST;
2187
    DB::$user = DB_USER;
2188
    DB::$password = DB_PASSWD_CLEAR;
2189
    DB::$dbName = DB_NAME;
2190
    DB::$port = DB_PORT;
2191
    DB::$encoding = DB_ENCODING;
2192
2193
    if (file_exists($tp_config_file) === false || $action === 'rebuild') {
2194
        // perform a copy
2195
        if (file_exists($tp_config_file)) {
2196
            if (!copy($tp_config_file, $tp_config_file . '.' . date('Y_m_d_His', time()))) {
2197
                return "ERROR: Could not copy file '" . $tp_config_file . "'";
2198
            }
2199
        }
2200
2201
2202
        // regenerate
2203
        $data = array();
2204
        $data[0] = "<?php\n";
2205
        $data[1] = "global \$SETTINGS;\n";
2206
        $data[2] = "\$SETTINGS = array (\n";
2207
        $rows = DB::query(
2208
            'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s',
2209
            'admin'
2210
        );
2211
        foreach ($rows as $record) {
2212
            array_push($data, "    '" . $record['intitule'] . "' => '" . $record['valeur'] . "',\n");
2213
        }
2214
        array_push($data, ");\n");
2215
        $data = array_unique($data);
2216
        // ---
2217
    } elseif ($action === 'update' && empty($field) === false) {
2218
        $data = file($tp_config_file);
2219
        $inc = 0;
2220
        $bFound = false;
2221
        foreach ($data as $line) {
2222
            if (stristr($line, ');')) {
2223
                break;
2224
            }
2225
2226
            if (stristr($line, "'" . $field . "' => '")) {
2227
                $data[$inc] = "    '" . $field . "' => '" . filter_var($value, FILTER_SANITIZE_STRING) . "',\n";
2228
                $bFound = true;
2229
                break;
2230
            }
2231
            ++$inc;
2232
        }
2233
        if ($bFound === false) {
2234
            $data[($inc)] = "    '" . $field . "' => '" . filter_var($value, FILTER_SANITIZE_STRING) . "',\n);\n";
2235
        }
2236
    }
2237
2238
    // update file
2239
    file_put_contents($tp_config_file, implode('', isset($data) ? $data : array()));
2240
2241
    return true;
2242
}
2243
2244
2245
/**
2246
 * Permits to replace &#92; to permit correct display
2247
 *
2248
 * @param string $input Some text
2249
 *
2250
 * @return string
2251
 */
2252
function handleBackslash($input)
2253
{
2254
    return str_replace('&amp;#92;', '&#92;', $input);
2255
}
2256
2257
/*
2258
** Permits to loas settings
2259
*/
2260
function loadSettings()
2261
{
2262
    global $SETTINGS;
2263
2264
    /* LOAD CPASSMAN SETTINGS */
2265
    if (!isset($SETTINGS['loaded']) || $SETTINGS['loaded'] != 1) {
2266
        $SETTINGS['duplicate_folder'] = 0; //by default, this is set to 0;
2267
        $SETTINGS['duplicate_item'] = 0; //by default, this is set to 0;
2268
        $SETTINGS['number_of_used_pw'] = 5; //by default, this value is set to 5;
2269
        $settings = array();
2270
2271
        $rows = DB::query(
2272
            'SELECT * FROM ' . prefixTable('misc') . ' WHERE type=%s_type OR type=%s_type2',
2273
            array(
2274
                'type' => 'admin',
2275
                'type2' => 'settings',
2276
            )
2277
        );
2278
        foreach ($rows as $record) {
2279
            if ($record['type'] === 'admin') {
2280
                $SETTINGS[$record['intitule']] = $record['valeur'];
2281
            } else {
2282
                $settings[$record['intitule']] = $record['valeur'];
2283
            }
2284
        }
2285
        $SETTINGS['loaded'] = 1;
2286
        $SETTINGS['default_session_expiration_time'] = 5;
2287
    }
2288
}
2289
2290
/*
2291
** check if folder has custom fields.
2292
** Ensure that target one also has same custom fields
2293
*/
2294
function checkCFconsistency($source_id, $target_id)
2295
{
2296
    $source_cf = array();
2297
    $rows = DB::QUERY(
2298
        'SELECT id_category
2299
        FROM ' . prefixTable('categories_folders') . '
2300
        WHERE id_folder = %i',
2301
        $source_id
2302
    );
2303
    foreach ($rows as $record) {
2304
        array_push($source_cf, $record['id_category']);
2305
    }
2306
2307
    $target_cf = array();
2308
    $rows = DB::QUERY(
2309
        'SELECT id_category
2310
        FROM ' . prefixTable('categories_folders') . '
2311
        WHERE id_folder = %i',
2312
        $target_id
2313
    );
2314
    foreach ($rows as $record) {
2315
        array_push($target_cf, $record['id_category']);
2316
    }
2317
2318
    $cf_diff = array_diff($source_cf, $target_cf);
2319
    if (count($cf_diff) > 0) {
2320
        return false;
2321
    }
2322
2323
    return true;
2324
}
2325
2326
/**
2327
 * Will encrypte/decrypt a fil eusing Defuse.
2328
 *
2329
 * @param string $type        can be either encrypt or decrypt
2330
 * @param string $source_file path to source file
2331
 * @param string $target_file path to target file
2332
 * @param array  $SETTINGS    Settings
2333
 * @param string $password    A password
2334
 *
2335
 * @return string|boolean
2336
 */
2337
function prepareFileWithDefuse(
2338
    $type,
2339
    $source_file,
2340
    $target_file,
2341
    $SETTINGS,
2342
    $password = null
2343
) {
2344
    // Load AntiXSS
2345
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/AntiXSS.php';
2346
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/ASCII.php';
2347
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/UTF8.php';
2348
    $antiXss = new voku\helper\AntiXSS();
2349
2350
    // Protect against bad inputs
2351
    if (is_array($source_file) === true || is_array($target_file) === true) {
2352
        return 'error_cannot_be_array';
2353
    }
2354
2355
    // Sanitize
2356
    $source_file = $antiXss->xss_clean($source_file);
2357
    $target_file = $antiXss->xss_clean($target_file);
2358
2359
    if (empty($password) === true || is_null($password) === true) {
2360
        // get KEY to define password
2361
        $ascii_key = file_get_contents(SECUREPATH . '/teampass-seckey.txt');
2362
        $password = \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key);
2363
    }
2364
2365
    $err = '';
2366
    if ($type === 'decrypt') {
2367
        // Decrypt file
2368
        $err = defuseFileDecrypt(
2369
            $source_file,
2370
            $target_file,
2371
            $SETTINGS,
2372
            /** @scrutinizer ignore-type */ $password
2373
        );
2374
        // ---
2375
    } elseif ($type === 'encrypt') {
2376
        // Encrypt file
2377
        $err = defuseFileEncrypt(
2378
            $source_file,
2379
            $target_file,
2380
            $SETTINGS,
2381
            /** @scrutinizer ignore-type */ $password
2382
        );
2383
    }
2384
2385
    // return error
2386
    return empty($err) === false ? $err : "";
2387
}
2388
2389
/**
2390
 * Encrypt a file with Defuse.
2391
 *
2392
 * @param string $source_file path to source file
2393
 * @param string $target_file path to target file
2394
 * @param array  $SETTINGS    Settings
2395
 * @param string $password    A password
2396
 *
2397
 * @return string|bool
2398
 */
2399
function defuseFileEncrypt(
2400
    $source_file,
2401
    $target_file,
2402
    $SETTINGS,
2403
    $password = null
2404
) {
2405
    // load PhpEncryption library
2406
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2407
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Crypto.php';
2408
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Encoding.php';
2409
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'DerivedKeys.php';
2410
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Key.php';
2411
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyOrPassword.php';
2412
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'File.php';
2413
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'RuntimeTests.php';
2414
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyProtectedByPassword.php';
2415
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Core.php';
2416
2417
    try {
2418
        \Defuse\Crypto\File::encryptFileWithPassword(
2419
            $source_file,
2420
            $target_file,
2421
            $password
2422
        );
2423
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2424
        $err = 'wrong_key';
2425
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2426
        $err = $ex;
2427
    } catch (Defuse\Crypto\Exception\IOException $ex) {
2428
        $err = $ex;
2429
    }
2430
2431
    // return error
2432
    return empty($err) === false ? $err : true;
2433
}
2434
2435
/**
2436
 * Decrypt a file with Defuse.
2437
 *
2438
 * @param string $source_file path to source file
2439
 * @param string $target_file path to target file
2440
 * @param array  $SETTINGS    Settings
2441
 * @param string $password    A password
2442
 *
2443
 * @return string|bool
2444
 */
2445
function defuseFileDecrypt(
2446
    $source_file,
2447
    $target_file,
2448
    $SETTINGS,
2449
    $password = null
2450
) {
2451
    // load PhpEncryption library
2452
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2453
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Crypto.php';
2454
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Encoding.php';
2455
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'DerivedKeys.php';
2456
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Key.php';
2457
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyOrPassword.php';
2458
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'File.php';
2459
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'RuntimeTests.php';
2460
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'KeyProtectedByPassword.php';
2461
    include_once $SETTINGS['cpassman_dir'] . $path_to_encryption . 'Core.php';
2462
2463
    try {
2464
        \Defuse\Crypto\File::decryptFileWithPassword(
2465
            $source_file,
2466
            $target_file,
2467
            $password
2468
        );
2469
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2470
        $err = 'wrong_key';
2471
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2472
        $err = $ex;
2473
    } catch (Defuse\Crypto\Exception\IOException $ex) {
2474
        $err = $ex;
2475
    }
2476
2477
    // return error
2478
    return empty($err) === false ? $err : true;
2479
}
2480
2481
/*
2482
* NOT TO BE USED
2483
*/
2484
/**
2485
 * Undocumented function.
2486
 *
2487
 * @param string $text Text to debug
2488
 */
2489
function debugTeampass($text)
2490
{
2491
    $debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+');
2492
    if ($debugFile !== false) {
2493
        fputs($debugFile, $text);
2494
        fclose($debugFile);
2495
    }    
2496
}
2497
2498
/**
2499
 * DELETE the file with expected command depending on server type.
2500
 *
2501
 * @param string $file     Path to file
2502
 * @param array  $SETTINGS Teampass settings
2503
 */
2504
function fileDelete($file, $SETTINGS)
2505
{
2506
    // Load AntiXSS
2507
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/ASCII.php';
2508
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/UTF8.php';
2509
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/voku/helper/AntiXSS.php';
2510
    $antiXss = new voku\helper\AntiXSS();
2511
2512
    $file = $antiXss->xss_clean($file);
2513
    if (is_file($file)) {
2514
        unlink($file);
2515
    }
2516
}
2517
2518
/**
2519
 * Permits to extract the file extension.
2520
 *
2521
 * @param string $file File name
2522
 *
2523
 * @return string
2524
 */
2525
function getFileExtension($file)
2526
{
2527
    if (strpos($file, '.') === false) {
2528
        return $file;
2529
    }
2530
2531
    return substr($file, strrpos($file, '.') + 1);
2532
}
2533
2534
/**
2535
 * Performs chmod operation on subfolders.
2536
 *
2537
 * @param string $dir             Parent folder
2538
 * @param int    $dirPermissions  New permission on folders
2539
 * @param int    $filePermissions New permission on files
2540
 *
2541
 * @return bool
2542
 */
2543
function chmodRecursive($dir, $dirPermissions, $filePermissions)
2544
{
2545
    $pointer_dir = opendir($dir);
2546
    if ($pointer_dir === false) {
2547
        return false;
2548
    }  
2549
    $res = true;
2550
    while (false !== ($file = readdir($pointer_dir))) {
2551
        if (($file === '.') || ($file === '..')) {
2552
            continue;
2553
        }
2554
2555
        $fullPath = $dir . '/' . $file;
2556
2557
        if (is_dir($fullPath)) {
2558
            if ($res = @chmod($fullPath, $dirPermissions)) {
2559
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2560
            }
2561
        } else {
2562
            $res = chmod($fullPath, $filePermissions);
2563
        }
2564
        if (!$res) {
2565
            closedir($pointer_dir);
2566
2567
            return false;
2568
        }
2569
    }
2570
    closedir($pointer_dir);
2571
    if (is_dir($dir) && $res) {
2572
        $res = @chmod($dir, $dirPermissions);
2573
    }
2574
2575
    return $res;
2576
}
2577
2578
/**
2579
 * Check if user can access to this item.
2580
 *
2581
 * @param int $item_id ID of item
2582
 */
2583
function accessToItemIsGranted($item_id, $SETTINGS)
2584
{
2585
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2586
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2587
2588
    // Prepare superGlobal variables
2589
    $session_groupes_visibles = $superGlobal->get('groupes_visibles', 'SESSION');
2590
    $session_list_restricted_folders_for_items = $superGlobal->get('list_restricted_folders_for_items', 'SESSION');
2591
2592
    // Load item data
2593
    $data = DB::queryFirstRow(
2594
        'SELECT id_tree
2595
        FROM ' . prefixTable('items') . '
2596
        WHERE id = %i',
2597
        $item_id
2598
    );
2599
2600
    // Check if user can access this folder
2601
    if (in_array($data['id_tree'], $session_groupes_visibles) === false) {
2602
        // Now check if this folder is restricted to user
2603
        if (isset($session_list_restricted_folders_for_items[$data['id_tree']]) === true
2604
            && in_array($item_id, $session_list_restricted_folders_for_items[$data['id_tree']]) === false
2605
        ) {
2606
            return 'ERR_FOLDER_NOT_ALLOWED';
2607
        }
2608
    }
2609
2610
    return true;
2611
}
2612
2613
/**
2614
 * Creates a unique key.
2615
 *
2616
 * @param integer $lenght Key lenght
2617
 *
2618
 * @return string
2619
 */
2620
function uniqidReal($lenght = 13)
2621
{
2622
    if (function_exists('random_bytes')) {
2623
        $bytes = random_bytes(intval(ceil($lenght / 2)));
2624
    } elseif (function_exists('openssl_random_pseudo_bytes')) {
2625
        $bytes = openssl_random_pseudo_bytes(intval(ceil($lenght / 2)));
2626
    } else {
2627
        throw new Exception('no cryptographically secure random function available');
2628
    }
2629
2630
    return substr(bin2hex($bytes), 0, $lenght);
2631
}
2632
2633
/**
2634
 * Obfuscate an email.
2635
 *
2636
 * @param string $email Email address
2637
 *
2638
 * @return string
2639
 */
2640
function obfuscateEmail($email)
2641
{
2642
    $prop = 2;
2643
    $start = '';
2644
    $end = '';
2645
    $domain = substr(strrchr($email, '@'), 1);
2646
    $mailname = str_replace($domain, '', $email);
2647
    $name_l = strlen($mailname);
2648
    $domain_l = strlen($domain);
2649
    for ($i = 0; $i <= $name_l / $prop - 1; ++$i) {
2650
        $start .= 'x';
2651
    }
2652
2653
    for ($i = 0; $i <= $domain_l / $prop - 1; ++$i) {
2654
        $end .= 'x';
2655
    }
2656
2657
    return substr_replace($mailname, $start, 2, $name_l / $prop)
0 ignored issues
show
Bug introduced by
Are you sure substr_replace($mailname...rt, 2, $name_l / $prop) of type array|string can be used in concatenation? ( Ignorable by Annotation )

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

2657
    return /** @scrutinizer ignore-type */ substr_replace($mailname, $start, 2, $name_l / $prop)
Loading history...
2658
        . substr_replace($domain, $end, 2, $domain_l / $prop);
2659
}
2660
2661
2662
2663
2664
2665
/**
2666
 * Perform a Query.
2667
 *
2668
 * @param array  $SETTINGS Teamapss settings
2669
 * @param string $fields   Fields to use
2670
 * @param string $table    Table to use
2671
 *
2672
 * @return array
2673
 */
2674
function performDBQuery($SETTINGS, $fields, $table)
2675
{
2676
    // include librairies & connect to DB
2677
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
2678
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
2679
    if (defined('DB_PASSWD_CLEAR') === false) {
2680
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
2681
    }
2682
    DB::$host = DB_HOST;
2683
    DB::$user = DB_USER;
2684
    DB::$password = DB_PASSWD_CLEAR;
2685
    DB::$dbName = DB_NAME;
2686
    DB::$port = DB_PORT;
2687
    DB::$encoding = DB_ENCODING;
2688
2689
    // Insert log in DB
2690
    return DB::query(
2691
        'SELECT ' . $fields . '
2692
        FROM ' . prefixTable($table)
2693
    );
2694
}
2695
2696
/**
2697
 * Undocumented function.
2698
 *
2699
 * @param int $bytes Size of file
2700
 *
2701
 * @return string
2702
 */
2703
function formatSizeUnits($bytes)
2704
{
2705
    if ($bytes >= 1073741824) {
2706
        $bytes = number_format($bytes / 1073741824, 2) . ' GB';
2707
    } elseif ($bytes >= 1048576) {
2708
        $bytes = number_format($bytes / 1048576, 2) . ' MB';
2709
    } elseif ($bytes >= 1024) {
2710
        $bytes = number_format($bytes / 1024, 2) . ' KB';
2711
    } elseif ($bytes > 1) {
2712
        $bytes = $bytes . ' bytes';
2713
    } elseif ($bytes == 1) {
2714
        $bytes = $bytes . ' byte';
2715
    } else {
2716
        $bytes = '0 bytes';
2717
    }
2718
2719
    return $bytes;
2720
}
2721
2722
/**
2723
 * Generate user pair of keys.
2724
 *
2725
 * @param string $userPwd User password
2726
 *
2727
 * @return array
2728
 */
2729
function generateUserKeys($userPwd)
2730
{
2731
    // include library
2732
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2733
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2734
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2735
2736
    // Load classes
2737
    $rsa = new Crypt_RSA();
2738
    $cipher = new Crypt_AES();
2739
2740
    // Create the private and public key
2741
    $res = $rsa->createKey(4096);
2742
2743
    // Encrypt the privatekey
2744
    $cipher->setPassword($userPwd);
2745
    $privatekey = $cipher->encrypt($res['privatekey']);
2746
2747
    return array(
2748
        'private_key' => base64_encode($privatekey),
2749
        'public_key' => base64_encode($res['publickey']),
2750
        'private_key_clear' => base64_encode($res['privatekey']),
2751
    );
2752
}
2753
2754
/**
2755
 * Permits to decrypt the user's privatekey.
2756
 *
2757
 * @param string $userPwd        User password
2758
 * @param string $userPrivateKey User private key
2759
 *
2760
 * @return string
2761
 */
2762
function decryptPrivateKey($userPwd, $userPrivateKey)
2763
{
2764
    if (empty($userPwd) === false) {
2765
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2766
2767
        // Load classes
2768
        $cipher = new Crypt_AES();
2769
2770
        // Encrypt the privatekey
2771
        $cipher->setPassword($userPwd);
2772
2773
        return base64_encode($cipher->decrypt(base64_decode($userPrivateKey)));
2774
    }
2775
}
2776
2777
/**
2778
 * Permits to encrypt the user's privatekey.
2779
 *
2780
 * @param string $userPwd        User password
2781
 * @param string $userPrivateKey User private key
2782
 *
2783
 * @return string
2784
 */
2785
function encryptPrivateKey($userPwd, $userPrivateKey)
2786
{
2787
    if (empty($userPwd) === false) {
2788
        include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2789
2790
        // Load classes
2791
        $cipher = new Crypt_AES();
2792
2793
        // Encrypt the privatekey
2794
        $cipher->setPassword($userPwd);
2795
2796
        return base64_encode($cipher->encrypt(base64_decode($userPrivateKey)));
2797
    }
2798
}
2799
2800
2801
/**
2802
 * Encrypts a string using AES.
2803
 *
2804
 * @param string $data String to encrypt
2805
 *
2806
 * @return array
2807
 */
2808
function doDataEncryption($data)
2809
{
2810
    // Includes
2811
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2812
2813
    // Load classes
2814
    $cipher = new Crypt_AES(CRYPT_AES_MODE_CBC);
2815
2816
    // Generate an object key
2817
    $objectKey = uniqidReal(32);
2818
2819
    // Set it as password
2820
    $cipher->setPassword($objectKey);
2821
2822
    return array(
2823
        'encrypted' => base64_encode($cipher->encrypt($data)),
2824
        'objectKey' => base64_encode($objectKey),
2825
    );
2826
}
2827
2828
/**
2829
 * Decrypts a string using AES.
2830
 *
2831
 * @param string $data Encrypted data
2832
 * @param string $key  Key to uncrypt
2833
 *
2834
 * @return string
2835
 */
2836
function doDataDecryption($data, $key)
2837
{
2838
    // Includes
2839
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2840
2841
    // Load classes
2842
    $cipher = new Crypt_AES();
2843
2844
    // Set the object key
2845
    $cipher->setPassword(base64_decode($key));
2846
2847
    return base64_encode($cipher->decrypt(base64_decode($data)));
2848
}
2849
2850
/**
2851
 * Encrypts using RSA a string using a public key.
2852
 *
2853
 * @param string $key       Key to be encrypted
2854
 * @param string $publicKey User public key
2855
 *
2856
 * @return string
2857
 */
2858
function encryptUserObjectKey($key, $publicKey)
2859
{
2860
    // Includes
2861
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2862
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2863
2864
    // Load classes
2865
    $rsa = new Crypt_RSA();
2866
    $rsa->loadKey(base64_decode($publicKey));
2867
2868
    // Encrypt
2869
    return base64_encode($rsa->encrypt(base64_decode($key)));
2870
}
2871
2872
/**
2873
 * Decrypts using RSA an encrypted string using a private key.
2874
 *
2875
 * @param string $key        Encrypted key
2876
 * @param string $privateKey User private key
2877
 *
2878
 * @return string
2879
 */
2880
function decryptUserObjectKey($key, $privateKey)
2881
{
2882
    // Includes
2883
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2884
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2885
2886
    // Load classes
2887
    $rsa = new Crypt_RSA();
2888
    $rsa->loadKey(base64_decode($privateKey));
2889
    
2890
    
2891
    // Decrypt
2892
    try {
2893
        $ret = base64_encode($rsa->decrypt(base64_decode($key)));
2894
    } catch(Exception $e) {
2895
        return $e;
2896
    }
2897
2898
    return $ret;
2899
}
2900
2901
/**
2902
 * Encrypts a file.
2903
 *
2904
 * @param string $fileInName File name
2905
 * @param string $fileInPath Path to file
2906
 *
2907
 * @return array
2908
 */
2909
function encryptFile($fileInName, $fileInPath)
2910
{
2911
    if (defined('FILE_BUFFER_SIZE') === false) {
2912
        define('FILE_BUFFER_SIZE', 128 * 1024);
2913
    }
2914
2915
    // Includes
2916
    include_once '../includes/config/include.php';
2917
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2918
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2919
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2920
2921
    // Load classes
2922
    $cipher = new Crypt_AES();
2923
2924
    // Generate an object key
2925
    $objectKey = uniqidReal(32);
2926
2927
    // Set it as password
2928
    $cipher->setPassword($objectKey);
2929
2930
    // Prevent against out of memory
2931
    $cipher->enableContinuousBuffer();
2932
    //$cipher->disablePadding();
2933
2934
    // Encrypt the file content
2935
    $plaintext = file_get_contents(
2936
        filter_var($fileInPath . '/' . $fileInName, FILTER_SANITIZE_URL)
2937
    );
2938
2939
    $ciphertext = $cipher->encrypt($plaintext);
2940
2941
    // Save new file
2942
    $hash = md5($plaintext);
2943
    $fileOut = $fileInPath . '/' . TP_FILE_PREFIX . $hash;
2944
    file_put_contents($fileOut, $ciphertext);
2945
    unlink($fileInPath . '/' . $fileInName);
2946
2947
    return array(
2948
        'fileHash' => base64_encode($hash),
2949
        'objectKey' => base64_encode($objectKey),
2950
    );
2951
}
2952
2953
/**
2954
 * Decrypt a file.
2955
 *
2956
 * @param string $fileName File name
2957
 * @param string $filePath Path to file
2958
 * @param string $key      Key to use
2959
 *
2960
 * @return string
2961
 */
2962
function decryptFile($fileName, $filePath, $key)
2963
{
2964
    if (!defined('FILE_BUFFER_SIZE')) {
2965
        define('FILE_BUFFER_SIZE', 128 * 1024);
2966
    }
2967
2968
    // Includes
2969
    include_once '../includes/config/include.php';
2970
    include_once '../includes/libraries/Encryption/phpseclib/Math/BigInteger.php';
2971
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/RSA.php';
2972
    include_once '../includes/libraries/Encryption/phpseclib/Crypt/AES.php';
2973
2974
    // Get file name
2975
    $fileName = base64_decode($fileName);
2976
2977
    // Load classes
2978
    $cipher = new Crypt_AES();
2979
2980
    // Set the object key
2981
    $cipher->setPassword(base64_decode($key));
2982
2983
    // Prevent against out of memory
2984
    $cipher->enableContinuousBuffer();
2985
    $cipher->disablePadding();
2986
2987
    // Get file content
2988
    $ciphertext = file_get_contents($filePath . '/' . TP_FILE_PREFIX . $fileName);
2989
2990
    // Decrypt file content and return
2991
    return base64_encode($cipher->decrypt($ciphertext));
2992
}
2993
2994
/**
2995
 * Generate a simple password
2996
 *
2997
 * @param integer $length          Length of string
2998
 * @param boolean $symbolsincluded Allow symbols
2999
 *
3000
 * @return string
3001
 */
3002
function generateQuickPassword($length = 16, $symbolsincluded = true)
3003
{
3004
    // Generate new user password
3005
    $small_letters = range('a', 'z');
3006
    $big_letters = range('A', 'Z');
3007
    $digits = range(0, 9);
3008
    $symbols = $symbolsincluded === true ?
3009
        array('#', '_', '-', '@', '$', '+', '&') : array();
3010
3011
    $res = array_merge($small_letters, $big_letters, $digits, $symbols);
3012
    $count = count($res);
3013
    // first variant
3014
3015
    $random_string = '';
3016
    for ($i = 0; $i < $length; ++$i) {
3017
        $random_string .= $res[random_int(0, $count - 1)];
3018
    }
3019
3020
    return $random_string;
3021
}
3022
3023
/**
3024
 * Permit to store the sharekey of an object for users.
3025
 *
3026
 * @param string $object_name             Type for table selection
3027
 * @param int    $post_folder_is_personal Personal
3028
 * @param int    $post_folder_id          Folder
3029
 * @param int    $post_object_id          Object
3030
 * @param string $objectKey               Object key
3031
 * @param array  $SETTINGS                Teampass settings
3032
 *
3033
 * @return void
3034
 */
3035
function storeUsersShareKey(
3036
    $object_name,
3037
    $post_folder_is_personal,
3038
    $post_folder_id,
3039
    $post_object_id,
3040
    $objectKey,
3041
    $SETTINGS
3042
) {
3043
    // include librairies & connect to DB
3044
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
3045
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
3046
    if (defined('DB_PASSWD_CLEAR') === false) {
3047
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
3048
    }
3049
    DB::$host = DB_HOST;
3050
    DB::$user = DB_USER;
3051
    DB::$password = DB_PASSWD_CLEAR;
3052
    DB::$dbName = DB_NAME;
3053
    DB::$port = DB_PORT;
3054
    DB::$encoding = DB_ENCODING;
3055
3056
    // Delete existing entries for this object
3057
    DB::delete(
3058
        $object_name,
3059
        'object_id = %i',
3060
        $post_object_id
3061
    );
3062
3063
    // Superglobals
3064
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
3065
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
3066
3067
    // Prepare superGlobal variables
3068
    $sessionPpersonaFolders = $superGlobal->get('personal_folders', 'SESSION');
3069
    $sessionUserId = $superGlobal->get('user_id', 'SESSION');
3070
    $sessionUserPublicKey = $superGlobal->get('public_key', 'SESSION', 'user');
3071
3072
    if (
3073
        (int) $post_folder_is_personal === 1
3074
        && in_array($post_folder_id, $sessionPpersonaFolders) === true
3075
    ) {
3076
        // If this is a personal object
3077
        // Only create the sharekey for user
3078
        DB::insert(
3079
            $object_name,
3080
            array(
3081
                'object_id' => $post_object_id,
3082
                'user_id' => $sessionUserId,
3083
                'share_key' => encryptUserObjectKey($objectKey, $sessionUserPublicKey),
3084
            )
3085
        );
3086
    } else {
3087
        // This is a public object
3088
        // Create sharekey for each user
3089
        $users = DB::query(
3090
            'SELECT id, public_key
3091
            FROM ' . prefixTable('users') . '
3092
            WHERE id NOT IN ("' . OTV_USER_ID . '","' . SSH_USER_ID . '","' . API_USER_ID . '")
3093
            AND public_key != ""'
3094
        );
3095
        foreach ($users as $user) {
3096
            // Insert in DB the new object key for this item by user
3097
            DB::insert(
3098
                $object_name,
3099
                array(
3100
                    'object_id' => $post_object_id,
3101
                    'user_id' => $user['id'],
3102
                    'share_key' => encryptUserObjectKey(
3103
                        $objectKey,
3104
                        $user['public_key']
3105
                    ),
3106
                )
3107
            );
3108
        }
3109
    }
3110
}
3111
3112
/**
3113
 * Is this string base64 encoded?
3114
 *
3115
 * @param string $str Encoded string?
3116
 *
3117
 * @return bool
3118
 */
3119
function isBase64($str)
3120
{
3121
    $str = (string) trim($str);
3122
3123
    if (!isset($str[0])) {
3124
        return false;
3125
    }
3126
3127
    $base64String = (string) base64_decode($str, true);
3128
    if ($base64String && base64_encode($base64String) === $str) {
3129
        return true;
3130
    }
3131
3132
    return false;
3133
}
3134
3135
/**
3136
 * Undocumented function
3137
 *
3138
 * @param string $field Parameter
3139
 *
3140
 * @return array|bool|resource|string
3141
 */
3142
function filterString($field)
3143
{
3144
    // Sanitize string
3145
    $field = filter_var(trim($field), FILTER_SANITIZE_STRING);
3146
    if (empty($field) === false) {
3147
        // Load AntiXSS
3148
        include_once '../includes/libraries/voku/helper/AntiXSS.php';
3149
        $antiXss = new voku\helper\AntiXSS();
3150
        // Return
3151
        return $antiXss->xss_clean($field);
3152
    }
3153
3154
    return false;
3155
}
3156
3157
3158
/**
3159
 * CHeck if provided credentials are allowed on server
3160
 *
3161
 * @param string $login    User Login
3162
 * @param string $password User Pwd
3163
 * @param array  $SETTINGS Teampass settings
3164
 *
3165
 * @return bool
3166
 */
3167
function ldapCheckUserPassword($login, $password, $SETTINGS)
3168
{
3169
    // Build ldap configuration array
3170
    $config = [
3171
        // Mandatory Configuration Options
3172
        'hosts'            => [$SETTINGS['ldap_hosts']],
3173
        'base_dn'          => $SETTINGS['ldap_bdn'],
3174
        'username'         => $SETTINGS['ldap_username'],
3175
        'password'         => $SETTINGS['ldap_password'],
3176
3177
        // Optional Configuration Options
3178
        'port'             => $SETTINGS['ldap_port'],
3179
        'use_ssl'          => $SETTINGS['ldap_ssl'] === 1 ? true : false,
3180
        'use_tls'          => $SETTINGS['ldap_tls'] === 1 ? true : false,
3181
        'version'          => 3,
3182
        'timeout'          => 5,
3183
        'follow_referrals' => false,
3184
3185
        // Custom LDAP Options
3186
        'options' => [
3187
            // See: http://php.net/ldap_set_option
3188
            LDAP_OPT_X_TLS_REQUIRE_CERT => LDAP_OPT_X_TLS_HARD
3189
        ]
3190
    ];
3191
3192
    // Load expected libraries
3193
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Traits/Macroable.php';
3194
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Tightenco/Collect/Support/Arr.php';
3195
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/DetectsErrors.php';
3196
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/Connection.php';
3197
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/LdapInterface.php';
3198
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/LdapBase.php';
3199
    require_once $SETTINGS['cpassman_dir'] . '/includes/libraries/LdapRecord/Ldap.php';
3200
3201
    $ad = new SplClassLoader('LdapRecord', '../includes/libraries');
3202
    $ad->register();
3203
    $connection = new Connection($config);
3204
3205
    // COnnect to LDAP
3206
    try {
3207
        $connection->connect();
3208
3209
    } catch (\LdapRecord\Auth\BindException $e) {
3210
        $error = $e->getDetailedError();
3211
3212
        echo "Error : ".$error->getErrorCode()." - ".$error->getErrorMessage(). "<br>".$error->getDiagnosticMessage();
3213
        return false;
3214
    }
3215
3216
    // Authenticate user
3217
    try {
3218
        $connection->auth()->attempt($SETTINGS['ldap_user_attribute']."=".$login.",".$SETTINGS['ldap_bdn'], $password, $stayAuthenticated = true);
3219
3220
    } catch (\LdapRecord\Auth\BindException $e) {
3221
        $error = $e->getDetailedError();
3222
        
3223
        echo "Error : ".$error->getErrorCode()." - ".$error->getErrorMessage(). "<br>".$error->getDiagnosticMessage();
3224
        return false;
3225
    }
3226
3227
    return true;
3228
}
3229
3230
3231
3232
3233
/**
3234
 * Removes from DB all sharekeys of this user
3235
 *
3236
 * @param integer $userId   User's id
3237
 * @param array   $SETTINGS Teampass settings
3238
 *
3239
 * @return void|bool
3240
 */
3241
function deleteUserObjetsKeys($userId, $SETTINGS)
3242
{
3243
    // include librairies & connect to DB
3244
    include_once $SETTINGS['cpassman_dir'] . '/includes/config/settings.php';
3245
    include_once $SETTINGS['cpassman_dir'] . '/includes/libraries/Database/Meekrodb/db.class.php';
3246
    if (defined('DB_PASSWD_CLEAR') === false) {
3247
        define('DB_PASSWD_CLEAR', defuseReturnDecrypted(DB_PASSWD, $SETTINGS));
3248
    }
3249
    DB::$host = DB_HOST;
3250
    DB::$user = DB_USER;
3251
    DB::$password = DB_PASSWD_CLEAR;
3252
    DB::$dbName = DB_NAME;
3253
    DB::$port = DB_PORT;
3254
    DB::$encoding = DB_ENCODING;
3255
3256
    // Remove all item sharekeys items
3257
    DB::delete(
3258
        prefixTable('sharekeys_items'),
3259
        'user_id = %i',
3260
        $userId
3261
    );
3262
3263
    // Remove all item sharekeys files
3264
    DB::delete(
3265
        prefixTable('sharekeys_files'),
3266
        'user_id = %i',
3267
        $userId
3268
    );
3269
3270
    // Remove all item sharekeys fields
3271
    DB::delete(
3272
        prefixTable('sharekeys_fields'),
3273
        'user_id = %i',
3274
        $userId
3275
    );
3276
3277
    // Remove all item sharekeys logs
3278
    DB::delete(
3279
        prefixTable('sharekeys_logs'),
3280
        'user_id = %i',
3281
        $userId
3282
    );
3283
3284
    // Remove all item sharekeys suggestions
3285
    DB::delete(
3286
        prefixTable('sharekeys_suggestions'),
3287
        'user_id = %i',
3288
        $userId
3289
    );
3290
3291
    return false;
3292
}
3293
3294
// Manage list of timezones
3295
function timezone_list() {
3296
    static $timezones = null;
3297
3298
    if ($timezones === null) {
3299
        $timezones = [];
3300
        $offsets = [];
3301
        $now = new DateTime('now', new DateTimeZone('UTC'));
3302
3303
        foreach (DateTimeZone::listIdentifiers() as $timezone) {
3304
            $now->setTimezone(new DateTimeZone($timezone));
3305
            $offsets[] = $offset = $now->getOffset();
3306
            $timezones[$timezone] = '(' . format_GMT_offset($offset) . ') ' . format_timezone_name($timezone);
3307
        }
3308
3309
        array_multisort($offsets, $timezones);
3310
    }
3311
3312
    return $timezones;
3313
}
3314
3315
function format_GMT_offset($offset) {
3316
    $hours = intval($offset / 3600);
3317
    $minutes = abs(intval($offset % 3600 / 60));
3318
    return 'GMT' . ($offset ? sprintf('%+03d:%02d', $hours, $minutes) : '');
3319
}
3320
3321
function format_timezone_name($name) {
3322
    $name = str_replace('/', ', ', $name);
3323
    $name = str_replace('_', ' ', $name);
3324
    $name = str_replace('St ', 'St. ', $name);
3325
    return $name;
3326
}