wikimedia /
mediawiki
This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
| 1 | <?php |
||
| 2 | /** |
||
| 3 | * Sqlite-specific installer. |
||
| 4 | * |
||
| 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 2 of the License, or |
||
| 8 | * (at your option) any later version. |
||
| 9 | * |
||
| 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 | * |
||
| 15 | * You should have received a copy of the GNU General Public License along |
||
| 16 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
| 17 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
| 18 | * http://www.gnu.org/copyleft/gpl.html |
||
| 19 | * |
||
| 20 | * @file |
||
| 21 | * @ingroup Deployment |
||
| 22 | */ |
||
| 23 | |||
| 24 | /** |
||
| 25 | * Class for setting up the MediaWiki database using SQLLite. |
||
| 26 | * |
||
| 27 | * @ingroup Deployment |
||
| 28 | * @since 1.17 |
||
| 29 | */ |
||
| 30 | class SqliteInstaller extends DatabaseInstaller { |
||
| 31 | const MINIMUM_VERSION = '3.3.7'; |
||
| 32 | |||
| 33 | /** |
||
| 34 | * @var DatabaseSqlite |
||
| 35 | */ |
||
| 36 | public $db; |
||
| 37 | |||
| 38 | protected $globalNames = [ |
||
| 39 | 'wgDBname', |
||
| 40 | 'wgSQLiteDataDir', |
||
| 41 | ]; |
||
| 42 | |||
| 43 | public function getName() { |
||
| 44 | return 'sqlite'; |
||
| 45 | } |
||
| 46 | |||
| 47 | public function isCompiled() { |
||
| 48 | return self::checkExtension( 'pdo_sqlite' ); |
||
| 49 | } |
||
| 50 | |||
| 51 | /** |
||
| 52 | * |
||
| 53 | * @return Status |
||
| 54 | */ |
||
| 55 | public function checkPrerequisites() { |
||
| 56 | $result = Status::newGood(); |
||
| 57 | // Bail out if SQLite is too old |
||
| 58 | $db = DatabaseSqlite::newStandaloneInstance( ':memory:' ); |
||
| 59 | if ( version_compare( $db->getServerVersion(), self::MINIMUM_VERSION, '<' ) ) { |
||
| 60 | $result->fatal( 'config-outdated-sqlite', $db->getServerVersion(), self::MINIMUM_VERSION ); |
||
| 61 | } |
||
| 62 | // Check for FTS3 full-text search module |
||
| 63 | if ( DatabaseSqlite::getFulltextSearchModule() != 'FTS3' ) { |
||
| 64 | $result->warning( 'config-no-fts3' ); |
||
| 65 | } |
||
| 66 | |||
| 67 | return $result; |
||
| 68 | } |
||
| 69 | |||
| 70 | public function getGlobalDefaults() { |
||
|
0 ignored issues
–
show
|
|||
| 71 | $defaults = parent::getGlobalDefaults(); |
||
| 72 | if ( isset( $_SERVER['DOCUMENT_ROOT'] ) ) { |
||
| 73 | $path = str_replace( |
||
| 74 | [ '/', '\\' ], |
||
| 75 | DIRECTORY_SEPARATOR, |
||
| 76 | dirname( $_SERVER['DOCUMENT_ROOT'] ) . '/data' |
||
| 77 | ); |
||
| 78 | |||
| 79 | $defaults['wgSQLiteDataDir'] = $path; |
||
| 80 | } |
||
| 81 | return $defaults; |
||
| 82 | } |
||
| 83 | |||
| 84 | public function getConnectForm() { |
||
| 85 | return $this->getTextBox( |
||
| 86 | 'wgSQLiteDataDir', |
||
| 87 | 'config-sqlite-dir', [], |
||
| 88 | $this->parent->getHelpBox( 'config-sqlite-dir-help' ) |
||
| 89 | ) . |
||
| 90 | $this->getTextBox( |
||
| 91 | 'wgDBname', |
||
| 92 | 'config-db-name', |
||
| 93 | [], |
||
| 94 | $this->parent->getHelpBox( 'config-sqlite-name-help' ) |
||
| 95 | ); |
||
| 96 | } |
||
| 97 | |||
| 98 | /** |
||
| 99 | * Safe wrapper for PHP's realpath() that fails gracefully if it's unable to canonicalize the path. |
||
| 100 | * |
||
| 101 | * @param string $path |
||
| 102 | * |
||
| 103 | * @return string |
||
| 104 | */ |
||
| 105 | private static function realpath( $path ) { |
||
| 106 | $result = realpath( $path ); |
||
| 107 | if ( !$result ) { |
||
| 108 | return $path; |
||
| 109 | } |
||
| 110 | |||
| 111 | return $result; |
||
| 112 | } |
||
| 113 | |||
| 114 | /** |
||
| 115 | * @return Status |
||
| 116 | */ |
||
| 117 | public function submitConnectForm() { |
||
| 118 | $this->setVarsFromRequest( [ 'wgSQLiteDataDir', 'wgDBname' ] ); |
||
| 119 | |||
| 120 | # Try realpath() if the directory already exists |
||
| 121 | $dir = self::realpath( $this->getVar( 'wgSQLiteDataDir' ) ); |
||
| 122 | $result = self::dataDirOKmaybeCreate( $dir, true /* create? */ ); |
||
| 123 | if ( $result->isOK() ) { |
||
| 124 | # Try expanding again in case we've just created it |
||
| 125 | $dir = self::realpath( $dir ); |
||
| 126 | $this->setVar( 'wgSQLiteDataDir', $dir ); |
||
| 127 | } |
||
| 128 | # Table prefix is not used on SQLite, keep it empty |
||
| 129 | $this->setVar( 'wgDBprefix', '' ); |
||
| 130 | |||
| 131 | return $result; |
||
| 132 | } |
||
| 133 | |||
| 134 | /** |
||
| 135 | * @param string $dir |
||
| 136 | * @param bool $create |
||
| 137 | * @return Status |
||
| 138 | */ |
||
| 139 | private static function dataDirOKmaybeCreate( $dir, $create = false ) { |
||
| 140 | if ( !is_dir( $dir ) ) { |
||
| 141 | if ( !is_writable( dirname( $dir ) ) ) { |
||
| 142 | $webserverGroup = Installer::maybeGetWebserverPrimaryGroup(); |
||
| 143 | if ( $webserverGroup !== null ) { |
||
| 144 | return Status::newFatal( |
||
| 145 | 'config-sqlite-parent-unwritable-group', |
||
| 146 | $dir, dirname( $dir ), basename( $dir ), |
||
| 147 | $webserverGroup |
||
| 148 | ); |
||
| 149 | } else { |
||
| 150 | return Status::newFatal( |
||
| 151 | 'config-sqlite-parent-unwritable-nogroup', |
||
| 152 | $dir, dirname( $dir ), basename( $dir ) |
||
| 153 | ); |
||
| 154 | } |
||
| 155 | } |
||
| 156 | |||
| 157 | # Called early on in the installer, later we just want to sanity check |
||
| 158 | # if it's still writable |
||
| 159 | if ( $create ) { |
||
| 160 | MediaWiki\suppressWarnings(); |
||
| 161 | $ok = wfMkdirParents( $dir, 0700, __METHOD__ ); |
||
| 162 | MediaWiki\restoreWarnings(); |
||
| 163 | if ( !$ok ) { |
||
| 164 | return Status::newFatal( 'config-sqlite-mkdir-error', $dir ); |
||
| 165 | } |
||
| 166 | # Put a .htaccess file in in case the user didn't take our advice |
||
| 167 | file_put_contents( "$dir/.htaccess", "Deny from all\n" ); |
||
| 168 | } |
||
| 169 | } |
||
| 170 | if ( !is_writable( $dir ) ) { |
||
| 171 | return Status::newFatal( 'config-sqlite-dir-unwritable', $dir ); |
||
| 172 | } |
||
| 173 | |||
| 174 | # We haven't blown up yet, fall through |
||
| 175 | return Status::newGood(); |
||
| 176 | } |
||
| 177 | |||
| 178 | /** |
||
| 179 | * @return Status |
||
| 180 | */ |
||
| 181 | public function openConnection() { |
||
| 182 | $status = Status::newGood(); |
||
| 183 | $dir = $this->getVar( 'wgSQLiteDataDir' ); |
||
| 184 | $dbName = $this->getVar( 'wgDBname' ); |
||
| 185 | try { |
||
| 186 | # @todo FIXME: Need more sensible constructor parameters, e.g. single associative array |
||
| 187 | $db = Database::factory( 'sqlite', [ 'dbname' => $dbName, 'dbDirectory' => $dir ] ); |
||
| 188 | $status->value = $db; |
||
| 189 | } catch ( DBConnectionError $e ) { |
||
| 190 | $status->fatal( 'config-sqlite-connection-error', $e->getMessage() ); |
||
| 191 | } |
||
| 192 | |||
| 193 | return $status; |
||
| 194 | } |
||
| 195 | |||
| 196 | /** |
||
| 197 | * @return bool |
||
| 198 | */ |
||
| 199 | public function needsUpgrade() { |
||
| 200 | $dir = $this->getVar( 'wgSQLiteDataDir' ); |
||
| 201 | $dbName = $this->getVar( 'wgDBname' ); |
||
| 202 | // Don't create the data file yet |
||
| 203 | if ( !file_exists( DatabaseSqlite::generateFileName( $dir, $dbName ) ) ) { |
||
| 204 | return false; |
||
| 205 | } |
||
| 206 | |||
| 207 | // If the data file exists, look inside it |
||
| 208 | return parent::needsUpgrade(); |
||
| 209 | } |
||
| 210 | |||
| 211 | /** |
||
| 212 | * @return Status |
||
| 213 | */ |
||
| 214 | public function setupDatabase() { |
||
| 215 | $dir = $this->getVar( 'wgSQLiteDataDir' ); |
||
| 216 | |||
| 217 | # Sanity check. We checked this before but maybe someone deleted the |
||
| 218 | # data dir between then and now |
||
| 219 | $dir_status = self::dataDirOKmaybeCreate( $dir, false /* create? */ ); |
||
| 220 | if ( !$dir_status->isOK() ) { |
||
| 221 | return $dir_status; |
||
| 222 | } |
||
| 223 | |||
| 224 | $db = $this->getVar( 'wgDBname' ); |
||
| 225 | |||
| 226 | # Make the main and cache stub DB files |
||
| 227 | $status = Status::newGood(); |
||
| 228 | $status->merge( $this->makeStubDBFile( $dir, $db ) ); |
||
| 229 | $status->merge( $this->makeStubDBFile( $dir, "wikicache" ) ); |
||
| 230 | if ( !$status->isOK() ) { |
||
| 231 | return $status; |
||
| 232 | } |
||
| 233 | |||
| 234 | # Nuke the unused settings for clarity |
||
| 235 | $this->setVar( 'wgDBserver', '' ); |
||
| 236 | $this->setVar( 'wgDBuser', '' ); |
||
| 237 | $this->setVar( 'wgDBpassword', '' ); |
||
| 238 | $this->setupSchemaVars(); |
||
| 239 | |||
| 240 | # Create the global cache DB |
||
| 241 | try { |
||
| 242 | $conn = Database::factory( 'sqlite', [ 'dbname' => 'wikicache', 'dbDirectory' => $dir ] ); |
||
| 243 | # @todo: don't duplicate objectcache definition, though it's very simple |
||
| 244 | $sql = |
||
| 245 | <<<EOT |
||
| 246 | CREATE TABLE IF NOT EXISTS objectcache ( |
||
| 247 | keyname BLOB NOT NULL default '' PRIMARY KEY, |
||
| 248 | value BLOB, |
||
| 249 | exptime TEXT |
||
| 250 | ) |
||
| 251 | EOT; |
||
| 252 | $conn->query( $sql ); |
||
| 253 | $conn->query( "CREATE INDEX IF NOT EXISTS exptime ON objectcache (exptime)" ); |
||
| 254 | $conn->query( "PRAGMA journal_mode=WAL" ); // this is permanent |
||
| 255 | $conn->close(); |
||
| 256 | } catch ( DBConnectionError $e ) { |
||
| 257 | return Status::newFatal( 'config-sqlite-connection-error', $e->getMessage() ); |
||
| 258 | } |
||
| 259 | |||
| 260 | # Open the main DB |
||
| 261 | return $this->getConnection(); |
||
| 262 | } |
||
| 263 | |||
| 264 | /** |
||
| 265 | * @param $dir |
||
| 266 | * @param $db |
||
| 267 | * @return Status |
||
| 268 | */ |
||
| 269 | protected function makeStubDBFile( $dir, $db ) { |
||
| 270 | $file = DatabaseSqlite::generateFileName( $dir, $db ); |
||
| 271 | if ( file_exists( $file ) ) { |
||
| 272 | if ( !is_writable( $file ) ) { |
||
| 273 | return Status::newFatal( 'config-sqlite-readonly', $file ); |
||
| 274 | } |
||
| 275 | } else { |
||
| 276 | if ( file_put_contents( $file, '' ) === false ) { |
||
| 277 | return Status::newFatal( 'config-sqlite-cant-create-db', $file ); |
||
| 278 | } |
||
| 279 | } |
||
| 280 | |||
| 281 | return Status::newGood(); |
||
| 282 | } |
||
| 283 | |||
| 284 | /** |
||
| 285 | * @return Status |
||
| 286 | */ |
||
| 287 | public function createTables() { |
||
| 288 | $status = parent::createTables(); |
||
| 289 | |||
| 290 | return $this->setupSearchIndex( $status ); |
||
| 291 | } |
||
| 292 | |||
| 293 | /** |
||
| 294 | * @param Status $status |
||
| 295 | * @return Status |
||
| 296 | */ |
||
| 297 | public function setupSearchIndex( &$status ) { |
||
| 298 | global $IP; |
||
| 299 | |||
| 300 | $module = DatabaseSqlite::getFulltextSearchModule(); |
||
| 301 | $fts3tTable = $this->db->checkForEnabledSearch(); |
||
| 302 | if ( $fts3tTable && !$module ) { |
||
| 303 | $status->warning( 'config-sqlite-fts3-downgrade' ); |
||
| 304 | $this->db->sourceFile( "$IP/maintenance/sqlite/archives/searchindex-no-fts.sql" ); |
||
| 305 | } elseif ( !$fts3tTable && $module == 'FTS3' ) { |
||
| 306 | $this->db->sourceFile( "$IP/maintenance/sqlite/archives/searchindex-fts3.sql" ); |
||
| 307 | } |
||
| 308 | |||
| 309 | return $status; |
||
| 310 | } |
||
| 311 | |||
| 312 | /** |
||
| 313 | * @return string |
||
| 314 | */ |
||
| 315 | public function getLocalSettings() { |
||
| 316 | $dir = LocalSettingsGenerator::escapePhpString( $this->getVar( 'wgSQLiteDataDir' ) ); |
||
| 317 | |||
| 318 | return "# SQLite-specific settings |
||
| 319 | \$wgSQLiteDataDir = \"{$dir}\"; |
||
| 320 | \$wgObjectCaches[CACHE_DB] = [ |
||
| 321 | 'class' => 'SqlBagOStuff', |
||
| 322 | 'loggroup' => 'SQLBagOStuff', |
||
| 323 | 'server' => [ |
||
| 324 | 'type' => 'sqlite', |
||
| 325 | 'dbname' => 'wikicache', |
||
| 326 | 'tablePrefix' => '', |
||
| 327 | 'dbDirectory' => \$wgSQLiteDataDir, |
||
| 328 | 'flags' => 0 |
||
| 329 | ] |
||
| 330 | ];"; |
||
| 331 | } |
||
| 332 | } |
||
| 333 |
Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable: