Passed
Push — development ( 0b8a08...f2f2fb )
by Nils
04:53
created

cleanText()   B

Complexity

Conditions 5
Paths 3

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 5
eloc 7
c 2
b 0
f 0
nc 3
nop 2
dl 0
loc 14
rs 8.8571
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 13 and the first side effect is on line 16.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 *
4
 * @file          main.functions.php
5
 * @author        Nils Laumaillé
6
 * @version       2.1.27
7
 * @copyright     (c) 2009-2018 Nils Laumaillé
8
 * @licensing     GNU GPL-3.0
9
 * @link
10
 */
11
12
//define pbkdf2 iteration count
13
define('ITCOUNT', '2072');
14
15
if (!isset($_SESSION['CPM']) || $_SESSION['CPM'] != 1) {
16
    die('Hacking attempt...');
17
}
18
19
// Load config if $SETTINGS not defined
20
if (!isset($SETTINGS['cpassman_dir']) || empty($SETTINGS['cpassman_dir'])) {
21
    if (file_exists('../includes/config/tp.config.php')) {
22
        include_once '../includes/config/tp.config.php';
23
    } elseif (file_exists('./includes/config/tp.config.php')) {
24
        include_once './includes/config/tp.config.php';
25
    } elseif (file_exists('../../includes/config/tp.config.php')) {
26
        include_once '../../includes/config/tp.config.php';
27
    } else {
28
        throw new Exception("Error file '/includes/config/tp.config.php' not exists", 1);
29
    }
30
}
31
32
// load phpCrypt
33
if (!isset($SETTINGS['cpassman_dir']) || empty($SETTINGS['cpassman_dir'])) {
34
    include_once '../includes/libraries/phpcrypt/phpCrypt.php';
35
    include_once '../includes/config/settings.php';
36
} else {
37
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/phpcrypt/phpCrypt.php';
38
    include_once $SETTINGS['cpassman_dir'].'/includes/config/settings.php';
39
}
40
41
// Prepare PHPCrypt class calls
42
use PHP_Crypt\PHP_Crypt as PHP_Crypt;
43
44
// Prepare Encryption class calls
45
use \Defuse\Crypto\Crypto;
46
use \Defuse\Crypto\Exception as Ex;
47
48
//Generate N# of random bits for use as salt
49
/**
50
 * @param integer $size
51
 */
52
function getBits($size)
53
{
54
    $str = '';
55
    $var_x = $size + 10;
56
    for ($var_i = 0; $var_i < $var_x; $var_i++) {
57
        $str .= base_convert(mt_rand(1, 36), 10, 36);
58
    }
59
    return substr($str, 0, $size);
60
}
61
62
//generate pbkdf2 compliant hash
63
function strHashPbkdf2($var_p, $var_s, $var_c, $var_kl, $var_a = 'sha256', $var_st = 0)
64
{
65
    $var_kb = $var_st + $var_kl; // Key blocks to compute
66
    $var_dk = ''; // Derived key
67
68
    for ($block = 1; $block <= $var_kb; $block++) { // Create key
69
        $var_ib = $var_h = hash_hmac($var_a, $var_s.pack('N', $block), $var_p, true); // Initial hash for this block
70
        for ($var_i = 1; $var_i < $var_c; $var_i++) { // Perform block iterations
71
            $var_ib ^= ($var_h = hash_hmac($var_a, $var_h, $var_p, true)); // XOR each iterate
72
        }
73
        $var_dk .= $var_ib; // Append iterated block
74
    }
75
    return substr($var_dk, $var_st, $var_kl); // Return derived key of correct length
76
}
77
78
/**
79
 * stringUtf8Decode()
80
 *
81
 * utf8_decode
82
 */
83
function stringUtf8Decode($string)
84
{
85
    return str_replace(" ", "+", utf8_decode($string));
86
}
87
88
/**
89
 * encryptOld()
90
 *
91
 * crypt a string
92
 * @param string $text
93
 */
94
function encryptOld($text, $personalSalt = "")
95
{
96
    if (empty($personalSalt) === false) {
97
        return trim(
98
            base64_encode(
99
                mcrypt_encrypt(
1 ignored issue
show
Deprecated Code introduced by
The function mcrypt_encrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

99
                /** @scrutinizer ignore-deprecated */ mcrypt_encrypt(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
100
                    MCRYPT_RIJNDAEL_256,
101
                    $personalSalt,
102
                    $text,
103
                    MCRYPT_MODE_ECB,
104
                    mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

104
                    /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
105
                        mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_get_iv_size() has been deprecated: 7.1 ( Ignorable by Annotation )

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

105
                        /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
106
                        MCRYPT_RAND
107
                    )
108
                )
109
            )
110
        );
111
    }
112
113
    // If $personalSalt is not empty
114
    return trim(
115
        base64_encode(
116
            mcrypt_encrypt(
1 ignored issue
show
Deprecated Code introduced by
The function mcrypt_encrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

116
            /** @scrutinizer ignore-deprecated */ mcrypt_encrypt(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
117
                MCRYPT_RIJNDAEL_256,
118
                SALT,
0 ignored issues
show
Bug introduced by
The constant SALT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
119
                $text,
120
                MCRYPT_MODE_ECB,
121
                mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

121
                /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
122
                    mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_get_iv_size() has been deprecated: 7.1 ( Ignorable by Annotation )

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

122
                    /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
123
                    MCRYPT_RAND
124
                )
125
            )
126
        )
127
    );
128
}
129
130
/**
131
 * decryptOld()
132
 *
133
 * decrypt a crypted string
134
 */
135
function decryptOld($text, $personalSalt = "")
136
{
137
    if (!empty($personalSalt)) {
138
        return trim(
139
            mcrypt_decrypt(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_decrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

139
            /** @scrutinizer ignore-deprecated */ mcrypt_decrypt(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
140
                MCRYPT_RIJNDAEL_256,
141
                $personalSalt,
142
                base64_decode($text),
143
                MCRYPT_MODE_ECB,
144
                mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

144
                /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
145
                    mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_get_iv_size() has been deprecated: 7.1 ( Ignorable by Annotation )

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

145
                    /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
146
                    MCRYPT_RAND
147
                )
148
            )
149
        );
150
    }
151
152
    // No personal SK
153
    return trim(
154
        mcrypt_decrypt(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_decrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

154
        /** @scrutinizer ignore-deprecated */ mcrypt_decrypt(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
155
            MCRYPT_RIJNDAEL_256,
156
            SALT,
0 ignored issues
show
Bug introduced by
The constant SALT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
157
            base64_decode($text),
158
            MCRYPT_MODE_ECB,
159
            mcrypt_create_iv(
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

159
            /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
160
                mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_get_iv_size() has been deprecated: 7.1 ( Ignorable by Annotation )

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

160
                /** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, MCRYPT_MODE_ECB),

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
161
                MCRYPT_RAND
162
            )
163
        )
164
    );
165
}
166
167
/**
168
 * encrypt()
169
 *
170
 * crypt a string
171
 * @param string $decrypted
172
 */
173
function encrypt($decrypted, $personalSalt = "")
174
{
175
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
176
177
    if (!isset($SETTINGS['cpassman_dir']) || empty($SETTINGS['cpassman_dir'])) {
178
        require_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
179
    } else {
180
        require_once $SETTINGS['cpassman_dir'].'/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
181
    }
182
183
    if (!empty($personalSalt)) {
184
        $staticSalt = $personalSalt;
185
    } else {
186
        $staticSalt = SALT;
0 ignored issues
show
Bug introduced by
The constant SALT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
187
    }
188
189
    //set our salt to a variable
190
    // Get 64 random bits for the salt for pbkdf2
191
    $pbkdf2Salt = getBits(64);
192
    // generate a pbkdf2 key to use for the encryption.
193
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
194
    // Build $init_vect and $ivBase64.  We use a block size of 256 bits (AES compliant)
195
    // and CTR mode.  (Note: ECB mode is inadequate as IV is not used.)
196
    $init_vect = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, 'ctr'), MCRYPT_RAND);
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_create_iv() has been deprecated: 7.1 ( Ignorable by Annotation )

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

196
    $init_vect = /** @scrutinizer ignore-deprecated */ mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, 'ctr'), MCRYPT_RAND);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
Deprecated Code introduced by
The function mcrypt_get_iv_size() has been deprecated: 7.1 ( Ignorable by Annotation )

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

196
    $init_vect = mcrypt_create_iv(/** @scrutinizer ignore-deprecated */ mcrypt_get_iv_size(MCRYPT_RIJNDAEL_256, 'ctr'), MCRYPT_RAND);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
197
198
    //base64 trim
199
    if (strlen($ivBase64 = rtrim(base64_encode($init_vect), '=')) != 43) {
200
        return false;
201
    }
202
    // Encrypt $decrypted
203
    $encrypted = mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $decrypted, 'ctr', $init_vect);
1 ignored issue
show
Deprecated Code introduced by
The function mcrypt_encrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

203
    $encrypted = /** @scrutinizer ignore-deprecated */ mcrypt_encrypt(MCRYPT_RIJNDAEL_256, $key, $decrypted, 'ctr', $init_vect);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
204
    // MAC the encrypted text
205
    $mac = hash_hmac('sha256', $encrypted, $staticSalt);
206
    // We're done!
207
    return base64_encode($ivBase64.$encrypted.$mac.$pbkdf2Salt);
208
}
209
210
/**
211
 * decrypt()
212
 *
213
 * decrypt a crypted string
214
 */
215
function decrypt($encrypted, $personalSalt = "")
216
{
217
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
218
219
    if (!isset($SETTINGS['cpassman_dir']) || empty($SETTINGS['cpassman_dir'])) {
220
        include_once '../includes/libraries/Encryption/PBKDF2/PasswordHash.php';
221
    } else {
222
        include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Encryption/PBKDF2/PasswordHash.php';
223
    }
224
225
    if (!empty($personalSalt)) {
226
        $staticSalt = $personalSalt;
227
    } else {
228
        $staticSalt = file_get_contents(SECUREPATH."/teampass-seckey.txt");
0 ignored issues
show
Bug introduced by
The constant SECUREPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
229
    }
230
    //base64 decode the entire payload
231
    $encrypted = base64_decode($encrypted);
232
    // get the salt
233
    $pbkdf2Salt = substr($encrypted, -64);
234
    //remove the salt from the string
235
    $encrypted = substr($encrypted, 0, -64);
236
    $key = substr(pbkdf2('sha256', $staticSalt, $pbkdf2Salt, ITCOUNT, 16 + 32, true), 32, 16);
237
    // Retrieve $init_vect which is the first 22 characters plus ==, base64_decoded.
238
    $init_vect = base64_decode(substr($encrypted, 0, 43).'==');
239
    // Remove $init_vect from $encrypted.
240
    $encrypted = substr($encrypted, 43);
241
    // Retrieve $mac which is the last 64 characters of $encrypted.
242
    $mac = substr($encrypted, -64);
243
    // Remove the last 64 chars from encrypted (remove MAC)
244
    $encrypted = substr($encrypted, 0, -64);
245
    //verify the sha256hmac from the encrypted data before even trying to decrypt it
246
    if (hash_hmac('sha256', $encrypted, $staticSalt) != $mac) {
247
        return false;
248
    }
249
    // Decrypt the data.
250
    $decrypted = rtrim(mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, 'ctr', $init_vect), "\0\4");
0 ignored issues
show
Deprecated Code introduced by
The function mcrypt_decrypt() has been deprecated: 7.1 ( Ignorable by Annotation )

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

250
    $decrypted = rtrim(/** @scrutinizer ignore-deprecated */ mcrypt_decrypt(MCRYPT_RIJNDAEL_256, $key, $encrypted, 'ctr', $init_vect), "\0\4");

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
251
    // Yay!
252
    return $decrypted;
253
}
254
255
256
/**
257
 * genHash()
258
 *
259
 * Generate a hash for user login
260
 * @param string $password
261
 */
262
function bCrypt($password, $cost)
263
{
264
    $salt = sprintf('$2y$%02d$', $cost);
265
    if (function_exists('openssl_random_pseudo_bytes')) {
266
        $salt .= bin2hex(openssl_random_pseudo_bytes(11));
267
    } else {
268
        $chars = './ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
269
        for ($i = 0; $i < 22; $i++) {
270
            $salt .= $chars[mt_rand(0, 63)];
271
        }
272
    }
273
    return crypt($password, $salt);
274
}
275
276
/*
277
 * cryption() - Encrypt and decrypt string based upon phpCrypt library
278
 *
279
 * Using AES_128 and mode CBC
280
 *
281
 * $key and $init_vect have to be given in hex format
282
 */
