Completed
Push — development ( 813154...a4b5b1 )
by Nils
07:28
created

main.functions.php ➔ GenerateCryptKey()   C

Complexity

Conditions 7
Paths 64

Size

Total Lines 30
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 17
nc 64
nop 6
dl 0
loc 30
rs 6.7272
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
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...
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 (L58-70) 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...
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)) {
1334
        $pwgen->setLength($size);
1335
    }
1336
    if (!empty($secure)) {
1337
        $pwgen->setSecure($secure);
1338
    }
1339
    if (!empty($numerals)) {
1340
        $pwgen->setNumerals($numerals);
1341
    }
1342
    if (!empty($capitalize)) {
1343
        $pwgen->setCapitalize($capitalize);
1344
    }
1345
    if (!empty($ambiguous)) {
1346
        $pwgen->setAmbiguous($ambiguous);
1347
    }
1348
    if (!empty($symbols)) {
1349
        $pwgen->setSymbols($symbols);
1350
    }
1351
1352
    // generate and send back
1353
    return $pwgen->generate();
1354
}
1355
1356
/*
1357
* Send sysLOG message
1358
*/
1359
/**
1360
 * @param string $message
1361
 */
1362
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...
1363
{
1364
    $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
1365
        //$syslog_message = "<123>" . date('M d H:i:s ') . " " .$host . " " . $component . ": " . $message;
1366
    $syslog_message = "<123>".date('M d H:i:s ').$component.": ".$message;
1367
        socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, $host, $port);
1368
    socket_close($sock);
1369
}
1370
1371
1372
1373
/**
1374
 * logEvents()
1375
 *
1376
 * permits to log events into DB
1377
 * @param string $type
1378
 * @param string $label
1379
 * @param string $field_1
1380
 */
1381
function logEvents($type, $label, $who, $login = "", $field_1 = NULL)
1382
{
1383
    global $server, $user, $pass, $database, $pre, $port, $encoding;
1384
1385
    if (empty($who)) {
1386
        $who = get_client_ip_server();
1387
    }
1388
1389
    // include librairies & connect to DB
1390
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1391
    DB::$host = $server;
1392
    DB::$user = $user;
1393
    DB::$password = $pass;
1394
    DB::$dbName = $database;
1395
    DB::$port = $port;
1396
    DB::$encoding = $encoding;
1397
    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...
1398
    $link = mysqli_connect($server, $user, $pass, $database, $port);
1399
    $link->set_charset($encoding);
1400
1401
    DB::insert(
1402
        prefix_table("log_system"),
1403
        array(
1404
            'type' => $type,
1405
            'date' => time(),
1406
            'label' => $label,
1407
            'qui' => $who,
1408
            '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...
1409
        )
1410
    );
1411
    if (isset($_SESSION['settings']['syslog_enable']) && $_SESSION['settings']['syslog_enable'] == 1) {
1412
        if ($type == "user_mngt") {
1413
            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']);
1414
        } else {
1415
            send_syslog("The User ".$login." perform the acction off ".$label." - ".$type, "teampass", "php", $_SESSION['settings']['syslog_host'], $_SESSION['settings']['syslog_port']);
1416
        }
1417
    }
1418
}
1419
1420
/**
1421
 * @param string $item
1422
 * @param string $action
1423
 */
