Completed
Push — development ( 6a24df...5afdf5 )
by Nils
07:52
created

main.functions.php ➔ defuse_validate_personal_key()   A

Complexity

Conditions 3
Paths 7

Size

Total Lines 23
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 19
nc 7
nop 2
dl 0
loc 23
rs 9.0856
c 0
b 0
f 0
1
<?php
2
/**
3
 *
4
 * @file          main.functions.php
5
 * @author        Nils Laumaillé
6
 * @version       2.1.27
7
 * @copyright     (c) 2009-2017 Nils Laumaillé
8
 * @licensing     GNU AFFERO GPL 3.0
9
 * @link
10
 */
11
12
//define pbkdf2 iteration count
13
@define('ITCOUNT', '2072');
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
14
15
if (!isset($_SESSION['CPM']) || $_SESSION['CPM'] != 1) {
16
    die('Hacking attempt...');
17
}
18
19
// load phpCrypt
20
if (!isset($_SESSION['settings']['cpassman_dir']) || empty($_SESSION['settings']['cpassman_dir'])) {
21
    require_once '../includes/libraries/phpcrypt/phpCrypt.php';
22
    require_once '../includes/config/settings.php';
23 View Code Duplication
} else {
24
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/libraries/phpcrypt/phpCrypt.php';
25
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/config/settings.php';
26
}
27
use PHP_Crypt\PHP_Crypt as PHP_Crypt;
28
29
30
// prepare Encryption class calls
31
use \Defuse\Crypto\Crypto;
32
use \Defuse\Crypto\Exception as Ex;
33
34
//Generate N# of random bits for use as salt
35
/**
36
 * @param integer $n
37
 */
38
function getBits($n)
39
{
40
    $str = '';
41
    $x = $n + 10;
42
    for ($i = 0; $i < $x; $i++) {
43
        $str .= base_convert(mt_rand(1, 36), 10, 36);
44
    }
45
    return substr($str, 0, $n);
46
}
47
48
//generate pbkdf2 compliant hash
49 View Code Duplication
function strHashPbkdf2($p, $s, $c, $kl, $a = 'sha256', $st = 0)
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
50
{
51
    $kb = $st + $kl; // Key blocks to compute
52
    $dk = ''; // Derived key
53
54
    for ($block = 1; $block <= $kb; $block++) { // Create key
55
        $ib = $h = hash_hmac($a, $s.pack('N', $block), $p, true); // Initial hash for this block
56
        for ($i = 1; $i < $c; $i++) { // Perform block iterations
57
            $ib ^= ($h = hash_hmac($a, $h, $p, true)); // XOR each iterate
58
        }
59
        $dk .= $ib; // Append iterated block
60
    }
61
    return substr($dk, $st, $kl); // Return derived key of correct length
62
}
63
64
/**
65
 * stringUtf8Decode()
66
 *
67
 * utf8_decode
68
 */
69
function stringUtf8Decode($string)
70
{
71
    return str_replace(" ", "+", utf8_decode($string));
72
}
73
74
/**
75
 * encryptOld()
76
 *
77
 * crypt a string
78
 * @param string $text
79
 */
80 View Code Duplication
function encryptOld($text, $personalSalt = "")
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
81
{
82
    if (!empty($personalSalt)) {
83
        return trim(
84
            base64_encode(
85
                mcrypt_encrypt(
86
                    MCRYPT_RIJNDAEL_256,
87
                    $personalSalt,
88
                    $text,
89
                    MCRYPT_MODE_ECB,
90
                    mcrypt_create_iv(
91
                        mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
92
                        MCRYPT_RAND
93
                    )
94
                )
95
            )
96
        );
97
    } else {
98
        return trim(
99
            base64_encode(
100
                mcrypt_encrypt(
101
                    MCRYPT_RIJNDAEL_256,
102
                    SALT,
103
                    $text,
104
                    MCRYPT_MODE_ECB,
105
                    mcrypt_create_iv(
106
                        mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
107
                        MCRYPT_RAND
108
                    )
109
                )
110
            )
111
        );
112
    }
113
}
114
115
/**
116
 * decryptOld()
117
 *
118
 * decrypt a crypted string
119
 */
120 View Code Duplication
function decryptOld($text, $personalSalt = "")
0 ignored issues
show
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
121
{
122
    if (!empty($personalSalt)) {
123
        return trim(
124
            mcrypt_decrypt(
125
                MCRYPT_RIJNDAEL_256,
126
                $personalSalt,
127
                base64_decode($text),
128
                MCRYPT_MODE_ECB,
129
                mcrypt_create_iv(
130
                    mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
131
                    MCRYPT_RAND
132
                )
133
            )
134
        );
135
    } else {
136
        return trim(
137
            mcrypt_decrypt(
138
                MCRYPT_RIJNDAEL_256,
139
                SALT,
140
                base64_decode($text),
141
                MCRYPT_MODE_ECB,
142
                mcrypt_create_iv(
143
                    mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
144
                    MCRYPT_RAND
145
                )
146
            )
147
        );
148
    }
149
}
150
151
/**
152
 * encrypt()
153
 *
154
 * crypt a string
155
 * @param string $decrypted
156
 */
157
function encrypt($decrypted, $personalSalt = "")
158
{
159 View Code Duplication
    if (!isset($_SESSION['settings']['cpassman_dir']) || empty($_SESSION['settings']['cpassman_dir'])) {
160
        require_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
161
    } else {
162
        require_once $_SESSION['settings']['cpassman_dir'].'/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
163
    }
164
165
    if (!empty($personalSalt)) {
166
            $staticSalt = $personalSalt;
167
    } else {
168
            $staticSalt = SALT;
169
    }
170
171
    //set our salt to a variable
172
    // Get 64 random bits for the salt for pbkdf2
173
    $pbkdf2Salt = getBits(64);
174
    // generate a pbkdf2 key to use for the encryption.
175
    //$key = strHashPbkdf2($staticSalt, $pbkdf2Salt, ITCOUNT, 16, 'sha256', 32);
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
176
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
177
    // Build $iv and $ivBase64.  We use a block size of 256 bits (AES compliant)
178
    // and CTR mode.  (Note: ECB mode is inadequate as IV is not used.)
179
    $iv = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, 'ctr'), MCRYPT_RAND);
180
181
    //base64 trim
182
    if (strlen($ivBase64 = rtrim(base64_encode($iv), '=')) != 43) {
183
        return false;
184
    }
185
    // Encrypt $decrypted
186
    $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $decrypted, 'ctr', $iv);
187
    // MAC the encrypted text
188
    $mac = hash_hmac('sha256', $encrypted, $staticSalt);
189
    // We're done!
190
    return base64_encode($ivBase64.$encrypted.$mac.$pbkdf2Salt);
191
}
192
193
/**
194
 * decrypt()
195
 *
196
 * decrypt a crypted string
197
 */
198
function decrypt($encrypted, $personalSalt = "")
199
{
200 View Code Duplication
    if (!isset($_SESSION['settings']['cpassman_dir']) || empty($_SESSION['settings']['cpassman_dir'])) {
201
        require_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
202
    } else {
203
        require_once $_SESSION['settings']['cpassman_dir'].'/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
204
    }
205
206
    if (!empty($personalSalt)) {
207
        $staticSalt = $personalSalt;
208
    } else {
209
        $staticSalt = SALT;
210
    }
211
    //base64 decode the entire payload
212
    $encrypted = base64_decode($encrypted);
213
    // get the salt
214
    $pbkdf2Salt = substr($encrypted, -64);
215
    //remove the salt from the string
216
    $encrypted = substr($encrypted, 0, -64);
217
    //$key = strHashPbkdf2($staticSalt, $pbkdf2Salt, ITCOUNT, 16, 'sha256', 32);
0 ignored issues
show
Unused Code Comprehensibility introduced by
59% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
218
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
219
    // Retrieve $iv which is the first 22 characters plus ==, base64_decoded.
220
    $iv = base64_decode(substr($encrypted, 0, 43).'==');
221
    // Remove $iv from $encrypted.
222
    $encrypted = substr($encrypted, 43);
223
    // Retrieve $mac which is the last 64 characters of $encrypted.
224
    $mac = substr($encrypted, -64);
225
    // Remove the last 64 chars from encrypted (remove MAC)
226
    $encrypted = substr($encrypted, 0, -64);
227
    //verify the sha256hmac from the encrypted data before even trying to decrypt it
228
    if (hash_hmac('sha256', $encrypted, $staticSalt) != $mac) {
229
        return false;
230
    }
231
    // Decrypt the data.
232
    $decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, 'ctr', $iv), "\0\4");
233
    // Yay!
234
    return $decrypted;
235
}
236
237
238
/**
239
 * genHash()
240
 *
241
 * Generate a hash for user login
242
 * @param string $password
243
 */
244 View Code Duplication
function bCrypt($password, $cost)
0 ignored issues
show
Best Practice introduced by
The function bCrypt() has been defined more than once; this definition is ignored, only the first definition in install/install.queries.php (L56-68) is considered.

This check looks for functions that have already been defined in other files.

Some Codebases, like WordPress, make a practice of defining functions multiple times. This may lead to problems with the detection of function parameters and types. If you really need to do this, you can mark the duplicate definition with the @ignore annotation.