283
function cryption_phpCrypt($string, $key, $init_vect, $type)
284
{
285
    // manage key origin
286
    if (null != SALT && $key != SALT) {
0 ignored issues
show
Bug introduced by
The constant SALT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
287
        // check key (AES-128 requires a 16 bytes length key)
288
        if (strlen($key) < 16) {
289
            for ($inc = strlen($key) + 1; $inc <= 16; $inc++) {
290
                $key .= chr(0);
291
            }
292
        } elseif (strlen($key) > 16) {
293
            $key = substr($key, 16);
294
        }
295
    }
296
297
    // load crypt
298
    $crypt = new PHP_Crypt($key, PHP_Crypt::CIPHER_AES_128, PHP_Crypt::MODE_CBC);
299
300
    if ($type == "encrypt") {
301
        // generate IV and encrypt
302
        $init_vect = $crypt->createIV();
303
        $encrypt = $crypt->encrypt($string);
304
        // return
305
        return array(
306
            "string" => bin2hex($encrypt),
307
            "iv" => bin2hex($init_vect),
308
            "error" => empty($encrypt) ? "ERR_ENCRYPTION_NOT_CORRECT" : ""
309
        );
310
    } elseif ($type == "decrypt") {
311
        // case if IV is empty
312
        if (empty($init_vect)) {
313
            return array(
314
                'string' => "",
315
                'error' => "ERR_ENCRYPTION_NOT_CORRECT"
316
            );
317
        }
318
319
        // convert
320
        try {
321
            $string = testHex2Bin(trim($string));
322
            $init_vect = testHex2Bin($init_vect);
323
        } catch (Exception $e) {
324
            return array(
325
                'string' => "",
326
                'error' => "ERR_ENCRYPTION_NOT_CORRECT"
327
            );
328
        }
329
330
        // load IV
331
        $crypt->IV($init_vect);
332
        // decrypt
333
        $decrypt = $crypt->decrypt($string);
334
        // return
335
        return array(
336
            'string' => str_replace(chr(0), "", $decrypt),
337
            'error' => ""
338
        );
339
    }
340
}
341
342
function testHex2Bin($val)
343
{
344
    if (!@hex2bin($val)) {
345
        throw new Exception("ERROR");
346
    }
347
    return hex2bin($val);
348
}
349
350
/**
351
 * Defuse cryption function
352
 *
353
 * @param  string $message   what to de/crypt
354
 * @param  string $ascii_key key to use
355
 * @param  string $type      operation to perform
356
 * @return array
357
 */
358
function cryption($message, $ascii_key, $type) //defuse_crypto
359
{
360
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
361
362
    // load PhpEncryption library
363
    if (isset($SETTINGS['cpassman_dir']) === false || empty($SETTINGS['cpassman_dir']) === true) {
364
        $path = '../includes/libraries/Encryption/Encryption/';
365
    } else {
366
        $path = $SETTINGS['cpassman_dir'].'/includes/libraries/Encryption/Encryption/';
367
    }
368
369
    include_once $path.'Crypto.php';
370
    include_once $path.'Encoding.php';
371
    include_once $path.'DerivedKeys.php';
372
    include_once $path.'Key.php';
373
    include_once $path.'KeyOrPassword.php';
374
    include_once $path.'File.php';
375
    include_once $path.'RuntimeTests.php';
376
    include_once $path.'KeyProtectedByPassword.php';
377
    include_once $path.'Core.php';
378
379
    // init
380
    $err = '';
381
    if (empty($ascii_key)) {
382
        $ascii_key = file_get_contents(SECUREPATH."/teampass-seckey.txt");
0 ignored issues
show
Bug introduced by
The constant SECUREPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
383
    }
384
385
    // convert KEY
386
    $key = \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key);
387
388
    try {
389
        if ($type === "encrypt") {
390
            $text = \Defuse\Crypto\Crypto::encrypt($message, $key);
391
        } elseif ($type === "decrypt") {
392
            $text = \Defuse\Crypto\Crypto::decrypt($message, $key);
393
        }
394
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
395
        $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.";
396
    } catch (Defuse\Crypto\Exception\BadFormatException $ex) {
397
        $err = $ex;
398
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
399
        $err = $ex;
400
    } catch (Defuse\Crypto\Exception\CryptoException $ex) {
401
        $err = $ex;
402
    } catch (Defuse\Crypto\Exception\IOException $ex) {
403
        $err = $ex;
404
    }
405
406
    return array(
407
        'string' => isset($text) ? $text : "",
408
        'error' => $err
409
    );
410
}
411
412
/**
413
 * Generating a defuse key
414
 *
415
 * @return string
416
 */
417
function defuse_generate_key()
418
{
419
    include_once '../includes/libraries/Encryption/Encryption/Crypto.php';
420
    include_once '../includes/libraries/Encryption/Encryption/Encoding.php';
421
    include_once '../includes/libraries/Encryption/Encryption/DerivedKeys.php';
422
    include_once '../includes/libraries/Encryption/Encryption/Key.php';
423
    include_once '../includes/libraries/Encryption/Encryption/KeyOrPassword.php';
424
    include_once '../includes/libraries/Encryption/Encryption/File.php';
425
    include_once '../includes/libraries/Encryption/Encryption/RuntimeTests.php';
426
    include_once '../includes/libraries/Encryption/Encryption/KeyProtectedByPassword.php';
427
    include_once '../includes/libraries/Encryption/Encryption/Core.php';
428
429
    $key = \Defuse\Crypto\Key::createNewRandomKey();
430
    $key = $key->saveToAsciiSafeString();
431
    return $key;
432
}
433
434
/**
435
 * Generate a Defuse personal key
436
 *
437
 * @param  string $psk psk used
438
 * @return string
439
 */
440
function defuse_generate_personal_key($psk)
441
{
442
    require_once '../includes/libraries/Encryption/Encryption/Crypto.php';
443
    require_once '../includes/libraries/Encryption/Encryption/Encoding.php';
444
    require_once '../includes/libraries/Encryption/Encryption/DerivedKeys.php';
445
    require_once '../includes/libraries/Encryption/Encryption/Key.php';
446
    require_once '../includes/libraries/Encryption/Encryption/KeyOrPassword.php';
447
    require_once '../includes/libraries/Encryption/Encryption/File.php';
448
    require_once '../includes/libraries/Encryption/Encryption/RuntimeTests.php';
449
    require_once '../includes/libraries/Encryption/Encryption/KeyProtectedByPassword.php';
450
    require_once '../includes/libraries/Encryption/Encryption/Core.php';
451
452
    $protected_key = \Defuse\Crypto\KeyProtectedByPassword::createRandomPasswordProtectedKey($psk);
453
    $protected_key_encoded = $protected_key->saveToAsciiSafeString();
454
455
    return $protected_key_encoded; // save this in user table
456
}
457
458
/**
459
 * Validate persoanl key with defuse
460
 *
461
 * @param  string $psk                   the user's psk
462
 * @param  string $protected_key_encoded special key
463
 * @return string
464
 */
465
function defuse_validate_personal_key($psk, $protected_key_encoded)
466
{
467
    require_once '../includes/libraries/Encryption/Encryption/Crypto.php';
468
    require_once '../includes/libraries/Encryption/Encryption/Encoding.php';
469
    require_once '../includes/libraries/Encryption/Encryption/DerivedKeys.php';
470
    require_once '../includes/libraries/Encryption/Encryption/Key.php';
471
    require_once '../includes/libraries/Encryption/Encryption/KeyOrPassword.php';
472
    require_once '../includes/libraries/Encryption/Encryption/File.php';
473
    require_once '../includes/libraries/Encryption/Encryption/RuntimeTests.php';
474
    require_once '../includes/libraries/Encryption/Encryption/KeyProtectedByPassword.php';
475
    require_once '../includes/libraries/Encryption/Encryption/Core.php';
476
477
    try {
478
        $protected_key = \Defuse\Crypto\KeyProtectedByPassword::loadFromAsciiSafeString($protected_key_encoded);
479
        $user_key = $protected_key->unlockKey($psk);
480
        $user_key_encoded = $user_key->saveToAsciiSafeString();
481
    } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
482
        return "Error - Major issue as the encryption is broken.";
483
    } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
484
        return "Error - The saltkey is not the correct one.";
485
    }
486
487
    return $user_key_encoded; // store it in session once user has entered his psk
488
}
489
490
/**
491
 * Decrypt a defuse string if encrypted
492
 *
493
 * @param  string $value Encrypted string
494
 * @return string        Decrypted string
495
 */
496
function defuse_return_decrypted($value)
497
{
498
    if (substr($value, 0, 3) === "def") {
499
        $value = cryption($value, "", "decrypt")['string'];
500
    }
501
    return $value;
502
}
503
504
/**
505
 * trimElement()
506
 *
507
 * trim a string depending on a specific string
508
 * @param  string $chaine  what to trim
509
 * @param  string $element trim on what
510
 * @return string
511
 */
512
function trimElement($chaine, $element)
513
{
514
    if (!empty($chaine)) {
515
        if (is_array($chaine) === true) {
0 ignored issues
show
introduced by
The condition is_array($chaine) === true is always false.
Loading history...
516
            $chaine = implode(";", $chaine);
517
        }
518
        $chaine = trim($chaine);
519
        if (substr($chaine, 0, 1) == $element) {
520
            $chaine = substr($chaine, 1);
521
        }
522
        if (substr($chaine, strlen($chaine) - 1, 1) == $element) {
523
            $chaine = substr($chaine, 0, strlen($chaine) - 1);
524
        }
525
    }
526
    return $chaine;
527
}
528
529
/**
530
 * Permits to suppress all "special" characters from string
531
 *
532
 * @param  string  $string  what to clean
533
 * @param  boolean $special use of special chars?
534
 * @return string
535
 */
536
function cleanString($string, $special = false)
537
{
538
    // Create temporary table for special characters escape
539
    $tabSpecialChar = array();
540
    for ($i = 0; $i <= 31; $i++) {
541
        $tabSpecialChar[] = chr($i);
542
    }
543
    array_push($tabSpecialChar, "<br />");
544
    if ($special == "1") {
545
        $tabSpecialChar = array_merge($tabSpecialChar, array("</li>", "<ul>", "<ol>"));
546
    }
547
548
    return str_replace($tabSpecialChar, "\n", $string);
549
}
550
551
/**
552
 * Erro manager for DB
553
 *
554
 * @param  array $params output from query
555
 * @return void
556
 */
557
function db_error_handler($params)
558
{
559
    echo "Error: ".$params['error']."<br>\n";
560
    echo "Query: ".$params['query']."<br>\n";
561
    throw new Exception("Error - Query", 1);
562
}
563
564
/**
565
 * [identifyUserRights description]
566
 * @param  string $groupesVisiblesUser  [description]
567
 * @param  string $groupesInterditsUser [description]
568
 * @param  string $isAdmin              [description]
569
 * @param  string $idFonctions          [description]
570
 * @return string                       [description]
571
 */
