Passed
Push — devel-3.0 ( 218a48...a00b1f )
by Rubén
03:54
created

Util::getVersionString()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * sysPass
4
 *
5
 * @author    nuxsmin
6
 * @link      https://syspass.org
7
 * @copyright 2012-2018, Rubén Domínguez nuxsmin@$syspass.org
8
 *
9
 * This file is part of sysPass.
10
 *
11
 * sysPass is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by
13
 * the Free Software Foundation, either version 3 of the License, or
14
 * (at your option) any later version.
15
 *
16
 * sysPass is distributed in the hope that it will be useful,
17
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
 * GNU General Public License for more details.
20
 *
21
 * You should have received a copy of the GNU General Public License
22
 *  along with sysPass.  If not, see <http://www.gnu.org/licenses/>.
23
 */
24
25
namespace SP\Util;
26
27
use Defuse\Crypto\Core;
28
use Defuse\Crypto\Encoding;
29
use SP\Bootstrap;
30
use SP\Config\ConfigData;
31
use SP\Core\Exceptions\SPException;
32
use SP\Core\PhpExtensionChecker;
33
34
defined('APP_ROOT') || die();
35
36
/**
37
 * Clase con utilizades para la aplicación
38
 */
39
final class Util
40
{
41
    /**
42
     * Generar una clave aleatoria
43
     *
44
     * @param int  $length     Longitud de la clave
45
     * @param bool $useNumbers Usar números
46
     * @param bool $useSpecial Usar carácteres especiales
47
     * @param bool $checKStrength
48
     *
49
     * @return string
50
     */
51
    public static function randomPassword($length = 16, $useNumbers = true, $useSpecial = true, $checKStrength = true)
52
    {
53
        $charsLower = 'abcdefghijklmnopqrstuwxyz';
54
        $charsUpper = 'ABCDEFGHIJKLMNOPQRSTUWXYZ';
55
56
        $alphabet = $charsLower . $charsUpper;
57
58
        if ($useSpecial === true) {
59
            $charsSpecial = '@$%&/()!_:.;{}^';
60
            $alphabet .= $charsSpecial;
61
        }
62
63
        if ($useNumbers === true) {
64
            $charsNumbers = '0123456789';
65
            $alphabet .= $charsNumbers;
66
        }
67
68
        /**
69
         * @return array
70
         */
71
        $passGen = function () use ($alphabet, $length) {
72
            $pass = [];
73
            $alphaLength = strlen($alphabet) - 1; //put the length -1 in cache
74
75
            for ($i = 0; $i < $length; $i++) {
76
                $n = mt_rand(0, $alphaLength);
77
                $pass[] = $alphabet[$n];
78
            }
79
80
            return $pass;
81
        };
82
83
        if ($checKStrength === true) {
84
            do {
85
                $pass = $passGen();
86
                $strength = ['lower' => 0, 'upper' => 0, 'special' => 0, 'number' => 0];
87
88
                foreach ($pass as $char) {
89
                    if (strpos($charsLower, $char) !== false) {
90
                        $strength['lower']++;
91
                    } elseif (strpos($charsUpper, $char) !== false) {
92
                        $strength['upper']++;
93
                    } elseif ($useSpecial === true && strpos($charsSpecial, $char) !== false) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $charsSpecial does not seem to be defined for all execution paths leading up to this point.
Loading history...
94
                        $strength['special']++;
95
                    } elseif ($useNumbers === true && strpos($charsNumbers, $char) !== false) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $charsNumbers does not seem to be defined for all execution paths leading up to this point.
Loading history...
96
                        $strength['number']++;
97
                    }
98
                }
99
100
                if ($useSpecial === false) {
101
                    unset($strength['special']);
102
                }
103
104
                if ($useNumbers === false) {
105
                    unset($strength['number']);
106
                }
107
            } while (in_array(0, $strength, true));
108
109
            return implode($pass);
110
        }
111
112
        return implode($passGen());
113
    }
114
115
    /**
116
     * Generar una cadena aleatoria usuando criptografía.
117
     *
118
     * @param int $length opcional, con la longitud de la cadena
119
     *
120
     * @return string
121
     * @throws \Defuse\Crypto\Exception\EnvironmentIsBrokenException
122
     */