/**
 * @ignore
 */
function getUser() {

}

function getUser($id, $realm) {

}

See also the PhpDoc documentation for @ignore.

Loading history...
Duplication introduced by
This function seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
245
{
246
    $salt = sprintf('$2y$%02d$', $cost);
247
    if (function_exists('openssl_random_pseudo_bytes')) {
248
        $salt .= bin2hex(openssl_random_pseudo_bytes(11));
249
    } else {
250
        $chars = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
251
        for ($i = 0; $i < 22; $i++) {
252
            $salt .= $chars[mt_rand(0, 63)];
253
        }
254
    }
255
    return crypt($password, $salt);
256
}
257
258
function cryption_before_defuse($message, $sk, $iv, $type = null, $scope = "public")
259
{
260
    if (DEFUSE_ENCRYPTION === TRUE) {
261
        if ($scope === "perso") {
262
            return defuse_crypto(
263
                $message,
264
                $sk,
265
                $type
266
            );
267
        } else {
268
            return defuse_crypto(
269
                $message,
270
                file_get_contents(SECUREPATH."/teampass-seckey.txt"),
271
                $type
272
            );
273
        }
274
    } else {
275
        return cryption_phpCrypt($message, $sk, $iv, $type);
276
    }
277
}
278
279
/*
280
 * cryption() - Encrypt and decrypt string based upon phpCrypt library
281
 *
282
 * Using AES_128 and mode CBC
283
 *
284
 * $key and $iv have to be given in hex format
285
 */
286
function cryption_phpCrypt($string, $key, $iv, $type)
287
{
288
    // manage key origin
289
    define('SALT', 'LEfzTjADMTzV6qHC');
290
291
    if ($key != SALT) {
292
        // check key (AES-128 requires a 16 bytes length key)
293
        if (strlen($key) < 16) {
294
            for ($x = strlen($key) + 1; $x <= 16; $x++) {
295
                $key .= chr(0);
296
            }
297
        } else if (strlen($key) > 16) {
298
            $key = substr($key, 16);
299
        }
300
    }
301
302
    // load crypt
303
    $crypt = new PHP_Crypt($key, PHP_Crypt::CIPHER_AES_128, PHP_Crypt::MODE_CBC);
304
305
    if ($type == "encrypt") {
306
        // generate IV and encrypt
307
        $iv = $crypt->createIV();
308
        $encrypt = $crypt->encrypt($string);
309
        // return
310
        return array(
311
            "string" => bin2hex($encrypt),
312
            "iv" => bin2hex($iv),
313
            "error" => empty($encrypt) ? "ERR_ENCRYPTION_NOT_CORRECT" : ""
314
        );
315
    } else if ($type == "decrypt") {
316
        // case if IV is empty
317
        if (empty($iv)) {
318
                    return array(
319
                'string' => "",
320
                'error' => "ERR_ENCRYPTION_NOT_CORRECT"
321
            );
322
        }
323
324
        // convert
325
        try {
326
            $string = testHex2Bin(trim($string));
327
            $iv = testHex2Bin($iv);
328
        } catch (Exception $e) {
329
            // error - $e->getMessage();
0 ignored issues
show
Unused Code Comprehensibility introduced by
46% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
330
            return array(
331
                'string' => "",
332
                'error' => "ERR_ENCRYPTION_NOT_CORRECT"
333
            );
334
        }
335
336
        // load IV
337
        $crypt->IV($iv);
338
        // decrypt
339
        $decrypt = $crypt->decrypt($string);
340
        // return
341
        //return str_replace(chr(0), "", $decrypt);
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
342
        return array(
343
            'string' => str_replace(chr(0), "", $decrypt),
344
            'error' => ""
345
        );
346
    }
347
}
348
349
function testHex2Bin($val)
350
{
351
    if (!@hex2bin($val)) {
352
        throw new Exception("ERROR");
353
    }
354
    return hex2bin($val);
355
}
356
357
/**
358
 * @param string $ascii_key
359
 * @param string $type
360
 */
361
function cryption($message, $ascii_key, $type) //defuse_crypto
362
{
363
    // load PhpEncryption library
364 View Code Duplication
    if (!isset($_SESSION['settings']['cpassman_dir']) || empty($_SESSION['settings']['cpassman_dir'])) {
365
        $path = '../includes/libraries/Encryption/Encryption/';
366
    } else {
367
        $path = $_SESSION['settings']['cpassman_dir'].'/includes/libraries/Encryption/Encryption/';
368
    }
369
370
    require_once $path.'Crypto.php';
371
    require_once $path.'Encoding.php';
372
    require_once $path.'DerivedKeys.php';
373
    require_once $path.'Key.php';
374
    require_once $path.'KeyOrPassword.php';
375
    require_once $path.'File.php';
376
    require_once $path.'RuntimeTests.php';
377
    require_once $path.'KeyProtectedByPassword.php';
378
    require_once $path.'Core.php';
379
380
    // init
381
    $err = '';
382
    if (empty($ascii_key)) {
383
        $ascii_key = file_get_contents(SECUREPATH."/teampass-seckey.txt");
384
    }
385
386
    // convert KEY
387
    $key = \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key);
388
389
    try {
390
        if ($type === "encrypt") {
391
            $text = \Defuse\Crypto\Crypto::encrypt($message, $key);
392
        } else if ($type === "decrypt") {
393
            $text = \Defuse\Crypto\Crypto::decrypt($message, $key);
394
        }
395
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
396
        $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.";
397
    } catch (Defuse\Crypto\Exception\BadFormatException $ex) {
398
        $err = $ex;
399
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
400
        $err = $ex;
401
    } catch (Defuse\Crypto\Exception\CryptoException $ex) {
402
        $err = $ex;
403
    } catch (Defuse\Crypto\Exception\IOException $ex) {
404
        $err = $ex;
405
    }
406
407
    return array(
408
        'string' => isset($text) ? $text : "",
409
        'error' => $err
410
    );
411
}
412
413
function defuse_generate_key() {
414
    require_once '../includes/libraries/Encryption/Encryption/Crypto.php';
415
    require_once '../includes/libraries/Encryption/Encryption/Encoding.php';
416
    require_once '../includes/libraries/Encryption/Encryption/DerivedKeys.php';
417
    require_once '../includes/libraries/Encryption/Encryption/Key.php';
418
    require_once '../includes/libraries/Encryption/Encryption/KeyOrPassword.php';
419
    require_once '../includes/libraries/Encryption/Encryption/File.php';
420
    require_once '../includes/libraries/Encryption/Encryption/RuntimeTests.php';
421
    require_once '../includes/libraries/Encryption/Encryption/KeyProtectedByPassword.php';
422
    require_once '../includes/libraries/Encryption/Encryption/Core.php';
423
424
    $key = \Defuse\Crypto\Key::createNewRandomKey();
425
    $key = $key->saveToAsciiSafeString();
426
    return $key;
427
}
428
429
function defuse_generate_personal_key($psk) {
430
    require_once '../includes/libraries/Encryption/Encryption/Crypto.php';
431
    require_once '../includes/libraries/Encryption/Encryption/Encoding.php';
432
    require_once '../includes/libraries/Encryption/Encryption/DerivedKeys.php';
433
    require_once '../includes/libraries/Encryption/Encryption/Key.php';
434
    require_once '../includes/libraries/Encryption/Encryption/KeyOrPassword.php';
435
    require_once '../includes/libraries/Encryption/Encryption/File.php';
436
    require_once '../includes/libraries/Encryption/Encryption/RuntimeTests.php';
437
    require_once '../includes/libraries/Encryption/Encryption/KeyProtectedByPassword.php';
438
    require_once '../includes/libraries/Encryption/Encryption/Core.php';
439
440
    $protected_key = \Defuse\Crypto\KeyProtectedByPassword::createRandomPasswordProtectedKey($psk);
441
    $protected_key_encoded = $protected_key->saveToAsciiSafeString();
442
443
    return $protected_key_encoded; // save this in user table
444
}
445
446
/**
447
 * @param string $psk
448
 */
449
function defuse_validate_personal_key($psk, $protected_key_encoded) {
450
    require_once '../includes/libraries/Encryption/Encryption/Crypto.php';
451
    require_once '../includes/libraries/Encryption/Encryption/Encoding.php';
452
    require_once '../includes/libraries/Encryption/Encryption/DerivedKeys.php';
453
    require_once '../includes/libraries/Encryption/Encryption/Key.php';
454
    require_once '../includes/libraries/Encryption/Encryption/KeyOrPassword.php';
455
    require_once '../includes/libraries/Encryption/Encryption/File.php';
456
    require_once '../includes/libraries/Encryption/Encryption/RuntimeTests.php';
457
    require_once '../includes/libraries/Encryption/Encryption/KeyProtectedByPassword.php';
458
    require_once '../includes/libraries/Encryption/Encryption/Core.php';
459
460
    try {
461
        $protected_key = \Defuse\Crypto\KeyProtectedByPassword::loadFromAsciiSafeString($protected_key_encoded);
462
        $user_key = $protected_key->unlockKey($psk);
463
        $user_key_encoded = $user_key->saveToAsciiSafeString();
464
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
465
        return "Error - Major issue as the encryption is broken.";
466
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
467
        return "Error - The saltkey is not the correct one.";
468
    }
469
470
    return $user_key_encoded; // store it in session once user has entered his psk
471
}
472
473
/**
474
 * trimElement()
475
 *
476
 * trim a string depending on a specific string
477
 * @param string $element
478
 * @return string
479
 */
