Passed
Push — devel-3.0 ( 6b6d4a...0e3c8b )
by Rubén
03:12
created

Bootstrap::initializePluginClasses()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 3
nc 2
nop 0
dl 0
loc 6
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;
26
27
use DI\Container;
28
use Klein\Klein;
29
use Klein\Response;
30
use PHPMailer\PHPMailer\Exception;
31
use Psr\Container\ContainerInterface;
32
use RuntimeException;
33
use SP\Config\Config;
34
use SP\Config\ConfigData;
35
use SP\Config\ConfigUtil;
36
use SP\Core\Exceptions\ConfigException;
37
use SP\Core\Exceptions\InitializationException;
38
use SP\Core\Language;
39
use SP\Core\PhpExtensionChecker;
40
use SP\Http\Request;
41
use SP\Modules\Api\Init as InitApi;
42
use SP\Modules\Web\Init as InitWeb;
43
use SP\Plugin\PluginManager;
44
use SP\Services\Api\ApiRequest;
45
use SP\Services\Api\JsonRpcResponse;
46
use SP\Services\Upgrade\UpgradeConfigService;
47
use SP\Services\Upgrade\UpgradeUtil;
48
use SP\Util\Checks;
49
use SP\Util\Filter;
50
use SP\Util\Version;
51
use Symfony\Component\Debug\Debug;
52
53
defined('APP_ROOT') || die();
54
55
/**
56
 * Class Bootstrap
57
 *
58
 * @package SP
59
 */