572
function identifyUserRights(
573
    $groupesVisiblesUser,
574
    $groupesInterditsUser,
575
    $isAdmin,
576
    $idFonctions,
577
    $server,
578
    $user,
579
    $pass,
580
    $database,
581
    $port,
582
    $encoding,
583
    $SETTINGS
584
) {
585
    //load ClassLoader
586
    include_once $SETTINGS['cpassman_dir'].'/sources/SplClassLoader.php';
587
588
    //Connect to DB
589
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
590
    $pass = defuse_return_decrypted($pass);
591
    DB::$host = $server;
592
    DB::$user = $user;
593
    DB::$password = $pass;
594
    DB::$dbName = $database;
595
    DB::$port = $port;
596
    DB::$encoding = $encoding;
597
    DB::$error_handler = true;
598
    $link = mysqli_connect($server, $user, $pass, $database, $port);
599
    $link->set_charset($encoding);
600
601
    //Build tree
602
    $tree = new SplClassLoader('Tree\NestedTree', $SETTINGS['cpassman_dir'].'/includes/libraries');
603
    $tree->register();
604
    $tree = new Tree\NestedTree\NestedTree(prefix_table("nested_tree"), 'id', 'parent_id', 'title');
605
606
    // Check if user is ADMINISTRATOR
607
    if ($isAdmin === '1') {
608
        $groupesVisibles = array();
609
        $_SESSION['personal_folders'] = array();
610
        $_SESSION['groupes_visibles'] = array();
611
        $_SESSION['groupes_interdits'] = array();
612
        $_SESSION['personal_visible_groups'] = array();
613
        $_SESSION['read_only_folders'] = array();
614
        $_SESSION['list_restricted_folders_for_items'] = array();
615
        $_SESSION['list_folders_editable_by_role'] = array();
616
        $_SESSION['list_folders_limited'] = array();
617
        $_SESSION['no_access_folders'] = array();
618
        $_SESSION['groupes_visibles_list'] = "";
619
        $rows = DB::query("SELECT id FROM ".prefix_table("nested_tree")." WHERE personal_folder = %i", 0);
620
        foreach ($rows as $record) {
621
            array_push($groupesVisibles, $record['id']);
622
        }
623
        $_SESSION['groupes_visibles'] = $groupesVisibles;
624
        $_SESSION['all_non_personal_folders'] = $groupesVisibles;
625
        // Exclude all PF
626
        $_SESSION['forbiden_pfs'] = array();
627
        $where = new WhereClause('and'); // create a WHERE statement of pieces joined by ANDs
628
        $where->add('personal_folder=%i', 1);
629
        if (isset($SETTINGS['enable_pf_feature']) && $SETTINGS['enable_pf_feature'] == 1) {
630
            $where->add('title=%s', $_SESSION['user_id']);
631
            $where->negateLast();
632
        }
633
        // Get ID of personal folder
634
        $persfld = DB::queryfirstrow(
635
            "SELECT id FROM ".prefix_table("nested_tree")." WHERE title = %s",
636
            $_SESSION['user_id']
637
        );
638
        if (!empty($persfld['id'])) {
639
            if (!in_array($persfld['id'], $_SESSION['groupes_visibles'])) {
640
                array_push($_SESSION['groupes_visibles'], $persfld['id']);
641
                array_push($_SESSION['personal_visible_groups'], $persfld['id']);
642
                // get all descendants
643
                $tree = new Tree\NestedTree\NestedTree(prefix_table("nested_tree"), 'id', 'parent_id', 'title');
644
                $tree->rebuild();
645
                $tst = $tree->getDescendants($persfld['id']);
646
                foreach ($tst as $t) {
647
                    array_push($_SESSION['groupes_visibles'], $t->id);
648
                    array_push($_SESSION['personal_visible_groups'], $t->id);
649
                }
650
            }
651
        }
652
653
        // get complete list of ROLES
654
        $tmp = explode(";", $idFonctions);
655
        $rows = DB::query(
656
            "SELECT * FROM ".prefix_table("roles_title")."
657
            ORDER BY title ASC"
658
        );
659
        foreach ($rows as $record) {
660
            if (!empty($record['id']) && !in_array($record['id'], $tmp)) {
661
                array_push($tmp, $record['id']);
662
            }
663
        }
664
        $_SESSION['fonction_id'] = implode(";", $tmp);
665
666
        $_SESSION['groupes_visibles_list'] = implode(',', $_SESSION['groupes_visibles']);
667
        $_SESSION['is_admin'] = $isAdmin;
668
        // Check if admin has created Folders and Roles
669
        DB::query("SELECT * FROM ".prefix_table("nested_tree")."");
670
        $_SESSION['nb_folders'] = DB::count();
671
        DB::query("SELECT * FROM ".prefix_table("roles_title"));
672
        $_SESSION['nb_roles'] = DB::count();
673
    } else {
674
        // init
675
        $_SESSION['groupes_visibles'] = array();
676
        $_SESSION['personal_folders'] = array();
677
        $_SESSION['groupes_interdits'] = array();
678
        $_SESSION['personal_visible_groups'] = array();
679
        $_SESSION['read_only_folders'] = array();
680
        $_SESSION['fonction_id'] = $idFonctions;
681
        $groupesInterdits = array();
682
        if (is_array($groupesInterditsUser) === false) {
0 ignored issues
show
introduced by
The condition is_array($groupesInterditsUser) === false is always true.
Loading history...
683
            $groupesInterditsUser = explode(';', trimElement(/** @scrutinizer ignore-type */ $groupesInterditsUser, ";"));
684
        }
685
        if (empty($groupesInterditsUser) === false && count($groupesInterditsUser) > 0) {
686
            $groupesInterdits = $groupesInterditsUser;
687
        }
688
        $_SESSION['is_admin'] = $isAdmin;
689
        $fonctionsAssociees = explode(';', trimElement($idFonctions, ";"));
690
691
        $listAllowedFolders = $listFoldersLimited = $listFoldersEditableByRole = $listRestrictedFoldersForItems = $listReadOnlyFolders = array();
692
693
        // rechercher tous les groupes visibles en fonction des roles de l'utilisateur
694
        foreach ($fonctionsAssociees as $roleId) {
695
            if (empty($roleId) === false) {
696
                // Get allowed folders for each Role
697
                $rows = DB::query(
698
                    "SELECT folder_id FROM ".prefix_table("roles_values")." WHERE role_id=%i",
699
                    $roleId
700
                );
701
702
                if (DB::count() > 0) {
703
                    $tmp = DB::queryfirstrow(
704
                        "SELECT allow_pw_change FROM ".prefix_table("roles_title")." WHERE id = %i",
705
                        $roleId
706
                    );
707
                    foreach ($rows as $record) {
708
                        if (isset($record['folder_id']) && in_array($record['folder_id'], $listAllowedFolders) === false) {
709
                            array_push($listAllowedFolders, $record['folder_id']);
710
                        }
711
                        // Check if this group is allowed to modify any pw in allowed folders
712
                        if ($tmp['allow_pw_change'] == 1 && in_array($record['folder_id'], $listFoldersEditableByRole) === false) {
713
                            array_push($listFoldersEditableByRole, $record['folder_id']);
714
                        }
715
                    }
716
                    // Check for the users roles if some specific rights exist on items
717
                    $rows = DB::query(
718
                        "SELECT i.id_tree, r.item_id
719
                        FROM ".prefix_table("items")." as i
720
                        INNER JOIN ".prefix_table("restriction_to_roles")." as r ON (r.item_id=i.id)
721
                        WHERE r.role_id=%i
722
                        ORDER BY i.id_tree ASC",
723
                        $roleId
724
                    );
725
                    $inc = 0;
726
                    foreach ($rows as $record) {
727
                        if (isset($record['id_tree'])) {
728
                            $listFoldersLimited[$record['id_tree']][$inc] = $record['item_id'];
729
                            $inc++;
730
                        }
731
                    }
732
                }
733
            }
734
        }
735
        
736
        // Clean arrays
737
        $listAllowedFolders = array_unique($listAllowedFolders);
738
        $groupesVisiblesUser = explode(';', trimElement($groupesVisiblesUser, ";"));
739
        
740
        // Does this user is allowed to see other items
741
        $inc = 0;
742
        $rows = DB::query(
743
            "SELECT id, id_tree FROM ".prefix_table("items")."
744
            WHERE restricted_to LIKE %ss AND inactif=%s",
745
            $_SESSION['user_id'].';',
746
            '0'
747
        );
748
        foreach ($rows as $record) {
749
            // Exclude restriction on item if folder is fully accessible
750
            if (in_array($record['id_tree'], $listAllowedFolders) === false) {
751
                $listRestrictedFoldersForItems[$record['id_tree']][$inc] = $record['id'];
752
                $inc++;
753
            }
754
        }
755
        
756
        // => Build final lists
757
        // Add user allowed folders
758
        $allowedFoldersTmp = array_unique(
759
            array_merge($listAllowedFolders, $groupesVisiblesUser)
760
        );
761
        // Exclude from allowed folders all the specific user forbidden folders
762
        $allowedFolders = array();
763
        foreach ($allowedFoldersTmp as $ident) {
764
            if (!in_array($ident, $groupesInterditsUser) && !empty($ident)) {
765
                array_push($allowedFolders, $ident);
766
            }
767
        }
768
769
        // Clean array
770
        $listAllowedFolders = array_filter(array_unique($allowedFolders));
771
772
        // Exclude all PF
773
        $_SESSION['forbiden_pfs'] = array();
774
775
        $where = new WhereClause('and');
776
        $where->add('personal_folder=%i', 1);
777
        if (isset($SETTINGS['enable_pf_feature']) === true && $SETTINGS['enable_pf_feature'] === '1'
778
            && isset($_SESSION['personal_folder']) === true && $_SESSION['personal_folder'] === '1'
779
        ) {
780
            $where->add('title=%s', $_SESSION['user_id']);
781
            $where->negateLast();
782
        }
783
784
        $persoFlds = DB::query(
785
            "SELECT id
786
            FROM ".prefix_table("nested_tree")."
787
            WHERE %l",
788
            $where
789
        );
790
        foreach ($persoFlds as $persoFldId) {
791
            array_push($_SESSION['forbiden_pfs'], $persoFldId['id']);
792
        }
793
        // Get IDs of personal folders
794
        if (isset($SETTINGS['enable_pf_feature']) === true && $SETTINGS['enable_pf_feature'] === '1'
795
            && isset($_SESSION['personal_folder']) === true && $_SESSION['personal_folder'] === '1'
796
        ) {
797
            $persoFld = DB::queryfirstrow(
798
                "SELECT id
799
                FROM ".prefix_table("nested_tree")."
800
                WHERE title = %s AND personal_folder = %i",
801
                $_SESSION['user_id'],
802
                1
803
            );
804
            if (empty($persoFld['id']) === false) {
805
                if (in_array($persoFld['id'], $listAllowedFolders) === false) {
806
                    array_push($_SESSION['personal_folders'], $persoFld['id']);
807
                    array_push($listAllowedFolders, $persoFld['id']);
808
                    array_push($_SESSION['personal_visible_groups'], $persoFld['id']);
809
                    // get all descendants
810
                    $ids = $tree->getChildren($persoFld['id']);
811
                    foreach ($ids as $ident) {
812
                        array_push($listAllowedFolders, $ident->id);
813
                        array_push($_SESSION['personal_visible_groups'], $ident->id);
814
                        array_push($_SESSION['personal_folders'], $ident->id);
815
                    }
816
                }
817
            }
818
            // get list of readonly folders when pf is disabled.
819
            $_SESSION['personal_folders'] = array_unique($_SESSION['personal_folders']);
820
            // rule - if one folder is set as W or N in one of the Role, then User has access as W
821
            foreach ($listAllowedFolders as $folderId) {
822
                if (in_array($folderId, array_unique(array_merge($listReadOnlyFolders, $_SESSION['personal_folders']))) === false) {
823
                    DB::query(
824
                        "SELECT *
825
                        FROM ".prefix_table("roles_values")."
826
                        WHERE folder_id = %i AND role_id IN %li AND type IN %ls",
827
                        $folderId,
828
                        $fonctionsAssociees,
829
                        array("W", "ND", "NE", "NDNE")
830
                    );
831
                    if (DB::count() === 0 && in_array($folderId, $groupesVisiblesUser) === false) {
832
                        array_push($listReadOnlyFolders, $folderId);
833
                    }
834
                }
835
            }
836
        } else {
837
            // get list of readonly folders when pf is disabled.
838
            // rule - if one folder is set as W in one of the Role, then User has access as W
839
            foreach ($listAllowedFolders as $folderId) {
840
                if (in_array($folderId, $listReadOnlyFolders) === false) {
841
                    DB::query(
842
                        "SELECT *
843
                        FROM ".prefix_table("roles_values")."
844
                        WHERE folder_id = %i AND role_id IN %li AND type IN %ls",
845
                        $folderId,
846
                        $fonctionsAssociees,
847
                        array("W", "ND", "NE", "NDNE")
848
                    );
849
                    if (DB::count() == 0 && !in_array($folderId, $groupesVisiblesUser)) {
850
                        array_push($listReadOnlyFolders, $folderId);
851
                    }
852
                }
853
            }
854
        }
855
856
        // check if change proposals on User's items
857
        if (isset($SETTINGS['enable_suggestion']) === true && $SETTINGS['enable_suggestion'] === '1') {
858
            DB::query(
859
                "SELECT *
860
                FROM ".prefix_table("items_change")." AS c
861
                LEFT JOIN ".prefix_table("log_items")." AS i ON (c.item_id = i.id_item)
862
                WHERE i.action = %s AND i.id_user = %i",
863
                "at_creation",
864
                $_SESSION['user_id']
865
            );
866
            $_SESSION['nb_item_change_proposals'] = DB::count();
867
        } else {
868
            $_SESSION['nb_item_change_proposals'] = 0;
869
        }
870
871
        $_SESSION['all_non_personal_folders'] = $listAllowedFolders;
872
        $_SESSION['groupes_visibles'] = $listAllowedFolders;
873
        $_SESSION['groupes_visibles_list'] = implode(',', $listAllowedFolders);
874
        $_SESSION['personal_visible_groups_list'] = implode(',', $_SESSION['personal_visible_groups']);
875
        $_SESSION['read_only_folders'] = $listReadOnlyFolders;
876
        $_SESSION['no_access_folders'] = $groupesInterdits;
877
878
        $_SESSION['list_folders_limited'] = $listFoldersLimited;
879
        $_SESSION['list_folders_editable_by_role'] = $listFoldersEditableByRole;
880
        $_SESSION['list_restricted_folders_for_items'] = $listRestrictedFoldersForItems;
881
        // Folders and Roles numbers
882
        DB::queryfirstrow("SELECT id FROM ".prefix_table("nested_tree")."");
883
        $_SESSION['nb_folders'] = DB::count();
884
        DB::queryfirstrow("SELECT id FROM ".prefix_table("roles_title"));
885
        $_SESSION['nb_roles'] = DB::count();
886
    }
887
888
    // update user's timestamp
889
    DB::update(
890
        prefix_table('users'),
891
        array(
892
            'timestamp' => time()
893
        ),
894
        "id=%i",
895
        $_SESSION['user_id']
896
    );
897
}
898
899
/**
900
 * updateCacheTable()
901
 *
902
 * Update the CACHE table
903
 * @param string $action
904
 */