1424
function logItems($id, $item, $id_user, $action, $login = "", $raison = NULL, $raison_iv = NULL, $encryption_type = "")
1425
{
1426
    global $server, $user, $pass, $database, $pre, $port, $encoding;
1427
    // include librairies & connect to DB
1428
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1429
    DB::$host = $server;
1430
    DB::$user = $user;
1431
    DB::$password = $pass;
1432
    DB::$dbName = $database;
1433
    DB::$port = $port;
1434
    DB::$encoding = $encoding;
1435
    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...
1436
    $link = mysqli_connect($server, $user, $pass, $database, $port);
1437
    $link->set_charset($encoding);
1438
    DB::insert(
1439
        prefix_table(
1440
            "log_items"),
1441
            array(
1442
                'id_item' => $id,
1443
                'date' => time(),
1444
                'id_user' => $id_user,
1445
                'action' => $action,
1446
                'raison' => $raison,
1447
                'raison_iv' => $raison_iv,
1448
                'encryption_type' => $encryption_type
1449
            )
1450
        );
1451
        if (isset($_SESSION['settings']['syslog_enable']) && $_SESSION['settings']['syslog_enable'] == 1) {
1452
                send_syslog("The Item ".$item." was ".$action." by ".$login." ".$raison, "teampass", "php", $_SESSION['settings']['syslog_host'], $_SESSION['settings']['syslog_port']);
1453
        }
1454
}
1455
1456
/*
1457
* Function to get the client ip address
1458
 */
1459
function get_client_ip_server() {
1460
    $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...
1461
    if (getenv('HTTP_CLIENT_IP')) {
1462
            $ipaddress = getenv('HTTP_CLIENT_IP');
1463
    } else if (getenv('HTTP_X_FORWARDED_FOR')) {
1464
            $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
1465
    } else if (getenv('HTTP_X_FORWARDED')) {
1466
            $ipaddress = getenv('HTTP_X_FORWARDED');
1467
    } else if (getenv('HTTP_FORWARDED_FOR')) {
1468
            $ipaddress = getenv('HTTP_FORWARDED_FOR');
1469
    } else if (getenv('HTTP_FORWARDED')) {
1470
            $ipaddress = getenv('HTTP_FORWARDED');
1471
    } else if (getenv('REMOTE_ADDR')) {
1472
            $ipaddress = getenv('REMOTE_ADDR');
1473
    } else {
1474
            $ipaddress = 'UNKNOWN';
1475
    }
1476
1477
    return $ipaddress;
1478
}
1479
1480
/**
1481
 * Escape all HTML, JavaScript, and CSS
1482
 *
1483
 * @param string $input The input string
1484
 * @param string $encoding Which character encoding are we using?
1485
 * @return string
1486
 */
1487
function noHTML($input, $encoding = 'UTF-8')
1488
{
1489
    return htmlspecialchars($input, ENT_QUOTES | ENT_XHTML, $encoding, false);
1490
}
1491
1492
/**
1493
 * handleConfigFile()
1494
 *
1495
 * permits to handle the Teampass config file
1496
 * $action accepts "rebuild" and "update"
1497
 */
1498
function handleConfigFile($action, $field = null, $value = null)
1499
{
1500
    global $server, $user, $pass, $database, $pre, $port, $encoding;
1501
    $tp_config_file = "../includes/config/tp.config.php";
1502
1503
    // include librairies & connect to DB
1504
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1505
    DB::$host = $server;
1506
    DB::$user = $user;
1507
    DB::$password = $pass;
1508
    DB::$dbName = $database;
1509
    DB::$port = $port;
1510
    DB::$encoding = $encoding;
1511
    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...
1512
    $link = mysqli_connect($server, $user, $pass, $database, $port);
1513
    $link->set_charset($encoding);
1514
1515
    if (!file_exists($tp_config_file) || $action == "rebuild") {
1516
        // perform a copy
1517
        if (file_exists($tp_config_file)) {
1518
            if (!copy($tp_config_file, $tp_config_file.'.'.date("Y_m_d_His", time()))) {
1519
                return "ERROR: Could not copy file '".$tp_config_file."'";
1520
            }
1521
        }
1522
1523
        // regenerate
1524
        $data = array();
1525
        $data[0] = "<?php\n";
1526
        $data[1] = "global \$SETTINGS;\n";
1527
        $data[2] = "\$SETTINGS = array (\n";
1528
        $rows = DB::query(
1529
            "SELECT * FROM ".prefix_table("misc")." WHERE type=%s",
1530
            "admin"
1531
        );
1532
        foreach ($rows as $record) {
1533
            array_push($data, "    '".$record['intitule']."' => '".$record['valeur']."',\n");
1534
        }
1535
        array_push($data, ");");
1536
        $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...
1537
    } else if ($action == "update" && !empty($field)) {
1538
        $data = file($tp_config_file);
1539
        $x = 0;
1540
        $bFound = false;
1541
        foreach ($data as $line) {
1542
            if (stristr($line, ");")) {
1543
                break;
1544
            }
1545
            if (stristr($line, "'".$field."' => '")) {
1546
                $data[$x] = "    '".$field."' => '".$value."',\n";
1547
                $bFound = true;
1548
                break;
1549
            }
1550
            $x++;
1551
        }
1552
        if ($bFound === false) {
1553
            $data[($x - 1)] = "    '".$field."' => '".$value."',\n";
1554
        }
1555
    } 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...
1556
        // ERROR
1557
    }