60
final class Bootstrap
61
{
62
    /**
63
     * @var string The current request path relative to the sysPass root (e.g. files/index.php)
64
     */
65
    public static $WEBROOT = '';
66
    /**
67
     * @var string The full URL to reach sysPass (e.g. https://sub.example.com/syspass/)
68
     */
69
    public static $WEBURI = '';
70
    /**
71
     * @var string
72
     */
73
    public static $SUBURI = '';
74
    /**
75
     * @var mixed
76
     */
77
    public static $LOCK;
78
    /**
79
     * @var bool Indica si la versión de PHP es correcta
80
     */
81
    public static $checkPhpVersion;
82
    /**
83
     * @var ContainerInterface
84
     */
85
    private static $container;
86
    /**
87
     * @var Klein
88
     */
89
    private $router;
90
    /**
91
     * @var Language
92
     */
93
    private $language;
94
    /**
95
     * @var Request
96
     */
97
    private $request;
98
    /**
99
     * @var Config
100
     */
101
    private $config;
102
    /**
103
     * @var ConfigData
104
     */
105
    private $configData;
106
107
    /**
108
     * Bootstrap constructor.
109
     *
110
     * @param Container $container
111
     *
112
     * @throws \DI\DependencyException
113
     * @throws \DI\NotFoundException
114
     */
115
    private final function __construct(Container $container)
116
    {
117
        self::$container = $container;
118
119
        // Set the default language
120
        Language::setLocales('en_US');
121
122
        $this->config = $container->get(Config::class);
123
        $this->configData = $this->config->getConfigData();
124
        $this->router = $container->get(Klein::class);
125
        $this->request = $container->get(Request::class);
126
        $this->language = $container->get(Language::class);
127
128
        $this->initRouter();
129
    }
130
131
    /**
132
     * Inicializar router
133
     */
134
    protected function initRouter()
135
    {
136
        $oops = "Oops, it looks like this content does not exist...";
137
138
        $this->router->onError(function ($router, $err_msg, $type, $err) {
139
            logger('Routing error: ' . $err_msg);
140
141
            /** @var Exception|\Throwable $err */
142
            logger('Routing error: ' . $err->getTraceAsString());
143
144
            /** @var Klein $router */
145
            $router->response()->body(__($err_msg));
146
        });
147
148
        // Manage requests for api module
149
        $this->router->respond(['POST'],
150
            '@/api\.php',
151
            function ($request, $response, $service) use ($oops) {
152
                try {
153
                    $apiRequest = self::$container->get(ApiRequest::class);
154
155
                    list($controller, $action) = explode('/', $apiRequest->getMethod());
156
157
                    $controllerClass = 'SP\\Modules\\' . ucfirst(APP_MODULE) . '\\Controllers\\' . ucfirst($controller) . 'Controller';
158
                    $method = $action . 'Action';
159
160
                    if (!method_exists($controllerClass, $method)) {
161
                        logger($controllerClass . '::' . $method);
162
163
                        /** @var Response $response */
164
                        $response->headers()->set('Content-type', 'application/json; charset=utf-8');
165
                        return $response->body(JsonRpcResponse::getResponseError($oops, JsonRpcResponse::METHOD_NOT_FOUND, $apiRequest->getId()));
166
                    }
167
168
                    $this->initializeCommon();
169
170
                    self::$container->get(InitApi::class)
171
                        ->initialize($controller);
172
173
                    logger('Routing call: ' . $controllerClass . '::' . $method);
174
175
                    return call_user_func([new $controllerClass(self::$container, $method, $apiRequest), $method]);
176
                } catch (\Exception $e) {
177
                    processException($e);
178
179
                    /** @var Response $response */
180
                    $response->headers()->set('Content-type', 'application/json; charset=utf-8');
181
                    return $response->body(JsonRpcResponse::getResponseException($e, 0));
182
183
                }
184
            }
185
        );
186
187
        // Manage requests for web module
188
        $this->router->respond(['GET', 'POST'],
189
            '@/index\.php',
190
            function ($request, $response, $service) use ($oops) {
191
                try {
192
                    /** @var \Klein\Request $request */
193
                    $route = Filter::getString($request->param('r', 'index/index'));
194
195
                    if (!preg_match_all('#(?P<controller>[a-zA-Z]+)(?:/(?P<action>[a-zA-Z]+))?(?P<params>(/[a-zA-Z\d\.]+)+)?#', $route, $matches)) {
196
                        throw new RuntimeException($oops);
197
                    }
198
199
//                    $app = $matches['app'][0] ?: 'web';
200
                    $controller = $matches['controller'][0];
201
                    $method = !empty($matches['action'][0]) ? $matches['action'][0] . 'Action' : 'indexAction';
202
                    $params = !empty($matches['params'][0]) ? Filter::getArray(explode('/', trim($matches['params'][0], '/'))) : [];
203
204
                    $controllerClass = 'SP\\Modules\\' . ucfirst(APP_MODULE) . '\\Controllers\\' . ucfirst($controller) . 'Controller';
205
206
                    $this->initializePluginClasses();
207
208
                    if (!method_exists($controllerClass, $method)) {
209
                        logger($controllerClass . '::' . $method);
210
211
                        /** @var Response $response */
212
                        $response->code(404);
213
214
                        throw new RuntimeException($oops);
215
                    }
216
217
                    $this->initializeCommon();
218
219
                    switch (APP_MODULE) {
220
                        case 'web':
221
                            self::$container->get(InitWeb::class)
222
                                ->initialize($controller);
223
                            break;
224
                    }
225
226
                    logger('Routing call: ' . $controllerClass . '::' . $method . '::' . print_r($params, true));
227
228
                    return call_user_func_array([new $controllerClass(self::$container, $method), $method], $params);
229
                } catch (\Exception $e) {
230
                    processException($e);
231
232
                    /** @var Response $response */
233
                    if ($response->status()->getCode() !== 404) {
234
                        $response->code(503);
235
                    }
236
237
                    return __($e->getMessage());
238
                }
239
            }
240
        );
241
242
        // Manejar URLs que no empiecen por '/admin'
243
//        $this->Router->respond('GET', '!@^/(admin|public|service)',
244
//            function ($request, $response) {
245
//                /** @var Response $response */
246
//                $response->redirect('index.php');
247
//            }
248
//        );
249
    }
250
251
    /**
252
     * @throws ConfigException
253
     * @throws Core\Exceptions\CheckException
254
     * @throws InitializationException
255
     * @throws Services\Upgrade\UpgradeException
256
     * @throws \DI\DependencyException
257
     * @throws \DI\NotFoundException
258
     */
259
    protected function initializeCommon()
260
    {
261
        logger(__FUNCTION__);
262
263
        self::$checkPhpVersion = Checks::checkPhpVersion();
264
265
        // Initialize authentication variables
266
        $this->initAuthVariables();
267
268
        // Initialize logging
269
        $this->initPHPVars();
270
271
        // Set application paths
272
        $this->initPaths();
273
274
        self::$container->get(PhpExtensionChecker::class)->checkMandatory();
275
276
        if (!self::$checkPhpVersion) {
277
            throw new InitializationException(
278
                sprintf(__('Versión de PHP requerida >= %s <= %s'), '7.0', '7.2'),
279
                InitializationException::ERROR,
280
                __u('Actualice la versión de PHP para que la aplicación funcione correctamente')
281
            );
282
        }
283
284
        // Check and intitialize configuration
285
        $this->initConfig();
286
    }
287
288
    /**
289
     * Establecer variables de autentificación
290
     */
291
    private function initAuthVariables()
292
    {
293
        $server = $this->router->request()->server();
294
295
        // Copiar la cabecera http de autentificación para apache+php-fcgid
296
        if ($server->get('HTTP_XAUTHORIZATION') !== null
297
            && $server->get('HTTP_AUTHORIZATION') === null) {
298
            $server->set('HTTP_AUTHORIZATION', $server->get('HTTP_XAUTHORIZATION'));
299
        }
300
301
        // Establecer las cabeceras de autentificación para apache+php-cgi
302
        // Establecer las cabeceras de autentificación para que apache+php-cgi funcione si la variable es renombrada por apache
303
        if (($server->get('HTTP_AUTHORIZATION') !== null
304
                && preg_match('/Basic\s+(.*)$/i', $server->get('HTTP_AUTHORIZATION'), $matches))
305
            || ($server->get('REDIRECT_HTTP_AUTHORIZATION') !== null
306
                && preg_match('/Basic\s+(.*)$/i', $server->get('REDIRECT_HTTP_AUTHORIZATION'), $matches))
307
        ) {
308
            list($name, $password) = explode(':', base64_decode($matches[1]), 2);
309
            $server->set('PHP_AUTH_USER', strip_tags($name));
310
            $server->set('PHP_AUTH_PW', strip_tags($password));
311
        }
312
    }
313
314
    /**
315
     * Establecer el nivel de logging
316
     */
317
    public function initPHPVars()
318
    {
319
        // Set debug mode if an Xdebug session is active
320
        if ($this->router->request()->cookies()->get('XDEBUG_SESSION')
321
            && !defined('DEBUG')
322
        ) {
323
            define('DEBUG', true);
324
        }
325
326
        if (defined('DEBUG') && DEBUG) {
327
            Debug::enable();
328
        } else {
329
            error_reporting(E_ALL & ~(E_DEPRECATED | E_STRICT | E_NOTICE));
330
            ini_set('display_errors', 'Off');
331
        }
332
333
        if (!file_exists(LOG_FILE)
334
            && touch(LOG_FILE)
335
            && chmod(LOG_FILE, 0600)
336
        ) {
337
            logger('Setup log file: ' . LOG_FILE);
338
        }
339
340
        if (date_default_timezone_get() === 'UTC') {
341
            date_default_timezone_set('UTC');
342
        }
343
344
        // Avoid PHP session cookies from JavaScript
345
        ini_set('session.cookie_httponly', '1');
346
        ini_set('session.save_handler', 'files');
347
    }
348
349
    /**
350
     * Establecer las rutas de la aplicación.
351
     * Esta función establece las rutas del sistema de archivos y web de la aplicación.
352
     * La variables de clase definidas son $SERVERROOT, $WEBROOT y $SUBURI
353
     */
354
    private function initPaths()
355
    {
356
        self::$SUBURI = str_replace("\\", '/', substr(realpath($this->request->getServer('SCRIPT_FILENAME')), strlen(APP_ROOT)));
357
358
        $scriptName = $this->request->getServer('REQUEST_URI');
359
360
        if (substr($scriptName, -1) === '/') {
361
            $scriptName .= 'index.php';
362
363
            // Asegurar que suburi sigue las mismas reglas que scriptName
364
            if (substr(self::$SUBURI, -9) !== 'index.php') {
365
                if (substr(self::$SUBURI, -1) !== '/') {
366
                    self::$SUBURI .= '/';
367
                }
368
                self::$SUBURI .= 'index.php';
369
            }
370
        }
371
372
        if (($pos = strpos($scriptName, self::$SUBURI)) === false) {
373
            $pos = strpos($scriptName, '?');
374
        }
375
376
        self::$WEBROOT = substr($scriptName, 0, $pos);
377
378
        if (self::$WEBROOT !== '' && self::$WEBROOT[0] !== '/') {
379
            self::$WEBROOT = '/' . self::$WEBROOT;
380
        }
381
382
        self::$WEBURI = $this->request->getHttpHost() . self::$WEBROOT;
383
    }
384
385
    /**
386
     * Cargar la configuración
387
     *
388
     * @throws ConfigException
389
     * @throws Services\Upgrade\UpgradeException
390
     * @throws \DI\DependencyException
391
     * @throws \DI\NotFoundException
392
     */
393
    private function initConfig()
394
    {
395
        $this->checkConfigVersion();
396
397
        ConfigUtil::checkConfigDir();
398
    }
399
400
    /**
401
     * Comprobar la versión de configuración y actualizarla
402
     *
403
     * @throws Services\Upgrade\UpgradeException
404
     * @throws \DI\DependencyException
405
     * @throws \DI\NotFoundException
406
     */
407
    private function checkConfigVersion()
408
    {
409
        if (file_exists(OLD_CONFIG_FILE)) {
410
            $upgradeConfigService = self::$container->get(UpgradeConfigService::class);
411
            $upgradeConfigService->upgradeOldConfigFile(Version::getVersionStringNormalized());
412
        }
413
414
        $configVersion = UpgradeUtil::fixVersionNumber($this->configData->getConfigVersion());
415
416
        if ($this->configData->isInstalled()
417
            && UpgradeConfigService::needsUpgrade($configVersion)
418
        ) {
419
            $upgradeConfigService = self::$container->get(UpgradeConfigService::class);
420
            $upgradeConfigService->upgrade($configVersion, $this->configData);
421
        }
422
    }
423
424
    /**
425
     * initializePluginClasses
426
     */
427
    protected function initializePluginClasses()
428
    {
429
        $loader = require APP_ROOT . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
430
431
        foreach (PluginManager::getPlugins() as $name => $base) {
432
            $loader->addPsr4($base['namespace'], $base['dir']);
433
        }
434
    }
435
436
    /**
437
     * @return ContainerInterface
438
     */
439
    public static function getContainer()
440
    {
441
        return self::$container;
442
    }
443
444
    /**
445
     * @param Container $container
446
     * @param string    $module
447
     *
448
     * @throws InitializationException
449
     * @throws \DI\DependencyException
450
     * @throws \DI\NotFoundException
451
     */
452
    public static function run(Container $container, $module = APP_MODULE)
453
    {
454
        $bs = new static($container);
455
456
        switch ($module) {
457
            case 'web':
458
                logger('------------');
459
                logger('Boostrap:web');
460
461
                $bs->router->dispatch($bs->request->getRequest());
462
                break;
463
            case 'api':
464
                logger('------------');
465
                logger('Boostrap:api');
466
467
                $bs->router->dispatch($bs->request->getRequest());
468
                break;
469
            default;
470
                throw new InitializationException('Unknown module');
471
        }
472
    }
473
}