905
function updateCacheTable($action, $ident = null)
906
{
907
    global $server, $user, $pass, $database, $port, $encoding;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
908
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
909
910
    require_once $SETTINGS['cpassman_dir'].'/sources/SplClassLoader.php';
911
912
    //Connect to DB
913
    require_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
914
    $pass = defuse_return_decrypted($pass);
915
    DB::$host = $server;
916
    DB::$user = $user;
917
    DB::$password = $pass;
918
    DB::$dbName = $database;
919
    DB::$port = $port;
920
    DB::$encoding = $encoding;
921
    DB::$error_handler = true;
922
    $link = mysqli_connect($server, $user, $pass, $database, $port);
923
    $link->set_charset($encoding);
924
925
    //Load Tree
926
    $tree = new SplClassLoader('Tree\NestedTree', '../includes/libraries');
927
    $tree->register();
928
    $tree = new Tree\NestedTree\NestedTree(prefix_table("nested_tree"), 'id', 'parent_id', 'title');
929
930
    // Rebuild full cache table
931
    if ($action === "reload") {
932
        // truncate table
933
        DB::query("TRUNCATE TABLE ".prefix_table("cache"));
934
935
        // reload date
936
        $rows = DB::query(
937
            "SELECT *
938
            FROM ".prefix_table('items')." as i
939
            INNER JOIN ".prefix_table('log_items')." as l ON (l.id_item = i.id)
940
            AND l.action = %s
941
            AND i.inactif = %i",
942
            'at_creation',
943
            0
944
        );
945
        foreach ($rows as $record) {
946
            if (empty($record['id_tree']) === false) {
947
                // Get all TAGS
948
                $tags = "";
949
                $itemTags = DB::query("SELECT tag FROM ".prefix_table('tags')." WHERE item_id=%i", $record['id']);
950
                foreach ($itemTags as $itemTag) {
951
                    if (!empty($itemTag['tag'])) {
952
                        $tags .= $itemTag['tag']." ";
953
                    }
954
                }
955
                // Get renewal period
956
                $resNT = DB::queryfirstrow("SELECT renewal_period FROM ".prefix_table('nested_tree')." WHERE id=%i", $record['id_tree']);
957
958
                // form id_tree to full foldername
959
                $folder = "";
960
                $arbo = $tree->getPath($record['id_tree'], true);
961
                foreach ($arbo as $elem) {
962
                    if ($elem->title == $_SESSION['user_id'] && $elem->nlevel == 1) {
963
                        $elem->title = $_SESSION['login'];
964
                    }
965
                    if (empty($folder)) {
966
                        $folder = stripslashes($elem->title);
967
                    } else {
968
                        $folder .= " » ".stripslashes($elem->title);
969
                    }
970
                }
971
                // store data
972
                DB::insert(
973
                    prefix_table('cache'),
974
                    array(
975
                        'id' => $record['id'],
976
                        'label' => $record['label'],
977
                        'description' => isset($record['description']) ? $record['description'] : "",
978
                        'url' => (isset($record['url']) && !empty($record['url'])) ? $record['url'] : "0",
979
                        'tags' => $tags,
980
                        'id_tree' => $record['id_tree'],
981
                        'perso' => $record['perso'],
982
                        'restricted_to' => (isset($record['restricted_to']) && !empty($record['restricted_to'])) ? $record['restricted_to'] : "0",
983
                        'login' => isset($record['login']) ? $record['login'] : "",
984
                        'folder' => $folder,
985
                        'author' => $record['id_user'],
986
                        'renewal_period' => isset($resNT['renewal_period']) ? $resNT['renewal_period'] : "0",
987
                        'timestamp' => $record['date']
988
                        )
989
                );
990
            }
991
        }
992
        // UPDATE an item
993
    } elseif ($action === "update_value" && is_null($ident) === false) {
994
        // get new value from db
995
        $data = DB::queryfirstrow(
996
            "SELECT label, description, id_tree, perso, restricted_to, login, url
997
            FROM ".prefix_table('items')."
998
            WHERE id=%i",
999
            $ident
1000
        );
1001
        // Get all TAGS
1002
        $tags = "";
1003
        $itemTags = DB::query("SELECT tag FROM ".prefix_table('tags')." WHERE item_id=%i", $ident);
1004
        foreach ($itemTags as $itemTag) {
1005
            if (!empty($itemTag['tag'])) {
1006
                $tags .= $itemTag['tag']." ";
1007
            }
1008
        }
1009
        // form id_tree to full foldername
1010
        $folder = "";
1011
        $arbo = $tree->getPath($data['id_tree'], true);
1012
        foreach ($arbo as $elem) {
1013
            if ($elem->title == $_SESSION['user_id'] && $elem->nlevel == 1) {
1014
                $elem->title = $_SESSION['login'];
1015
            }
1016
            if (empty($folder)) {
1017
                $folder = stripslashes($elem->title);
1018
            } else {
1019
                $folder .= " » ".stripslashes($elem->title);
1020
            }
1021
        }
1022
        // finaly update
1023
        DB::update(
1024
            prefix_table('cache'),
1025
            array(
1026
                'label' => $data['label'],
1027
                'description' => $data['description'],
1028
                'tags' => $tags,
1029
                'url' => (isset($data['url']) && !empty($data['url'])) ? $data['url'] : "0",
1030
                'id_tree' => $data['id_tree'],
1031
                'perso' => $data['perso'],
1032
                'restricted_to' => (isset($data['restricted_to']) && !empty($data['restricted_to'])) ? $data['restricted_to'] : "0",
1033
                'login' => isset($data['login']) ? $data['login'] : "",
1034
                'folder' => $folder,
1035
                'author' => $_SESSION['user_id'],
1036
                ),
1037
            "id = %i",
1038
            $ident
1039
        );
1040
    // ADD an item
1041
    } elseif ($action === "add_value" && is_null($ident) === false) {
1042
        // get new value from db
1043
        $data = DB::queryFirstRow(
1044
            "SELECT i.label, i.description, i.id_tree as id_tree, i.perso, i.restricted_to, i.id, i.login, i.url, l.date
1045
            FROM ".prefix_table('items')." as i
1046
            INNER JOIN ".prefix_table('log_items')." as l ON (l.id_item = i.id)
1047
            WHERE i.id = %i
1048
            AND l.action = %s",
1049
            $ident,
1050
            'at_creation'
1051
        );
1052
        // Get all TAGS
1053
        $tags = "";
1054
        $itemTags = DB::query("SELECT tag FROM ".prefix_table('tags')." WHERE item_id = %i", $ident);
1055
        foreach ($itemTags as $itemTag) {
1056
            if (!empty($itemTag['tag'])) {
1057
                $tags .= $itemTag['tag']." ";
1058
            }
1059
        }
1060
        // form id_tree to full foldername
1061
        $folder = "";
1062
        $arbo = $tree->getPath($data['id_tree'], true);
1063
        foreach ($arbo as $elem) {
1064
            if ($elem->title == $_SESSION['user_id'] && $elem->nlevel == 1) {
1065
                $elem->title = $_SESSION['login'];
1066
            }
1067
            if (empty($folder)) {
1068
                $folder = stripslashes($elem->title);
1069
            } else {
1070
                $folder .= " » ".stripslashes($elem->title);
1071
            }
1072
        }
1073
        // finaly update
1074
        DB::insert(
1075
            prefix_table('cache'),
1076
            array(
1077
                'id' => $data['id'],
1078
                'label' => $data['label'],
1079
                'description' => $data['description'],
1080
                'tags' => (isset($tags) && !empty($tags)) ? $tags : "None",
1081
                'url' => (isset($data['url']) && !empty($data['url'])) ? $data['url'] : "0",
1082
                'id_tree' => $data['id_tree'],
1083
                'perso' => (isset($data['perso']) && !empty($data['perso']) && $data['perso'] !== "None") ? $data['perso'] : "0",
1084
                'restricted_to' => (isset($data['restricted_to']) && !empty($data['restricted_to'])) ? $data['restricted_to'] : "0",
1085
                'login' => isset($data['login']) ? $data['login'] : "",
1086
                'folder' => $folder,
1087
                'author' => $_SESSION['user_id'],
1088
                'timestamp' => $data['date']
1089
            )
1090
        );
1091
1092
    // DELETE an item
1093
    } elseif ($action === "delete_value" && is_null($ident) === false) {
1094
        DB::delete(prefix_table('cache'), "id = %i", $ident);
1095
    }
1096
}
1097
1098
/*
1099
*
1100
*/
1101
function getStatisticsData()
1102
{
1103
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1104
1105
    DB::query(
1106
        "SELECT id FROM ".prefix_table("nested_tree")." WHERE personal_folder = %i",
1107
        0
1108
    );
1109
    $counter_folders = DB::count();
1110
1111
    DB::query(
1112
        "SELECT id FROM ".prefix_table("nested_tree")." WHERE personal_folder = %i",
1113
        1
1114
    );
1115
    $counter_folders_perso = DB::count();
1116
1117
    DB::query(
1118
        "SELECT id FROM ".prefix_table("items")." WHERE perso = %i",
1119
        0
1120
    );
1121
    $counter_items = DB::count();
1122
1123
    DB::query(
1124
        "SELECT id FROM ".prefix_table("items")." WHERE perso = %i",
1125
        1
1126
    );
1127
    $counter_items_perso = DB::count();
1128
1129
    DB::query(
1130
        "SELECT id FROM ".prefix_table("users").""
1131
    );
1132
    $counter_users = DB::count();
1133
1134
    DB::query(
1135
        "SELECT id FROM ".prefix_table("users")." WHERE admin = %i",
1136
        1
1137
    );
1138
    $admins = DB::count();
1139
1140
    DB::query(
1141
        "SELECT id FROM ".prefix_table("users")." WHERE gestionnaire = %i",
1142
        1
1143
    );
1144
    $managers = DB::count();
1145
1146
    DB::query(
1147
        "SELECT id FROM ".prefix_table("users")." WHERE read_only = %i",
1148
        1
1149
    );
1150
    $readOnly = DB::count();
1151
1152
    // list the languages
1153
    $usedLang = [];
1154
    $tp_languages = DB::query(
1155
        "SELECT name FROM ".prefix_table("languages")
1156
    );
1157
    foreach ($tp_languages as $tp_language) {
1158
        DB::query(
1159
            "SELECT * FROM ".prefix_table("users")." WHERE user_language = %s",
1160
            $tp_language['name']
1161
        );
1162
        $usedLang[$tp_language['name']] = round((DB::count() * 100 / $counter_users), 0);
1163
    }
1164
1165
    // get list of ips
1166
    $usedIp = [];
1167
    $tp_ips = DB::query(
1168
        "SELECT user_ip FROM ".prefix_table("users")
1169
    );
1170
    foreach ($tp_ips as $ip) {
1171
        if (array_key_exists($ip['user_ip'], $usedIp)) {
1172
            $usedIp[$ip['user_ip']] = $usedIp[$ip['user_ip']] + 1;
1173
        } elseif (!empty($ip['user_ip']) && $ip['user_ip'] !== "none") {
1174
            $usedIp[$ip['user_ip']] = 1;
1175
        }
1176
    }
1177
1178
    return array(
1179
        "error" => "",
1180
        "stat_phpversion" => phpversion(),
1181
        "stat_folders" => $counter_folders,
1182
        "stat_folders_shared" => intval($counter_folders) - intval($counter_folders_perso),
1183
        "stat_items" => $counter_items,
1184
        "stat_items_shared" => intval($counter_items) - intval($counter_items_perso),
1185
        "stat_users" => $counter_users,
1186
        "stat_admins" => $admins,
1187
        "stat_managers" => $managers,
1188
        "stat_ro" => $readOnly,
1189
        "stat_kb" => $SETTINGS['enable_kb'],
1190
        "stat_pf" => $SETTINGS['enable_pf_feature'],
1191
        "stat_fav" => $SETTINGS['enable_favourites'],
1192
        "stat_teampassversion" => $SETTINGS['cpassman_version'],
1193
        "stat_ldap" => $SETTINGS['ldap_mode'],
1194
        "stat_agses" => $SETTINGS['agses_authentication_enabled'],
1195
        "stat_duo" => $SETTINGS['duo'],
1196
        "stat_suggestion" => $SETTINGS['enable_suggestion'],
1197
        "stat_api" => $SETTINGS['api'],
1198
        "stat_customfields" => $SETTINGS['item_extra_fields'],
1199
        "stat_syslog" => $SETTINGS['syslog_enable'],
1200
        "stat_2fa" => $SETTINGS['google_authentication'],
1201
        "stat_stricthttps" => $SETTINGS['enable_sts'],
1202
        "stat_mysqlversion" => DB::serverVersion(),
1203
        "stat_languages" => $usedLang,
1204
        "stat_country" => $usedIp
1205
    );
1206
}
1207
1208
/**
1209
 * Permits to send an email
1210
 *
1211
 * @param  string $subject     email subject
1212
 * @param  string $textMail    email message
1213
 * @param  string $email       email
1214
 * @param  array  $LANG        Language
1215
 * @param  array  $SETTINGS    settings
1216
 * @param  string $textMailAlt email message alt
1217
 * @return  string  some json info
1218
 */