1558
1559
    // update file
1560
    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 1553
  6. $data is passed through implode()
    in sources/main.functions.php on line 1560

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...
1561
1562
    return true;
1563
}
1564
1565
/*
1566
** Permits to replace &#92; to permit correct display
1567
*/
1568
function handleBackslash($input)
1569
{
1570
    return str_replace("&amp;#92;", "&#92;", $input);
1571
}
1572
1573
/*
1574
** Permits to loas settings
1575
*/
1576
function loadSettings()
1577
{
1578
    /* LOAD CPASSMAN SETTINGS */
1579 View Code Duplication
    if (!isset($_SESSION['settings']['loaded']) || $_SESSION['settings']['loaded'] != 1) {
1580
        $_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...
1581
        $_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...
1582
        $_SESSION['settings']['number_of_used_pw'] = 5; //by default, this value is 5;
1583
1584
        $rows = DB::query("SELECT * FROM ".prefix_table("misc")." WHERE type=%s_type OR type=%s_type2",
1585
            array(
1586
                'type' => "admin",
1587
                'type2' => "settings"
1588
            )
1589
        );
1590
        foreach ($rows as $record) {
1591
            if ($record['type'] == 'admin') {
1592
                $_SESSION['settings'][$record['intitule']] = $record['valeur'];
1593
            } else {
1594
                $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...
1595
            }
1596
        }
1597
        $_SESSION['settings']['loaded'] = 1;
1598
        $_SESSION['settings']['default_session_expiration_time'] = 5;
1599
    }
1600
}
1601
1602
/*
1603
** check if folder has custom fields.
1604
** Ensure that target one also has same custom fields
1605
*/
1606
function checkCFconsistency($source_id, $target_id) {
1607
    $source_cf = array();
1608
    $rows = DB::QUERY(
1609
        "SELECT id_category
1610
        FROM ".prefix_table("categories_folders")."
1611
        WHERE id_folder = %i",
1612
        $source_id
1613
    );
1614
    foreach ($rows as $record) {
1615
        array_push($source_cf, $record['id_category']);
1616
    }
1617
1618
    $target_cf = array();
1619
    $rows = DB::QUERY(
1620
        "SELECT id_category
1621
        FROM ".prefix_table("categories_folders")."
1622
        WHERE id_folder = %i",
1623
        $target_id
1624
    );
1625
    foreach ($rows as $record) {
1626
        array_push($target_cf, $record['id_category']);
1627
    }
1628
1629
    $cf_diff = array_diff($source_cf, $target_cf);
1630
    if (count($cf_diff) > 0) {
1631
        return false;
1632
    }
1633
1634
    return true;
1635
}
1636
1637
/*
1638
*
1639
*/
1640
function encrypt_or_decrypt_file($image_code, $image_status, $opts) {
1641
    global $server, $user, $pass, $database, $pre, $port, $encoding;
1642
    $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...
1643
1644
    // include librairies & connect to DB
1645
    require_once $_SESSION['settings']['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1646
    DB::$host = $server;
1647
    DB::$user = $user;
1648
    DB::$password = $pass;
1649
    DB::$dbName = $database;
1650
    DB::$port = $port;
1651
    DB::$encoding = $encoding;
1652
    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...
1653
    $link = mysqli_connect($server, $user, $pass, $database, $port);
1654
    $link->set_charset($encoding);
1655
1656
    if (isset($_SESSION['settings']['enable_attachment_encryption']) && $_SESSION['settings']['enable_attachment_encryption'] === "1" && isset($image_status) && $image_status === "clear") {
1657
        // file needs to be encrypted
1658 View Code Duplication
        if (file_exists($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code)) {
1659
            // make a copy of file
1660
            if (!copy(
1661
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code,
1662
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code.".copy"
1663
            )) {
1664
                $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...
1665
                exit;
1666
            } else {
1667
                // do a bck
1668
                copy(
1669
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code,
1670
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code.".bck"
1671
                );
1672
            }
1673
1674
            // Open the file
1675
            unlink($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code);
1676
            $fp = fopen($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code.".copy", "rb");
1677
            $out = fopen($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code, 'wb');
1678
1679
            // ecnrypt
1680
            stream_filter_append($out, 'mcrypt.tripledes', STREAM_FILTER_WRITE, $opts);
1681
1682
            // read file and create new one
1683
            while (($line = fgets($fp)) !== false) {
1684
                fputs($out, $line);
1685
            }
1686
            fclose($fp);
1687
            fclose($out);
1688
1689
            // update table
1690
            DB::update(
1691
                prefix_table('files'),
1692
                array(
1693
                    'status' => 'encrypted'
1694
                    ),
1695
                "id=%i",
1696
                substr($_POST['uri'], 1)
1697
            );
1698
        }
1699
    } elseif (isset($_SESSION['settings']['enable_attachment_encryption']) && $_SESSION['settings']['enable_attachment_encryption'] === "0" && isset($image_status) && $image_status === "encrypted") {
1700
        // file needs to be decrypted
1701 View Code Duplication
        if (file_exists($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code)) {
1702
            // make a copy of file
1703
            if (!copy(
1704
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code,
1705
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code.".copy"
1706
            )) {
1707
                $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...
1708
                exit;
1709
            } else {
1710
                // do a bck
1711
                copy(
1712
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code,
1713
                    $_SESSION['settings']['path_to_upload_folder'].'/'.$image_code.".bck"
1714
                );
1715
            }
1716
1717
            // Open the file
1718
            unlink($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code);
1719
            $fp = fopen($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code.".copy", "rb");
1720
            $out = fopen($_SESSION['settings']['path_to_upload_folder'].'/'.$image_code, 'wb');
1721
1722
            // ecnrypt
1723
            stream_filter_append($fp, 'mdecrypt.tripledes', STREAM_FILTER_READ, $opts);
1724
1725
            // read file and create new one
1726
            while (($line = fgets($fp)) !== false) {
1727
                fputs($out, $line);
1728
            }
1729
            fclose($fp);
1730
            fclose($out);
1731
1732
            // update table
1733
            DB::update(
1734
                prefix_table('files'),
1735
                array(
1736
                    'status' => 'clear'
1737
                    ),
1738
                "id=%i",
1739
                substr($_POST['uri'], 1)
1740
            );
1741
        }
1742
    }
1743
}
1744
1745
/*
1746
* NOT TO BE USED
1747
*/
1748
function debugTeampass($text) {
1749
    $debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+');
1750
    fputs($debugFile, $text);
1751
    fclose($debugFile);
1752
}
1753
1754
/*
1755
* DELETE the file with expected command depending on server type
1756
*/
1757
function fileDelete($file) {
1758
    if (is_file($file)) {
1759
        @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...
1760
        // define if we under Windows
1761
        if (strpos(dirname(__FILE__), '/', 0) !== false) {
1762
            unlink($file);
1763
        } else {
1764
            $lines = array();
1765
            exec("DEL /F/Q \"".$file."\"", $lines, $deleteError);
1766
        }
1767
    }
1768
}