480
function trimElement($chaine, $element)
481
{
482
    if (!empty($chaine)) {
483
        $chaine = trim($chaine);
484
        if (substr($chaine, 0, 1) == $element) {
485
            $chaine = substr($chaine, 1);
486
        }
487
        if (substr($chaine, strlen($chaine) - 1, 1) == $element) {
488
            $chaine = substr($chaine, 0, strlen($chaine) - 1);
489
        }
490
    }
491
    return $chaine;
492
}
493
494
/**
495
 * cleanString()
496
 *
497
 * permits to suppress all "special" characters from string
498
 */
499
function cleanString($string, $special = false)
500
{
501
    // Create temporary table for special characters escape
502
    $tabSpecialChar = array();
503
    for ($i = 0; $i <= 31; $i++) {
504
        $tabSpecialChar[] = chr($i);
505
    }
506
    array_push($tabSpecialChar, "<br />");
507
    if ($special == "1") {
508
        $tabSpecialChar = array_merge($tabSpecialChar, array("</li>", "<ul>", "<ol>"));
509
    }
510
511
    return str_replace($tabSpecialChar, "\n", $string);
512
}
513
514
function db_error_handler($params) {
515
    echo "Error: ".$params['error']."<br>\n";
516
    echo "Query: ".$params['query']."<br>\n";
517
    die; // don't want to keep going if a query broke
518
}
519
520
/**
521
 * identifyUserRights()
522
 *
523
 * @return
524
 * @param boolean $refresh
525
 */
