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 | * Contain log classes |
||
| 4 | * |
||
| 5 | * Copyright © 2002, 2004 Brion Vibber <[email protected]> |
||
| 6 | * https://www.mediawiki.org/ |
||
| 7 | * |
||
| 8 | * This program is free software; you can redistribute it and/or modify |
||
| 9 | * it under the terms of the GNU General Public License as published by |
||
| 10 | * the Free Software Foundation; either version 2 of the License, or |
||
| 11 | * (at your option) any later version. |
||
| 12 | * |
||
| 13 | * This program is distributed in the hope that it will be useful, |
||
| 14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
| 15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||
| 16 | * GNU General Public License for more details. |
||
| 17 | * |
||
| 18 | * You should have received a copy of the GNU General Public License along |
||
| 19 | * with this program; if not, write to the Free Software Foundation, Inc., |
||
| 20 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
||
| 21 | * http://www.gnu.org/copyleft/gpl.html |
||
| 22 | * |
||
| 23 | * @file |
||
| 24 | */ |
||
| 25 | |||
| 26 | /** |
||
| 27 | * Class to simplify the use of log pages. |
||
| 28 | * The logs are now kept in a table which is easier to manage and trim |
||
| 29 | * than ever-growing wiki pages. |
||
| 30 | * |
||
| 31 | */ |
||
| 32 | class LogPage { |
||
| 33 | const DELETED_ACTION = 1; |
||
| 34 | const DELETED_COMMENT = 2; |
||
| 35 | const DELETED_USER = 4; |
||
| 36 | const DELETED_RESTRICTED = 8; |
||
| 37 | |||
| 38 | // Convenience fields |
||
| 39 | const SUPPRESSED_USER = 12; |
||
| 40 | const SUPPRESSED_ACTION = 9; |
||
| 41 | |||
| 42 | /** @var bool */ |
||
| 43 | public $updateRecentChanges; |
||
| 44 | |||
| 45 | /** @var bool */ |
||
| 46 | public $sendToUDP; |
||
| 47 | |||
| 48 | /** @var string Plaintext version of the message for IRC */ |
||
| 49 | private $ircActionText; |
||
| 50 | |||
| 51 | /** @var string Plaintext version of the message */ |
||
| 52 | private $actionText; |
||
| 53 | |||
| 54 | /** @var string One of '', 'block', 'protect', 'rights', 'delete', |
||
| 55 | * 'upload', 'move' |
||
| 56 | */ |
||
| 57 | private $type; |
||
| 58 | |||
| 59 | /** @var string One of '', 'block', 'protect', 'rights', 'delete', |
||
| 60 | * 'upload', 'move', 'move_redir' */ |
||
| 61 | private $action; |
||
| 62 | |||
| 63 | /** @var string Comment associated with action */ |
||
| 64 | private $comment; |
||
| 65 | |||
| 66 | /** @var string Blob made of a parameters array */ |
||
| 67 | private $params; |
||
| 68 | |||
| 69 | /** @var User The user doing the action */ |
||
| 70 | private $doer; |
||
| 71 | |||
| 72 | /** @var Title */ |
||
| 73 | private $target; |
||
| 74 | |||
| 75 | /** |
||
| 76 | * Constructor |
||
| 77 | * |
||
| 78 | * @param string $type One of '', 'block', 'protect', 'rights', 'delete', |
||
| 79 | * 'upload', 'move' |
||
| 80 | * @param bool $rc Whether to update recent changes as well as the logging table |
||
| 81 | * @param string $udp Pass 'UDP' to send to the UDP feed if NOT sent to RC |
||
| 82 | */ |
||
| 83 | public function __construct( $type, $rc = true, $udp = 'skipUDP' ) { |
||
| 84 | $this->type = $type; |
||
| 85 | $this->updateRecentChanges = $rc; |
||
| 86 | $this->sendToUDP = ( $udp == 'UDP' ); |
||
| 87 | } |
||
| 88 | |||
| 89 | /** |
||
| 90 | * @return int The log_id of the inserted log entry |
||
| 91 | */ |
||
| 92 | protected function saveContent() { |
||
| 93 | global $wgLogRestrictions; |
||
| 94 | |||
| 95 | $dbw = wfGetDB( DB_MASTER ); |
||
| 96 | $log_id = $dbw->nextSequenceValue( 'logging_log_id_seq' ); |
||
| 97 | |||
| 98 | // @todo FIXME private/protected/public property? |
||
| 99 | $this->timestamp = $now = wfTimestampNow(); |
||
|
0 ignored issues
–
show
|
|||
| 100 | $data = [ |
||
| 101 | 'log_id' => $log_id, |
||
| 102 | 'log_type' => $this->type, |
||
| 103 | 'log_action' => $this->action, |
||
| 104 | 'log_timestamp' => $dbw->timestamp( $now ), |
||
| 105 | 'log_user' => $this->doer->getId(), |
||
| 106 | 'log_user_text' => $this->doer->getName(), |
||
| 107 | 'log_namespace' => $this->target->getNamespace(), |
||
| 108 | 'log_title' => $this->target->getDBkey(), |
||
| 109 | 'log_page' => $this->target->getArticleID(), |
||
| 110 | 'log_comment' => $this->comment, |
||
| 111 | 'log_params' => $this->params |
||
| 112 | ]; |
||
| 113 | $dbw->insert( 'logging', $data, __METHOD__ ); |
||
| 114 | $newId = !is_null( $log_id ) ? $log_id : $dbw->insertId(); |
||
| 115 | |||
| 116 | # And update recentchanges |
||
| 117 | if ( $this->updateRecentChanges ) { |
||
| 118 | $titleObj = SpecialPage::getTitleFor( 'Log', $this->type ); |
||
| 119 | |||
| 120 | RecentChange::notifyLog( |
||
| 121 | $now, $titleObj, $this->doer, $this->getRcComment(), '', |
||
|
0 ignored issues
–
show
It seems like
$now defined by wfTimestampNow() on line 99 can also be of type false; however, RecentChange::notifyLog() does only seem to accept string, did you maybe forget to handle an error condition?
This check looks for type mismatches where the missing type is Consider the follow example <?php
function getDate($date)
{
if ($date !== null) {
return new DateTime($date);
}
return false;
}
This function either returns a new Loading history...
|
|||
| 122 | $this->type, $this->action, $this->target, $this->comment, |
||
| 123 | $this->params, $newId, $this->getRcCommentIRC() |
||
| 124 | ); |
||
| 125 | } elseif ( $this->sendToUDP ) { |
||
| 126 | # Don't send private logs to UDP |
||
| 127 | if ( isset( $wgLogRestrictions[$this->type] ) && $wgLogRestrictions[$this->type] != '*' ) { |
||
| 128 | return $newId; |
||
| 129 | } |
||
| 130 | |||
| 131 | # Notify external application via UDP. |
||
| 132 | # We send this to IRC but do not want to add it the RC table. |
||
| 133 | $titleObj = SpecialPage::getTitleFor( 'Log', $this->type ); |
||
| 134 | $rc = RecentChange::newLogEntry( |
||
| 135 | $now, $titleObj, $this->doer, $this->getRcComment(), '', |
||
|
0 ignored issues
–
show
It seems like
$now defined by wfTimestampNow() on line 99 can also be of type false; however, RecentChange::newLogEntry() does only seem to accept string, did you maybe forget to handle an error condition?
This check looks for type mismatches where the missing type is Consider the follow example <?php
function getDate($date)
{
if ($date !== null) {
return new DateTime($date);
}
return false;
}
This function either returns a new Loading history...
|
|||
| 136 | $this->type, $this->action, $this->target, $this->comment, |
||
| 137 | $this->params, $newId, $this->getRcCommentIRC() |
||
| 138 | ); |
||
| 139 | $rc->notifyRCFeeds(); |
||
| 140 | } |
||
| 141 | |||
| 142 | return $newId; |
||
| 143 | } |
||
| 144 | |||
| 145 | /** |
||
| 146 | * Get the RC comment from the last addEntry() call |
||
| 147 | * |
||
| 148 | * @return string |
||
| 149 | */ |
||
| 150 | View Code Duplication | public function getRcComment() { |
|
| 151 | $rcComment = $this->actionText; |
||
| 152 | |||
| 153 | if ( $this->comment != '' ) { |
||
| 154 | if ( $rcComment == '' ) { |
||
| 155 | $rcComment = $this->comment; |
||
| 156 | } else { |
||
| 157 | $rcComment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . |
||
| 158 | $this->comment; |
||
| 159 | } |
||
| 160 | } |
||
| 161 | |||
| 162 | return $rcComment; |
||
| 163 | } |
||
| 164 | |||
| 165 | /** |
||
| 166 | * Get the RC comment from the last addEntry() call for IRC |
||
| 167 | * |
||
| 168 | * @return string |
||
| 169 | */ |
||
| 170 | View Code Duplication | public function getRcCommentIRC() { |
|
| 171 | $rcComment = $this->ircActionText; |
||
| 172 | |||
| 173 | if ( $this->comment != '' ) { |
||
| 174 | if ( $rcComment == '' ) { |
||
| 175 | $rcComment = $this->comment; |
||
| 176 | } else { |
||
| 177 | $rcComment .= wfMessage( 'colon-separator' )->inContentLanguage()->text() . |
||
| 178 | $this->comment; |
||
| 179 | } |
||
| 180 | } |
||
| 181 | |||
| 182 | return $rcComment; |
||
| 183 | } |
||
| 184 | |||
| 185 | /** |
||
| 186 | * Get the comment from the last addEntry() call |
||
| 187 | * @return string |
||
| 188 | */ |
||
| 189 | public function getComment() { |
||
| 190 | return $this->comment; |
||
| 191 | } |
||
| 192 | |||
| 193 | /** |
||
| 194 | * Get the list of valid log types |
||
| 195 | * |
||
| 196 | * @return array Array of strings |
||
| 197 | */ |
||
| 198 | public static function validTypes() { |
||
| 199 | global $wgLogTypes; |
||
| 200 | |||
| 201 | return $wgLogTypes; |
||
| 202 | } |
||
| 203 | |||
| 204 | /** |
||
| 205 | * Is $type a valid log type |
||
| 206 | * |
||
| 207 | * @param string $type Log type to check |
||
| 208 | * @return bool |
||
| 209 | */ |
||
| 210 | public static function isLogType( $type ) { |
||
| 211 | return in_array( $type, LogPage::validTypes() ); |
||
| 212 | } |
||
| 213 | |||
| 214 | /** |
||
| 215 | * Generate text for a log entry. |
||
| 216 | * Only LogFormatter should call this function. |
||
| 217 | * |
||
| 218 | * @param string $type Log type |
||
| 219 | * @param string $action Log action |
||
| 220 | * @param Title|null $title Title object or null |
||
| 221 | * @param Skin|null $skin Skin object or null. If null, we want to use the wiki |
||
| 222 | * content language, since that will go to the IRC feed. |
||
| 223 | * @param array $params Parameters |
||
| 224 | * @param bool $filterWikilinks Whether to filter wiki links |
||
| 225 | * @return string HTML |
||
| 226 | */ |
||
| 227 | public static function actionText( $type, $action, $title = null, $skin = null, |
||
| 228 | $params = [], $filterWikilinks = false |
||
| 229 | ) { |
||
| 230 | global $wgLang, $wgContLang, $wgLogActions; |
||
| 231 | |||
| 232 | if ( is_null( $skin ) ) { |
||
| 233 | $langObj = $wgContLang; |
||
| 234 | $langObjOrNull = null; |
||
| 235 | } else { |
||
| 236 | $langObj = $wgLang; |
||
| 237 | $langObjOrNull = $wgLang; |
||
| 238 | } |
||
| 239 | |||
| 240 | $key = "$type/$action"; |
||
| 241 | |||
| 242 | if ( isset( $wgLogActions[$key] ) ) { |
||
| 243 | if ( is_null( $title ) ) { |
||
| 244 | $rv = wfMessage( $wgLogActions[$key] )->inLanguage( $langObj )->escaped(); |
||
| 245 | } else { |
||
| 246 | $titleLink = self::getTitleLink( $type, $langObjOrNull, $title, $params ); |
||
| 247 | |||
| 248 | if ( count( $params ) == 0 ) { |
||
| 249 | $rv = wfMessage( $wgLogActions[$key] )->rawParams( $titleLink ) |
||
| 250 | ->inLanguage( $langObj )->escaped(); |
||
| 251 | } else { |
||
| 252 | array_unshift( $params, $titleLink ); |
||
| 253 | |||
| 254 | $rv = wfMessage( $wgLogActions[$key] )->rawParams( $params ) |
||
| 255 | ->inLanguage( $langObj )->escaped(); |
||
| 256 | } |
||
| 257 | } |
||
| 258 | } else { |
||
| 259 | global $wgLogActionsHandlers; |
||
| 260 | |||
| 261 | if ( isset( $wgLogActionsHandlers[$key] ) ) { |
||
| 262 | $args = func_get_args(); |
||
| 263 | $rv = call_user_func_array( $wgLogActionsHandlers[$key], $args ); |
||
| 264 | } else { |
||
| 265 | wfDebug( "LogPage::actionText - unknown action $key\n" ); |
||
| 266 | $rv = "$action"; |
||
| 267 | } |
||
| 268 | } |
||
| 269 | |||
| 270 | // For the perplexed, this feature was added in r7855 by Erik. |
||
| 271 | // The feature was added because we liked adding [[$1]] in our log entries |
||
| 272 | // but the log entries are parsed as Wikitext on RecentChanges but as HTML |
||
| 273 | // on Special:Log. The hack is essentially that [[$1]] represented a link |
||
| 274 | // to the title in question. The first parameter to the HTML version (Special:Log) |
||
| 275 | // is that link in HTML form, and so this just gets rid of the ugly [[]]. |
||
| 276 | // However, this is a horrible hack and it doesn't work like you expect if, say, |
||
| 277 | // you want to link to something OTHER than the title of the log entry. |
||
| 278 | // The real problem, which Erik was trying to fix (and it sort-of works now) is |
||
| 279 | // that the same messages are being treated as both wikitext *and* HTML. |
||
| 280 | if ( $filterWikilinks ) { |
||
| 281 | $rv = str_replace( '[[', '', $rv ); |
||
| 282 | $rv = str_replace( ']]', '', $rv ); |
||
| 283 | } |
||
| 284 | |||
| 285 | return $rv; |
||
| 286 | } |
||
| 287 | |||
| 288 | /** |
||
| 289 | * @todo Document |
||
| 290 | * @param string $type |
||
| 291 | * @param Language|null $lang |
||
| 292 | * @param Title $title |
||
| 293 | * @param array $params |
||
| 294 | * @return string |
||
| 295 | */ |
||
| 296 | protected static function getTitleLink( $type, $lang, $title, &$params ) { |
||
| 297 | if ( !$lang ) { |
||
| 298 | return $title->getPrefixedText(); |
||
| 299 | } |
||
| 300 | |||
| 301 | if ( $title->isSpecialPage() ) { |
||
| 302 | list( $name, $par ) = SpecialPageFactory::resolveAlias( $title->getDBkey() ); |
||
| 303 | |||
| 304 | # Use the language name for log titles, rather than Log/X |
||
| 305 | if ( $name == 'Log' ) { |
||
| 306 | $logPage = new LogPage( $par ); |
||
| 307 | $titleLink = Linker::link( $title, $logPage->getName()->escaped() ); |
||
| 308 | $titleLink = wfMessage( 'parentheses' ) |
||
| 309 | ->inLanguage( $lang ) |
||
| 310 | ->rawParams( $titleLink ) |
||
| 311 | ->escaped(); |
||
| 312 | } else { |
||
| 313 | $titleLink = Linker::link( $title ); |
||
| 314 | } |
||
| 315 | } else { |
||
| 316 | $titleLink = Linker::link( $title ); |
||
| 317 | } |
||
| 318 | |||
| 319 | return $titleLink; |
||
| 320 | } |
||
| 321 | |||
| 322 | /** |
||
| 323 | * Add a log entry |
||
| 324 | * |
||
| 325 | * @param string $action One of '', 'block', 'protect', 'rights', 'delete', |
||
| 326 | * 'upload', 'move', 'move_redir' |
||
| 327 | * @param Title $target Title object |
||
| 328 | * @param string $comment Description associated |
||
| 329 | * @param array $params Parameters passed later to wfMessage function |
||
| 330 | * @param null|int|User $doer The user doing the action. null for $wgUser |
||
| 331 | * |
||
| 332 | * @return int The log_id of the inserted log entry |
||
| 333 | */ |
||
| 334 | public function addEntry( $action, $target, $comment, $params = [], $doer = null ) { |
||
| 335 | global $wgContLang; |
||
| 336 | |||
| 337 | if ( !is_array( $params ) ) { |
||
| 338 | $params = [ $params ]; |
||
| 339 | } |
||
| 340 | |||
| 341 | if ( $comment === null ) { |
||
| 342 | $comment = ''; |
||
| 343 | } |
||
| 344 | |||
| 345 | # Trim spaces on user supplied text |
||
| 346 | $comment = trim( $comment ); |
||
| 347 | |||
| 348 | # Truncate for whole multibyte characters. |
||
| 349 | $comment = $wgContLang->truncate( $comment, 255 ); |
||
| 350 | |||
| 351 | $this->action = $action; |
||
| 352 | $this->target = $target; |
||
| 353 | $this->comment = $comment; |
||
| 354 | $this->params = LogPage::makeParamBlob( $params ); |
||
| 355 | |||
| 356 | if ( $doer === null ) { |
||
| 357 | global $wgUser; |
||
| 358 | $doer = $wgUser; |
||
| 359 | } elseif ( !is_object( $doer ) ) { |
||
| 360 | $doer = User::newFromId( $doer ); |
||
| 361 | } |
||
| 362 | |||
| 363 | $this->doer = $doer; |
||
| 364 | |||
| 365 | $logEntry = new ManualLogEntry( $this->type, $action ); |
||
| 366 | $logEntry->setTarget( $target ); |
||
| 367 | $logEntry->setPerformer( $doer ); |
||
| 368 | $logEntry->setParameters( $params ); |
||
| 369 | // All log entries using the LogPage to insert into the logging table |
||
| 370 | // are using the old logging system and therefore the legacy flag is |
||
| 371 | // needed to say the LogFormatter the parameters have numeric keys |
||
| 372 | $logEntry->setLegacy( true ); |
||
| 373 | |||
| 374 | $formatter = LogFormatter::newFromEntry( $logEntry ); |
||
| 375 | $context = RequestContext::newExtraneousContext( $target ); |
||
| 376 | $formatter->setContext( $context ); |
||
| 377 | |||
| 378 | $this->actionText = $formatter->getPlainActionText(); |
||
| 379 | $this->ircActionText = $formatter->getIRCActionText(); |
||
| 380 | |||
| 381 | return $this->saveContent(); |
||
| 382 | } |
||
| 383 | |||
| 384 | /** |
||
| 385 | * Add relations to log_search table |
||
| 386 | * |
||
| 387 | * @param string $field |
||
| 388 | * @param array $values |
||
| 389 | * @param int $logid |
||
| 390 | * @return bool |
||
| 391 | */ |
||
| 392 | public function addRelations( $field, $values, $logid ) { |
||
| 393 | if ( !strlen( $field ) || empty( $values ) ) { |
||
| 394 | return false; // nothing |
||
| 395 | } |
||
| 396 | |||
| 397 | $data = []; |
||
| 398 | |||
| 399 | View Code Duplication | foreach ( $values as $value ) { |
|
| 400 | $data[] = [ |
||
| 401 | 'ls_field' => $field, |
||
| 402 | 'ls_value' => $value, |
||
| 403 | 'ls_log_id' => $logid |
||
| 404 | ]; |
||
| 405 | } |
||
| 406 | |||
| 407 | $dbw = wfGetDB( DB_MASTER ); |
||
| 408 | $dbw->insert( 'log_search', $data, __METHOD__, 'IGNORE' ); |
||
| 409 | |||
| 410 | return true; |
||
| 411 | } |
||
| 412 | |||
| 413 | /** |
||
| 414 | * Create a blob from a parameter array |
||
| 415 | * |
||
| 416 | * @param array $params |
||
| 417 | * @return string |
||
| 418 | */ |
||
| 419 | public static function makeParamBlob( $params ) { |
||
| 420 | return implode( "\n", $params ); |
||
| 421 | } |
||
| 422 | |||
| 423 | /** |
||
| 424 | * Extract a parameter array from a blob |
||
| 425 | * |
||
| 426 | * @param string $blob |
||
| 427 | * @return array |
||
| 428 | */ |
||
| 429 | public static function extractParams( $blob ) { |
||
| 430 | if ( $blob === '' ) { |
||
| 431 | return []; |
||
| 432 | } else { |
||
| 433 | return explode( "\n", $blob ); |
||
| 434 | } |
||
| 435 | } |
||
| 436 | |||
| 437 | /** |
||
| 438 | * Name of the log. |
||
| 439 | * @return Message |
||
| 440 | * @since 1.19 |
||
| 441 | */ |
||
| 442 | public function getName() { |
||
| 443 | global $wgLogNames; |
||
| 444 | |||
| 445 | // BC |
||
| 446 | if ( isset( $wgLogNames[$this->type] ) ) { |
||
| 447 | $key = $wgLogNames[$this->type]; |
||
| 448 | } else { |
||
| 449 | $key = 'log-name-' . $this->type; |
||
| 450 | } |
||
| 451 | |||
| 452 | return wfMessage( $key ); |
||
| 453 | } |
||
| 454 | |||
| 455 | /** |
||
| 456 | * Description of this log type. |
||
| 457 | * @return Message |
||
| 458 | * @since 1.19 |
||
| 459 | */ |
||
| 460 | public function getDescription() { |
||
| 461 | global $wgLogHeaders; |
||
| 462 | // BC |
||
| 463 | if ( isset( $wgLogHeaders[$this->type] ) ) { |
||
| 464 | $key = $wgLogHeaders[$this->type]; |
||
| 465 | } else { |
||
| 466 | $key = 'log-description-' . $this->type; |
||
| 467 | } |
||
| 468 | |||
| 469 | return wfMessage( $key ); |
||
| 470 | } |
||
| 471 | |||
| 472 | /** |
||
| 473 | * Returns the right needed to read this log type. |
||
| 474 | * @return string |
||
| 475 | * @since 1.19 |
||
| 476 | */ |
||
| 477 | public function getRestriction() { |
||
| 478 | global $wgLogRestrictions; |
||
| 479 | if ( isset( $wgLogRestrictions[$this->type] ) ) { |
||
| 480 | $restriction = $wgLogRestrictions[$this->type]; |
||
| 481 | } else { |
||
| 482 | // '' always returns true with $user->isAllowed() |
||
| 483 | $restriction = ''; |
||
| 484 | } |
||
| 485 | |||
| 486 | return $restriction; |
||
| 487 | } |
||
| 488 | |||
| 489 | /** |
||
| 490 | * Tells if this log is not viewable by all. |
||
| 491 | * @return bool |
||
| 492 | * @since 1.19 |
||
| 493 | */ |
||
| 494 | public function isRestricted() { |
||
| 495 | $restriction = $this->getRestriction(); |
||
| 496 | |||
| 497 | return $restriction !== '' && $restriction !== '*'; |
||
| 498 | } |
||
| 499 | } |
||
| 500 |
In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:
Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion: