xref: /webtrees/app/Http/Middleware/UseDatabase.php (revision 4c8fd9a21a2f83b2d6f6ff708f3d81a9938d02f3)
1<?php
2
3/**
4 * webtrees: online genealogy
5 * Copyright (C) 2023 webtrees development team
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <https://www.gnu.org/licenses/>.
16 */
17
18declare(strict_types=1);
19
20namespace Fisharebest\Webtrees\Http\Middleware;
21
22use Fisharebest\Webtrees\DB;
23use Fisharebest\Webtrees\Validator;
24use Fisharebest\Webtrees\Webtrees;
25use PDO;
26use PDOException;
27use Psr\Http\Message\ResponseInterface;
28use Psr\Http\Message\ServerRequestInterface;
29use Psr\Http\Server\MiddlewareInterface;
30use Psr\Http\Server\RequestHandlerInterface;
31use RuntimeException;
32
33/**
34 * Middleware to connect to the database.
35 */
36class UseDatabase implements MiddlewareInterface
37{
38    /**
39     * @param ServerRequestInterface  $request
40     * @param RequestHandlerInterface $handler
41     *
42     * @return ResponseInterface
43     */
44    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
45    {
46        // Earlier versions of webtrees did not have a dbtype config option.  They always used mysql.
47        $driver = Validator::attributes($request)->string('dbtype', 'mysql');
48
49        $dbname = Validator::attributes($request)->string('dbname');
50
51        if ($driver === 'sqlite') {
52            $dbname = Webtrees::ROOT_DIR . 'data/' . $dbname . '.sqlite';
53        }
54
55        $capsule = new DB();
56
57        // Newer versions of webtrees support utf8mb4.  Older ones only support 3-byte utf8
58        if ($driver === 'mysql' && Validator::attributes($request)->boolean('mysql_utf8mb4', false)) {
59            $charset   = 'utf8mb4';
60            $collation = 'utf8mb4_unicode_ci';
61        } else {
62            $charset   = 'utf8';
63            $collation = 'utf8_unicode_ci';
64        }
65
66        $options = [
67            // Some drivers do this and some don't.  Make them consistent.
68            PDO::ATTR_STRINGIFY_FETCHES => true,
69        ];
70
71        $dbkey    = Validator::attributes($request)->string('dbkey', '');
72        $dbcert   = Validator::attributes($request)->string('dbcert', '');
73        $dbca     = Validator::attributes($request)->string('dbca', '');
74        $dbverify = Validator::attributes($request)->boolean('dbverify', false);
75
76        // MySQL/MariaDB support encrypted connections
77        if ($dbkey !== '' && $dbcert !== '' && $dbca !== '') {
78            $options[PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT] = $dbverify;
79            $options[PDO::MYSQL_ATTR_SSL_KEY]                = Webtrees::ROOT_DIR . 'data/' . $dbkey;
80            $options[PDO::MYSQL_ATTR_SSL_CERT]               = Webtrees::ROOT_DIR . 'data/' . $dbcert;
81            $options[PDO::MYSQL_ATTR_SSL_CA]                 = Webtrees::ROOT_DIR . 'data/' . $dbca;
82        }
83
84        $capsule->addConnection([
85            'driver'                  => $driver,
86            'host'                    => Validator::attributes($request)->string('dbhost'),
87            'port'                    => Validator::attributes($request)->string('dbport'),
88            'database'                => $dbname,
89            'username'                => Validator::attributes($request)->string('dbuser'),
90            'password'                => Validator::attributes($request)->string('dbpass'),
91            'prefix'                  => Validator::attributes($request)->string('tblpfx'),
92            'prefix_indexes'          => true,
93            'options'                 => $options,
94            // For MySQL
95            'charset'                 => $charset,
96            'collation'               => $collation,
97            'timezone'                => '+00:00',
98            'engine'                  => 'InnoDB',
99            'modes'                   => [
100                'ANSI',
101                'STRICT_ALL_TABLES',
102                // Use SQL injection(!) to override MAX_JOIN_SIZE and GROUP_CONCAT_MAX_LEN settings.
103                "', SQL_BIG_SELECTS=1, GROUP_CONCAT_MAX_LEN=1048576, @foobar='"
104            ],
105            // For SQLite
106            'foreign_key_constraints' => true,
107        ]);
108
109        $capsule->setAsGlobal();
110
111        if ($driver === 'sqlsrv') {
112            DB::connection()->unprepared('SET language us_english'); // For timestamp columns
113        }
114
115        try {
116            // Eager-load the connection, to prevent database credentials appearing in error logs.
117            DB::connection()->getPdo();
118        } catch (PDOException $exception) {
119            throw new RuntimeException($exception->getMessage());
120        }
121
122        return $handler->handle($request);
123    }
124}
125