526
function identifyUserRights($groupesVisiblesUser, $groupesInterditsUser, $isAdmin, $idFonctions, $refresh)
527
{
528
    global $server, $user, $pass, $database, $pre, $port, $encoding;
529
530
    //load ClassLoader
531
    require_once $_SESSION['settings']['cpassman_dir'].'/sources/SplClassLoader.php';
532
533
    //Connect to DB
534
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
535
    DB::$host = $server;
536
    DB::$user = $user;
537
    DB::$password = $pass;
538
    DB::$dbName = $database;
539
    DB::$port = $port;
540
    DB::$encoding = $encoding;
541
    DB::$error_handler = 'db_error_handler';
0 ignored issues
show
Documentation Bug introduced by
The property $error_handler was declared of type boolean, but 'db_error_handler' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
542
    $link = mysqli_connect($server, $user, $pass, $database, $port);
543
    $link->set_charset($encoding);
544
545
    //Build tree
546
    $tree = new SplClassLoader('Tree\NestedTree', $_SESSION['settings']['cpassman_dir'].'/includes/libraries');
547
    $tree->register();
548
    $tree = new Tree\NestedTree\NestedTree(prefix_table("nested_tree"), 'id', 'parent_id', 'title');
0 ignored issues
show
Security Bug introduced by
It seems like prefix_table('nested_tree') targeting prefix_table() can also be of type false; however, Tree\NestedTree\NestedTree::__construct() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
549
550
    // Check if user is ADMINISTRATOR
551
    if ($isAdmin == 1) {
552
        $groupesVisibles = array();
553
        $_SESSION['personal_folders'] = array();
554
        $_SESSION['groupes_visibles'] = array();
555
        $_SESSION['groupes_interdits'] = array();
556
        $_SESSION['personal_visible_groups'] = array();
557
        $_SESSION['read_only_folders'] = array();
558
        $_SESSION['list_restricted_folders_for_items'] = array();
559
        $_SESSION['list_folders_editable_by_role'] = array();
560
        $_SESSION['list_folders_limited'] = array();
561
        $_SESSION['groupes_visibles_list'] = "";
562
        $_SESSION['list_folders_limited'] = "";
563
        $rows = DB::query("SELECT id FROM ".prefix_table("nested_tree")." WHERE personal_folder = %i", 0);
564
        foreach ($rows as $record) {
565
            array_push($groupesVisibles, $record['id']);
566
        }
567
        $_SESSION['groupes_visibles'] = $groupesVisibles;
568
        $_SESSION['all_non_personal_folders'] = $groupesVisibles;
569
        // Exclude all PF
570
        $_SESSION['forbiden_pfs'] = array();
571
        $where = new WhereClause('and'); // create a WHERE statement of pieces joined by ANDs
572
        $where->add('personal_folder=%i', 1);
573
        if (isset($_SESSION['settings']['enable_pf_feature']) && $_SESSION['settings']['enable_pf_feature'] == 1) {
574
            $where->add('title=%s', $_SESSION['user_id']);
575
            $where->negateLast();
576
        }
577
        // Get ID of personal folder
578
        $pf = DB::queryfirstrow(
579
            "SELECT id FROM ".prefix_table("nested_tree")." WHERE title = %s",
580
            $_SESSION['user_id']
581
        );
582
        if (!empty($pf['id'])) {
583
            if (!in_array($pf['id'], $_SESSION['groupes_visibles'])) {
584
                array_push($_SESSION['groupes_visibles'], $pf['id']);
585
                array_push($_SESSION['personal_visible_groups'], $pf['id']);
586
                // get all descendants
587
                $tree = new Tree\NestedTree\NestedTree(prefix_table("nested_tree"), 'id', 'parent_id', 'title', 'personal_folder');
0 ignored issues
show
Security Bug introduced by
It seems like prefix_table('nested_tree') targeting prefix_table() can also be of type false; however, Tree\NestedTree\NestedTree::__construct() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
Unused Code introduced by
The call to NestedTree::__construct() has too many arguments starting with 'personal_folder'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
588
                $tree->rebuild();
589
                $tst = $tree->getDescendants($pf['id']);
590
                foreach ($tst as $t) {
591
                    array_push($_SESSION['groupes_visibles'], $t->id);
592
                    array_push($_SESSION['personal_visible_groups'], $t->id);
593
                }
594
            }
595
        }
596
597
        // get complete list of ROLES
598
        $tmp = explode(";", $_SESSION['fonction_id']);
599
        $rows = DB::query(
600
            "SELECT * FROM ".prefix_table("roles_title")."
601
            ORDER BY title ASC");
602
        foreach ($rows as $record) {
603
            if (!empty($record['id']) && !in_array($record['id'], $tmp)) {
604
                array_push($tmp, $record['id']);
605
            }
606
        }
607
        $_SESSION['fonction_id'] = implode(";", $tmp);
608
609
        $_SESSION['groupes_visibles_list'] = implode(',', $_SESSION['groupes_visibles']);
610
        $_SESSION['is_admin'] = $isAdmin;
611
        // Check if admin has created Folders and Roles
612
        DB::query("SELECT * FROM ".prefix_table("nested_tree")."");
613
        $_SESSION['nb_folders'] = DB::count();
614
        DB::query("SELECT * FROM ".prefix_table("roles_title"));
615
        $_SESSION['nb_roles'] = DB::count();
616
    } else {
617
        // init
618
        $_SESSION['groupes_visibles'] = array();
619
        $_SESSION['personal_folders'] = array();
620
        $_SESSION['groupes_interdits'] = array();
621
        $_SESSION['personal_visible_groups'] = array();
622
        $_SESSION['read_only_folders'] = array();
623
        $groupesVisibles = array();
0 ignored issues
show
Unused Code introduced by
$groupesVisibles is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
624
        $groupesInterdits = array();
625
        $groupesInterditsUser = explode(';', trimElement($groupesInterditsUser, ";"));
626
        if (!empty($groupesInterditsUser) && count($groupesInterditsUser) > 0) {
627
            $groupesInterdits = $groupesInterditsUser;
628
        }
629
        $_SESSION['is_admin'] = $isAdmin;
630
        $fonctionsAssociees = explode(';', trimElement($idFonctions, ";"));
631
        $newListeGpVisibles = array();
0 ignored issues
show
Unused Code introduced by
$newListeGpVisibles is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
632
        $listeGpInterdits = array();
0 ignored issues
show
Unused Code introduced by
$listeGpInterdits is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
633
634
        $listAllowedFolders = $listForbidenFolders = $listFoldersLimited = $listFoldersEditableByRole = $listRestrictedFoldersForItems = $listReadOnlyFolders = $listNoAccessFolders = array();
0 ignored issues
show
Unused Code introduced by
$listNoAccessFolders is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
Unused Code introduced by
$listForbidenFolders is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
635
636
        // rechercher tous les groupes visibles en fonction des roles de l'utilisateur
637
        foreach ($fonctionsAssociees as $roleId) {
638
            if (!empty($roleId)) {
639
                // Get allowed folders for each Role
640
                $rows = DB::query("SELECT folder_id FROM ".prefix_table("roles_values")." WHERE role_id=%i", $roleId);
641
642
                if (DB::count() > 0) {
643
                    $tmp = DB::queryfirstrow("SELECT allow_pw_change FROM ".prefix_table("roles_title")." WHERE id = %i", $roleId);
644
                    foreach ($rows as $record) {
645
                        if (isset($record['folder_id']) && !in_array($record['folder_id'], $listAllowedFolders)) {
646
                            array_push($listAllowedFolders, $record['folder_id']); //echo $record['folder_id'].";";
0 ignored issues
show
Unused Code Comprehensibility introduced by
78% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
647
                        }
648
                        // Check if this group is allowed to modify any pw in allowed folders
649
                        if ($tmp['allow_pw_change'] == 1 && !in_array($record['folder_id'], $listFoldersEditableByRole)) {
650
                            array_push($listFoldersEditableByRole, $record['folder_id']);
651
                        }
652
                    }
653
                    // Check for the users roles if some specific rights exist on items
654
                    $rows = DB::query(
655
                        "SELECT i.id_tree, r.item_id
656
                        FROM ".prefix_table("items")." as i
657
                        INNER JOIN ".prefix_table("restriction_to_roles")." as r ON (r.item_id=i.id)
658
                        WHERE r.role_id=%i
659
                        ORDER BY i.id_tree ASC",
660
                        $roleId
661
                    );
662
                    $x = 0;
663
                    foreach ($rows as $record) {
664
                        if (isset($record['id_tree'])) {
665
                            $listFoldersLimited[$record['id_tree']][$x] = $record['item_id'];
666
                            $x++;
667
                        }
668
                    }
669
                }
670
            }
671
        }
672
673
        // Does this user is allowed to see other items
674
        $x = 0;
675
        $rows = DB::query(
676
            "SELECT id, id_tree FROM ".prefix_table("items")."
677
            WHERE restricted_to LIKE %ss AND inactif=%s",
678
            $_SESSION['user_id'].';',
679
            '0'
680
        );
681
        foreach ($rows as $record) {
682
            $listRestrictedFoldersForItems[$record['id_tree']][$x] = $record['id'];
683
            $x++;
684
        }
685
        // => Build final lists
686
        // Clean arrays
687
        $allowedFoldersTmp = array();
0 ignored issues
show
Unused Code introduced by
$allowedFoldersTmp is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
688
        $listAllowedFolders = array_unique($listAllowedFolders);
689
        $groupesVisiblesUser = explode(';', trimElement($groupesVisiblesUser, ";"));
690
        // Add user allowed folders
691
        $allowedFoldersTmp = array_unique(
692
            array_merge($listAllowedFolders, $groupesVisiblesUser)
693
        );
694
        // Exclude from allowed folders all the specific user forbidden folders
695
        $allowedFolders = array();
696
        foreach ($allowedFoldersTmp as $id) {
697
            if (!in_array($id, $groupesInterditsUser) && !empty($id)) {
698
                array_push($allowedFolders, $id);
699
            }
700
        }
701
702
        // Clean array
703
        $listAllowedFolders = array_filter(array_unique($allowedFolders));
704
705
        // Exclude all PF
706
        $_SESSION['forbiden_pfs'] = array();
707
708
        $where = new WhereClause('and');
709
        $where->add('personal_folder=%i', 1);
710
        if (
711
            isset($_SESSION['settings']['enable_pf_feature']) &&
712
            $_SESSION['settings']['enable_pf_feature'] == 1 &&
713
            isset($_SESSION['personal_folder']) &&
714
            $_SESSION['personal_folder'] == 1
715
        ) {
716
            $where->add('title=%s', $_SESSION['user_id']);
717
            $where->negateLast();
718
        }
719
720
        $pfs = DB::query("SELECT id FROM ".prefix_table("nested_tree")." WHERE %l", $where);
721
        foreach ($pfs as $pfId) {
722
            array_push($_SESSION['forbiden_pfs'], $pfId['id']);
723
        }
724
        // Get IDs of personal folders
725
        if (
726
            isset($_SESSION['settings']['enable_pf_feature']) &&
727
            $_SESSION['settings']['enable_pf_feature'] == 1 &&
728
            isset($_SESSION['personal_folder']) &&
729
            $_SESSION['personal_folder'] == 1
730
        ) {
731
            $pf = DB::queryfirstrow("SELECT id FROM ".prefix_table("nested_tree")." WHERE title = %s", $_SESSION['user_id']);
732
            if (!empty($pf['id'])) {
733
                if (!in_array($pf['id'], $listAllowedFolders)) {
734
                    array_push($_SESSION['personal_folders'], $pf['id']);
735
                    // get all descendants
736
                    $ids = $tree->getDescendants($pf['id'], true, false);
737
                    foreach ($ids as $id) {
738
                        array_push($listAllowedFolders, $id->id);
739
                        array_push($_SESSION['personal_visible_groups'], $id->id);
740
                        array_push($_SESSION['personal_folders'], $id->id);
741
                    }
742
                }
743
            }
744
            // get list of readonly folders when pf is disabled.
745
            // rule - if one folder is set as W or N in one of the Role, then User has access as W
746
            foreach ($listAllowedFolders as $folderId) {
747
                if (!in_array($folderId, array_unique(array_merge($listReadOnlyFolders, $_SESSION['personal_folders'])))) {   //
748
                    DB::query(
749
                        "SELECT *
750
                        FROM ".prefix_table("roles_values")."
751
                        WHERE folder_id = %i AND role_id IN %li AND type IN %ls",
752
                        $folderId,
753
                        $fonctionsAssociees,
754
                        array("W", "ND", "NE", "NDNE")
755
756
                    );
757
                    if (DB::count() == 0 && !in_array($folderId, $groupesVisiblesUser)) {
758
                        array_push($listReadOnlyFolders, $folderId);
759
                    }
760
                }
761
            }
762
        } else {
763
            // get list of readonly folders when pf is disabled.
764
            // rule - if one folder is set as W in one of the Role, then User has access as W
765
            foreach ($listAllowedFolders as $folderId) {
766
                if (!in_array($folderId, $listReadOnlyFolders)) {   // || (isset($pf) && $folderId != $pf['id'])
0 ignored issues
show
Unused Code Comprehensibility introduced by
67% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
767
                    DB::query(
768
                        "SELECT *
769
                        FROM ".prefix_table("roles_values")."
770
                        WHERE folder_id = %i AND role_id IN %li AND type IN %ls",
771
                        $folderId,
772
                        $fonctionsAssociees,
773
                        array("W", "ND", "NE", "NDNE")
774
                    );
775
                    if (DB::count() == 0 && !in_array($folderId, $groupesVisiblesUser)) {
776
                        array_push($listReadOnlyFolders, $folderId);
777
                    }
778
                }
779
            }
780
        }
781
782
        // check if change proposals on User's items
783
        if (isset($_SESSION['settings']['enable_suggestion']) && $_SESSION['settings']['enable_suggestion'] == 1) {
784
            DB::query(
785
                "SELECT *
786
                FROM ".prefix_table("items_change")." AS c
787
                LEFT JOIN ".prefix_table("log_items")." AS i ON (c.item_id = i.id_item)
788
                WHERE i.action = %s AND i.id_user = %i",
789
                "at_creation",
790
                $_SESSION['user_id']
791
            );
792
            $_SESSION['nb_item_change_proposals'] = DB::count();
793
        } else {
794
            $_SESSION['nb_item_change_proposals'] = 0;
795
        }
796
797
        $_SESSION['all_non_personal_folders'] = $listAllowedFolders;
798
        $_SESSION['groupes_visibles'] = $listAllowedFolders;
799
        $_SESSION['groupes_visibles_list'] = implode(',', $listAllowedFolders);
800
        $_SESSION['personal_visible_groups_list'] = implode(',', $_SESSION['personal_visible_groups']);
801
        $_SESSION['read_only_folders'] = $listReadOnlyFolders;
802
        $_SESSION['no_access_folders'] = $groupesInterdits;
803
804
        $_SESSION['list_folders_limited'] = $listFoldersLimited;
805
        $_SESSION['list_folders_editable_by_role'] = $listFoldersEditableByRole;
806
        $_SESSION['list_restricted_folders_for_items'] = $listRestrictedFoldersForItems;
807
        // Folders and Roles numbers
808
        DB::queryfirstrow("SELECT id FROM ".prefix_table("nested_tree")."");
809
        $_SESSION['nb_folders'] = DB::count();
810
        DB::queryfirstrow("SELECT id FROM ".prefix_table("roles_title"));
811
        $_SESSION['nb_roles'] = DB::count();
812
    }
813
814
    // update user's timestamp
815
    DB::update(
816
        prefix_table('users'),
817
        array(
818
            'timestamp' => time()
819
        ),
820
        "id=%i",
821
        $_SESSION['user_id']
822
    );
823
}
824
825
/**
826
 * updateCacheTable()
827
 *
828
 * Update the CACHE table
829
 * @param string $action
830
 */
831
function updateCacheTable($action, $id = "")
832
{
833
    global $db, $server, $user, $pass, $database, $pre, $port, $encoding;
834
    require_once $_SESSION['settings']['cpassman_dir'].'/sources/SplClassLoader.php';
835
836
    //Connect to DB
837
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
838
    DB::$host = $server;
839
    DB::$user = $user;
840
    DB::$password = $pass;
841
    DB::$dbName = $database;
842
    DB::$port = $port;
843
    DB::$encoding = $encoding;
844
    DB::$error_handler = 'db_error_handler';
0 ignored issues
show
Documentation Bug introduced by
The property $error_handler was declared of type boolean, but 'db_error_handler' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
845
    $link = mysqli_connect($server, $user, $pass, $database, $port);
846
    $link->set_charset($encoding);
847
848
    //Load Tree
849
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
850
    $tree->register();
851
    $tree = new Tree\NestedTree\NestedTree(prefix_table("nested_tree"), 'id', 'parent_id', 'title');
0 ignored issues
show
Security Bug introduced by
It seems like prefix_table('nested_tree') targeting prefix_table() can also be of type false; however, Tree\NestedTree\NestedTree::__construct() does only seem to accept string, did you maybe forget to handle an error condition?
Loading history...
852
853
    // Rebuild full cache table
854
    if ($action === "reload") {
855
        // truncate table
856
        DB::query("TRUNCATE TABLE ".$pre."cache");
857
858
        // reload date
859
        $rows = DB::query(
860
            "SELECT *
861
            FROM ".$pre."items as i
862
            INNER JOIN ".$pre."log_items as l ON (l.id_item = i.id)
863
            AND l.action = %s
864
            AND i.inactif = %i",
865
            'at_creation',
866
            0
867
        );
868
        foreach ($rows as $record) {
869
            // Get all TAGS
870
            $tags = "";
871
            $itemTags = DB::query("SELECT tag FROM ".$pre."tags WHERE item_id=%i", $record['id']);
872 View Code Duplication
            foreach ($itemTags as $itemTag) {
873
                if (!empty($itemTag['tag'])) {
874
                    $tags .= $itemTag['tag']." ";
875
                }
876
            }
877
            // Get renewal period
878
            $resNT = DB::queryfirstrow("SELECT renewal_period FROM ".$pre."nested_tree WHERE id=%i", $record['id_tree']);
879
880
            // form id_tree to full foldername
881
            $folder = "";
882
            $arbo = $tree->getPath($record['id_tree'], true);
883 View Code Duplication
            foreach ($arbo as $elem) {
884
                if ($elem->title == $_SESSION['user_id'] && $elem->nlevel == 1) {
885
                    $elem->title = $_SESSION['login'];
886
                }
887
                if (empty($folder)) {
888
                    $folder = stripslashes($elem->title);
889
                } else {
890
                    $folder .= " » ".stripslashes($elem->title);
891
                }
892
            }
893
            // store data
894
            DB::insert(
895
                $pre."cache",
896
                array(
897
                    'id' => $record['id'],
898
                    'label' => $record['label'],
899
                    'description' => $record['description'],
900
                    'url' => (isset($record['url']) && !empty($record['url'])) ? $record['url'] : "0",
901
                    'tags' => $tags,
902
                    'id_tree' => $record['id_tree'],
903
                    'perso' => $record['perso'],
904
                    'restricted_to' => (isset($record['restricted_to']) && !empty($record['restricted_to'])) ? $record['restricted_to'] : "0",
905
                    'login' => isset($record['login']) ? $record['login'] : "",
906
                    'folder' => $folder,
907
                    'author' => $record['id_user'],
908
                    'renewal_period' => isset($resNT['renewal_period']) ? $resNT['renewal_period'] : "0",
909
                    'timestamp' => $record['date']
910
                    )
911
            );
912
        }
913
        // UPDATE an item
914
    } elseif ($action === "update_value") {
915
        // get new value from db
916
        $data = DB::queryfirstrow(
917
            "SELECT label, description, id_tree, perso, restricted_to, login, url
918
            FROM ".$pre."items
919
            WHERE id=%i", $id);
920
        // Get all TAGS
921
        $tags = "";
922
        $itemTags = DB::query("SELECT tag FROM ".$pre."tags WHERE item_id=%i", $id);
923 View Code Duplication
        foreach ($itemTags as $itemTag) {
924
            if (!empty($itemTag['tag'])) {
925
                $tags .= $itemTag['tag']." ";
926
            }
927
        }
928
        // form id_tree to full foldername
929
        $folder = "";
930
        $arbo = $tree->getPath($data['id_tree'], true);
931 View Code Duplication
        foreach ($arbo as $elem) {
932
            if ($elem->title == $_SESSION['user_id'] && $elem->nlevel == 1) {
933
                $elem->title = $_SESSION['login'];
934
            }
935
            if (empty($folder)) {
936
                $folder = stripslashes($elem->title);
937
            } else {
938
                $folder .= " » ".stripslashes($elem->title);
939
            }
940
        }
941
        // finaly update
942
        DB::update(
943
            $pre."cache",
944
            array(
945
                'label' => $data['label'],
946
                'description' => $data['description'],
947
                'tags' => $tags,
948
                'url' => (isset($data['url']) && !empty($data['url'])) ? $data['url'] : "0",
949
                'id_tree' => $data['id_tree'],
950
                'perso' => $data['perso'],
951
                'restricted_to' => $data['restricted_to'],
952
                'login' => isset($data['login']) ? $data['login'] : "",
953
                'folder' => $folder,
954
                'author' => $_SESSION['user_id'],
955
                ),
956
            "id = %i",
957
            $id
958
        );
959
        // ADD an item
960
    } elseif ($action === "add_value") {
961
        // get new value from db
962
        $data = DB::queryFirstRow(
963
            "SELECT i.label, i.description, i.id_tree as id_tree, i.perso, i.restricted_to, i.id, i.login, i.url, l.date
964
            FROM ".$pre."items as i
965
            INNER JOIN ".$pre."log_items as l ON (l.id_item = i.id)
966
            WHERE i.id = %i
967
            AND l.action = %s",
968
            $id, 'at_creation'
969
        );
970
        // Get all TAGS
971
        $tags = "";
972
        $itemTags = DB::query("SELECT tag FROM ".$pre."tags WHERE item_id = %i", $id);
973 View Code Duplication
        foreach ($itemTags as $itemTag) {
974
            if (!empty($itemTag['tag'])) {
975
                $tags .= $itemTag['tag']." ";
976
            }
977
        }
978
        // form id_tree to full foldername
979
        $folder = "";
980
        $arbo = $tree->getPath($data['id_tree'], true);
981 View Code Duplication
        foreach ($arbo as $elem) {
982
            if ($elem->title == $_SESSION['user_id'] && $elem->nlevel == 1) {
983
                $elem->title = $_SESSION['login'];
984
            }
985
            if (empty($folder)) {
986
                $folder = stripslashes($elem->title);
987
            } else {
988
                $folder .= " » ".stripslashes($elem->title);
989
            }
990
        }
991
        // finaly update
992
        DB::insert(
993
            $pre."cache",
994
            array(
995
                'id' => $data['id'],
996
                'label' => $data['label'],
997
                'description' => $data['description'],
998
                'tags' => $tags,
999
                'url' => (isset($data['url']) && !empty($data['url'])) ? $data['url'] : "0",
1000
                'url' => $data['url'],
1001
                'id_tree' => $data['id_tree'],
1002
                'perso' => $data['perso'],
1003
                'restricted_to' => $data['restricted_to'],
1004
                'login' => isset($data['login']) ? $data['login'] : "",
1005
                'folder' => $folder,
1006
                'author' => $_SESSION['user_id'],
1007
                'timestamp' => $data['date']
1008
                )
1009
        );
1010
        // DELETE an item
1011
    } elseif ($action === "delete_value") {
1012
        DB::delete($pre."cache", "id = %i", $id);
1013
    }
1014
}
1015
1016
/*
1017
*
1018
*/
1019
function getStatisticsData() {
1020
        DB::query(
1021
        "SELECT id FROM ".prefix_table("nested_tree")." WHERE personal_folder = %i",
1022
        0
1023
    );
1024
    $counter_folders = DB::count();
1025
1026
    DB::query(
1027
        "SELECT id FROM ".prefix_table("nested_tree")." WHERE personal_folder = %i",
1028
        1
1029
    );
1030
    $counter_folders_perso = DB::count();
1031
1032
    DB::query(
1033
        "SELECT id FROM ".prefix_table("items")." WHERE perso = %i",
1034
        0
1035
    );
1036
    $counter_items = DB::count();
1037
1038
    DB::query(
1039
        "SELECT id FROM ".prefix_table("items")." WHERE perso = %i",
1040
        1
1041
    );
1042
    $counter_items_perso = DB::count();
1043
1044
    DB::query(
1045
        "SELECT id FROM ".prefix_table("users").""
1046
    );
1047
    $counter_users = DB::count();
1048
1049
    DB::query(
1050
        "SELECT id FROM ".prefix_table("users")." WHERE admin = %i",
1051
        1
1052
    );
1053
    $admins = DB::count();
1054
1055
    DB::query(
1056
        "SELECT id FROM ".prefix_table("users")." WHERE gestionnaire = %i",
1057
        1
1058
    );
1059
    $managers = DB::count();
1060
1061
    DB::query(
1062
        "SELECT id FROM ".prefix_table("users")." WHERE read_only = %i",
1063
        1
1064
    );
1065
    $ro = DB::count();
1066
1067
    // list the languages
1068
    $usedLang = [];
1069
    $tp_languages = DB::query(
1070
        "SELECT name FROM ".prefix_table("languages")
1071
    );
1072
    foreach ($tp_languages as $tp_language) {
1073
        DB::query(
1074
            "SELECT * FROM ".prefix_table("users")." WHERE user_language = %s",
1075
            $tp_language['name']
1076
        );
1077
        $usedLang[$tp_language['name']] = round((DB::count() * 100 / $counter_users), 0);
1078
    }
1079
1080
    // get list of ips
1081
    $usedIp = [];
1082
    $tp_ips = DB::query(
1083
        "SELECT user_ip FROM ".prefix_table("users")
1084
    );
1085
    foreach ($tp_ips as $ip) {
1086
        if (array_key_exists($ip['user_ip'], $usedIp)) {
1087
            $usedIp[$ip['user_ip']] = $usedIp[$ip['user_ip']] + 1;
1088
        } else if (!empty($ip['user_ip']) && $ip['user_ip'] !== "none") {
1089
            $usedIp[$ip['user_ip']] = 1;
1090
        }
1091
    }
1092
1093
    return array(
1094
        "error" => "",
1095
        "stat_phpversion" => phpversion(),
1096
        "stat_folders" => $counter_folders,
1097
        "stat_folders_shared" => intval($counter_folders) - intval($counter_folders_perso),
1098
        "stat_items" => $counter_items,
1099
        "stat_items_shared" => intval($counter_items) - intval($counter_items_perso),
1100
        "stat_users" => $counter_users,
1101
        "stat_admins" => $admins,
1102
        "stat_managers" => $managers,
1103
        "stat_ro" => $ro,
1104
        "stat_kb" => $_SESSION['settings']['enable_kb'],
1105
        "stat_pf" => $_SESSION['settings']['enable_pf_feature'],
1106
        "stat_fav" => $_SESSION['settings']['enable_favourites'],
1107
        "stat_teampassversion" => $_SESSION['settings']['cpassman_version'],
1108
        "stat_ldap" => $_SESSION['settings']['ldap_mode'],
1109
        "stat_agses" => $_SESSION['settings']['agses_authentication_enabled'],
1110
        "stat_duo" => $_SESSION['settings']['duo'],
1111
        "stat_suggestion" => $_SESSION['settings']['enable_suggestion'],
1112
        "stat_api" => $_SESSION['settings']['api'],
1113
        "stat_customfields" => $_SESSION['settings']['item_extra_fields'],
1114
        "stat_syslog" => $_SESSION['settings']['syslog_enable'],
1115
        "stat_2fa" => $_SESSION['settings']['google_authentication'],
1116
        "stat_stricthttps" => $_SESSION['settings']['enable_sts'],
1117
        "stat_mysqlversion" => DB::serverVersion(),
1118
        "stat_languages" => $usedLang,
1119
        "stat_country" => $usedIp
1120
    );
1121
}
1122
1123
/**
1124
 * sendEmail()
1125
 *
1126
 * @return
1127
 */
1128
function sendEmail($subject, $textMail, $email, $textMailAlt = "")
1129
{
1130
    global $LANG;
1131
    include $_SESSION['settings']['cpassman_dir'].'/includes/config/settings.php';
1132
    //load library
1133
    $user_language = isset($_SESSION['user_language']) ? $_SESSION['user_language'] : "english";
1134
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/language/'.$user_language.'.php';
1135
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/libraries/Email/Phpmailer/PHPMailerAutoload.php';
1136
1137
    // load PHPMailer
1138
    if (!isset($mail)) {
0 ignored issues
show
Bug introduced by
The variable $mail seems only to be defined at a later point. As such the call to isset() seems to always evaluate to false.

This check marks calls to isset(...) or empty(...) that are found before the variable itself is defined. These will always have the same result.

This is likely the result of code being shifted around. Consider removing these calls.

Loading history...
1139
        $mail = new PHPMailer();
1140
    }
1141
    // send to user
1142
    $mail->setLanguage("en", "../includes/libraries/Email/Phpmailer/language/");
1143
    $mail->SMTPDebug = 0; //value 1 can be used to debug
1144
    $mail->Port = $_SESSION['settings']['email_port']; //COULD BE USED
1145
    $mail->CharSet = "utf-8";
1146
    $smtp_security = $_SESSION['settings']['email_security'];
1147
    if ($smtp_security == "tls" || $smtp_security == "ssl") {
1148
        $mail->SMTPSecure = $smtp_security;
1149
    }
1150
    $mail->isSmtp(); // send via SMTP
1151
    $mail->Host = $_SESSION['settings']['email_smtp_server']; // SMTP servers
1152
    $mail->SMTPAuth = $_SESSION['settings']['email_smtp_auth'] == '1' ? true : false; // turn on SMTP authentication
1153
    $mail->Username = $_SESSION['settings']['email_auth_username']; // SMTP username
1154
    $mail->Password = $_SESSION['settings']['email_auth_pwd']; // SMTP password
1155
    $mail->From = $_SESSION['settings']['email_from'];
1156
    $mail->FromName = $_SESSION['settings']['email_from_name'];
1157
    $mail->addAddress($email); //Destinataire
1158
    $mail->WordWrap = 80; // set word wrap
1159
    $mail->isHtml(true); // send as HTML
1160
    $mail->Subject = $subject;
1161
    $mail->Body = $textMail;
1162
    $mail->AltBody = $textMailAlt;
1163
    // send email
1164
    if (!$mail->send()) {
1165
        return '"error":"error_mail_not_send" , "message":"'.str_replace(array("\n", "\t", "\r"), '', $mail->ErrorInfo).'"';
1166
    } else {
1167
        return '"error":"" , "message":"'.$LANG['forgot_my_pw_email_sent'].'"';
1168
    }
1169
}
1170
1171
/**
1172
 * generateKey()
1173
 *
1174
 * @return
1175
 */
1176
function generateKey()
1177
{
1178
    return substr(md5(rand().rand()), 0, 15);
1179
}
1180
1181
/**
1182
 * dateToStamp()
1183
 *
1184
 * @return
1185
 */
1186
function dateToStamp($date)
1187
{
1188
    $date = date_parse_from_format($_SESSION['settings']['date_format'], $date);
1189
    if ($date['warning_count'] == 0 && $date['error_count'] == 0) {
1190
        return mktime(0, 0, 0, $date['month'], $date['day'], $date['year']);
1191
    } else {
1192
        return false;
1193
    }
1194
}
1195
1196
function isDate($date)
1197
{
1198
    return (strtotime($date) !== false);
1199
}
1200
1201
/**
1202
 * isUTF8()
1203
 *
1204
 * @return integer is the string in UTF8 format.
1205
 */
1206
1207
function isUTF8($string)
1208
{
1209
    if (is_array($string) === true) {
1210
        $string = $string['string'];
1211
    }
1212
    return preg_match(
1213
        '%^(?:
1214
        [\x09\x0A\x0D\x20-\x7E] # ASCII
1215
        | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
1216
        | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
1217
        | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
1218
        | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
1219
        | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
1220
        | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
1221
        | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
1222
        )*$%xs',
1223
        $string
1224
    );
1225
}
1226
1227
/*
1228
* FUNCTION
1229
* permits to prepare data to be exchanged
1230
*/
1231
/**
1232
 * @param string $type
1233
 */
1234
function prepareExchangedData($data, $type)
1235
{
1236
    //load ClassLoader
1237
    require_once $_SESSION['settings']['cpassman_dir'].'/sources/SplClassLoader.php';
1238
    //Load AES
1239
    $aes = new SplClassLoader('Encryption\Crypt', '../includes/libraries');
1240
    $aes->register();
1241
1242
    if ($type == "encode") {
1243
        if (
1244
            isset($_SESSION['settings']['encryptClientServer'])
1245
            && $_SESSION['settings']['encryptClientServer'] == 0
1246
        ) {
1247
            return json_encode(
1248
                $data,
1249
                JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1250
            );
1251
        } else {
1252
            return Encryption\Crypt\aesctr::encrypt(
1253
                json_encode(
1254
                    $data,
1255
                    JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1256
                ),
1257
                $_SESSION['key'],
1258
                256
1259
            );
1260
        }
1261
    } elseif ($type == "decode") {
1262
        if (
1263
            isset($_SESSION['settings']['encryptClientServer'])
1264
            && $_SESSION['settings']['encryptClientServer'] == 0
1265
        ) {
1266
            return json_decode(
1267
                $data,
1268
                true
1269
            );
1270
        } else {
1271
            return json_decode(
1272
                Encryption\Crypt\aesctr::decrypt(
1273
                    $data,
1274
                    $_SESSION['key'],
1275
                    256
1276
                ),
1277
                true
1278
            );
1279
        }
1280
    }
1281
}
1282
1283
function make_thumb($src, $dest, $desired_width) {
1284
1285
    /* read the source image */
1286
    $source_image = imagecreatefrompng($src);
1287
    $width = imagesx($source_image);
1288
    $height = imagesy($source_image);
1289
1290
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1291
    $desired_height = floor($height * ($desired_width / $width));
1292
1293
    /* create a new, "virtual" image */
1294
    $virtual_image = imagecreatetruecolor($desired_width, $desired_height);
1295
1296
    /* copy source image at a resized size */
1297
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
1298
1299
    /* create the physical thumbnail image to its destination */
1300
    imagejpeg($virtual_image, $dest);
1301
}
1302
1303
/*
1304
** check table prefix in SQL query
1305
*/
1306
/**
1307
 * @param string $table
1308
 */
1309
function prefix_table($table)
1310
{
1311
    global $pre;
1312
    $safeTable = htmlspecialchars($pre.$table);
1313
    if (!empty($safeTable)) {
1314
        // sanitize string
1315
        return $safeTable;
1316
    } else {
1317
        // stop error no table
1318
        return false;
1319
    }
1320
}
1321
1322
/*
1323
 * Creates a KEY using PasswordLib
1324
 */
1325
function GenerateCryptKey($size = "", $secure = "", $numerals = "", $capitalize = "", $ambiguous = "", $symbols = "")
1326
{
1327
    // load library
1328
    $pwgen = new SplClassLoader('Encryption\PwGen', '../includes/libraries');
1329
    $pwgen->register();
1330
    $pwgen = new Encryption\PwGen\pwgen();
1331
1332
    // init
1333
    if (!empty($size)) $pwgen->setLength($size);
1334
    if (!empty($secure)) $pwgen->setSecure($secure);
1335
    if (!empty($numerals)) $pwgen->setNumerals($numerals);
1336
    if (!empty($capitalize)) $pwgen->setCapitalize($capitalize);
1337
    if (!empty($ambiguous)) $pwgen->setAmbiguous($ambiguous);
1338
    if (!empty($symbols)) $pwgen->setSymbols($symbols);
1339
1340
    // generate and send back
1341
    return $pwgen->generate();
1342
}
1343
1344
/*
1345
* Send sysLOG message
1346
*/
1347
/**
1348
 * @param string $message
1349
 */
1350
function send_syslog($message, $component = "teampass", $program = "php", $host , $port)
0 ignored issues
show
Unused Code introduced by
The parameter $program is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1351
{
1352
    $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
1353
        //$syslog_message = "<123>" . date('M d H:i:s ') . " " .$host . " " . $component . ": " . $message;
1354
    $syslog_message = "<123>" . date('M d H:i:s ') . $component . ": " . $message;
1355
        socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, $host, $port);
1356
    socket_close($sock);
1357
}
1358
1359
1360
1361
/**
1362
 * logEvents()
1363
 *
1364
 * permits to log events into DB
1365
 * @param string $type
1366
 * @param string $label
1367
 * @param string $field_1
1368
 */