1219
function sendEmail(
1220
    $subject,
1221
    $textMail,
1222
    $email,
1223
    $LANG,
1224
    $SETTINGS,
1225
    $textMailAlt = null
1226
) {
1227
    // CAse where email not defined
1228
    if ($email === "none") {
1229
        return '"error":"" , "message":"'.$LANG['forgot_my_pw_email_sent'].'"';
1230
    }
1231
1232
    // Load settings
1233
    include $SETTINGS['cpassman_dir'].'/includes/config/settings.php';
1234
1235
    // Load superglobal
1236
    require_once $SETTINGS['cpassman_dir'].'/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
1237
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
1238
1239
    // Get user language
1240
    $session_user_language = $superGlobal->get("user_language", "SESSION");
1241
    $user_language = isset($session_user_language) ? $session_user_language : "english";
1242
    require_once $SETTINGS['cpassman_dir'].'/includes/language/'.$user_language.'.php';
1243
1244
    // Load library
1245
    include_once $SETTINGS['cpassman_dir'].'/sources/SplClassLoader.php';
1246
1247
    // load PHPMailer
1248
    $mail = new SplClassLoader('Email\PHPMailer', '../includes/libraries');
1249
    $mail->register();
1250
    $mail = new Email\PHPMailer\PHPMailer(true);
1251
    try {
1252
        // send to user
1253
        $mail->setLanguage("en", $SETTINGS['cpassman_dir']."/includes/libraries/Email/PHPMailer/language/");
1254
        $mail->SMTPDebug = 0; //value 1 can be used to debug - 4 for debuging connections
1255
        $mail->Port = $SETTINGS['email_port']; //COULD BE USED
1256
        $mail->CharSet = "utf-8";
1257
        if ($SETTINGS['email_security'] === "tls" || $SETTINGS['email_security'] === "ssl") {
1258
            $mail->SMTPSecure = $SETTINGS['email_security'];
1259
            $SMTPAutoTLS = true;
1260
        } else {
1261
            $SMTPAutoTLS = false;
1262
            $mail->SMTPSecure = "";
1263
        }
1264
        $mail->SMTPAutoTLS = $SMTPAutoTLS;
1265
        $mail->isSmtp(); // send via SMTP
1266
        $mail->Host = $SETTINGS['email_smtp_server']; // SMTP servers
1267
        $mail->SMTPAuth = $SETTINGS['email_smtp_auth'] == '1' ? true : false; // turn on SMTP authentication
1268
        $mail->Username = $SETTINGS['email_auth_username']; // SMTP username
1269
        $mail->Password = $SETTINGS['email_auth_pwd']; // SMTP password
1270
        $mail->From = $SETTINGS['email_from'];
1271
        $mail->FromName = $SETTINGS['email_from_name'];
1272
1273
        // Prepare for each person
1274
        $dests = explode(",", $email);
1275
        foreach ($dests as $dest) {
1276
            $mail->addAddress($dest);
1277
        }
1278
1279
        // Prepare HTML
1280
        $text_html = '<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.=
1281
        w3.org/TR/html4/loose.dtd"><html>
1282
        <head><title>Email Template</title>
1283
        <style type="text/css">
1284
        body { background-color: #f0f0f0; padding: 10px 0; margin:0 0 10px =0; }
1285
        </style></head>
1286
        <body style="-ms-text-size-adjust: none; size-adjust: none; margin: 0; padding: 10px 0; background-color: #f0f0f0;" bgcolor="#f0f0f0" leftmargin="0" topmargin="0" marginwidth="0" marginheight="0">
1287
        <table border="0" width="100%" height="100%" cellpadding="0" cellspacing="0" bgcolor="#f0f0f0" style="border-spacing: 0;">
1288
        <tr><td style="border-collapse: collapse;"><br>
1289
            <table border="0" width="100%" cellpadding="0" cellspacing="0" bgcolor="#17357c" style="border-spacing: 0; margin-bottom: 25px;">
1290
            <tr><td style="border-collapse: collapse; padding: 11px 20px;">
1291
                <div style="max-width:150px; max-height:34px; color:#f0f0f0; font-weight:bold;">Teampass</div>
1292
            </td></tr></table></td>
1293
        </tr>
1294
        <tr><td align="center" valign="top" bgcolor="#f0f0f0" style="border-collapse: collapse; background-color: #f0f0f0;">
1295
            <table width="600" cellpadding="0" cellspacing="0" border="0" class="container" bgcolor="#ffffff" style="border-spacing: 0; border-bottom: 1px solid #e0e0e0; box-shadow: 0 0 3px #ddd; color: #434343; font-family: Helvetica, Verdana, sans-serif;">
1296
            <tr><td class="container-padding" bgcolor="#ffffff" style="border-collapse: collapse; border-left: 1px solid #e0e0e0; background-color: #ffffff; padding-left: 30px; padding-right: 30px;">
1297
            <br><div style="float:right;">'.
1298
        $textMail.
1299
        '<br><br></td></tr></table>
1300
        </td></tr></table>
1301
        <br></body></html>';
1302
1303
        $mail->WordWrap = 80; // set word wrap
1304
        $mail->isHtml(true); // send as HTML
1305
        $mail->Subject = $subject;
1306
        $mail->Body = $text_html;
1307
        if (is_null($textMailAlt) === false) {
1308
            $mail->AltBody = $textMailAlt;
1309
        } else {
1310
            $mail->AltBody = '';
1311
        }
1312
        // send email
1313
        if (!$mail->send()) {
1314
            return '"error":"error_mail_not_send" , "message":"'.str_replace(array("\n", "\t", "\r"), '', $mail->ErrorInfo).'"';
1315
        } else {
1316
            return '"error":"" , "message":"'.$LANG['forgot_my_pw_email_sent'].'"';
1317
        }
1318
    } catch (Exception $e) {
1319
        return '"error":"error_mail_not_send" , '.
1320
        '"message":"'.str_replace(array("\n", "\t", "\r"), '', $mail->ErrorInfo).'"';
1321
    }
1322
}
1323
1324
/**
1325
 * generateKey()
1326
 *
1327
 * @return
1328
 */
1329
function generateKey()
1330
{
1331
    return substr(md5(rand().rand()), 0, 15);
1332
}
1333
1334
/**
1335
 * dateToStamp()
1336
 *
1337
 * @return
1338
 */
1339
function dateToStamp($date)
1340
{
1341
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1342
1343
    $date = date_parse_from_format($SETTINGS['date_format'], $date);
1344
    if ($date['warning_count'] == 0 && $date['error_count'] == 0) {
1345
        return mktime(23, 59, 59, $date['month'], $date['day'], $date['year']);
1346
    } else {
1347
        return false;
1348
    }
1349
}
1350
1351
function isDate($date)
1352
{
1353
    return (strtotime($date) !== false);
1354
}
1355
1356
/**
1357
 * isUTF8()
1358
 *
1359
 * @return integer is the string in UTF8 format.
1360
 */
1361
1362
function isUTF8($string)
1363
{
1364
    if (is_array($string) === true) {
1365
        $string = $string['string'];
1366
    }
1367
    return preg_match(
1368
        '%^(?:
1369
        [\x09\x0A\x0D\x20-\x7E] # ASCII
1370
        | [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
1371
        | \xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
1372
        | [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
1373
        | \xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
1374
        | \xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
1375
        | [\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
1376
        | \xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
1377
        )*$%xs',
1378
        $string
1379
    );
1380
}
1381
1382
/*
1383
* FUNCTION
1384
* permits to prepare data to be exchanged
1385
*/
1386
/**
1387
 * @param string $type
1388
 */
1389
function prepareExchangedData($data, $type)
1390
{
1391
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1392
1393
    //load ClassLoader
1394
    require_once $SETTINGS['cpassman_dir'].'/sources/SplClassLoader.php';
1395
    //Load AES
1396
    $aes = new SplClassLoader('Encryption\Crypt', $SETTINGS['cpassman_dir'].'/includes/libraries');
1397
    $aes->register();
1398
1399
    if ($type == "encode") {
1400
        if (isset($SETTINGS['encryptClientServer'])
1401
            && $SETTINGS['encryptClientServer'] === "0"
1402
        ) {
1403
            return json_encode(
1404
                $data,
1405
                JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1406
            );
1407
        } else {
1408
            return Encryption\Crypt\aesctr::encrypt(
1409
                json_encode(
1410
                    $data,
1411
                    JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP
1412
                ),
1413
                $_SESSION['key'],
1414
                256
1415
            );
1416
        }
1417
    } elseif ($type == "decode") {
1418
        if (isset($SETTINGS['encryptClientServer'])
1419
            && $SETTINGS['encryptClientServer'] === "0"
1420
        ) {
1421
            return json_decode(
1422
                $data,
1423
                true
1424
            );
1425
        } else {
1426
            return json_decode(
1427
                Encryption\Crypt\aesctr::decrypt(
1428
                    $data,
1429
                    $_SESSION['key'],
1430
                    256
1431
                ),
1432
                true
1433
            );
1434
        }
1435
    }
1436
}
1437
1438
function make_thumb($src, $dest, $desired_width)
1439
{
1440
    /* read the source image */
1441
    $source_image = imagecreatefrompng($src);
1442
    $width = imagesx($source_image);
1443
    $height = imagesy($source_image);
1444
1445
    /* find the "desired height" of this thumbnail, relative to the desired width  */
1446
    $desired_height = floor($height * ($desired_width / $width));
1447
1448
    /* create a new, "virtual" image */
1449
    $virtual_image = imagecreatetruecolor($desired_width, $desired_height);
0 ignored issues
show
Bug introduced by
$desired_height of type double is incompatible with the type integer expected by parameter $height of imagecreatetruecolor(). ( Ignorable by Annotation )

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

1449
    $virtual_image = imagecreatetruecolor($desired_width, /** @scrutinizer ignore-type */ $desired_height);
Loading history...
1450
1451
    /* copy source image at a resized size */
1452
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, $desired_height, $width, $height);
0 ignored issues
show
Bug introduced by
$desired_height of type double is incompatible with the type integer expected by parameter $dst_h of imagecopyresampled(). ( Ignorable by Annotation )

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

1452
    imagecopyresampled($virtual_image, $source_image, 0, 0, 0, 0, $desired_width, /** @scrutinizer ignore-type */ $desired_height, $width, $height);
Loading history...
1453
1454
    /* create the physical thumbnail image to its destination */
1455
    imagejpeg($virtual_image, $dest);
1456
}
1457
1458
/*
1459
** check table prefix in SQL query
1460
*/
1461
/**
1462
 * @param string $table
1463
 */
1464
function prefix_table($table)
1465
{
1466
    global $pre;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1467
    $safeTable = htmlspecialchars($pre.$table);
1468
    if (!empty($safeTable)) {
1469
        // sanitize string
1470
        return $safeTable;
1471
    } else {
1472
        // stop error no table
1473
        return "table_not_exists";
1474
    }
1475
}
1476
1477
/*
1478
 * Creates a KEY using PasswordLib
1479
 */
1480
function GenerateCryptKey($size = null, $secure = false, $numerals = false, $capitalize = false, $symbols = false)
1481
{
1482
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1483
    require_once $SETTINGS['cpassman_dir'].'/sources/SplClassLoader.php';
1484
1485
    if ($secure === true) {
1486
        $numerals = true;
1487
        $capitalize = true;
1488
        $symbols = true;
1489
    }
1490
1491
    // Load libraries
1492
    $generator = new SplClassLoader('PasswordGenerator\Generator', '../includes/libraries');
1493
    $generator->register();
1494
    $generator = new PasswordGenerator\Generator\ComputerPasswordGenerator();
1495
1496
    // Can we use PHP7 random_int function?
1497
    if (version_compare(phpversion(), '7.0', '>=')) {
1498
        require_once $SETTINGS['cpassman_dir'].'/includes/libraries/PasswordGenerator/RandomGenerator/Php7RandomGenerator.php';
1499
        $generator->setRandomGenerator(new PasswordGenerator\RandomGenerator\Php7RandomGenerator());
1500
    }
1501
1502
    // init
1503
    if (empty($size) === false && is_null($size) === false) {
1504
        $generator->setLength(intval($size));
1505
    }
1506
    if (empty($numerals) === false) {
1507
        $generator->setNumbers($numerals);
1508
    }
1509
    if (empty($capitalize) === false) {
1510
        $generator->setUppercase($capitalize);
1511
    }
1512
    if (empty($symbols) === false) {
1513
        $generator->setSymbols($symbols);
1514
    }
1515
1516
    // generate and send back
1517
    return $generator->generatePassword();
1518
}
1519
1520
/*
1521
* Send sysLOG message
1522
* @param string $message
1523
* @param string $host
1524
*/
1525
function send_syslog($message, $host, $port, $component = "teampass")
1526
{
1527
    $sock = socket_create(AF_INET, SOCK_DGRAM, SOL_UDP);
1528
    $syslog_message = "<123>".date('M d H:i:s ').$component.": ".$message;
1529
    socket_sendto($sock, $syslog_message, strlen($syslog_message), 0, $host, $port);
1530
    socket_close($sock);
1531
}
1532
1533
1534
1535
/**
1536
 * logEvents()
1537
 *
1538
 * permits to log events into DB
1539
 * @param string $type
1540
 * @param string $label
1541
 * @param string $field_1
1542
 */
1543
function logEvents($type, $label, $who, $login = null, $field_1 = null)
1544
{
1545
    global $server, $user, $pass, $database, $port, $encoding;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1546
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1547
1548
    if (empty($who)) {
1549
        $who = get_client_ip_server();
1550
    }
1551
1552
    // include librairies & connect to DB
1553
    require_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1554
    $pass = defuse_return_decrypted($pass);
1555
    DB::$host = $server;
1556
    DB::$user = $user;
1557
    DB::$password = $pass;
1558
    DB::$dbName = $database;
1559
    DB::$port = $port;
1560
    DB::$encoding = $encoding;
1561
    DB::$error_handler = true;
1562
    $link = mysqli_connect($server, $user, $pass, $database, $port);
1563
    $link->set_charset($encoding);
1564
1565
    DB::insert(
1566
        prefix_table("log_system"),
1567
        array(
1568
            'type' => $type,
1569
            'date' => time(),
1570
            'label' => $label,
1571
            'qui' => $who,
1572
            'field_1' => $field_1 === null ? "" : $field_1
1573
        )
1574
    );
1575
    if (isset($SETTINGS['syslog_enable']) && $SETTINGS['syslog_enable'] == 1) {
1576
        if ($type == "user_mngt") {
1577
            send_syslog(
1578
                'action='.str_replace('at_', '', $label).' attribute=user user='.$who.' userid="'.$login.'" change="'.$field_1.'" ',
1579
                $SETTINGS['syslog_host'],
1580
                $SETTINGS['syslog_port'],
1581
                "teampass"
1582
            );
1583
        } else {
1584
            send_syslog(
1585
                'action='.$type.' attribute='.$label.' user='.$who.' userid="'.$login.'" ',
1586
                $SETTINGS['syslog_host'],
1587
                $SETTINGS['syslog_port'],
1588
                "teampass"
1589
            );
1590
        }
1591
    }
1592
}
1593
1594
/**
1595
 * Logs sent events
1596
 *
1597
 * @param string $ident
1598
 * @param string $item
1599
 * @param string $id_user
1600
 * @param string $action
1601
 * @param string $login
1602
 * @param string $raison
1603
 * @param string $encryption_type
1604
 * @return void
1605
 */
1606
function logItems(
1607
    $item_id,
1608
    $item_label,
1609
    $id_user,
1610
    $action,
1611
    $login = null,
1612
    $raison = null,
1613
    $encryption_type = null
1614
) {
1615
    global $server, $user, $pass, $database, $port, $encoding;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1616
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1617
    $dataItem = '';
1618
1619
    // include librairies & connect to DB
1620
    include_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1621
    $pass = defuse_return_decrypted($pass);
1622
    DB::$host = $server;
1623
    DB::$user = $user;
1624
    DB::$password = $pass;
1625
    DB::$dbName = $database;
1626
    DB::$port = $port;
1627
    DB::$encoding = $encoding;
1628
    DB::$error_handler = true;
1629
    $link = mysqli_connect($server, $user, $pass, $database, $port);
1630
    $link->set_charset($encoding);
1631
1632
    // Insert log in DB
1633
    DB::insert(
1634
        prefix_table("log_items"),
1635
        array(
1636
            'id_item' => $item_id,
1637
            'date' => time(),
1638
            'id_user' => $id_user,
1639
            'action' => $action,
1640
            'raison' => $raison,
1641
            'raison_iv' => '',
1642
            'encryption_type' => is_null($encryption_type) === true ? '' : $encryption_type
1643
        )
1644
    );
1645
1646
    // SYSLOG
1647
    if (isset($SETTINGS['syslog_enable']) === true && $SETTINGS['syslog_enable'] === '1') {
1648
        // Extract reason
1649
        $attribute = explode(' : ', $raison);
1650
1651
        // Get item info if not known
1652
        if (empty($item_label) === true) {
1653
            $dataItem = DB::queryfirstrow(
1654
                "SELECT id, id_tree, label
1655
                FROM ".prefix_table("items")."
1656
                WHERE id = %i",
1657
                $item_id
1658
            );
1659
1660
            $item_label = $dataItem['label'];
1661
        }
1662
1663
        send_syslog(
1664
            'action='.str_replace('at_', '', $action).' attribute='.str_replace('at_', '', $attribute[0]).' itemno='.$item_id.' user='.addslashes($login).' itemname="'.addslashes($item_label).'"',
1665
            $SETTINGS['syslog_host'],
1666
            $SETTINGS['syslog_port'],
1667
            "teampass"
1668
        );
1669
    }
1670
1671
    // send notification if enabled
1672
    if (isset($SETTINGS['enable_email_notification_on_item_shown']) === true
1673
        && $SETTINGS['enable_email_notification_on_item_shown'] === '1'
1674
        && $action === 'at_shown'
1675
    ) {
1676
        // Get info about item
1677
        if (empty($dataItem) === true && empty($item_label) === true) {
1678
            $dataItem = DB::queryfirstrow(
1679
                "SELECT id, id_tree, label
1680
                FROM ".prefix_table("items")."
1681
                WHERE id = %i",
1682
                $item_id
1683
            );
1684
            $item_label = $dataItem['label'];
1685
        }
1686
1687
        // send back infos
1688
        DB::insert(
1689
            prefix_table('emails'),
1690
            array(
1691
                'timestamp' => time(),
1692
                'subject' => $LANG['email_on_open_notification_subject'],
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $LANG seems to be never defined.
Loading history...
1693
                'body' => str_replace(
1694
                    array('#tp_user#', '#tp_item#', '#tp_link#'),
1695
                    array(
1696
                        addslashes($_SESSION['login']),
1697
                        addslashes($item_label),
1698
                        $SETTINGS['cpassman_url']."/index.php?page=items&group=".$dataItem['id_tree']."&id=".$dataItem['id']
1699
                    ),
1700
                    $LANG['email_on_open_notification_mail']
1701
                ),
1702
                'receivers' => $_SESSION['listNotificationEmails'],
1703
                'status' => ''
1704
            )
1705
        );
1706
    }
1707
}
1708
1709
/*
1710
* Function to get the client ip address
1711
 */
1712
function get_client_ip_server()
1713
{
1714
    if (getenv('HTTP_CLIENT_IP')) {
1715
        $ipaddress = getenv('HTTP_CLIENT_IP');
1716
    } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
1717
        $ipaddress = getenv('HTTP_X_FORWARDED_FOR');
1718
    } elseif (getenv('HTTP_X_FORWARDED')) {
1719
        $ipaddress = getenv('HTTP_X_FORWARDED');
1720
    } elseif (getenv('HTTP_FORWARDED_FOR')) {
1721
        $ipaddress = getenv('HTTP_FORWARDED_FOR');
1722
    } elseif (getenv('HTTP_FORWARDED')) {
1723
        $ipaddress = getenv('HTTP_FORWARDED');
1724
    } elseif (getenv('REMOTE_ADDR')) {
1725
        $ipaddress = getenv('REMOTE_ADDR');
1726
    } else {
1727
        $ipaddress = 'UNKNOWN';
1728
    }
1729
1730
    return $ipaddress;
1731
}
1732
1733
/**
1734
 * Escape all HTML, JavaScript, and CSS
1735
 *
1736
 * @param string $input The input string
1737
 * @param string $encoding Which character encoding are we using?
1738
 * @return string
1739
 */
1740
function noHTML($input, $encoding = 'UTF-8')
1741
{
1742
    return htmlspecialchars($input, ENT_QUOTES | ENT_XHTML, $encoding, false);
1743
}
1744
1745
/**
1746
 * handleConfigFile()
1747
 *
1748
 * permits to handle the Teampass config file
1749
 * $action accepts "rebuild" and "update"
1750
 */
1751
function handleConfigFile($action, $field = null, $value = null)
1752
{
1753
    global $server, $user, $pass, $database, $port, $encoding;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1754
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1755
1756
    $tp_config_file = "../includes/config/tp.config.php";
1757
1758
    // include librairies & connect to DB
1759
    require_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1760
    $pass = defuse_return_decrypted($pass);
1761
    DB::$host = $server;
1762
    DB::$user = $user;
1763
    DB::$password = $pass;
1764
    DB::$dbName = $database;
1765
    DB::$port = $port;
1766
    DB::$encoding = $encoding;
1767
    DB::$error_handler = true;
1768
    $link = mysqli_connect($server, $user, $pass, $database, $port);
1769
    $link->set_charset($encoding);
1770
1771
    if (!file_exists($tp_config_file) || $action == "rebuild") {
1772
        // perform a copy
1773
        if (file_exists($tp_config_file)) {
1774
            if (!copy($tp_config_file, $tp_config_file.'.'.date("Y_m_d_His", time()))) {
1775
                return "ERROR: Could not copy file '".$tp_config_file."'";
1776
            }
1777
        }
1778
1779
        // regenerate
1780
        $data = array();
1781
        $data[0] = "<?php\n";
1782
        $data[1] = "global \$SETTINGS;\n";
1783
        $data[2] = "\$SETTINGS = array (\n";
1784
        $rows = DB::query(
1785
            "SELECT * FROM ".prefix_table("misc")." WHERE type=%s",
1786
            "admin"
1787
        );
1788
        foreach ($rows as $record) {
1789
            array_push($data, "    '".$record['intitule']."' => '".$record['valeur']."',\n");
1790
        }
1791
        array_push($data, ");\n");
1792
        $data = array_unique($data);
1793
    } elseif ($action == "update" && empty($field) === false) {
1794
        $data = file($tp_config_file);
1795
        $inc = 0;
1796
        $bFound = false;
1797
        foreach ($data as $line) {
1798
            if (stristr($line, ");")) {
1799
                break;
1800
            }
1801
1802
            //
1803
            if (stristr($line, "'".$field."' => '")) {
1804
                $data[$inc] = "    '".$field."' => '".filter_var($value, FILTER_SANITIZE_STRING)."',\n";
1805
                $bFound = true;
1806
                break;
1807
            }
1808
            $inc++;
1809
        }
1810
        if ($bFound === false) {
1811
            $data[($inc)] = "    '".$field."' => '".filter_var($value, FILTER_SANITIZE_STRING)."',\n);\n";
1812
        }
1813
    }
1814
1815
    // update file
1816
    file_put_contents($tp_config_file, implode('', isset($data) ? $data : array()));
1817
1818
    return true;
1819
}
1820
1821
/*
1822
** Permits to replace &#92; to permit correct display
1823
*/
1824
/**
1825
 * @param string $input
1826
 */
1827
function handleBackslash($input)
1828
{
1829
    return str_replace("&amp;#92;", "&#92;", $input);
1830
}
1831
1832
/*
1833
** Permits to loas settings
1834
*/
1835
function loadSettings()
1836
{
1837
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1838
1839
    /* LOAD CPASSMAN SETTINGS */
1840
    if (!isset($SETTINGS['loaded']) || $SETTINGS['loaded'] != 1) {
1841
        $SETTINGS['duplicate_folder'] = 0; //by default, this is set to 0;
1842
        $SETTINGS['duplicate_item'] = 0; //by default, this is set to 0;
1843
        $SETTINGS['number_of_used_pw'] = 5; //by default, this value is set to 5;
1844
        $settings = array();
1845
1846
        $rows = DB::query(
1847
            "SELECT * FROM ".prefix_table("misc")." WHERE type=%s_type OR type=%s_type2",
1848
            array(
1849
                'type' => "admin",
1850
                'type2' => "settings"
1851
            )
1852
        );
1853
        foreach ($rows as $record) {
1854
            if ($record['type'] == 'admin') {
1855
                $SETTINGS[$record['intitule']] = $record['valeur'];
1856
            } else {
1857
                $settings[$record['intitule']] = $record['valeur'];
1858
            }
1859
        }
1860
        $SETTINGS['loaded'] = 1;
1861
        $SETTINGS['default_session_expiration_time'] = 5;
1862
    }
1863
}
1864
1865
/*
1866
** check if folder has custom fields.
1867
** Ensure that target one also has same custom fields
1868
*/
1869
function checkCFconsistency($source_id, $target_id)
1870
{
1871
    $source_cf = array();
1872
    $rows = DB::QUERY(
1873
        "SELECT id_category
1874
        FROM ".prefix_table("categories_folders")."
1875
        WHERE id_folder = %i",
1876
        $source_id
1877
    );
1878
    foreach ($rows as $record) {
1879
        array_push($source_cf, $record['id_category']);
1880
    }
1881
1882
    $target_cf = array();
1883
    $rows = DB::QUERY(
1884
        "SELECT id_category
1885
        FROM ".prefix_table("categories_folders")."
1886
        WHERE id_folder = %i",
1887
        $target_id
1888
    );
1889
    foreach ($rows as $record) {
1890
        array_push($target_cf, $record['id_category']);
1891
    }
1892
1893
    $cf_diff = array_diff($source_cf, $target_cf);
1894
    if (count($cf_diff) > 0) {
1895
        return false;
1896
    }
1897
1898
    return true;
1899
}
1900
1901
/*
1902
*
1903
*/
1904
function encrypt_or_decrypt_file($filename_to_rework, $filename_status)
1905
{
1906
    global $server, $user, $pass, $database, $port, $encoding;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1907
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
1908
1909
    // Include librairies & connect to DB
1910
    require_once $SETTINGS['cpassman_dir'].'/includes/libraries/Database/Meekrodb/db.class.php';
1911
    $pass = defuse_return_decrypted($pass);
1912
    DB::$host = $server;
1913
    DB::$user = $user;
1914
    DB::$password = $pass;
1915
    DB::$dbName = $database;
1916
    DB::$port = $port;
1917
    DB::$encoding = $encoding;
1918
    DB::$error_handler = true;
1919
    $link = mysqli_connect($server, $user, $pass, $database, $port);
1920
    $link->set_charset($encoding);
1921
1922
    // Get file info in DB
1923
    $fileInfo = DB::queryfirstrow(
1924
        "SELECT id FROM ".prefix_table("files")." WHERE file = %s",
1925
        filter_var($filename_to_rework, FILTER_SANITIZE_STRING)
1926
    );
1927
    if (empty($fileInfo['id']) === false) {
1928
        // Load PhpEncryption library
1929
        $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
1930
        require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Crypto.php';
1931
        require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Encoding.php';
1932
        require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'DerivedKeys.php';
1933
        require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Key.php';
1934
        require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'KeyOrPassword.php';
1935
        require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'File.php';
1936
        require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'RuntimeTests.php';
1937
        require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'KeyProtectedByPassword.php';
1938
        require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Core.php';
1939
1940
        // Get KEY
1941
        $ascii_key = file_get_contents(SECUREPATH."/teampass-seckey.txt");
0 ignored issues
show
Bug introduced by
The constant SECUREPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1942
1943
        if (isset($SETTINGS['enable_attachment_encryption'])
1944
            && $SETTINGS['enable_attachment_encryption'] === "1" &&
1945
            isset($filename_status)
1946
            && ($filename_status === "clear"
1947
                || $filename_status === "0")
1948
        ) {
1949
            // File needs to be encrypted
1950
            if (file_exists($SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework)) {
1951
                // Make a copy of file
1952
                if (!copy(
1953
                    $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework,
1954
                    $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.".copy"
1955
                )) {
1956
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1957
                } else {
1958
                    // Do a bck
1959
                    copy(
1960
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework,
1961
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.".bck"
1962
                    );
1963
                }
1964
1965
                unlink($SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework);
1966
1967
                // Now encrypt the file with saltkey
1968
                $err = '';
1969
                try {
1970
                    \Defuse\Crypto\File::encryptFile(
1971
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.".copy",
1972
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework,
1973
                        \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
1974
                    );
1975
                } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
1976
                    $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.";
1977
                } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
1978
                    $err = $ex;
1979
                } catch (Defuse\Crypto\Exception\IOException $ex) {
1980
                    $err = $ex;
1981
                }
1982
                if (empty($err) === false) {
1983
                    echo $err;
1984
                }
1985
1986
                unlink($SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.".copy");
1987
1988
                // update table
1989
                DB::update(
1990
                    prefix_table('files'),
1991
                    array(
1992
                        'status' => 'encrypted'
1993
                        ),
1994
                    "id = %i",
1995
                    $fileInfo['id']
1996
                );
1997
            }
1998
        } elseif (isset($SETTINGS['enable_attachment_encryption'])
1999
            && $SETTINGS['enable_attachment_encryption'] === "0"
2000
            && isset($filename_status)
2001
            && $filename_status === "encrypted"
2002
        ) {
2003
            // file needs to be decrypted
2004
            if (file_exists($SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework)) {
2005
                // make a copy of file
2006
                if (!copy(
2007
                    $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework,
2008
                    $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.".copy"
2009
                )) {
2010
                    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
2011
                } else {
2012
                    // do a bck
2013
                    copy(
2014
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework,
2015
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.".bck"
2016
                    );
2017
                }
2018
2019
                unlink($SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework);
2020
2021
                // Now encrypt the file with saltkey
2022
                $err = '';
2023
                try {
2024
                    \Defuse\Crypto\File::decryptFile(
2025
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.".copy",
2026
                        $SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework,
2027
                        \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
2028
                    );
2029
                } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2030
                    $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.";
2031
                } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2032
                    $err = $ex;
2033
                } catch (Defuse\Crypto\Exception\IOException $ex) {
2034
                    $err = $ex;
2035
                }
2036
                if (empty($err) === false) {
2037
                    echo $err;
2038
                }
2039
2040
                unlink($SETTINGS['path_to_upload_folder'].'/'.$filename_to_rework.".copy");
2041
2042
                // update table
2043
                DB::update(
2044
                    prefix_table('files'),
2045
                    array(
2046
                        'status' => 'clear'
2047
                        ),
2048
                    "id = %i",
2049
                    $fileInfo['id']
2050
                );
2051
            }
2052
        }
2053
    }
2054
2055
    // Exit
2056
    return false;
2057
}
2058
2059
/**
2060
 * Will encrypte/decrypt a fil eusing Defuse
2061
 * @param  string $type        can be either encrypt or decrypt
2062
 * @param  string $source_file path to source file
2063
 * @param  string $target_file path to target file
2064
 * @return string|boolean
2065
 */
2066
function prepareFileWithDefuse($type, $source_file, $target_file, $password = null)
2067
{
2068
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2069
2070
    // Load AntiXSS
2071
    require_once $SETTINGS['cpassman_dir'].'/includes/libraries/protect/AntiXSS/AntiXSS.php';
2072
    $antiXss = new protect\AntiXSS\AntiXSS();
2073
2074
    // Protect against bad inputs
2075
    if (is_array($source_file) || is_array($target_file)) {
0 ignored issues
show
introduced by
The condition is_array($target_file) is always false.
Loading history...
2076
        return 'error_cannot_be_array';
2077
    }
2078
2079
    // Sanitize
2080
    $source_file = $antiXss->xss_clean($source_file);
2081
    $target_file = $antiXss->xss_clean($target_file);
2082
2083
    // load PhpEncryption library
2084
    $path_to_encryption = '/includes/libraries/Encryption/Encryption/';
2085
    require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Crypto.php';
2086
    require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Encoding.php';
2087
    require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'DerivedKeys.php';
2088
    require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Key.php';
2089
    require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'KeyOrPassword.php';
2090
    require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'File.php';
2091
    require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'RuntimeTests.php';
2092
    require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'KeyProtectedByPassword.php';
2093
    require_once $SETTINGS['cpassman_dir'].$path_to_encryption.'Core.php';
2094
2095
    if (empty($password) === true || is_null($password) === true) {
2096
        /*
2097
        File encryption/decryption is done with the SALTKEY
2098
         */
2099
2100
        // get KEY
2101
        $ascii_key = file_get_contents(SECUREPATH."/teampass-seckey.txt");
0 ignored issues
show
Bug introduced by
The constant SECUREPATH was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
2102
2103
        // Now perform action on the file
2104
        $err = '';
2105
        if ($type === 'decrypt') {
2106
            try {
2107
                \Defuse\Crypto\File::decryptFile(
2108
                    $source_file,
2109
                    $target_file,
2110
                    \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
2111
                );
2112
            } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2113
                $err = "decryption_not_possible";
2114
            } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2115
                $err = $ex;
2116
            } catch (Defuse\Crypto\Exception\IOException $ex) {
2117
                $err = $ex;
2118
            }
2119
        } elseif ($type === 'encrypt') {
2120
            try {
2121
                \Defuse\Crypto\File::encryptFile(
2122
                    $source_file,
2123
                    $target_file,
2124
                    \Defuse\Crypto\Key::loadFromAsciiSafeString($ascii_key)
2125
                );
2126
            } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2127
                $err = "encryption_not_possible";
2128
            } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2129
                $err = $ex;