123
    public static function generateRandomBytes($length = 30)
124
    {
125
        return Encoding::binToHex(Core::secureRandom($length));
126
    }
127
128
    /**
129
     * Obtener datos desde una URL usando CURL
130
     *
131
     * @param string    $url
132
     * @param array     $data
133
     * @param bool|null $useCookie
134
     * @param bool      $weak
135
     *
136
     * @return bool|string
137
     * @throws \Psr\Container\NotFoundExceptionInterface
138
     * @throws \Psr\Container\ContainerExceptionInterface
139
     *
140
     * TODO: Use Guzzle
141
     *
142
     * @throws \SP\Core\Exceptions\CheckException
143
     * @throws SPException
144
     */
145
    public static function getDataFromUrl($url, array $data = null, $useCookie = false, $weak = false)
146
    {
147
        /** @var ConfigData $ConfigData */
148
        $ConfigData = Bootstrap::getContainer()->get(ConfigData::class);
149
150
        Bootstrap::getContainer()->get(PhpExtensionChecker::class)->checkCurlAvailable(true);
151
152
        $ch = curl_init($url);
153
154
        if ($ConfigData->isProxyEnabled()) {
155
            curl_setopt($ch, CURLOPT_PROXY, $ConfigData->getProxyServer());
156
            curl_setopt($ch, CURLOPT_PROXYPORT, $ConfigData->getProxyPort());
157
            curl_setopt($ch, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
158
159
            $proxyUser = $ConfigData->getProxyUser();
160
161
            if ($proxyUser) {
162
                $proxyAuth = $proxyUser . ':' . $ConfigData->getProxyPass();
163
                curl_setopt($ch, CURLOPT_PROXYUSERPWD, $proxyAuth);
164
            }
165
        }
166
167
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
168
        curl_setopt($ch, CURLOPT_HEADER, false);
169
        curl_setopt($ch, CURLINFO_HEADER_OUT, true);
170
        curl_setopt($ch, CURLOPT_USERAGENT, 'sysPass-App');
171
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
172
        curl_setopt($ch, CURLOPT_TIMEOUT, 60);
173
174
        if ($weak === true) {
175
            // Trust SSL enabled server
176
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
177
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
178
        }
179
180
        if (null !== $data) {
181
            curl_setopt($ch, CURLOPT_POST, 1);
182
            curl_setopt($ch, CURLOPT_HTTPHEADER, $data['type']);
183
            curl_setopt($ch, CURLOPT_POSTFIELDS, $data['data']);
184
        }
185
186
        if ($useCookie) {
187
            $cookie = self::getUserCookieFile();
188
189
            if ($cookie) {
190
                if (!SessionFactory::getCurlCookieSession()) {
0 ignored issues
show
Bug introduced by
The type SP\Util\SessionFactory 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...
191
                    curl_setopt($ch, CURLOPT_COOKIESESSION, true);
192
                    curl_setopt($ch, CURLOPT_COOKIEJAR, $cookie);
193
194
                    SessionFactory::setCurlCookieSession(true);
195
                }
196
197
                curl_setopt($ch, CURLOPT_COOKIEFILE, $cookie);
198
            }
199
        }
200
201
        $data = curl_exec($ch);
202
203
        $httpStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
204
205
        if ($data === false || $httpStatus !== 200) {
206
            throw new SPException(curl_error($ch), SPException::WARNING);
207
        }
208
209
        return $data;
210
    }
211
212
    /**
213
     * Devuelve el nombre de archivo a utilizar para las cookies del usuario
214
     *
215
     * @return string|false
216
     */
217
    public static function getUserCookieFile()
218
    {
219
        $tempDir = self::getTempDir();
220
221
        return $tempDir ? $tempDir . DIRECTORY_SEPARATOR . md5('syspass-' . SessionFactory::getUserData()->getLogin()) : false;
0 ignored issues
show
Bug introduced by
Are you sure $tempDir of type string|true can be used in concatenation? ( Ignorable by Annotation )

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

221
        return $tempDir ? /** @scrutinizer ignore-type */ $tempDir . DIRECTORY_SEPARATOR . md5('syspass-' . SessionFactory::getUserData()->getLogin()) : false;
Loading history...
222
    }
223
224
    /**
225
     * Comprueba y devuelve un directorio temporal válido
226
     *
227
     * @return bool|string
228
     */
229
    public static function getTempDir()
230
    {
231
        $sysTmp = sys_get_temp_dir();
232
        $appTmp = APP_PATH . DIRECTORY_SEPARATOR . 'temp';
233
        $file = 'syspass.test';
234
235
        $checkDir = function ($dir) use ($file) {
236
            if (file_exists($dir . DIRECTORY_SEPARATOR . $file)) {
237
                return $dir;
238
            }
239
240
            if (is_dir($dir) || @mkdir($dir)) {
241
                if (touch($dir . DIRECTORY_SEPARATOR . $file)) {
242
                    return $dir;
243
                }
244
            }
245
246
            return false;
247
        };
248
249
        if ($checkDir($appTmp)) {
250
            return $appTmp;
251
        }
252
253
        return $checkDir($sysTmp);
254
    }
255
256
    /**
257
     * Devuelve información sobre la aplicación.
258
     *
259
     * @param string $index con la key a devolver
260
     *
261
     * @return array|string con las propiedades de la aplicación
262
     */
263
    public static function getAppInfo($index = null)
264
    {
265
        $appinfo = [
266
            'appname' => 'sysPass',
267
            'appdesc' => 'Systems Password Manager',
268
            'appalias' => 'SPM',
269
            'appwebsite' => 'https://www.syspass.org',
270
            'appblog' => 'https://www.cygnux.org',
271
            'appdoc' => 'https://doc.syspass.org',
272
            'appupdates' => 'https://api.github.com/repos/nuxsmin/sysPass/releases/latest',
273
            'appnotices' => 'https://api.github.com/repos/nuxsmin/sysPass/issues?milestone=none&state=open&labels=Notices',
274
            'apphelp' => 'https://github.com/nuxsmin/sysPass/issues',
275
            'appchangelog' => 'https://github.com/nuxsmin/sysPass/blob/master/CHANGELOG'
276
        ];
277
278
        if (null !== $index && isset($appinfo[$index])) {
279
            return $appinfo[$index];
280
        }
281
282
        return $appinfo;
283
    }
284
285
    /**
286
     * Realiza el proceso de logout.
287
     *
288
     * FIXME
289
     */
290
    public static function logout()
291
    {
292
        exit('<script>sysPassApp.actions().main.logout();</script>');
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...
293
    }
294
295
    /**
296
     * Obtener el tamaño máximo de subida de PHP.
297
     */
298
    public static function getMaxUpload()
299
    {
300
        $max_upload = (int)ini_get('upload_max_filesize');
301
        $max_post = (int)ini_get('post_max_size');
302
        $memory_limit = (int)ini_get('memory_limit');
303
304
        return min($max_upload, $max_post, $memory_limit);
305
    }
306
307
    /**
308
     * Checks a variable to see if it should be considered a boolean true or false.
309
     * Also takes into account some text-based representations of true of false,
310
     * such as 'false','N','yes','on','off', etc.
311
     *
312
     * @author Samuel Levy <[email protected]>
313
     *
314
     * @param mixed $in     The variable to check
315
     * @param bool  $strict If set to false, consider everything that is not false to
316
     *                      be true.
317
     *
318
     * @return bool The boolean equivalent or null (if strict, and no exact equivalent)
319
     */
320
    public static function boolval($in, $strict = false)
321
    {
322
        $in = is_string($in) ? strtolower($in) : $in;
323
324
        // if not strict, we only have to check if something is false
325
        if (in_array($in, ['false', 'no', 'n', '0', 'off', false, 0], true) || !$in) {
326
            return false;
327
        }
328
329
        if ($strict && in_array($in, ['true', 'yes', 'y', '1', 'on', true, 1], true)) {
330
            return true;
331
        }
332
333
        // not strict? let the regular php bool check figure it out (will
334
        // largely default to true)
335
        return ($in ? true : false);
336
    }
337
338
    /**
339
     * Cast an object to another class, keeping the properties, but changing the methods
340
     *
341
     * @param string        $dstClass Class name
342
     * @param string|object $serialized
343
     * @param string        $srcClass Nombre de la clase serializada
344
     *
345
     * @return mixed
346
     * @link http://blog.jasny.net/articles/a-dark-corner-of-php-class-casting/
347
     */
348
    public static function unserialize($dstClass, $serialized, $srcClass = null)
349
    {
350
        if (!is_object($serialized)) {
351
            preg_match('/^O:\d+:"(?P<class>[^"]++)"/', $serialized, $matches);
352
353
            if (class_exists($matches['class'])
354
                && $matches['class'] === $dstClass
355
            ) {
356
                return unserialize($serialized);
357
            }
358
359
            // Si se indica la clase origen, se elimina el nombre
360
            // de la clase en los métodos privados
361
            if ($srcClass !== null) {
362
                $serializedOut = preg_replace_callback(
363
                    '/:\d+:"\x00' . preg_quote($srcClass, '/') . '\x00(\w+)"/',
364
                    function ($matches) {
365
                        return ':' . strlen($matches[1]) . ':"' . $matches[1] . '"';
366
                    },
367
                    $serialized);
368
369
                return self::castToClass($serializedOut, $dstClass);
370
            }
371
372
            if ($matches['class'] !== $dstClass) {
373
                return self::castToClass($serialized, $dstClass);
374
            }
375
        }
376
377
        return $serialized;
378
    }
379
380
    /**
381
     * Cast an object to another class
382
     *
383
     * @param $object
384
     * @param $class
385
     *
386
     * @return mixed
387
     */
388
    public static function castToClass($object, $class)
389
    {
390
        // should avoid '__PHP_Incomplete_Class'?
391
392
        if (is_object($object)) {
393
            return unserialize(preg_replace('/^O:\d+:"[^"]++"/', 'O:' . strlen($class) . ':"' . $class . '"', serialize($object)));
394
        }
395
396
        return unserialize(preg_replace('/^O:\d+:"[^"]++"/', 'O:' . strlen($class) . ':"' . $class . '"', $object));
397
    }
398
399
    /**
400
     * Bloquear la aplicación
401
     *
402
     * @param int    $userId
403
     * @param string $subject
404
     *
405
     * @return bool
406
     */
407
    public static function lockApp($userId, $subject)
408
    {
409
        $data = ['time' => time(), 'userId' => (int)$userId, 'subject' => $subject];
410
411
        return file_put_contents(LOCK_FILE, json_encode($data));
0 ignored issues
show
Bug Best Practice introduced by
The expression return file_put_contents...LE, json_encode($data)) returns the type integer which is incompatible with the documented return type boolean.
Loading history...
412
    }
