Completed
Branch master (939199)
by
unknown
39:35
created

includes/installer/OracleInstaller.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Oracle-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 Oracle.
26
 *
27
 * @ingroup Deployment
28
 * @since 1.17
29
 */
30
class OracleInstaller extends DatabaseInstaller {
31
32
	protected $globalNames = [
33
		'wgDBserver',
34
		'wgDBname',
35
		'wgDBuser',
36
		'wgDBpassword',
37
		'wgDBprefix',
38
	];
39
40
	protected $internalDefaults = [
41
		'_OracleDefTS' => 'USERS',
42
		'_OracleTempTS' => 'TEMP',
43
		'_InstallUser' => 'SYSTEM',
44
	];
45
46
	public $minimumVersion = '9.0.1'; // 9iR1
47
48
	protected $connError = null;
49
50
	public function getName() {
51
		return 'oracle';
52
	}
53
54
	public function isCompiled() {
55
		return self::checkExtension( 'oci8' );
56
	}
57
58
	public function getConnectForm() {
59
		if ( $this->getVar( 'wgDBserver' ) == 'localhost' ) {
60
			$this->parent->setVar( 'wgDBserver', '' );
61
		}
62
63
		return $this->getTextBox(
64
			'wgDBserver',
65
			'config-db-host-oracle',
66
			[],
67
			$this->parent->getHelpBox( 'config-db-host-oracle-help' )
68
		) .
69
			Html::openElement( 'fieldset' ) .
70
			Html::element( 'legend', [], wfMessage( 'config-db-wiki-settings' )->text() ) .
71
			$this->getTextBox( 'wgDBprefix', 'config-db-prefix' ) .
72
			$this->getTextBox( '_OracleDefTS', 'config-oracle-def-ts' ) .
73
			$this->getTextBox(
74
				'_OracleTempTS',
75
				'config-oracle-temp-ts',
76
				[],
77
				$this->parent->getHelpBox( 'config-db-oracle-help' )
78
			) .
79
			Html::closeElement( 'fieldset' ) .
80
			$this->parent->getWarningBox( wfMessage( 'config-db-account-oracle-warn' )->text() ) .
81
			$this->getInstallUserBox() .
82
			$this->getWebUserBox();
83
	}
84
85
	public function submitInstallUserBox() {
86
		parent::submitInstallUserBox();
87
		$this->parent->setVar( '_InstallDBname', $this->getVar( '_InstallUser' ) );
88
89
		return Status::newGood();
90
	}
91
92
	public function submitConnectForm() {
93
		// Get variables from the request
94
		$newValues = $this->setVarsFromRequest( [
95
			'wgDBserver',
96
			'wgDBprefix',
97
			'wgDBuser',
98
			'wgDBpassword'
99
		] );
100
		$this->parent->setVar( 'wgDBname', $this->getVar( 'wgDBuser' ) );
101
102
		// Validate them
103
		$status = Status::newGood();
104
		if ( !strlen( $newValues['wgDBserver'] ) ) {
105
			$status->fatal( 'config-missing-db-server-oracle' );
106
		} elseif ( !self::checkConnectStringFormat( $newValues['wgDBserver'] ) ) {
107
			$status->fatal( 'config-invalid-db-server-oracle', $newValues['wgDBserver'] );
108
		}
109
		if ( !preg_match( '/^[a-zA-Z0-9_]*$/', $newValues['wgDBprefix'] ) ) {
110
			$status->fatal( 'config-invalid-schema', $newValues['wgDBprefix'] );
111
		}
112
		if ( !$status->isOK() ) {
113
			return $status;
114
		}
115
116
		// Submit user box
117
		$status = $this->submitInstallUserBox();
118
		if ( !$status->isOK() ) {
119
			return $status;
120
		}
121
122
		// Try to connect trough multiple scenarios
123
		// Scenario 1: Install with a manually created account
124
		$status = $this->getConnection();
125
		if ( !$status->isOK() ) {
126
			if ( $this->connError == 28009 ) {
127
				// _InstallUser seems to be a SYSDBA
128
				// Scenario 2: Create user with SYSDBA and install with new user
129
				$status = $this->submitWebUserBox();
130
				if ( !$status->isOK() ) {
131
					return $status;
132
				}
133
				$status = $this->openSYSDBAConnection();
134
				if ( !$status->isOK() ) {
135
					return $status;
136
				}
137
				if ( !$this->getVar( '_CreateDBAccount' ) ) {
138
					$status->fatal( 'config-db-sys-create-oracle' );
139
				}
140
			} else {
141
				return $status;
142
			}
143
		} else {
144
			// check for web user credentials
145
			// Scenario 3: Install with a priviliged user but use a restricted user
146
			$statusIS3 = $this->submitWebUserBox();
147
			if ( !$statusIS3->isOK() ) {
148
				return $statusIS3;
149
			}
150
		}
151
152
		/**
153
		 * @var $conn Database
154
		 */
155
		$conn = $status->value;
156
157
		// Check version
158
		$version = $conn->getServerVersion();
159
		if ( version_compare( $version, $this->minimumVersion ) < 0 ) {
160
			return Status::newFatal( 'config-oracle-old', $this->minimumVersion, $version );
161
		}
162
163
		return $status;
164
	}
165
166 View Code Duplication
	public function openConnection() {
167
		$status = Status::newGood();
168
		try {
169
			$db = new DatabaseOracle(
170
				$this->getVar( 'wgDBserver' ),
171
				$this->getVar( '_InstallUser' ),
172
				$this->getVar( '_InstallPassword' ),
173
				$this->getVar( '_InstallDBname' ),
174
				0,
175
				$this->getVar( 'wgDBprefix' )
176
			);
177
			$status->value = $db;
178
		} catch ( DBConnectionError $e ) {
179
			$this->connError = $e->db->lastErrno();
180
			$status->fatal( 'config-connection-error', $e->getMessage() );
181
		}
182
183
		return $status;
184
	}
185
186 View Code Duplication
	public function openSYSDBAConnection() {
187
		$status = Status::newGood();
188
		try {
189
			$db = new DatabaseOracle(
190
				$this->getVar( 'wgDBserver' ),
191
				$this->getVar( '_InstallUser' ),
192
				$this->getVar( '_InstallPassword' ),
193
				$this->getVar( '_InstallDBname' ),
194
				DBO_SYSDBA,
195
				$this->getVar( 'wgDBprefix' )
196
			);
197
			$status->value = $db;
198
		} catch ( DBConnectionError $e ) {
199
			$this->connError = $e->db->lastErrno();
200
			$status->fatal( 'config-connection-error', $e->getMessage() );
201
		}
202
203
		return $status;
204
	}
205
206
	public function needsUpgrade() {
207
		$tempDBname = $this->getVar( 'wgDBname' );
208
		$this->parent->setVar( 'wgDBname', $this->getVar( 'wgDBuser' ) );
209
		$retVal = parent::needsUpgrade();
210
		$this->parent->setVar( 'wgDBname', $tempDBname );
211
212
		return $retVal;
213
	}
214
215 View Code Duplication
	public function preInstall() {
216
		# Add our user callback to installSteps, right before the tables are created.
217
		$callback = [
218
			'name' => 'user',
219
			'callback' => [ $this, 'setupUser' ]
220
		];
221
		$this->parent->addInstallStep( $callback, 'database' );
222
	}
223
224
	public function setupDatabase() {
225
		$status = Status::newGood();
226
227
		return $status;
228
	}
229
230
	public function setupUser() {
231
		global $IP;
232
233
		if ( !$this->getVar( '_CreateDBAccount' ) ) {
234
			return Status::newGood();
235
		}
236
237
		// normaly only SYSDBA users can create accounts
238
		$status = $this->openSYSDBAConnection();
239
		if ( !$status->isOK() ) {
240
			if ( $this->connError == 1031 ) {
241
				// insufficient  privileges (looks like a normal user)
242
				$status = $this->openConnection();
243
				if ( !$status->isOK() ) {
244
					return $status;
245
				}
246
			} else {
247
				return $status;
248
			}
249
		}
250
251
		$this->db = $status->value;
252
		$this->setupSchemaVars();
253
254
		if ( !$this->db->selectDB( $this->getVar( 'wgDBuser' ) ) ) {
255
			$this->db->setFlag( DBO_DDLMODE );
256
			$error = $this->db->sourceFile( "$IP/maintenance/oracle/user.sql" );
257
			if ( $error !== true || !$this->db->selectDB( $this->getVar( 'wgDBuser' ) ) ) {
258
				$status->fatal( 'config-install-user-failed', $this->getVar( 'wgDBuser' ), $error );
259
			}
260
		} elseif ( $this->db->getFlag( DBO_SYSDBA ) ) {
261
			$status->fatal( 'config-db-sys-user-exists-oracle', $this->getVar( 'wgDBuser' ) );
262
		}
263
264
		if ( $status->isOK() ) {
265
			// user created or already existing, switching back to a normal connection
266
			// as the new user has all needed privileges to setup the rest of the schema
267
			// i will be using that user as _InstallUser from this point on
268
			$this->db->close();
269
			$this->db = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type object<Database> of property $db.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
270
			$this->parent->setVar( '_InstallUser', $this->getVar( 'wgDBuser' ) );
271
			$this->parent->setVar( '_InstallPassword', $this->getVar( 'wgDBpassword' ) );
272
			$this->parent->setVar( '_InstallDBname', $this->getVar( 'wgDBuser' ) );
273
			$status = $this->getConnection();
274
		}
275
276
		return $status;
277
	}
278
279
	/**
280
	 * Overload: after this action field info table has to be rebuilt
281
	 * @return Status
282
	 */
283
	public function createTables() {
284
		$this->setupSchemaVars();
285
		$this->db->setFlag( DBO_DDLMODE );
286
		$this->parent->setVar( 'wgDBname', $this->getVar( 'wgDBuser' ) );
287
		$status = parent::createTables();
288
		$this->db->clearFlag( DBO_DDLMODE );
289
290
		$this->db->query( 'BEGIN fill_wiki_info; END;' );
291
292
		return $status;
293
	}
294
295
	public function getSchemaVars() {
296
		$varNames = [
297
			# These variables are used by maintenance/oracle/user.sql
298
			'_OracleDefTS',
299
			'_OracleTempTS',
300
			'wgDBuser',
301
			'wgDBpassword',
302
303
			# These are used by tables.sql
304
			'wgDBprefix',
305
		];
306
		$vars = [];
307
		foreach ( $varNames as $name ) {
308
			$vars[$name] = $this->getVar( $name );
309
		}
310
311
		return $vars;
312
	}
313
314
	public function getLocalSettings() {
315
		$prefix = $this->getVar( 'wgDBprefix' );
316
317
		return "# Oracle specific settings
318
\$wgDBprefix = \"{$prefix}\";
319
";
320
	}
321
322
	/**
323
	 * Function checks the format of Oracle connect string
324
	 * The actual validity of the string is checked by attempting to connect
325
	 *
326
	 * Regex should be able to validate all connect string formats
327
	 * [//](host|tns_name)[:port][/service_name][:POOLED]
328
	 * http://www.orafaq.com/wiki/EZCONNECT
329
	 *
330
	 * @since 1.22
331
	 *
332
	 * @param string $connect_string
333
	 *
334
	 * @return bool Whether the connection string is valid.
335
	 */
336
	public static function checkConnectStringFormat( $connect_string ) {
337
		// @@codingStandardsIgnoreStart Long lines with regular expressions.
338
		// @todo Very long regular expression. Make more readable?
339
		$isValid = preg_match( '/^[[:alpha:]][\w\-]*(?:\.[[:alpha:]][\w\-]*){0,2}$/', $connect_string ); // TNS name
340
		$isValid |= preg_match( '/^(?:\/\/)?[\w\-\.]+(?::[\d]+)?(?:\/(?:[\w\-\.]+(?::(pooled|dedicated|shared))?)?(?:\/[\w\-\.]+)?)?$/', $connect_string ); // EZConnect
341
		// @@codingStandardsIgnoreEnd
342
		return (bool)$isValid;
343
	}
344
}
345