2130
            } catch (Defuse\Crypto\Exception\IOException $ex) {
2131
                $err = $ex;
2132
            }
2133
        }
2134
    } else {
2135
        /*
2136
        File encryption/decryption is done with special password and not the SALTKEY
2137
         */
2138
2139
        $err = '';
2140
        if ($type === 'decrypt') {
2141
            try {
2142
                \Defuse\Crypto\File::decryptFileWithPassword(
2143
                    $source_file,
2144
                    $target_file,
2145
                    $password
2146
                );
2147
            } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2148
                $err = "wrong_key";
2149
            } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2150
                $err = $ex;
2151
            } catch (Defuse\Crypto\Exception\IOException $ex) {
2152
                $err = $ex;
2153
            }
2154
        } elseif ($type === 'encrypt') {
2155
            try {
2156
                \Defuse\Crypto\File::encryptFileWithPassword(
2157
                    $source_file,
2158
                    $target_file,
2159
                    $password
2160
                );
2161
            } catch (Defuse\Crypto\Exception\WrongKeyOrModifiedCiphertextException $ex) {
2162
                $err = "wrong_key";
2163
            } catch (Defuse\Crypto\Exception\EnvironmentIsBrokenException $ex) {
2164
                $err = $ex;
2165
            } catch (Defuse\Crypto\Exception\IOException $ex) {
2166
                $err = $ex;
2167
            }
2168
        }
2169
    }
2170
2171
    // return error
2172
    if (empty($err) === false) {
2173
        return $err;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $err also could return the type Defuse\Crypto\Exception\...o\Exception\IOException which is incompatible with the documented return type string|boolean.
Loading history...
2174
    } else {
2175
        return true;
2176
    }
2177
}
2178
2179
/*
2180
* NOT TO BE USED
2181
*/
2182
function debugTeampass($text)
2183
{
2184
    $debugFile = fopen('D:/wamp64/www/TeamPass/debug.txt', 'r+');
2185
    fputs($debugFile, $text);
0 ignored issues
show
Bug introduced by
It seems like $debugFile can also be of type false; however, parameter $handle of fputs() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

2185
    fputs(/** @scrutinizer ignore-type */ $debugFile, $text);
Loading history...
2186
    fclose($debugFile);
0 ignored issues
show
Bug introduced by
It seems like $debugFile can also be of type false; however, parameter $handle of fclose() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

2186
    fclose(/** @scrutinizer ignore-type */ $debugFile);
Loading history...
2187
}
2188
2189
2190
/**
2191
 * DELETE the file with expected command depending on server type
2192
 * @param  string $file Path to file
2193
 * @return              Nothing
0 ignored issues
show
Bug introduced by
The type Nothing was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2194
 */
2195
function fileDelete($file)
2196
{
2197
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2198
2199
    // Load AntiXSS
2200
    require_once $SETTINGS['cpassman_dir'].'/includes/libraries/protect/AntiXSS/AntiXSS.php';
2201
    $antiXss = new protect\AntiXSS\AntiXSS();
2202
2203
    $file = $antiXss->xss_clean($file);
2204
    if (is_file($file)) {
2205
        unlink($file);
2206
    }
2207
}
2208
2209
/**
2210
 * Permits to extract the file extension
2211
 *
2212
 * @param  string $file File name
2213
 * @return string
2214
 */
2215
function getFileExtension(string $file)
2216
{
2217
    if (strpos($file, '.') === false) {
2218
        return $file;
2219
    }
2220
2221
    return substr($file, strrpos($file, '.') + 1);
2222
}
2223
2224
/**
2225
 * Permits to clean and sanitize text to be displayed
2226
 * @param  string $text Text to clean
2227
 * @param  string $type What clean to perform
2228
 * @return string
2229
 */
2230
function cleanText(string $string, $type = null)
2231
{
2232
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2233
2234
    // Load AntiXSS
2235
    require_once $SETTINGS['cpassman_dir'].'/includes/libraries/protect/AntiXSS/AntiXSS.php';
2236
    $antiXss = new protect\AntiXSS\AntiXSS();
2237
2238
    if ($type === "css") {
2239
        // Escape text and quotes in UTF8 format
2240
        return htmlentities($string, ENT_QUOTES | ENT_HTML5, 'UTF-8');
2241
    } elseif (empty($type) === true || is_null($type) === true || $type === "html") {
2242
        // Html cleaner
2243
        return $antiXss->xss_clean($string);
2244
    }
2245
}
2246
2247
/**
2248
 * Performs chmod operation on subfolders
2249
 * @param  string  $dir             Parent folder
2250
 * @param  integer $dirPermissions  New permission on folders
2251
 * @param  integer $filePermissions New permission on files
2252
 * @return boolean
2253
 */