413
414
    /**
415
     * Desbloquear la aplicación
416
     *
417
     * @return bool
418
     */
419
    public static function unlockApp()
420
    {
421
        return @unlink(LOCK_FILE);
422
    }
423
424
    /**
425
     * Comprueba si la aplicación está bloqueada
426
     *
427
     * @return int
428
     */
429
    public static function getAppLock()
430
    {
431
        if (file_exists(LOCK_FILE)
432
            && ($data = file_get_contents(LOCK_FILE)) !== false
433
        ) {
434
            return json_decode($data) ?: false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return json_decode($data) ?: false could also return false which is incompatible with the documented return type integer. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
435
        }
436
437
        return false;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
438
    }
439
440
    /**
441
     * Devolver el tiempo aproximado en segundos de una operación
442
     *
443
     * @param $startTime
444
     * @param $numItems
445
     * @param $totalItems
446
     *
447
     * @return array Con el tiempo estimado y los elementos por segundo
448
     */
449
    public static function getETA($startTime, $numItems, $totalItems)
450
    {
451
        if ($numItems > 0 && $totalItems > 0) {
452
            $runtime = time() - $startTime;
453
            $eta = (int)((($totalItems * $runtime) / $numItems) - $runtime);
454
455
            return [$eta, $numItems / $runtime];
456
        }
457
458
        return [0, 0];
459
    }
460
}
461