1369
function logEvents($type, $label, $who, $login = "", $field_1 = NULL)
1370
{
1371
    global $server, $user, $pass, $database, $pre, $port, $encoding;
1372
1373
    if (empty($who)) {
1374
        $who = get_client_ip_server();
1375
    }
1376
1377
    // include librairies & connect to DB
1378
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1379
    DB::$host = $server;
1380
    DB::$user = $user;
1381
    DB::$password = $pass;
1382
    DB::$dbName = $database;
1383
    DB::$port = $port;
1384
    DB::$encoding = $encoding;
1385
    DB::$error_handler = 'db_error_handler';
0 ignored issues
show
Documentation Bug introduced by
The property $error_handler was declared of type boolean, but 'db_error_handler' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1386
    $link = mysqli_connect($server, $user, $pass, $database, $port);
1387
    $link->set_charset($encoding);
1388
1389
    DB::insert(
1390
        prefix_table("log_system"),
1391
        array(
1392
            'type' => $type,
1393
            'date' => time(),
1394
            'label' => $label,
1395
            'qui' => $who,
1396
            'field_1' => $field_1 == null ? "" : $field_1
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $field_1 of type string|null against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
1397
        )
1398
    );
1399
    if (isset($_SESSION['settings']['syslog_enable']) && $_SESSION['settings']['syslog_enable'] == 1) {
1400
        if ($type == "user_mngt") {
1401
            send_syslog("The User ".$login." perform the acction off ".$label." to the user ".$field_1." - ".$type, "teampass", "php", $_SESSION['settings']['syslog_host'], $_SESSION['settings']['syslog_port']);
1402
        } else {
1403
            send_syslog("The User ".$login." perform the acction off ".$label." - ".$type, "teampass", "php", $_SESSION['settings']['syslog_host'], $_SESSION['settings']['syslog_port']);
1404
        }
1405
    }
1406
}
1407
1408
/**
1409
 * @param string $item
1410
 * @param string $action
1411
 */
