* @param $db DatabaseBase object which threw the error
* @param string $error A simple error message to be used for debugging
*/
- function __construct( DatabaseBase &$db, $error ) {
+ function __construct( DatabaseBase $db = null, $error ) {
$this->db = $db;
parent::__construct( $error );
}
- /**
- * @param $html string
- * @return string
- */
- protected function getContentMessage( $html ) {
- if ( $html ) {
- return nl2br( htmlspecialchars( $this->getMessage() ) );
- } else {
- return $this->getMessage();
- }
- }
-
/**
* @return string
*/
function getText() {
global $wgShowDBErrorBacktrace;
- $s = $this->getContentMessage( false ) . "\n";
+ $s = $this->getTextContent() . "\n";
if ( $wgShowDBErrorBacktrace ) {
$s .= "Backtrace:\n" . $this->getTraceAsString() . "\n";
function getHTML() {
global $wgShowDBErrorBacktrace;
- $s = $this->getContentMessage( true );
+ $s = $this->getHTMLContent();
if ( $wgShowDBErrorBacktrace ) {
- $s .= '<p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) );
+ $s .= '<p>Backtrace:</p><pre>' . htmlspecialchars( $this->getTraceAsString() ) . '</pre>';
}
return $s;
}
+
+ /**
+ * @return string
+ */
+ protected function getTextContent() {
+ return $this->getMessage();
+ }
+
+ /**
+ * @return string
+ */
+ protected function getHTMLContent() {
+ return '<p>' . nl2br( htmlspecialchars( $this->getMessage() ) ) . '</p>';
+ }
}
/**
class DBConnectionError extends DBError {
public $error;
- function __construct( DatabaseBase &$db, $error = 'unknown error' ) {
+ function __construct( DatabaseBase $db = null, $error = 'unknown error' ) {
$msg = 'DB connection error';
if ( trim( $error ) != '' ) {
$msg .= ": $error";
+ } elseif ( $db ) {
+ $error = $this->db->getServer();
}
- $this->error = $error;
-
parent::__construct( $db, $msg );
+ $this->error = $error;
}
/**
* @return bool
*/
function getLogMessage() {
- # Don't send to the exception log
+ // Don't send to the exception log
return false;
}
- /**
- * @return string
- */
- function getPageTitle() {
- global $wgSitename;
- return htmlspecialchars( $this->msg( 'dberr-header', "$wgSitename has a problem" ) );
- }
-
/**
* @return string
*/
function getHTML() {
- global $wgShowDBErrorBacktrace;
+ global $wgShowDBErrorBacktrace, $wgShowHostnames, $wgShowSQLErrors;
$sorry = htmlspecialchars( $this->msg( 'dberr-problems', 'Sorry! This site is experiencing technical difficulties.' ) );
$again = htmlspecialchars( $this->msg( 'dberr-again', 'Try waiting a few minutes and reloading.' ) );
- $info = htmlspecialchars( $this->msg( 'dberr-info', '(Can\'t contact the database server: $1)' ) );
-
- # No database access
- MessageCache::singleton()->disable();
- if ( trim( $this->error ) == '' ) {
- $this->error = $this->db->getProperty( 'mServer' );
+ if ( $wgShowHostnames || $wgShowSQLErrors ) {
+ $info = str_replace(
+ '$1', Html::element( 'span', array( 'dir' => 'ltr' ), $this->error ),
+ htmlspecialchars( $this->msg( 'dberr-info', '(Cannot contact the database server: $1)' ) )
+ );
+ } else {
+ $info = htmlspecialchars( $this->msg( 'dberr-info-hidden', '(Cannot contact the database server)' ) );
}
- $this->error = Html::element( 'span', array( 'dir' => 'ltr' ), $this->error );
+ # No database access
+ MessageCache::singleton()->disable();
- $noconnect = "<h1>$sorry</h1><p>$again</p><p><small>$info</small></p>";
- $text = str_replace( '$1', $this->error, $noconnect );
+ $html = "<h1>$sorry</h1><p>$again</p><p><small>$info</small></p>";
if ( $wgShowDBErrorBacktrace ) {
- $text .= '<p>Backtrace:</p><p>' . nl2br( htmlspecialchars( $this->getTraceAsString() ) );
+ $html .= '<p>Backtrace:</p><pre>' . htmlspecialchars( $this->getTraceAsString() ) . '</pre>';
}
- $extra = $this->searchForm();
+ $html .= '<hr />';
+ $html .= $this->searchForm();
- return "$text<hr />$extra";
+ return $html;
+ }
+
+ protected function getTextContent() {
+ global $wgShowHostnames, $wgShowSQLErrors;
+
+ if ( $wgShowHostnames || $wgShowSQLErrors ) {
+ return $this->getMessage();
+ } else {
+ return 'DB connection error';
+ }
}
public function reportHTML() {
global $wgUseFileCache;
- # Check whether we can serve a file-cached copy of the page with the error underneath
+ // Check whether we can serve a file-cached copy of the page with the error underneath
if ( $wgUseFileCache ) {
try {
$cache = $this->fileCachedPage();
- # Cached version on file system?
+ // Cached version on file system?
if ( $cache !== null ) {
- # Hack: extend the body for error messages
+ // Hack: extend the body for error messages
$cache = str_replace( array( '</html>', '</body>' ), '', $cache );
- # Add cache notice...
- $cache .= '<div style="color:red;font-size:150%;font-weight:bold;">'.
+ // Add cache notice...
+ $cache .= '<div style="border:1px solid #ffd0d0;padding:1em;">' .
htmlspecialchars( $this->msg( 'dberr-cachederror',
- 'This is a cached copy of the requested page, and may not be up to date. ' ) ) .
+ 'This is a cached copy of the requested page, and may not be up to date.' ) ) .
'</div>';
- # Output cached page with notices on bottom and re-close body
+ // Output cached page with notices on bottom and re-close body
echo "{$cache}<hr />{$this->getHTML()}</body></html>";
return;
}
}
}
- # We can't, cough and die in the usual fashion
+ // We can't, cough and die in the usual fashion
parent::reportHTML();
}
$trygoogle = <<<EOT
<div style="margin: 1.5em">$usegoogle<br />
-<small>$outofdate</small></div>
-<!-- SiteSearch Google -->
+<small>$outofdate</small>
+</div>
<form method="get" action="//www.google.com/search" id="googlesearch">
<input type="hidden" name="domains" value="$server" />
<input type="hidden" name="num" value="50" />
<input type="text" name="q" size="31" maxlength="255" value="$search" />
<input type="submit" name="btnG" value="$googlesearch" />
- <div>
- <input type="radio" name="sitesearch" id="gwiki" value="$server" checked="checked" /><label for="gwiki">$sitename</label>
- <input type="radio" name="sitesearch" id="gWWW" value="" /><label for="gWWW">WWW</label>
- </div>
+ <p>
+ <label><input type="radio" name="sitesearch" value="$server" checked="checked" />$sitename</label>
+ <label><input type="radio" name="sitesearch" value="" />WWW</label>
+ </p>
</form>
-<!-- SiteSearch Google -->
EOT;
return $trygoogle;
}
global $wgTitle, $wgOut, $wgRequest;
if ( $wgOut->isDisabled() ) {
- return ''; // Done already?
+ // Done already?
+ return '';
}
- if ( $wgTitle ) { // use $wgTitle if we managed to set it
+ if ( $wgTitle ) {
+ // use $wgTitle if we managed to set it
$t = $wgTitle->getPrefixedDBkey();
} else {
- # Fallback to the raw title URL param. We can't use the Title
- # class is it may hit the interwiki table and give a DB error.
- # We may get a cache miss due to not sanitizing the title though.
+ // Fallback to the raw title URL param. We can't use the Title
+ // class is it may hit the interwiki table and give a DB error.
+ // We may get a cache miss due to not sanitizing the title though.
$t = str_replace( ' ', '_', $wgRequest->getVal( 'title' ) );
if ( $t == '' ) { // fallback to main page
$t = Title::newFromText(
* @param $sql string
* @param $fname string
*/
- function __construct( DatabaseBase &$db, $error, $errno, $sql, $fname ) {
+ function __construct( DatabaseBase $db, $error, $errno, $sql, $fname ) {
$message = "A database error has occurred. Did you forget to run maintenance/update.php after upgrading? See: https://www.mediawiki.org/wiki/Manual:Upgrading#Run_the_update_script\n" .
"Query: $sql\n" .
"Function: $fname\n" .
}
/**
- * @param $html string
+ * @return bool
+ */
+ function getLogMessage() {
+ # Don't send to the exception log
+ return false;
+ }
+
+ /**
+ * @return String
+ */
+ function getPageTitle() {
+ return $this->msg( 'databaseerror', 'Database error' );
+ }
+
+ /**
* @return string
*/
- function getContentMessage( $html ) {
- if ( $this->useMessageCache() ) {
- if ( $html ) {
- $msg = 'dberrortext';
- $sql = htmlspecialchars( $this->getSQL() );
- $fname = htmlspecialchars( $this->fname );
- $error = htmlspecialchars( $this->error );
- } else {
- $msg = 'dberrortextcl';
- $sql = $this->getSQL();
- $fname = $this->fname;
- $error = $this->error;
+ protected function getHTMLContent() {
+ $key = 'databaseerror-text';
+ $s = Html::element( 'p', array(), $this->msg( $key, $this->getFallbackMessage( $key ) ) );
+
+ $details = $this->getTechnicalDetails();
+ if ( $details ) {
+ $s .= '<ul>';
+ foreach ( $details as $key => $detail ) {
+ $s .= str_replace(
+ '$1', call_user_func_array( 'Html::element', $detail ),
+ Html::element( 'li', array(),
+ $this->msg( $key, $this->getFallbackMessage( $key ) )
+ )
+ );
}
- return wfMessage( $msg )->rawParams( $sql, $fname, $this->errno, $error )->text();
- } else {
- return parent::getContentMessage( $html );
+ $s .= '</ul>';
}
+
+ return $s;
}
/**
- * @return String
+ * @return string
*/
- function getSQL() {
- global $wgShowSQLErrors;
+ protected function getTextContent() {
+ $key = 'databaseerror-textcl';
+ $s = $this->msg( $key, $this->getFallbackMessage( $key ) ) . "\n";
- if ( !$wgShowSQLErrors ) {
- return $this->msg( 'sqlhidden', 'SQL hidden' );
- } else {
- return $this->sql;
+ foreach ( $this->getTechnicalDetails() as $key => $detail ) {
+ $s .= $this->msg( $key, $this->getFallbackMessage( $key ), $detail[2] ) . "\n";
}
+
+ return $s;
}
/**
- * @return bool
+ * Make a list of technical details that can be shown to the user. This information can
+ * aid in debugging yet may be useful to an attacker trying to exploit a security weakness
+ * in the software or server configuration.
+ *
+ * Thus no such details are shown by default, though if $wgShowHostnames is true, only the
+ * full SQL query is hidden; in fact, the error message often does contain a hostname, and
+ * sites using this option probably don't care much about "security by obscurity". Of course,
+ * if $wgShowSQLErrors is true, the SQL query *is* shown.
+ *
+ * @return array: Keys are message keys; values are arrays of arguments for Html::element().
+ * Array will be empty if users are not allowed to see any of these details at all.
*/
- function getLogMessage() {
- # Don't send to the exception log
- return false;
+ protected function getTechnicalDetails() {
+ global $wgShowHostnames, $wgShowSQLErrors;
+
+ $attribs = array( 'dir' => 'ltr' );
+ $details = array();
+
+ if ( $wgShowSQLErrors ) {
+ $details['databaseerror-query'] = array(
+ 'div', array( 'class' => 'mw-code' ) + $attribs, $this->sql );
+ }
+
+ if ( $wgShowHostnames || $wgShowSQLErrors ) {
+ $errorMessage = $this->errno . ' ' . $this->error;
+ $details['databaseerror-function'] = array( 'code', $attribs, $this->fname );
+ $details['databaseerror-error'] = array( 'samp', $attribs, $errorMessage );
+ }
+
+ return $details;
}
/**
- * @return String
+ * @param string $key Message key
+ * @return string: English message text
*/
- function getPageTitle() {
- return $this->msg( 'databaseerror', 'Database error' );
+ private function getFallbackMessage( $key ) {
+ $messages = array(
+ 'databaseerror-text' => 'A database query error has occurred.
+This may indicate a bug in the software.',
+ 'databaseerror-textcl' => 'A database query error has occurred.',
+ 'databaseerror-query' => 'Query: $1',
+ 'databaseerror-function' => 'Function: $1',
+ 'databaseerror-error' => 'Error: $1',
+ );
+ return $messages[$key];
}
+
}
/**