xref: /webtrees/index.php (revision 04a1fb9a2dda0ec2222e0264c283aee03fe6e3f1)
1<?php
2/**
3 * webtrees: online genealogy
4 * Copyright (C) 2018 webtrees development team
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 */
16declare(strict_types=1);
17
18namespace Fisharebest\Webtrees;
19
20use Fisharebest\Webtrees\Http\Controllers\ErrorController;
21use Symfony\Component\HttpFoundation\JsonResponse;
22use Symfony\Component\HttpFoundation\RedirectResponse;
23use Symfony\Component\HttpFoundation\Request;
24use Symfony\Component\HttpFoundation\Response;
25use Symfony\Component\HttpKernel\Exception\HttpExceptionInterface;
26use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
27use Throwable;
28use Whoops\Handler\PrettyPageHandler;
29use Whoops\Run;
30
31// Bootstrap the application
32require 'includes/session.php';
33
34DebugBar::startMeasure('routing');
35
36// The HTTP request.
37$request = Request::createFromGlobals();
38$method  = $request->getMethod();
39$route   = $request->get('route');
40
41try {
42	// Most requests will need the current tree and user.
43	$all_tree_names     = array_keys(Tree::getNameList());
44	$first_tree_name    = current($all_tree_names) ?? '';
45	$previous_tree_name = Session::get('GEDCOM', $first_tree_name);
46	$default_tree_name  = $previous_tree_name ?: Site::getPreference('DEFAULT_GEDCOM');
47	$tree_name          = $request->get('ged', $default_tree_name);
48	$tree               = Tree::findByName($tree_name);
49	Session::put('GEDCOM', $tree_name);
50
51	$request->attributes->set('tree', $tree);
52	$request->attributes->set('user', AUth::user());
53
54	// Load the routing table.
55	$routes = require 'routes/web.php';
56
57	// Find the action for the selected route
58	$controller_action = $routes[$method . ':' . $route] ?? 'ErrorController@noRouteFound';
59
60	DebugBar::stopMeasure('routing');
61
62	// Create the controller
63	DebugBar::startMeasure('create controller');
64
65	list($controller_name, $action) = explode('@', $controller_action);
66	$controller_class = __NAMESPACE__ . '\\Http\\Controllers\\' . $controller_name;
67	$controller = new $controller_class;
68
69	DebugBar::stopMeasure('create controller');
70
71	// Note that we can't stop this timer, as running the action will
72	// generate the response - which includes (and stops) the timer
73	DebugBar::startMeasure('controller_action', $controller_action);
74
75	if ($method === 'POST') {
76		// POST requests need a valid CSRF token
77		$csrf_token = $request->get('csrf', $request->headers->get('HTTP_X_CSRF_TOKEN'));
78		if ($csrf_token !== Filter::getCsrfToken()) {
79			throw new UnauthorizedHttpException(I18N::translate('This form has expired. Try again.'));
80		}
81
82		// Wrap POST requests in a database transaction.
83		Database::beginTransaction();
84		$response = call_user_func([$controller, $action], $request);
85		Database::commit();
86	} else {
87		$response = call_user_func([$controller, $action], $request);
88	}
89} catch (Throwable $ex) {
90	if ($method === 'POST') {
91		Database::rollBack();
92	}
93	DebugBar::addThrowable($ex);
94
95	// Clear any buffered output.
96	while (ob_get_level() > 0) {
97		ob_end_clean();
98	}
99
100	if ($ex instanceof HttpExceptionInterface) {
101		// Show a friendly page for expected exceptions.
102		if ($request->isXmlHttpRequest()) {
103			$response = new Response($ex->getMessage(), $ex->getStatusCode());
104		} else {
105			$controller = new ErrorController;
106			$response   = $controller->errorResponse($ex->getMessage());
107		}
108	} else {
109		// Show an error page for unexpected exceptions.
110		if ($request->server->get('SERVER_ADDR') !== $request->server->get('REMOTE_ADDR')) {
111			// Running locally?  Show full debug.
112			$whoops = new Run;
113			$whoops->pushHandler(new PrettyPageHandler);
114			$whoops->handleException($ex);
115		} else {
116			// Running remotely?  Show a friendly error page.
117			$controller = new ErrorController;
118			$response   = $controller->unhandledExceptionResponse($request, $ex);
119		}
120	}
121}
122
123// Send response
124if ($response instanceof RedirectResponse) {
125	// Show the debug data on the next page
126	DebugBar::stackData();
127} elseif ($response instanceof JsonResponse) {
128	// Use HTTP headers and some jQuery to add debug to the current page.
129	DebugBar::sendDataInHeaders();
130}
131
132return $response->prepare($request)->send();
133