1412
function logItems($id, $item, $id_user, $action, $login = "", $raison = NULL, $raison_iv = NULL, $encryption_type = "")
1413
{
1414
    global $server, $user, $pass, $database, $pre, $port, $encoding;
1415
    // include librairies & connect to DB
1416
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1417
    DB::$host = $server;
1418
    DB::$user = $user;
1419
    DB::$password = $pass;
1420
    DB::$dbName = $database;
1421
    DB::$port = $port;
1422
    DB::$encoding = $encoding;
1423
    DB::$error_handler = 'db_error_handler';
0 ignored issues
show
Documentation Bug introduced by
The property $error_handler was declared of type boolean, but 'db_error_handler' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1424
    $link = mysqli_connect($server, $user, $pass, $database, $port);
1425
    $link->set_charset($encoding);
1426
    DB::insert(
1427
        prefix_table(
1428
            "log_items"),
1429
            array(
1430
                'id_item' => $id,
1431
                'date' => time(),
1432
                'id_user' => $id_user,
1433
                'action' => $action,
1434
                'raison' => $raison,
1435
                'raison_iv' => $raison_iv,
1436
                'encryption_type' => $encryption_type
1437
            )
1438
        );
1439
        if (isset($_SESSION['settings']['syslog_enable']) && $_SESSION['settings']['syslog_enable'] == 1) {
1440
                send_syslog("The Item ".$item." was ".$action." by ".$login." ".$raison, "teampass", "php", $_SESSION['settings']['syslog_host'], $_SESSION['settings']['syslog_port']);
1441
        }
1442
}
1443
1444
/*
1445
* Function to get the client ip address
1446
 */