2254
function chmodRecursive($dir, $dirPermissions, $filePermissions)
2255
{
2256
    $pointer_dir = opendir($dir);
2257
    $res = true;
2258
    while ($file = readdir($pointer_dir)) {
0 ignored issues
show
Bug introduced by
It seems like $pointer_dir can also be of type false; however, parameter $dir_handle of readdir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

2258
    while ($file = readdir(/** @scrutinizer ignore-type */ $pointer_dir)) {
Loading history...
2259
        if (($file == ".") || ($file == "..")) {
2260
            continue;
2261
        }
2262
2263
        $fullPath = $dir."/".$file;
2264
2265
        if (is_dir($fullPath)) {
2266
            if ($res = @chmod($fullPath, $dirPermissions)) {
2267
                $res = @chmodRecursive($fullPath, $dirPermissions, $filePermissions);
2268
            }
2269
        } else {
2270
            $res = chmod($fullPath, $filePermissions);
2271
        }
2272
        if (!$res) {
2273
            closedir($pointer_dir);
0 ignored issues
show
Bug introduced by
It seems like $pointer_dir can also be of type false; however, parameter $dir_handle of closedir() does only seem to accept resource, maybe add an additional type check? ( Ignorable by Annotation )

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

2273
            closedir(/** @scrutinizer ignore-type */ $pointer_dir);
Loading history...
2274
            return false;
2275
        }
2276
    }
2277
    closedir($pointer_dir);
2278
    if (is_dir($dir) && $res) {
2279
        $res = @chmod($dir, $dirPermissions);
2280
    }
2281
2282
    return $res;
2283
}
2284
2285
/**
2286
 * Check if user can access to this item
2287
 * @param integer $item_id ID of item
2288
 */
2289
function accessToItemIsGranted($item_id)
2290
{
2291
    global $SETTINGS;
0 ignored issues
show
Compatibility Best Practice introduced by
Use of global functionality is not recommended; it makes your code harder to test, and less reusable.

Instead of relying on global state, we recommend one of these alternatives:

1. Pass all data via parameters

function myFunction($a, $b) {
    // Do something
}

2. Create a class that maintains your state

class MyClass {
    private $a;
    private $b;

    public function __construct($a, $b) {
        $this->a = $a;
        $this->b = $b;
    }

    public function myFunction() {
        // Do something
    }
}
Loading history...
2292
2293
    require_once $SETTINGS['cpassman_dir'].'/includes/libraries/protect/SuperGlobal/SuperGlobal.php';
2294
    $superGlobal = new protect\SuperGlobal\SuperGlobal();
2295
2296
    // Prepare superGlobal variables
2297
    $session_groupes_visibles = $superGlobal->get("groupes_visibles", "SESSION");
2298
    $session_list_restricted_folders_for_items = $superGlobal->get("list_restricted_folders_for_items", "SESSION");
2299
2300
    // Load item data
2301
    $data = DB::queryFirstRow(
2302
        "SELECT id_tree
2303
        FROM ".prefix_table("items")."
2304
        WHERE id = %i",
2305
        $item_id
2306
    );
2307
2308
    // Check if user can access this folder
2309
    if (in_array($data['id_tree'], $session_groupes_visibles) === false) {
2310
        // Now check if this folder is restricted to user
2311
        if (isset($session_list_restricted_folders_for_items[$data['id_tree']])
2312
            && !in_array($item_id, $session_list_restricted_folders_for_items[$data['id_tree']])
2313
        ) {
2314
            return "ERR_FOLDER_NOT_ALLOWED";
2315
        } else {
2316
            return "ERR_FOLDER_NOT_ALLOWED";
2317
        }
2318
    }
2319
2320
    return true;
2321
}
2322
2323
/**
2324
 * Creates a unique key
2325
 * @lenght  integer $lenght Key lenght
2326
 * @return string
2327
 */
2328
function uniqidReal($lenght = 13)
2329
{
2330
    // uniqid gives 13 chars, but you could adjust it to your needs.
2331
    if (function_exists("random_bytes")) {
2332
        $bytes = random_bytes(ceil($lenght / 2));
0 ignored issues
show
Bug introduced by
ceil($lenght / 2) of type double is incompatible with the type integer expected by parameter $length of random_bytes(). ( Ignorable by Annotation )

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

2332
        $bytes = random_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2333
    } elseif (function_exists("openssl_random_pseudo_bytes")) {
2334
        $bytes = openssl_random_pseudo_bytes(ceil($lenght / 2));
0 ignored issues
show
Bug introduced by
ceil($lenght / 2) of type double is incompatible with the type integer expected by parameter $length of openssl_random_pseudo_bytes(). ( Ignorable by Annotation )

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

2334
        $bytes = openssl_random_pseudo_bytes(/** @scrutinizer ignore-type */ ceil($lenght / 2));
Loading history...
2335
    } else {
2336
        throw new Exception("no cryptographically secure random function available");
2337
    }
2338
    return substr(bin2hex($bytes), 0, $lenght);
2339
}
2340
2341
/**
2342
 * Obfuscate an email address
2343
 * @email {string}  email address
2344
 */
2345
function obfuscate_email($email)
2346
{
2347
    $prop = 2;
2348
    $start = '';
2349
    $end = '';
2350
    $domain = substr(strrchr($email, "@"), 1);
2351
    $mailname = str_replace($domain, '', $email);
2352
    $name_l = strlen($mailname);
2353
    $domain_l = strlen($domain);
2354
    for ($i = 0; $i <= $name_l / $prop - 1; $i++) {
2355
        $start .= 'x';
2356
    }
2357
2358
    for ($i = 0; $i <= $domain_l / $prop - 1; $i++) {
2359
        $end .= 'x';
2360
    }
2361
2362
    return substr_replace($mailname, $start, 2, $name_l / $prop)
2363
        .substr_replace($domain, $end, 2, $domain_l / $prop);
2364
}
2365
2366
/**
2367
 * Permits to get LDAP information about a user
2368
 *
2369
 * @param string $username  User name
2370
 * @param string $password  User password
2371
 * @return string
2372
 */
2373
function connectLDAP($username, $password, $SETTINGS)
2374
{
2375
    $user_email = '';
2376
    $user_found = false;
2377
    $user_lastname = '';
2378
    $user_name = '';
2379
    $ldapConnection = false;
2380
2381
    // Prepare LDAP connection if set up
2382
    //Multiple Domain Names
2383
    if (strpos(html_entity_decode($username), '\\') === true) {
2384
        $ldap_suffix = "@".substr(html_entity_decode($username), 0, strpos(html_entity_decode($username), '\\'));
0 ignored issues
show
Unused Code introduced by
The assignment to $ldap_suffix is dead and can be removed.
Loading history...
2385
        $username = substr(html_entity_decode($username), strpos(html_entity_decode($username), '\\') + 1);
2386
    }
2387
    if ($SETTINGS['ldap_type'] === 'posix-search') {
2388
        $ldapURIs = "";
2389
        foreach (explode(",", $SETTINGS['ldap_domain_controler']) as $domainControler) {
2390
            if ($SETTINGS['ldap_ssl'] == 1) {
2391
                $ldapURIs .= "ldaps://".$domainControler.":".$SETTINGS['ldap_port']." ";
2392
            } else {
2393
                $ldapURIs .= "ldap://".$domainControler.":".$SETTINGS['ldap_port']." ";
2394
            }
2395
        }
2396
        $ldapconn = ldap_connect($ldapURIs);
2397
2398
        if ($SETTINGS['ldap_tls']) {
2399
            ldap_start_tls($ldapconn);
2400
        }
2401
        ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
2402
        ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
2403
2404
        // Is LDAP connection ready?
2405
        if ($ldapconn !== false) {
2406
            // Should we bind the connection?
2407
            if ($SETTINGS['ldap_bind_dn'] !== "" && $SETTINGS['ldap_bind_passwd'] !== "") {
2408
                $ldapbind = ldap_bind($ldapconn, $SETTINGS['ldap_bind_dn'], $SETTINGS['ldap_bind_passwd']);
2409
            } else {
2410
                $ldapbind = false;
2411
            }
2412
            if (($SETTINGS['ldap_bind_dn'] === "" && $SETTINGS['ldap_bind_passwd'] === "") || $ldapbind === true) {
2413
                $filter = "(&(".$SETTINGS['ldap_user_attribute']."=".$username.")(objectClass=".$SETTINGS['ldap_object_class']."))";
2414
                $result = ldap_search(
2415
                    $ldapconn,
2416
                    $SETTINGS['ldap_search_base'],
2417
                    $filter,
2418
                    array('dn', 'mail', 'givenname', 'sn', 'samaccountname')
2419
                );
2420
2421
                // Check if user was found in AD
2422
                if (ldap_count_entries($ldapconn, $result) > 0) {
2423
                    // Get user's info and especially the DN
2424
                    $result = ldap_get_entries($ldapconn, $result);
2425
                    $user_dn = $result[0]['dn'];
2426
                    $user_email = $result[0]['mail'][0];
2427
                    $user_lastname = $result[0]['sn'][0];
2428
                    $user_name = isset($result[0]['givenname'][0]) === true ? $result[0]['givenname'][0] : '';
2429
                    $user_found = true;
2430
2431
                    // Should we restrain the search in specified user groups
2432
                    $GroupRestrictionEnabled = false;
2433
                    if (isset($SETTINGS['ldap_usergroup']) === true && empty($SETTINGS['ldap_usergroup']) === false) {
2434
                        // New way to check User's group membership
2435
                        $filter_group = "memberUid=".$username;
2436
                        $result_group = ldap_search(
2437
                            $ldapconn,
2438
                            $SETTINGS['ldap_search_base'],
2439
                            $filter_group,
2440
                            array('dn', 'samaccountname')
2441
                        );
2442
2443
                        if ($result_group) {
0 ignored issues
show
introduced by
$result_group is of type resource, thus it always evaluated to false.
Loading history...
2444
                            $entries = ldap_get_entries($ldapconn, $result_group);
2445
2446
                            if ($entries['count'] > 0) {
2447
                                // Now check if group fits
2448
                                for ($i = 0; $i < $entries['count']; $i++) {
2449
                                    $parsr = ldap_explode_dn($entries[$i]['dn'], 0);
2450
                                    if (str_replace(array('CN=', 'cn='), '', $parsr[0]) === $SETTINGS['ldap_usergroup']) {
2451
                                        $GroupRestrictionEnabled = true;
2452
                                        break;
2453
                                    }
2454
                                }
2455
                            }
2456
                        }
2457
                    }
2458
2459
                    // Is user in the LDAP?
2460
                    if ($GroupRestrictionEnabled === true
2461
                        || (
2462
                            $GroupRestrictionEnabled === false
2463
                            && (
2464
                                isset($SETTINGS['ldap_usergroup']) === false
2465
                                || (isset($SETTINGS['ldap_usergroup']) === true && empty($SETTINGS['ldap_usergroup']) === true)
2466
                            )
2467
                        )
2468
                    ) {
2469
                        // Try to auth inside LDAP
2470
                        $ldapbind = ldap_bind($ldapconn, $user_dn, $password);
2471
                        if ($ldapbind === true) {
2472
                            $ldapConnection = true;
2473
                        } else {
2474
                            $ldapConnection = false;
2475
                        }
2476
                    }
2477
                } else {
2478
                    $ldapConnection = false;
2479
                }
2480
            } else {
2481
                $ldapConnection = false;
2482
            }
2483
        } else {
2484
            $ldapConnection = false;
2485
        }
2486
    } else {
2487
        $adldap = new SplClassLoader('adLDAP', '../includes/libraries/LDAP');
2488
        $adldap->register();
2489
2490
        // Posix style LDAP handles user searches a bit differently
2491
        if ($SETTINGS['ldap_type'] === 'posix') {
2492
            $ldap_suffix = ','.$SETTINGS['ldap_suffix'].','.$SETTINGS['ldap_domain_dn'];
2493
        } else {
2494
            // case where $SETTINGS['ldap_type'] equals 'windows'
0 ignored issues
show
Unused Code Comprehensibility introduced by
47% 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...
2495
            //Multiple Domain Names
2496
            $ldap_suffix = $SETTINGS['ldap_suffix'];
2497
        }
2498
2499
        // Ensure no double commas exist in ldap_suffix
2500
        $ldap_suffix = str_replace(',,', ',', $ldap_suffix);
2501
2502
        // Create LDAP connection
2503
        $adldap = new adLDAP\adLDAP(
2504
            array(
2505
                'base_dn' => $SETTINGS['ldap_domain_dn'],
2506
                'account_suffix' => $ldap_suffix,
2507
                'domain_controllers' => explode(",", $SETTINGS['ldap_domain_controler']),
2508
                'ad_port' => $SETTINGS['ldap_port'],
2509
                'use_ssl' => $SETTINGS['ldap_ssl'],
2510
                'use_tls' => $SETTINGS['ldap_tls']
2511
            )
2512
        );
2513
2514
        // OpenLDAP expects an attribute=value pair
2515
        if ($SETTINGS['ldap_type'] === 'posix') {
2516
            $auth_username = $SETTINGS['ldap_user_attribute'].'='.$username;
2517
        } else {
2518
            $auth_username = $username;
2519
        }
2520
2521
        // Authenticate the user
2522
        if ($adldap->authenticate($auth_username, html_entity_decode($password))) {
2523
            // Get user info
2524
            $result = $adldap->user()->info($auth_username, array('mail', 'givenname', 'sn'));
2525
            $user_email = $result[0]['mail'][0];
2526
            $user_lastname = $result[0]['sn'][0];
2527
            $user_name = $result[0]['givenname'][0];
2528
            $user_found = true;
2529
2530
            // Is user in allowed group
2531
            if (isset($SETTINGS['ldap_allowed_usergroup']) === true
2532
                && empty($SETTINGS['ldap_allowed_usergroup']) === false
2533
            ) {
2534
                if ($adldap->user()->inGroup($auth_username, $SETTINGS['ldap_allowed_usergroup']) === true) {
2535
                    $ldapConnection = true;
2536
                } else {
2537
                    $ldapConnection = false;
2538
                }
2539
            } else {
2540
                $ldapConnection = true;
2541
            }
2542
        } else {
2543
            $ldapConnection = false;
2544
        }
2545
    }
2546
2547
    return json_encode(
2548
        array(
2549
            'lastname' => $user_lastname,
2550
            'name' => $user_name,
2551
            'email' => $user_email,
2552
            'auth_success' => $ldapConnection,
2553
            'user_found' => $user_found
2554
        )
2555
    );
2556
}
2557