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