1447
function get_client_ip_server() {
1448
    $ipaddress = '';
0 ignored issues
show
Unused Code introduced by
$ipaddress is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1449
    if (getenv('HTTP_CLIENT_IP'))
1450
        $ipaddress = getenv('HTTP_CLIENT_IP');
1451
    else if (getenv('HTTP_X_FORWARDED_FOR'))
1452
        $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
1453
    else if (getenv('HTTP_X_FORWARDED'))
1454
        $ipaddress = getenv('HTTP_X_FORWARDED');
1455
    else if (getenv('HTTP_FORWARDED_FOR'))
1456
        $ipaddress = getenv('HTTP_FORWARDED_FOR');
1457
    else if (getenv('HTTP_FORWARDED'))
1458
        $ipaddress = getenv('HTTP_FORWARDED');
1459
    else if (getenv('REMOTE_ADDR'))
1460
        $ipaddress = getenv('REMOTE_ADDR');
1461
    else
1462
        $ipaddress = 'UNKNOWN';
1463
1464
    return $ipaddress;
1465
}
1466
1467
/**
1468
 * Escape all HTML, JavaScript, and CSS
1469
 *
1470
 * @param string $input The input string
1471
 * @param string $encoding Which character encoding are we using?
1472
 * @return string
1473
 */
1474
function noHTML($input, $encoding = 'UTF-8')
1475
{
1476
    return htmlspecialchars($input, ENT_QUOTES | ENT_XHTML, $encoding, false);
1477
}
1478
1479
/**
1480
 * handleConfigFile()
1481
 *
1482
 * permits to handle the Teampass config file
1483
 * $action accepts "rebuild" and "update"
1484
 */
1485
function handleConfigFile($action, $field = null, $value = null)
1486
{
1487
    global $server, $user, $pass, $database, $pre, $port, $encoding;
1488
    $tp_config_file = "../includes/config/tp.config.php";
1489
1490
    // include librairies & connect to DB
1491
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1492
    DB::$host = $server;
1493
    DB::$user = $user;
1494
    DB::$password = $pass;
1495
    DB::$dbName = $database;
1496
    DB::$port = $port;
1497
    DB::$encoding = $encoding;
1498
    DB::$error_handler = 'db_error_handler';
0 ignored issues
show
Documentation Bug introduced by
The property $error_handler was declared of type boolean, but 'db_error_handler' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1499
    $link = mysqli_connect($server, $user, $pass, $database, $port);
1500
    $link->set_charset($encoding);
1501
1502
    if (!file_exists($tp_config_file) || $action == "rebuild") {
1503
        // perform a copy
1504
        if (file_exists($tp_config_file)) {
1505
            if (!copy($tp_config_file, $tp_config_file.'.'.date("Y_m_d_His", time()))) {
1506
                return "ERROR: Could not copy file '".$tp_config_file."'";
1507
            }
1508
        }
1509
1510
        // regenerate
1511
        $data = array();
1512
        $data[0] = "<?php\n";
1513
        $data[1] = "global \$SETTINGS;\n";
1514
        $data[2] = "\$SETTINGS = array (\n";
1515
        $rows = DB::query(
1516
            "SELECT * FROM ".prefix_table("misc")." WHERE type=%s",
1517
            "admin"
1518
        );
1519
        foreach ($rows as $record) {
1520
            array_push($data, "    '".$record['intitule']."' => '".$record['valeur']."',\n");
1521
        }
1522
        array_push($data, ");");
1523
        $dat = array_unique($data);
0 ignored issues
show
Unused Code introduced by
$dat is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1524
    } else if ($action == "update" && !empty($field)) {
1525
        $data = file($tp_config_file);
1526
        $x = 0;
1527
        $bFound = false;
1528
        foreach ($data as $line) {
1529
            if (stristr($line, ");")) break;
1530
            if (stristr($line, "'".$field."' => '")) {
1531
                $data[$x] = "    '".$field."' => '".$value."',\n";
1532
                $bFound = true;
1533
                break;
1534
            }
1535
            $x++;
1536
        }
1537
        if ($bFound === false) {
1538
            $data[($x - 1)] = "    '".$field."' => '".$value."',\n";
1539
        }
1540
    } else {
0 ignored issues
show
Unused Code introduced by
This else statement is empty and can be removed.

This check looks for the else branches of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These else branches can be removed.

if (rand(1, 6) > 3) {
print "Check failed";
} else {
    //print "Check succeeded";
}

could be turned into

if (rand(1, 6) > 3) {
    print "Check failed";
}

This is much more concise to read.

Loading history...
1541
        // ERROR
1542
    }
1543
1544
    // update file
1545
    file_put_contents($tp_config_file, implode('', $data));
0 ignored issues
show
Bug introduced by
The variable $data does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Security File Manipulation introduced by
implode('', $data) can contain request data and is used in file manipulation context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST
    in sources/admin.queries.php on line 1793
  2. Data is decoded by json_decode()
    in vendor/sources/main.functions.php on line 1267
  3. $dataReceived is assigned
    in sources/admin.queries.php on line 1793
  4. $dataReceived['field'] is passed to handleConfigFile()
    in sources/admin.queries.php on line 1913
  5. $data is assigned
    in sources/main.functions.php on line 1538
  6. $data is passed through implode()
    in sources/main.functions.php on line 1545

General Strategies to prevent injection

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

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

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

$sanitized = (integer) $tainted;
Loading history...
1546
1547
    return true;
1548
}
1549
1550
/*
1551
** Permits to replace &#92; to permit correct display
1552
*/
1553
function handleBackslash($input)
1554
{
1555
    return str_replace("&amp;#92;", "&#92;", $input);
1556
}
1557
1558
/*
1559
** Permits to loas settings
1560
*/
1561
function loadSettings()
1562
{
1563
    /* LOAD CPASSMAN SETTINGS */
1564 View Code Duplication
    if (!isset($_SESSION['settings']['loaded']) || $_SESSION['settings']['loaded'] != 1) {
1565
        $_SESSION['settings']['duplicate_folder'] = 0; //by default, this is false;
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1566
        $_SESSION['settings']['duplicate_item'] = 0; //by default, this is false;
0 ignored issues
show
Unused Code Comprehensibility introduced by
37% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
1567
        $_SESSION['settings']['number_of_used_pw'] = 5; //by default, this value is 5;
1568
1569
        $rows = DB::query("SELECT * FROM ".prefix_table("misc")." WHERE type=%s_type OR type=%s_type2",
1570
            array(
1571
                'type' => "admin",
1572
                'type2' => "settings"
1573
            )
1574
        );
1575
        foreach ($rows as $record) {
1576
            if ($record['type'] == 'admin') {
1577
                $_SESSION['settings'][$record['intitule']] = $record['valeur'];
1578
            } else {
1579
                $settings[$record['intitule']] = $record['valeur'];
0 ignored issues
show
Coding Style Comprehensibility introduced by
$settings was never initialized. Although not strictly required by PHP, it is generally a good practice to add $settings = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
1580
            }
1581
        }
1582
        $_SESSION['settings']['loaded'] = 1;
1583
        $_SESSION['settings']['default_session_expiration_time'] = 5;
1584
    }
1585
}
1586
1587
/*
1588
** check if folder has custom fields.
1589
** Ensure that target one also has same custom fields
1590
*/
1591
function checkCFconsistency($source_id, $target_id) {
1592
    $source_cf = array();
1593
    $rows = DB::QUERY(
1594
        "SELECT id_category
1595
        FROM ".prefix_table("categories_folders")."
1596
        WHERE id_folder = %i",
1597
        $source_id
1598
    );
1599
    foreach ($rows as $record) {
1600
        array_push($source_cf, $record['id_category']);
1601
    }
1602
1603
    $target_cf = array();
1604
    $rows = DB::QUERY(
1605
        "SELECT id_category
1606
        FROM ".prefix_table("categories_folders")."
1607
        WHERE id_folder = %i",
1608
        $target_id
1609
    );
1610
    foreach ($rows as $record) {
1611
        array_push($target_cf, $record['id_category']);
1612
    }
1613
1614
    $cf_diff = array_diff($source_cf, $target_cf);
1615
    if (count($cf_diff) > 0) {
1616
        return false;
1617
    }
1618
1619
    return true;
1620
}
1621
1622
/*
1623
*
1624
*/
1625
function encrypt_or_decrypt_file($image_code, $image_status, $opts) {
1626
    global $server, $user, $pass, $database, $pre, $port, $encoding;
1627
    $tp_config_file = "../includes/config/tp.config.php";
0 ignored issues
show
Unused Code introduced by
$tp_config_file is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1628
1629
    // include librairies & connect to DB
1630
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1631
    DB::$host = $server;
1632
    DB::$user = $user;
1633
    DB::$password = $pass;
1634
    DB::$dbName = $database;
1635
    DB::$port = $port;
1636
    DB::$encoding = $encoding;
1637
    DB::$error_handler = 'db_error_handler';
0 ignored issues
show
Documentation Bug introduced by
The property $error_handler was declared of type boolean, but 'db_error_handler' is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
1638
    $link = mysqli_connect($server, $user, $pass, $database, $port);
1639
    $link->set_charset($encoding);
1640
1641
    if (isset($_SESSION['settings']['enable_attachment_encryption']) && $_SESSION['settings']['enable_attachment_encryption'] === "1" && isset($image_status) && $image_status === "clear") {
1642
        // file needs to be encrypted
1643 View Code Duplication
        if (file_exists($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code)) {
1644
            // make a copy of file
1645
            if (!copy(
1646
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code,
1647
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code.".copy"
1648
            )) {
1649
                $error = "Copy not possible";
0 ignored issues
show
Unused Code introduced by
$error is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1650
                exit;
1651
            } else {
1652
                // do a bck
1653
                copy(
1654
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code,
1655
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code.".bck"
1656
                );
1657
            }
1658
1659
            // Open the file
1660
            unlink($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code);
1661
            $fp = fopen($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code.".copy", "rb");
1662
            $out = fopen($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code, 'wb');
1663
1664
            // ecnrypt
1665
            stream_filter_append($out, 'mcrypt.tripledes', STREAM_FILTER_WRITE, $opts);
1666
1667
            // read file and create new one
1668
            while (($line = fgets($fp)) !== false) {
1669
                fputs($out, $line);
1670
            }
1671
            fclose($fp);
1672
            fclose($out);
1673
1674
            // update table
1675
            DB::update(
1676
                prefix_table('files'),
1677
                array(
1678
                    'status' => 'encrypted'
1679
                    ),
1680
                "id=%i",
1681
                substr($_POST['uri'], 1)
1682
            );
1683
        }
1684
    } elseif (isset($_SESSION['settings']['enable_attachment_encryption']) && $_SESSION['settings']['enable_attachment_encryption'] === "0" && isset($image_status) && $image_status === "encrypted") {
1685
        // file needs to be decrypted
1686 View Code Duplication
        if (file_exists($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code)) {
1687
            // make a copy of file
1688
            if (!copy(
1689
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code,
1690
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code.".copy"
1691
            )) {
1692
                $error = "Copy not possible";
0 ignored issues
show
Unused Code introduced by
$error is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1693
                exit;
1694
            } else {
1695
                // do a bck
1696
                copy(
1697
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code,
1698
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code.".bck"
1699
                );
1700
            }
1701
1702
            // Open the file
1703
            unlink($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code);
1704
            $fp = fopen($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code.".copy", "rb");
1705
            $out = fopen($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code, 'wb');
1706
1707
            // ecnrypt
1708
            stream_filter_append($fp, 'mdecrypt.tripledes', STREAM_FILTER_READ, $opts);
1709
1710
            // read file and create new one
1711
            while (($line = fgets($fp)) !== false) {
1712
                fputs($out, $line);
1713
            }
1714
            fclose($fp);
1715
            fclose($out);
1716
1717
            // update table
1718
            DB::update(
1719
                prefix_table('files'),
1720
                array(
1721
                    'status' => 'clear'
1722
                    ),
1723
                "id=%i",
1724
                substr($_POST['uri'], 1)
1725
            );
1726
        }
1727
    }
1728
}
1729
1730
/*
1731
* NOT TO BE USED
1732
*/
1733
function debugTeampass($text) {
1734
    $debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+');
1735
    fputs($debugFile, $text);
1736
    fclose($debugFile);
1737
}
1738
1739
/*
1740
* DELETE the file with expected command depending on server type
1741
*/
1742
function fileDelete($file) {
1743
    if (is_file($file)) {
1744
        @close($file);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
1745
        // define if we under Windows
1746
        if (strpos(dirname(__FILE__), '/', 0) !== false) {
1747
            unlink($file);
1748
        } else {
1749
            $lines = array();
1750
            exec("DEL /F/Q \"".$file."\"", $lines, $deleteError);
1751
        }
1752
    }
1753
}