2008-06-27 06:18:51 +00:00
|
|
|
<?php
|
|
|
|
|
2016-06-13 11:53:50 +00:00
|
|
|
use MediaWiki\Logger\LoggerFactory;
|
|
|
|
use MediaWiki\MediaWikiServices;
|
|
|
|
|
2013-10-24 22:10:36 +00:00
|
|
|
/**
|
|
|
|
* This class contains most of the business logic of AbuseFilter. It consists of mostly
|
|
|
|
* static functions that handle activities such as parsing edits, applying filters,
|
|
|
|
* logging actions, etc.
|
|
|
|
*/
|
2008-06-27 06:18:51 +00:00
|
|
|
class AbuseFilter {
|
2008-07-17 02:43:45 +00:00
|
|
|
public static $statsStoragePeriod = 86400;
|
2008-08-02 11:10:42 +00:00
|
|
|
public static $condLimitEnabled = true;
|
2016-06-28 22:50:38 +00:00
|
|
|
|
|
|
|
/** @var array Map of (filter ID => stdClass) */
|
|
|
|
private static $filterCache = [];
|
|
|
|
|
2008-08-02 11:10:42 +00:00
|
|
|
public static $condCount = 0;
|
2016-06-28 22:50:38 +00:00
|
|
|
public static $tagsToSet = array(); // FIXME: avoid global state here
|
|
|
|
|
2009-02-07 09:34:11 +00:00
|
|
|
public static $history_mappings = array(
|
|
|
|
'af_pattern' => 'afh_pattern',
|
|
|
|
'af_user' => 'afh_user',
|
|
|
|
'af_user_text' => 'afh_user_text',
|
|
|
|
'af_timestamp' => 'afh_timestamp',
|
|
|
|
'af_comments' => 'afh_comments',
|
|
|
|
'af_public_comments' => 'afh_public_comments',
|
|
|
|
'af_deleted' => 'afh_deleted',
|
2012-05-06 06:44:45 +00:00
|
|
|
'af_id' => 'afh_filter',
|
|
|
|
'af_group' => 'afh_group',
|
2009-02-07 09:34:11 +00:00
|
|
|
);
|
2009-01-27 01:31:42 +00:00
|
|
|
public static $builderValues = array(
|
2009-02-07 09:34:11 +00:00
|
|
|
'op-arithmetic' => array(
|
|
|
|
'+' => 'addition',
|
2012-02-10 18:20:41 +00:00
|
|
|
'-' => 'subtraction',
|
|
|
|
'*' => 'multiplication',
|
|
|
|
'/' => 'divide',
|
2009-02-07 09:34:11 +00:00
|
|
|
'%' => 'modulo',
|
|
|
|
'**' => 'pow'
|
|
|
|
),
|
|
|
|
'op-comparison' => array(
|
|
|
|
'==' => 'equal',
|
|
|
|
'!=' => 'notequal',
|
|
|
|
'<' => 'lt',
|
|
|
|
'>' => 'gt',
|
|
|
|
'<=' => 'lte',
|
|
|
|
'>=' => 'gte'
|
|
|
|
),
|
|
|
|
'op-bool' => array(
|
|
|
|
'!' => 'not',
|
|
|
|
'&' => 'and',
|
|
|
|
'|' => 'or',
|
|
|
|
'^' => 'xor'
|
|
|
|
),
|
|
|
|
'misc' => array(
|
|
|
|
'in' => 'in',
|
2009-03-19 05:11:55 +00:00
|
|
|
'contains' => 'contains',
|
2009-02-07 09:34:11 +00:00
|
|
|
'like' => 'like',
|
|
|
|
'""' => 'stringlit',
|
2009-03-19 05:11:55 +00:00
|
|
|
'rlike' => 'rlike',
|
2010-03-28 00:50:51 +00:00
|
|
|
'irlike' => 'irlike',
|
2009-03-19 05:11:55 +00:00
|
|
|
'cond ? iftrue : iffalse' => 'tern',
|
2011-06-17 16:25:46 +00:00
|
|
|
'if cond then iftrue elseiffalse end' => 'cond',
|
2009-02-07 09:34:11 +00:00
|
|
|
),
|
|
|
|
'funcs' => array(
|
|
|
|
'length(string)' => 'length',
|
|
|
|
'lcase(string)' => 'lcase',
|
2013-04-17 16:36:10 +00:00
|
|
|
'ucase(string)' => 'ucase',
|
2009-02-07 09:34:11 +00:00
|
|
|
'ccnorm(string)' => 'ccnorm',
|
|
|
|
'rmdoubles(string)' => 'rmdoubles',
|
|
|
|
'specialratio(string)' => 'specialratio',
|
|
|
|
'norm(string)' => 'norm',
|
2009-03-07 01:26:42 +00:00
|
|
|
'count(needle,haystack)' => 'count',
|
|
|
|
'rcount(needle,haystack)' => 'rcount',
|
2009-03-19 05:06:39 +00:00
|
|
|
'rmwhitespace(text)' => 'rmwhitespace',
|
|
|
|
'rmspecials(text)' => 'rmspecials',
|
|
|
|
'ip_in_range(ip, range)' => 'ip_in_range',
|
2009-03-26 02:03:32 +00:00
|
|
|
'contains_any(haystack,needle1,needle2,needle3)' => 'contains-any',
|
2009-04-01 05:05:23 +00:00
|
|
|
'substr(subject, offset, length)' => 'substr',
|
|
|
|
'strpos(haystack, needle)' => 'strpos',
|
|
|
|
'str_replace(subject, search, replace)' => 'str_replace',
|
2011-10-18 17:54:25 +00:00
|
|
|
'rescape(string)' => 'rescape',
|
2009-04-01 06:53:18 +00:00
|
|
|
'set_var(var,value)' => 'set_var',
|
2009-02-07 09:34:11 +00:00
|
|
|
),
|
|
|
|
'vars' => array(
|
2009-04-01 03:59:58 +00:00
|
|
|
'timestamp' => 'timestamp',
|
2009-02-11 20:00:33 +00:00
|
|
|
'accountname' => 'accountname',
|
|
|
|
'action' => 'action',
|
|
|
|
'added_lines' => 'addedlines',
|
|
|
|
'edit_delta' => 'delta',
|
|
|
|
'edit_diff' => 'diff',
|
|
|
|
'new_size' => 'newsize',
|
|
|
|
'old_size' => 'oldsize',
|
2016-09-13 08:40:21 +00:00
|
|
|
'new_content_model' => 'new-content-model',
|
|
|
|
'old_content_model' => 'old-content-model',
|
2009-02-11 20:00:33 +00:00
|
|
|
'removed_lines' => 'removedlines',
|
|
|
|
'summary' => 'summary',
|
|
|
|
'article_articleid' => 'article-id',
|
|
|
|
'article_namespace' => 'article-ns',
|
|
|
|
'article_text' => 'article-text',
|
|
|
|
'article_prefixedtext' => 'article-prefixedtext',
|
2015-09-28 18:03:35 +00:00
|
|
|
// 'article_views' => 'article-views', # May not be enabled, defined in getBuilderValues()
|
2009-02-11 20:00:33 +00:00
|
|
|
'moved_from_articleid' => 'movedfrom-id',
|
|
|
|
'moved_from_namespace' => 'movedfrom-ns',
|
|
|
|
'moved_from_text' => 'movedfrom-text',
|
|
|
|
'moved_from_prefixedtext' => 'movedfrom-prefixedtext',
|
|
|
|
'moved_to_articleid' => 'movedto-id',
|
|
|
|
'moved_to_namespace' => 'movedto-ns',
|
|
|
|
'moved_to_text' => 'movedto-text',
|
|
|
|
'moved_to_prefixedtext' => 'movedto-prefixedtext',
|
2010-08-19 21:12:09 +00:00
|
|
|
'user_editcount' => 'user-editcount',
|
2009-02-11 20:00:33 +00:00
|
|
|
'user_age' => 'user-age',
|
|
|
|
'user_name' => 'user-name',
|
|
|
|
'user_groups' => 'user-groups',
|
2014-03-31 04:59:36 +00:00
|
|
|
'user_rights' => 'user-rights',
|
2012-12-14 09:04:17 +00:00
|
|
|
'user_blocked' => 'user-blocked',
|
2009-02-11 20:00:33 +00:00
|
|
|
'user_emailconfirm' => 'user-emailconfirm',
|
|
|
|
'old_wikitext' => 'old-text',
|
|
|
|
'new_wikitext' => 'new-text',
|
|
|
|
'added_links' => 'added-links',
|
|
|
|
'removed_links' => 'removed-links',
|
|
|
|
'all_links' => 'all-links',
|
2013-04-24 14:53:12 +00:00
|
|
|
'new_pst' => 'new-pst',
|
2013-10-01 07:07:50 +00:00
|
|
|
'edit_diff_pst' => 'diff-pst',
|
|
|
|
'added_lines_pst' => 'addedlines-pst',
|
2009-02-11 20:00:33 +00:00
|
|
|
'new_text' => 'new-text-stripped',
|
|
|
|
'new_html' => 'new-html',
|
|
|
|
'article_restrictions_edit' => 'restrictions-edit',
|
|
|
|
'article_restrictions_move' => 'restrictions-move',
|
2012-10-25 14:02:40 +00:00
|
|
|
'article_restrictions_create' => 'restrictions-create',
|
|
|
|
'article_restrictions_upload' => 'restrictions-upload',
|
2009-02-11 20:00:33 +00:00
|
|
|
'article_recent_contributors' => 'recent-contributors',
|
2014-07-09 16:58:07 +00:00
|
|
|
'article_first_contributor' => 'first-contributor',
|
2015-09-28 18:03:35 +00:00
|
|
|
// 'old_text' => 'old-text-stripped', # Disabled, performance
|
|
|
|
// 'old_html' => 'old-html', # Disabled, performance
|
2009-02-26 12:15:14 +00:00
|
|
|
'old_links' => 'old-links',
|
2009-03-04 02:04:48 +00:00
|
|
|
'minor_edit' => 'minor-edit',
|
2009-06-03 15:10:44 +00:00
|
|
|
'file_sha1' => 'file-sha1',
|
2015-01-08 04:56:35 +00:00
|
|
|
'file_size' => 'file-size',
|
Add more file_* variables for file metadata
* file_mime
The MIME type of the file, e.g. 'image/png'.
* file_mediatype
The media type of the file, one of 'UNKNOWN', 'BITMAP', 'DRAWING',
'AUDIO', 'VIDEO', 'MULTIMEDIA', 'OFFICE', 'TEXT', 'EXECUTABLE', 'ARCHIVE'.
* file_width
Width of the image in pixels, or 0 if it's inapplicable (e.g. for
audio files).
* file_height
Height of the image in pixels, or 0 if it's inapplicable (e.g. for
audio files).
* file_bits_per_channel
Bits per color channel of the image, or 0 if it's inapplicable (e.g.
for audio files). The most common value is 8.
Bug: T131643
Change-Id: Id355515a18d3674393332c0f4094e34f9f522623
2016-04-04 19:12:08 +00:00
|
|
|
'file_mime' => 'file-mime',
|
|
|
|
'file_mediatype' => 'file-mediatype',
|
|
|
|
'file_width' => 'file-width',
|
|
|
|
'file_height' => 'file-height',
|
|
|
|
'file_bits_per_channel' => 'file-bits-per-channel',
|
2009-02-07 09:34:11 +00:00
|
|
|
),
|
2009-01-27 01:31:42 +00:00
|
|
|
);
|
2012-12-14 17:37:01 +00:00
|
|
|
|
2011-08-26 20:12:34 +00:00
|
|
|
public static $editboxName = null;
|
2009-03-11 07:49:56 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $context IContextSource
|
|
|
|
* @param $pageType
|
|
|
|
*/
|
2011-11-17 00:55:53 +00:00
|
|
|
public static function addNavigationLinks( IContextSource $context, $pageType ) {
|
2009-03-11 05:55:06 +00:00
|
|
|
$linkDefs = array(
|
2009-10-07 13:57:06 +00:00
|
|
|
'home' => 'Special:AbuseFilter',
|
|
|
|
'recentchanges' => 'Special:AbuseFilter/history',
|
|
|
|
'examine' => 'Special:AbuseFilter/examine',
|
|
|
|
'log' => 'Special:AbuseLog',
|
|
|
|
);
|
|
|
|
|
2011-11-17 00:55:53 +00:00
|
|
|
if ( $context->getUser()->isAllowed( 'abusefilter-modify' ) ) {
|
2009-07-17 16:59:14 +00:00
|
|
|
$linkDefs = array_merge( $linkDefs, array(
|
2015-09-28 18:03:35 +00:00
|
|
|
'test' => 'Special:AbuseFilter/test',
|
|
|
|
'tools' => 'Special:AbuseFilter/tools',
|
|
|
|
'import' => 'Special:AbuseFilter/import',
|
|
|
|
) );
|
2009-07-17 16:59:14 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-11 05:55:06 +00:00
|
|
|
// Save some translator work
|
|
|
|
$msgOverrides = array(
|
2009-10-07 13:57:06 +00:00
|
|
|
'recentchanges' => 'abusefilter-filter-log',
|
|
|
|
);
|
|
|
|
|
2009-03-11 05:55:06 +00:00
|
|
|
$links = array();
|
2016-12-04 15:13:16 +00:00
|
|
|
$linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $linkDefs as $name => $page ) {
|
2011-04-29 14:48:33 +00:00
|
|
|
// Give grep a chance to find the usages:
|
|
|
|
// abusefilter-topnav-home, abusefilter-topnav-test, abusefilter-topnav-examine
|
|
|
|
// abusefilter-topnav-log, abusefilter-topnav-tools, abusefilter-topnav-import
|
2009-03-11 05:55:06 +00:00
|
|
|
$msgName = "abusefilter-topnav-$name";
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2010-08-19 21:12:09 +00:00
|
|
|
if ( isset( $msgOverrides[$name] ) ) {
|
2009-03-11 05:55:06 +00:00
|
|
|
$msgName = $msgOverrides[$name];
|
2010-08-19 21:12:09 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2016-06-01 12:43:37 +00:00
|
|
|
$msg = $context->msg( $msgName )->parse();
|
2009-03-11 05:55:06 +00:00
|
|
|
$title = Title::newFromText( $page );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
if ( $name == $pageType ) {
|
2009-03-11 05:55:06 +00:00
|
|
|
$links[] = Xml::tags( 'strong', null, $msg );
|
|
|
|
} else {
|
2016-12-04 15:13:16 +00:00
|
|
|
$links[] = $linkRenderer->makeLink( $title, new HtmlArmor( $msg ) );
|
2009-03-11 05:55:06 +00:00
|
|
|
}
|
|
|
|
}
|
2009-03-11 07:49:56 +00:00
|
|
|
|
2016-06-01 12:43:37 +00:00
|
|
|
$linkStr = $context->msg( 'parentheses', $context->getLanguage()->pipeList( $links ) )->text();
|
|
|
|
$linkStr = $context->msg( 'abusefilter-topnav' )->parse() . " $linkStr";
|
2009-03-11 07:49:56 +00:00
|
|
|
|
2009-03-11 05:55:06 +00:00
|
|
|
$linkStr = Xml::tags( 'div', array( 'class' => 'mw-abusefilter-navigation' ), $linkStr );
|
2009-03-11 07:49:56 +00:00
|
|
|
|
2011-11-17 00:55:53 +00:00
|
|
|
$context->getOutput()->setSubtitle( $linkStr );
|
2009-03-11 05:55:06 +00:00
|
|
|
}
|
2008-07-15 08:46:17 +00:00
|
|
|
|
2011-02-10 17:32:57 +00:00
|
|
|
/**
|
|
|
|
* @static
|
|
|
|
* @param $user User
|
|
|
|
* @return AbuseFilterVariableHolder
|
|
|
|
*/
|
2008-06-27 06:18:51 +00:00
|
|
|
public static function generateUserVars( $user ) {
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars = new AbuseFilterVariableHolder;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-01-07 00:02:41 +00:00
|
|
|
$vars->setLazyLoadVar(
|
|
|
|
'user_editcount',
|
|
|
|
'simple-user-accessor',
|
|
|
|
array( 'user' => $user, 'method' => 'getEditCount' )
|
|
|
|
);
|
|
|
|
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars->setVar( 'user_name', $user->getName() );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-01-07 00:02:41 +00:00
|
|
|
$vars->setLazyLoadVar(
|
|
|
|
'user_emailconfirm',
|
|
|
|
'simple-user-accessor',
|
|
|
|
array( 'user' => $user, 'method' => 'getEmailAuthenticationTimestamp' )
|
|
|
|
);
|
|
|
|
|
|
|
|
$vars->setLazyLoadVar(
|
|
|
|
'user_age',
|
|
|
|
'user-age',
|
|
|
|
array( 'user' => $user, 'asof' => wfTimestampNow() )
|
|
|
|
);
|
|
|
|
|
|
|
|
$vars->setLazyLoadVar(
|
|
|
|
'user_groups',
|
|
|
|
'simple-user-accessor',
|
|
|
|
array( 'user' => $user, 'method' => 'getEffectiveGroups' )
|
|
|
|
);
|
|
|
|
|
2014-03-31 04:59:36 +00:00
|
|
|
$vars->setLazyLoadVar(
|
|
|
|
'user_rights',
|
|
|
|
'simple-user-accessor',
|
|
|
|
array( 'user' => $user, 'method' => 'getRights' )
|
|
|
|
);
|
|
|
|
|
2013-01-07 00:02:41 +00:00
|
|
|
$vars->setLazyLoadVar(
|
|
|
|
'user_blocked',
|
|
|
|
'simple-user-accessor',
|
|
|
|
array( 'user' => $user, 'method' => 'isBlocked' )
|
|
|
|
);
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2015-08-06 17:15:42 +00:00
|
|
|
Hooks::run( 'AbuseFilter-generateUserVars', array( $vars, $user ) );
|
2012-12-21 17:43:48 +00:00
|
|
|
|
2008-06-27 06:18:51 +00:00
|
|
|
return $vars;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @return array
|
|
|
|
*/
|
2009-03-05 02:43:05 +00:00
|
|
|
public static function getBuilderValues() {
|
|
|
|
static $realValues = null;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2010-08-19 21:12:09 +00:00
|
|
|
if ( $realValues ) {
|
|
|
|
return $realValues;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-05 02:43:05 +00:00
|
|
|
$realValues = self::$builderValues;
|
2012-12-14 17:37:01 +00:00
|
|
|
global $wgDisableCounters;
|
|
|
|
if ( !$wgDisableCounters ) {
|
|
|
|
$realValues['vars']['article_views'] = 'article-views';
|
|
|
|
}
|
2015-08-06 17:15:42 +00:00
|
|
|
Hooks::run( 'AbuseFilter-builder', array( &$realValues ) );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-05 02:43:05 +00:00
|
|
|
return $realValues;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $filter
|
|
|
|
* @return bool
|
|
|
|
*/
|
2009-03-17 13:18:33 +00:00
|
|
|
public static function filterHidden( $filter ) {
|
2012-01-03 17:29:10 +00:00
|
|
|
$globalIndex = self::decodeGlobalName( $filter );
|
|
|
|
if ( $globalIndex ) {
|
|
|
|
global $wgAbuseFilterCentralDB;
|
|
|
|
if ( !$wgAbuseFilterCentralDB ) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
$dbr = wfGetDB( DB_SLAVE, array(), $wgAbuseFilterCentralDB );
|
|
|
|
$filter = $globalIndex;
|
|
|
|
} else {
|
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
|
|
|
}
|
2015-02-20 21:39:37 +00:00
|
|
|
if ( $filter === 'new' ) {
|
|
|
|
return false;
|
|
|
|
};
|
2009-10-07 13:57:06 +00:00
|
|
|
$hidden = $dbr->selectField(
|
|
|
|
'abuse_filter',
|
|
|
|
'af_hidden',
|
|
|
|
array( 'af_id' => $filter ),
|
|
|
|
__METHOD__
|
|
|
|
);
|
2015-09-28 18:03:35 +00:00
|
|
|
|
2015-06-28 01:22:04 +00:00
|
|
|
return (bool)$hidden;
|
2009-03-17 13:18:33 +00:00
|
|
|
}
|
2009-01-28 23:54:41 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $val int
|
|
|
|
* @throws MWException
|
|
|
|
*/
|
2009-01-23 19:23:19 +00:00
|
|
|
public static function triggerLimiter( $val = 1 ) {
|
|
|
|
self::$condCount += $val;
|
|
|
|
|
|
|
|
global $wgAbuseFilterConditionLimit;
|
|
|
|
|
2013-05-27 22:43:33 +00:00
|
|
|
if ( self::$condLimitEnabled && self::$condCount > $wgAbuseFilterConditionLimit ) {
|
2009-10-07 13:57:06 +00:00
|
|
|
throw new MWException( 'Condition limit reached.' );
|
2009-01-23 19:23:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-18 02:18:58 +00:00
|
|
|
public static function disableConditionLimit() {
|
|
|
|
// For use in batch scripts and the like
|
|
|
|
self::$condLimitEnabled = false;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2011-02-10 17:32:57 +00:00
|
|
|
/**
|
2012-03-11 20:40:04 +00:00
|
|
|
* @param $title Title
|
|
|
|
* @param $prefix
|
2011-02-10 17:32:57 +00:00
|
|
|
* @return AbuseFilterVariableHolder
|
|
|
|
*/
|
2008-06-27 06:18:51 +00:00
|
|
|
public static function generateTitleVars( $title, $prefix ) {
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars = new AbuseFilterVariableHolder;
|
2009-02-03 18:48:16 +00:00
|
|
|
|
2010-08-19 21:12:09 +00:00
|
|
|
if ( !$title ) {
|
2015-06-28 01:22:04 +00:00
|
|
|
return $vars;
|
2010-08-19 21:12:09 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 19:04:37 +00:00
|
|
|
$vars->setVar( $prefix . '_ARTICLEID', $title->getArticleID() );
|
2009-10-07 13:57:06 +00:00
|
|
|
$vars->setVar( $prefix . '_NAMESPACE', $title->getNamespace() );
|
2010-08-19 21:12:09 +00:00
|
|
|
$vars->setVar( $prefix . '_TEXT', $title->getText() );
|
2009-10-07 13:57:06 +00:00
|
|
|
$vars->setVar( $prefix . '_PREFIXEDTEXT', $title->getPrefixedText() );
|
2015-06-28 01:02:51 +00:00
|
|
|
|
2012-12-14 17:37:01 +00:00
|
|
|
global $wgDisableCounters;
|
2013-01-21 19:56:23 +00:00
|
|
|
if ( !$wgDisableCounters && !$title->isSpecialPage() ) {
|
2015-06-28 01:02:51 +00:00
|
|
|
// Support: MediaWiki 1.24 and earlier
|
|
|
|
if ( method_exists( 'WikiPage', 'getCount' ) ) {
|
|
|
|
$vars->setVar( $prefix . '_VIEWS', WikiPage::factory( $title )->getCount() );
|
2015-09-28 18:03:35 +00:00
|
|
|
// Support: MediaWiki 1.25+ with HitCounters extension
|
2015-06-28 01:02:51 +00:00
|
|
|
} elseif ( method_exists( 'HitCounters\HitCounters', 'getCount' ) ) {
|
|
|
|
$vars->setVar( $prefix . '_VIEWS', HitCounters\HitCounters::getCount( $title ) );
|
|
|
|
}
|
2012-12-14 17:37:01 +00:00
|
|
|
}
|
2009-01-23 19:23:19 +00:00
|
|
|
|
|
|
|
// Use restrictions.
|
2009-02-26 12:15:14 +00:00
|
|
|
global $wgRestrictionTypes;
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $wgRestrictionTypes as $action ) {
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars->setLazyLoadVar( "{$prefix}_restrictions_$action", 'get-page-restrictions',
|
|
|
|
array( 'title' => $title->getText(),
|
2015-09-28 18:03:35 +00:00
|
|
|
'namespace' => $title->getNamespace(),
|
|
|
|
'action' => $action
|
|
|
|
)
|
|
|
|
);
|
2008-07-18 08:30:25 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars->setLazyLoadVar( "{$prefix}_recent_contributors", 'load-recent-authors',
|
2015-09-28 18:03:35 +00:00
|
|
|
array(
|
|
|
|
'title' => $title->getText(),
|
|
|
|
'namespace' => $title->getNamespace()
|
|
|
|
) );
|
2014-07-09 16:58:07 +00:00
|
|
|
|
|
|
|
$vars->setLazyLoadVar( "{$prefix}_first_contributor", 'load-first-author',
|
2015-09-28 18:03:35 +00:00
|
|
|
array(
|
|
|
|
'title' => $title->getText(),
|
|
|
|
'namespace' => $title->getNamespace()
|
|
|
|
) );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2015-08-06 17:15:42 +00:00
|
|
|
Hooks::run( 'AbuseFilter-generateTitleVars', array( $vars, $title, $prefix ) );
|
2012-12-21 17:43:48 +00:00
|
|
|
|
2008-08-02 11:10:42 +00:00
|
|
|
return $vars;
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $filter
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2008-08-03 14:04:26 +00:00
|
|
|
public static function checkSyntax( $filter ) {
|
|
|
|
global $wgAbuseFilterParserClass;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-10-15 12:35:03 +00:00
|
|
|
/** @var $parser AbuseFilterParser */
|
2008-08-03 14:04:26 +00:00
|
|
|
$parser = new $wgAbuseFilterParserClass;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2008-08-03 14:04:26 +00:00
|
|
|
return $parser->checkSyntax( $filter );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $expr
|
|
|
|
* @param array $vars
|
|
|
|
* @return string
|
|
|
|
*/
|
2008-08-04 14:27:48 +00:00
|
|
|
public static function evaluateExpression( $expr, $vars = array() ) {
|
|
|
|
global $wgAbuseFilterParserClass;
|
2009-01-30 23:23:52 +00:00
|
|
|
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( self::checkSyntax( $expr ) !== true ) {
|
2009-03-07 01:26:42 +00:00
|
|
|
return 'BADSYNTAX';
|
2009-01-30 23:23:52 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-10-15 12:35:03 +00:00
|
|
|
/** @var $parser AbuseFilterParser */
|
2013-01-07 00:02:41 +00:00
|
|
|
$parser = new $wgAbuseFilterParserClass( $vars );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2008-08-04 14:27:48 +00:00
|
|
|
return $parser->evaluateExpression( $expr );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $conds
|
|
|
|
* @param $vars
|
|
|
|
* @param $ignoreError bool
|
|
|
|
* @return bool
|
|
|
|
* @throws Exception
|
|
|
|
*/
|
2012-02-10 23:41:05 +00:00
|
|
|
public static function checkConditions(
|
Removing AbuseFilter::checkConditions' param
I'm not really sure why it exists; I assume to minimize overhead of init'ing
multiple AbuseFilterParser objects that would be exactly the same.
However, checkConditions() - which checks one specific condition - is not really
an "endpoint" you will want to call in <your-extension-that-uses-AbuseFilter>.
You're more likely to call something higher-lever (like
AbuseFilter::filterAction, which will take care of fetching all appropriate
filters, as well as run them through checkConditions, and much more). This will
trickle down to ::checkAllFilter, down to ::checkFilter, which would eventually
call ::checkConditions with the 'keepvars' argument.
Basically, unless you're re-implementing much of AbuseFilter yourself, you won't
get to pass anything other than 'keepvars' to checkFilter.
As a result, even though you may call AbuseFilter to call on multiple different
vars, it will re-use the same parser with the first vars.
I'm proposing to drop the 'keepvars' and just keep the vars around instead.
checkFilter can compare previous vars with new vars, and only init a new Parser
if the vars are different.
Change-Id: I96ccc60c77f3cdbb82c0f9f16782a1a44ffb1592
2013-12-30 11:06:34 +00:00
|
|
|
$conds, $vars, $ignoreError = true
|
2012-02-10 23:41:05 +00:00
|
|
|
) {
|
2008-08-02 11:10:42 +00:00
|
|
|
global $wgAbuseFilterParserClass;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
Removing AbuseFilter::checkConditions' param
I'm not really sure why it exists; I assume to minimize overhead of init'ing
multiple AbuseFilterParser objects that would be exactly the same.
However, checkConditions() - which checks one specific condition - is not really
an "endpoint" you will want to call in <your-extension-that-uses-AbuseFilter>.
You're more likely to call something higher-lever (like
AbuseFilter::filterAction, which will take care of fetching all appropriate
filters, as well as run them through checkConditions, and much more). This will
trickle down to ::checkAllFilter, down to ::checkFilter, which would eventually
call ::checkConditions with the 'keepvars' argument.
Basically, unless you're re-implementing much of AbuseFilter yourself, you won't
get to pass anything other than 'keepvars' to checkFilter.
As a result, even though you may call AbuseFilter to call on multiple different
vars, it will re-use the same parser with the first vars.
I'm proposing to drop the 'keepvars' and just keep the vars around instead.
checkFilter can compare previous vars with new vars, and only init a new Parser
if the vars are different.
Change-Id: I96ccc60c77f3cdbb82c0f9f16782a1a44ffb1592
2013-12-30 11:06:34 +00:00
|
|
|
static $parser, $lastVars;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
Removing AbuseFilter::checkConditions' param
I'm not really sure why it exists; I assume to minimize overhead of init'ing
multiple AbuseFilterParser objects that would be exactly the same.
However, checkConditions() - which checks one specific condition - is not really
an "endpoint" you will want to call in <your-extension-that-uses-AbuseFilter>.
You're more likely to call something higher-lever (like
AbuseFilter::filterAction, which will take care of fetching all appropriate
filters, as well as run them through checkConditions, and much more). This will
trickle down to ::checkAllFilter, down to ::checkFilter, which would eventually
call ::checkConditions with the 'keepvars' argument.
Basically, unless you're re-implementing much of AbuseFilter yourself, you won't
get to pass anything other than 'keepvars' to checkFilter.
As a result, even though you may call AbuseFilter to call on multiple different
vars, it will re-use the same parser with the first vars.
I'm proposing to drop the 'keepvars' and just keep the vars around instead.
checkFilter can compare previous vars with new vars, and only init a new Parser
if the vars are different.
Change-Id: I96ccc60c77f3cdbb82c0f9f16782a1a44ffb1592
2013-12-30 11:06:34 +00:00
|
|
|
if ( is_null( $parser ) || $vars !== $lastVars ) {
|
2013-10-15 12:35:03 +00:00
|
|
|
/** @var $parser AbuseFilterParser */
|
2013-01-07 00:02:41 +00:00
|
|
|
$parser = new $wgAbuseFilterParserClass( $vars );
|
Removing AbuseFilter::checkConditions' param
I'm not really sure why it exists; I assume to minimize overhead of init'ing
multiple AbuseFilterParser objects that would be exactly the same.
However, checkConditions() - which checks one specific condition - is not really
an "endpoint" you will want to call in <your-extension-that-uses-AbuseFilter>.
You're more likely to call something higher-lever (like
AbuseFilter::filterAction, which will take care of fetching all appropriate
filters, as well as run them through checkConditions, and much more). This will
trickle down to ::checkAllFilter, down to ::checkFilter, which would eventually
call ::checkConditions with the 'keepvars' argument.
Basically, unless you're re-implementing much of AbuseFilter yourself, you won't
get to pass anything other than 'keepvars' to checkFilter.
As a result, even though you may call AbuseFilter to call on multiple different
vars, it will re-use the same parser with the first vars.
I'm proposing to drop the 'keepvars' and just keep the vars around instead.
checkFilter can compare previous vars with new vars, and only init a new Parser
if the vars are different.
Change-Id: I96ccc60c77f3cdbb82c0f9f16782a1a44ffb1592
2013-12-30 11:06:34 +00:00
|
|
|
$lastVars = $vars;
|
2009-03-26 04:09:07 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-26 04:09:07 +00:00
|
|
|
try {
|
2008-08-03 14:04:26 +00:00
|
|
|
$result = $parser->parse( $conds, self::$condCount );
|
2009-10-07 13:57:06 +00:00
|
|
|
} catch ( Exception $excep ) {
|
2008-08-03 14:04:26 +00:00
|
|
|
// Sigh.
|
|
|
|
$result = false;
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2009-10-07 13:57:06 +00:00
|
|
|
wfDebugLog( 'AbuseFilter', 'AbuseFilter parser error: ' . $excep->getMessage() . "\n" );
|
|
|
|
|
|
|
|
if ( !$ignoreError ) {
|
2009-01-23 19:23:19 +00:00
|
|
|
throw $excep;
|
|
|
|
}
|
2008-08-03 14:04:26 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2008-08-02 11:10:42 +00:00
|
|
|
return $result;
|
2008-07-18 08:30:25 +00:00
|
|
|
}
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2011-08-24 22:11:52 +00:00
|
|
|
/**
|
|
|
|
* Returns an associative array of filters which were tripped
|
|
|
|
*
|
2016-06-13 11:53:50 +00:00
|
|
|
* @param $vars AbuseFilterVariableHolder
|
2014-11-07 12:21:45 +00:00
|
|
|
* @param string $group The filter's group (as defined in $wgAbuseFilterValidGroups)
|
2011-08-24 22:11:52 +00:00
|
|
|
*
|
2016-06-28 22:50:38 +00:00
|
|
|
* @return bool[] Map of (integer filter ID => bool)
|
2011-08-24 22:11:52 +00:00
|
|
|
*/
|
2012-05-06 06:44:45 +00:00
|
|
|
public static function checkAllFilters( $vars, $group = 'default' ) {
|
2015-05-13 05:46:33 +00:00
|
|
|
global $wgAbuseFilterCentralDB, $wgAbuseFilterIsCentral;
|
|
|
|
|
2008-06-27 06:18:51 +00:00
|
|
|
// Fetch from the database.
|
2009-02-02 17:57:06 +00:00
|
|
|
$filter_matched = array();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-30 06:12:12 +00:00
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
2010-08-19 21:12:09 +00:00
|
|
|
$res = $dbr->select(
|
|
|
|
'abuse_filter',
|
|
|
|
'*',
|
2012-05-06 06:44:45 +00:00
|
|
|
array(
|
|
|
|
'af_enabled' => 1,
|
|
|
|
'af_deleted' => 0,
|
|
|
|
'af_group' => $group,
|
|
|
|
),
|
2010-08-19 21:12:09 +00:00
|
|
|
__METHOD__
|
|
|
|
);
|
2009-02-02 17:57:06 +00:00
|
|
|
|
2015-09-28 18:03:35 +00:00
|
|
|
foreach ( $res as $row ) {
|
2009-05-26 13:08:15 +00:00
|
|
|
$filter_matched[$row->af_id] = self::checkFilter( $row, $vars, true );
|
2009-03-30 06:12:12 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
if ( $wgAbuseFilterCentralDB && !$wgAbuseFilterIsCentral ) {
|
2009-03-30 06:12:12 +00:00
|
|
|
// Global filters
|
2012-08-03 21:55:35 +00:00
|
|
|
$globalRulesKey = self::getGlobalRulesKey( $group );
|
|
|
|
|
2015-05-13 05:46:33 +00:00
|
|
|
$fname = __METHOD__;
|
|
|
|
$res = ObjectCache::getMainWANInstance()->getWithSetCallback(
|
|
|
|
$globalRulesKey,
|
2015-10-08 02:11:54 +00:00
|
|
|
WANObjectCache::TTL_INDEFINITE,
|
|
|
|
function () use ( $group, $fname ) {
|
2015-05-13 05:46:33 +00:00
|
|
|
global $wgAbuseFilterCentralDB;
|
|
|
|
|
|
|
|
$fdb = wfGetLB( $wgAbuseFilterCentralDB )->getConnectionRef(
|
|
|
|
DB_SLAVE, array(), $wgAbuseFilterCentralDB
|
|
|
|
);
|
|
|
|
|
|
|
|
return iterator_to_array( $fdb->select(
|
|
|
|
'abuse_filter',
|
|
|
|
'*',
|
|
|
|
array(
|
|
|
|
'af_enabled' => 1,
|
|
|
|
'af_deleted' => 0,
|
|
|
|
'af_global' => 1,
|
|
|
|
'af_group' => $group,
|
|
|
|
),
|
|
|
|
$fname
|
|
|
|
) );
|
|
|
|
},
|
2015-10-08 02:11:54 +00:00
|
|
|
array(
|
|
|
|
'checkKeys' => array( $globalRulesKey ),
|
|
|
|
'lockTSE' => 300
|
|
|
|
)
|
2015-05-13 05:46:33 +00:00
|
|
|
);
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2015-09-28 18:03:35 +00:00
|
|
|
foreach ( $res as $row ) {
|
2009-10-07 13:57:06 +00:00
|
|
|
$filter_matched['global-' . $row->af_id] =
|
2009-05-26 13:08:15 +00:00
|
|
|
self::checkFilter( $row, $vars, true, 'global-' );
|
2009-03-19 02:40:48 +00:00
|
|
|
}
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-01-23 19:23:19 +00:00
|
|
|
|
|
|
|
// Update statistics, and disable filters which are over-blocking.
|
2012-11-07 13:45:58 +00:00
|
|
|
self::recordStats( $filter_matched, $group );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-01-23 19:23:19 +00:00
|
|
|
return $filter_matched;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @static
|
|
|
|
* @param $row
|
|
|
|
* @param $vars
|
|
|
|
* @param $profile bool
|
|
|
|
* @param $prefix string
|
|
|
|
* @return bool
|
|
|
|
*/
|
2009-03-30 06:12:12 +00:00
|
|
|
public static function checkFilter( $row, $vars, $profile = false, $prefix = '' ) {
|
2016-04-08 16:22:39 +00:00
|
|
|
global $wgAbuseFilterProfile;
|
|
|
|
|
2010-02-13 14:10:36 +00:00
|
|
|
$filterID = $prefix . $row->af_id;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-10-15 12:28:39 +00:00
|
|
|
$startConds = $startTime = null;
|
2016-04-08 16:22:39 +00:00
|
|
|
if ( $profile && $wgAbuseFilterProfile ) {
|
2009-07-03 13:46:51 +00:00
|
|
|
$startConds = self::$condCount;
|
2009-10-07 13:57:06 +00:00
|
|
|
$startTime = microtime( true );
|
2009-07-03 13:46:51 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-30 06:12:12 +00:00
|
|
|
// Store the row somewhere convenient
|
2016-06-28 22:50:38 +00:00
|
|
|
self::$filterCache[$filterID] = $row;
|
2009-03-30 06:12:12 +00:00
|
|
|
|
|
|
|
// Check conditions...
|
2009-10-07 13:57:06 +00:00
|
|
|
$pattern = trim( $row->af_pattern );
|
2015-09-28 18:03:35 +00:00
|
|
|
if (
|
|
|
|
self::checkConditions(
|
|
|
|
$pattern,
|
|
|
|
$vars,
|
|
|
|
true /* ignore errors */
|
|
|
|
)
|
|
|
|
) {
|
2009-03-30 06:12:12 +00:00
|
|
|
// Record match.
|
|
|
|
$result = true;
|
|
|
|
} else {
|
|
|
|
// Record non-match.
|
|
|
|
$result = false;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2016-04-08 16:22:39 +00:00
|
|
|
if ( $profile && $wgAbuseFilterProfile ) {
|
2009-10-07 13:57:06 +00:00
|
|
|
$endTime = microtime( true );
|
2009-07-03 13:46:51 +00:00
|
|
|
$endConds = self::$condCount;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-30 06:12:12 +00:00
|
|
|
$timeTaken = $endTime - $startTime;
|
2009-07-03 13:46:51 +00:00
|
|
|
$condsUsed = $endConds - $startConds;
|
2016-04-08 13:51:54 +00:00
|
|
|
self::recordProfilingResult( $row->af_id, $timeTaken, $condsUsed );
|
2009-03-30 06:12:12 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-30 06:12:12 +00:00
|
|
|
return $result;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2016-04-08 13:51:54 +00:00
|
|
|
/**
|
|
|
|
* @param $filter
|
|
|
|
*/
|
|
|
|
public static function resetFilterProfile( $filter ) {
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash = ObjectCache::getMainStashInstance();
|
2016-04-08 13:51:54 +00:00
|
|
|
$countKey = wfMemcKey( 'abusefilter', 'profile', $filter, 'count' );
|
|
|
|
$totalKey = wfMemcKey( 'abusefilter', 'profile', $filter, 'total' );
|
2016-04-08 17:43:02 +00:00
|
|
|
$condsKey = wfMemcKey( 'abusefilter', 'profile', $filter, 'conds' );
|
2016-04-08 13:51:54 +00:00
|
|
|
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash->delete( $countKey );
|
|
|
|
$stash->delete( $totalKey );
|
|
|
|
$stash->delete( $condsKey );
|
2016-04-08 13:51:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param $filter
|
|
|
|
* @param $time
|
|
|
|
* @param $conds
|
|
|
|
*/
|
|
|
|
public static function recordProfilingResult( $filter, $time, $conds ) {
|
2016-04-13 18:06:24 +00:00
|
|
|
// Defer updates to avoid massive (~1 second) edit time increases
|
|
|
|
DeferredUpdates::addCallableUpdate( function () use ( $filter, $time, $conds ) {
|
|
|
|
$stash = ObjectCache::getMainStashInstance();
|
|
|
|
$countKey = wfMemcKey( 'abusefilter', 'profile', $filter, 'count' );
|
|
|
|
$totalKey = wfMemcKey( 'abusefilter', 'profile', $filter, 'total' );
|
|
|
|
$condsKey = wfMemcKey( 'abusefilter', 'profile', $filter, 'conds' );
|
|
|
|
|
|
|
|
$curCount = $stash->get( $countKey );
|
|
|
|
$curTotal = $stash->get( $totalKey );
|
|
|
|
$curConds = $stash->get( $condsKey );
|
|
|
|
|
|
|
|
if ( $curCount ) {
|
|
|
|
$stash->set( $condsKey, $curConds + $conds, 3600 );
|
|
|
|
$stash->set( $totalKey, $curTotal + $time, 3600 );
|
|
|
|
$stash->incr( $countKey );
|
|
|
|
} else {
|
|
|
|
$stash->set( $countKey, 1, 3600 );
|
|
|
|
$stash->set( $totalKey, $time, 3600 );
|
|
|
|
$stash->set( $condsKey, $conds, 3600 );
|
|
|
|
}
|
|
|
|
} );
|
2016-04-08 13:51:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @param $filter
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public static function getFilterProfile( $filter ) {
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash = ObjectCache::getMainStashInstance();
|
2016-04-08 13:51:54 +00:00
|
|
|
$countKey = wfMemcKey( 'abusefilter', 'profile', $filter, 'count' );
|
|
|
|
$totalKey = wfMemcKey( 'abusefilter', 'profile', $filter, 'total' );
|
2016-04-08 17:43:02 +00:00
|
|
|
$condsKey = wfMemcKey( 'abusefilter', 'profile', $filter, 'conds' );
|
2016-04-08 13:51:54 +00:00
|
|
|
|
2016-04-13 18:06:24 +00:00
|
|
|
$curCount = $stash->get( $countKey );
|
|
|
|
$curTotal = $stash->get( $totalKey );
|
|
|
|
$curConds = $stash->get( $condsKey );
|
2016-04-08 13:51:54 +00:00
|
|
|
|
|
|
|
if ( !$curCount ) {
|
|
|
|
return array( 0, 0 );
|
|
|
|
}
|
|
|
|
|
|
|
|
$timeProfile = ( $curTotal / $curCount ) * 1000; // 1000 ms in a sec
|
|
|
|
$timeProfile = round( $timeProfile, 2 ); // Return in ms, rounded to 2dp
|
|
|
|
|
2016-04-08 17:43:02 +00:00
|
|
|
$condProfile = ( $curConds / $curCount );
|
2016-04-08 13:51:54 +00:00
|
|
|
$condProfile = round( $condProfile, 0 );
|
|
|
|
|
|
|
|
return array( $timeProfile, $condProfile );
|
|
|
|
}
|
|
|
|
|
2011-08-24 22:11:52 +00:00
|
|
|
/**
|
|
|
|
* Utility function to decode global-$index to $index. Returns false if not global
|
|
|
|
*
|
|
|
|
* @param $filter string
|
|
|
|
*
|
2012-03-11 20:40:04 +00:00
|
|
|
* @return string|bool
|
2011-08-24 22:11:52 +00:00
|
|
|
*/
|
2009-03-30 06:12:12 +00:00
|
|
|
public static function decodeGlobalName( $filter ) {
|
|
|
|
if ( strpos( $filter, 'global-' ) == 0 ) {
|
2009-10-07 13:57:06 +00:00
|
|
|
return substr( $filter, strlen( 'global-' ) );
|
2009-03-30 06:12:12 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-30 06:12:12 +00:00
|
|
|
return false;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $filters array
|
|
|
|
* @return array
|
|
|
|
*/
|
2009-03-30 06:12:12 +00:00
|
|
|
public static function getConsequencesForFilters( $filters ) {
|
|
|
|
$globalFilters = array();
|
|
|
|
$localFilters = array();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $filters as $filter ) {
|
2009-03-30 06:12:12 +00:00
|
|
|
$globalIndex = self::decodeGlobalName( $filter );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2010-08-19 21:12:09 +00:00
|
|
|
if ( $globalIndex ) {
|
2009-03-30 06:12:12 +00:00
|
|
|
$globalFilters[] = $globalIndex;
|
2010-08-19 21:12:09 +00:00
|
|
|
} else {
|
2009-03-30 06:12:12 +00:00
|
|
|
$localFilters[] = $filter;
|
2010-08-19 21:12:09 +00:00
|
|
|
}
|
2009-03-30 06:12:12 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-30 06:12:12 +00:00
|
|
|
global $wgAbuseFilterCentralDB;
|
|
|
|
// Load local filter info
|
2009-01-23 19:23:19 +00:00
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
2008-06-27 06:18:51 +00:00
|
|
|
// Retrieve the consequences.
|
2009-03-30 06:12:12 +00:00
|
|
|
$consequences = array();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
if ( count( $localFilters ) ) {
|
2009-03-30 06:12:12 +00:00
|
|
|
$consequences = self::loadConsequencesFromDB( $dbr, $localFilters );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
if ( count( $globalFilters ) ) {
|
2009-03-30 06:12:12 +00:00
|
|
|
$fdb = wfGetDB( DB_SLAVE, array(), $wgAbuseFilterCentralDB );
|
2013-07-09 11:57:22 +00:00
|
|
|
$consequences = $consequences + self::loadConsequencesFromDB( $fdb, $globalFilters, 'global-' );
|
2009-03-30 06:12:12 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-30 06:12:12 +00:00
|
|
|
return $consequences;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $dbr DatabaseBase
|
|
|
|
* @param $filters array
|
|
|
|
* @param $prefix string
|
|
|
|
* @return array
|
|
|
|
*/
|
2009-10-07 13:57:06 +00:00
|
|
|
public static function loadConsequencesFromDB( $dbr, $filters, $prefix = '' ) {
|
2009-03-30 06:12:12 +00:00
|
|
|
$actionsByFilter = array();
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $filters as $filter ) {
|
2009-10-07 13:57:06 +00:00
|
|
|
$actionsByFilter[$prefix . $filter] = array();
|
2009-03-30 06:12:12 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
$res = $dbr->select(
|
|
|
|
array( 'abuse_filter_action', 'abuse_filter' ),
|
|
|
|
'*',
|
|
|
|
array( 'af_id' => $filters ),
|
|
|
|
__METHOD__,
|
|
|
|
array(),
|
|
|
|
array( 'abuse_filter_action' => array( 'LEFT JOIN', 'afa_filter=af_id' ) )
|
|
|
|
);
|
|
|
|
|
2009-01-23 19:23:19 +00:00
|
|
|
// Categorise consequences by filter.
|
2016-06-03 18:01:56 +00:00
|
|
|
global $wgAbuseFilterRestrictions;
|
2015-09-28 18:03:35 +00:00
|
|
|
foreach ( $res as $row ) {
|
2009-02-07 09:34:11 +00:00
|
|
|
if ( $row->af_throttled
|
2016-06-03 18:01:56 +00:00
|
|
|
&& !empty( $wgAbuseFilterRestrictions[$row->afa_consequence] )
|
2015-09-28 18:03:35 +00:00
|
|
|
) {
|
2010-08-19 21:12:09 +00:00
|
|
|
# Don't do the action
|
2009-02-13 01:40:57 +00:00
|
|
|
} elseif ( $row->afa_filter != $row->af_id ) {
|
|
|
|
// We probably got a NULL, as it's a LEFT JOIN.
|
2009-10-07 13:57:06 +00:00
|
|
|
// Don't add it.
|
2009-01-27 20:18:58 +00:00
|
|
|
} else {
|
2009-10-07 13:57:06 +00:00
|
|
|
$actionsByFilter[$prefix . $row->afa_filter][$row->afa_consequence] = array(
|
2009-02-07 09:34:11 +00:00
|
|
|
'action' => $row->afa_consequence,
|
|
|
|
'parameters' => explode( "\n", $row->afa_parameters )
|
|
|
|
);
|
2009-01-27 20:18:58 +00:00
|
|
|
}
|
2009-01-23 19:23:19 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-30 06:12:12 +00:00
|
|
|
return $actionsByFilter;
|
|
|
|
}
|
|
|
|
|
2011-08-24 22:11:52 +00:00
|
|
|
/**
|
2013-01-08 14:52:49 +00:00
|
|
|
* Executes a list of actions.
|
2011-08-24 22:11:52 +00:00
|
|
|
*
|
|
|
|
* @param $filters array
|
|
|
|
* @param $title Title
|
2016-06-13 11:53:50 +00:00
|
|
|
* @param $vars AbuseFilterVariableHolder
|
2013-01-08 14:52:49 +00:00
|
|
|
* @return Status returns the operation's status. $status->isOK() will return true if
|
|
|
|
* there were no actions taken, false otherwise. $status->getValue() will return
|
|
|
|
* an array listing the actions taken. $status-getErrors(), etc, will provide
|
|
|
|
* the errors and warnings to be shown to the user to explain the actions.
|
2011-08-24 22:11:52 +00:00
|
|
|
*/
|
2009-03-30 06:12:12 +00:00
|
|
|
public static function executeFilterActions( $filters, $title, $vars ) {
|
2013-10-28 05:47:14 +00:00
|
|
|
global $wgMainCacheType;
|
2009-03-30 06:12:12 +00:00
|
|
|
|
|
|
|
$actionsByFilter = self::getConsequencesForFilters( $filters );
|
|
|
|
$actionsTaken = array_fill_keys( $filters, array() );
|
2009-01-23 19:23:19 +00:00
|
|
|
|
|
|
|
$messages = array();
|
|
|
|
|
2016-06-03 18:01:56 +00:00
|
|
|
global $wgOut, $wgAbuseFilterDisallowGlobalLocalBlocks, $wgAbuseFilterRestrictions;
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $actionsByFilter as $filter => $actions ) {
|
2009-01-23 19:23:19 +00:00
|
|
|
// Special-case handling for warnings.
|
2009-02-07 09:34:11 +00:00
|
|
|
$parsed_public_comments = $wgOut->parseInline(
|
2016-06-28 22:50:38 +00:00
|
|
|
self::getFilter( $filter )->af_public_comments
|
|
|
|
);
|
2009-01-23 19:23:44 +00:00
|
|
|
|
2017-01-02 11:42:06 +00:00
|
|
|
$global_filter = self::decodeGlobalName( $filter ) !== false;
|
2012-08-01 21:29:06 +00:00
|
|
|
|
2013-10-28 05:47:14 +00:00
|
|
|
// If the filter is throttled and throttling is available via object
|
|
|
|
// caching, check to see if the user has hit the throttle.
|
|
|
|
if ( !empty( $actions['throttle'] ) && $wgMainCacheType !== CACHE_NONE ) {
|
2009-01-23 19:23:44 +00:00
|
|
|
$parameters = $actions['throttle']['parameters'];
|
|
|
|
$throttleId = array_shift( $parameters );
|
|
|
|
list( $rateCount, $ratePeriod ) = explode( ',', array_shift( $parameters ) );
|
|
|
|
|
|
|
|
$hitThrottle = false;
|
|
|
|
|
|
|
|
// The rest are throttle-types.
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $parameters as $throttleType ) {
|
2009-02-07 09:34:11 +00:00
|
|
|
$hitThrottle = $hitThrottle || self::isThrottled(
|
2015-09-28 18:03:35 +00:00
|
|
|
$throttleId, $throttleType, $title, $rateCount, $ratePeriod, $global_filter );
|
2009-01-23 19:23:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
unset( $actions['throttle'] );
|
2010-02-13 14:10:36 +00:00
|
|
|
if ( !$hitThrottle ) {
|
2009-01-23 19:23:44 +00:00
|
|
|
$actionsTaken[$filter][] = 'throttle';
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-08-03 21:55:35 +00:00
|
|
|
if ( $wgAbuseFilterDisallowGlobalLocalBlocks && $global_filter ) {
|
2016-06-03 18:01:56 +00:00
|
|
|
$actions = array_diff_key( $actions, array_filter( $wgAbuseFilterRestrictions ) );
|
2012-08-03 21:55:35 +00:00
|
|
|
}
|
|
|
|
|
2009-01-23 19:23:19 +00:00
|
|
|
if ( !empty( $actions['warn'] ) ) {
|
|
|
|
$parameters = $actions['warn']['parameters'];
|
2015-09-28 18:03:35 +00:00
|
|
|
$warnKey = 'abusefilter-warned-' . md5( $title->getPrefixedText() ) . '-' . $filter;
|
2013-07-31 16:46:43 +00:00
|
|
|
|
|
|
|
// Make sure the session is started prior to using it
|
|
|
|
if ( session_id() === '' ) {
|
|
|
|
wfSetupSession();
|
|
|
|
}
|
|
|
|
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( !isset( $_SESSION[$warnKey] ) || !$_SESSION[$warnKey] ) {
|
2009-01-23 19:23:19 +00:00
|
|
|
$_SESSION[$warnKey] = true;
|
|
|
|
|
|
|
|
// Threaten them a little bit
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( !empty( $parameters[0] ) && strlen( $parameters[0] ) ) {
|
2009-02-07 09:34:11 +00:00
|
|
|
$msg = $parameters[0];
|
|
|
|
} else {
|
|
|
|
$msg = 'abusefilter-warning';
|
|
|
|
}
|
2013-01-08 14:52:49 +00:00
|
|
|
$messages[] = array( $msg, $parsed_public_comments, $filter );
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2009-01-23 19:23:44 +00:00
|
|
|
$actionsTaken[$filter][] = 'warn';
|
2009-01-23 19:23:19 +00:00
|
|
|
|
|
|
|
continue; // Don't do anything else.
|
|
|
|
} else {
|
|
|
|
// We already warned them
|
|
|
|
$_SESSION[$warnKey] = false;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-01-23 19:23:19 +00:00
|
|
|
unset( $actions['warn'] );
|
|
|
|
}
|
|
|
|
|
2009-03-13 08:02:05 +00:00
|
|
|
// prevent double warnings
|
2016-06-03 18:01:56 +00:00
|
|
|
if ( count( array_intersect_key( $actions, array_filter( $wgAbuseFilterRestrictions ) ) ) > 0 &&
|
2015-09-28 18:03:35 +00:00
|
|
|
!empty( $actions['disallow'] )
|
|
|
|
) {
|
2009-01-29 23:24:24 +00:00
|
|
|
unset( $actions['disallow'] );
|
|
|
|
}
|
|
|
|
|
2009-01-23 19:23:19 +00:00
|
|
|
// Do the rest of the actions
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $actions as $action => $info ) {
|
2009-02-07 09:34:11 +00:00
|
|
|
$newMsg = self::takeConsequenceAction(
|
2016-06-28 22:50:38 +00:00
|
|
|
$action,
|
|
|
|
$info['parameters'],
|
|
|
|
$title,
|
|
|
|
$vars,
|
|
|
|
self::getFilter( $filter )->af_public_comments,
|
2013-06-30 17:32:31 +00:00
|
|
|
$filter
|
2009-10-07 13:57:06 +00:00
|
|
|
);
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2013-01-08 14:52:49 +00:00
|
|
|
if ( $newMsg !== null ) {
|
2009-01-23 19:23:19 +00:00
|
|
|
$messages[] = $newMsg;
|
2010-08-19 21:12:09 +00:00
|
|
|
}
|
2009-01-23 19:23:19 +00:00
|
|
|
$actionsTaken[$filter][] = $action;
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
|
|
|
}
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2015-02-04 18:25:21 +00:00
|
|
|
return self::buildStatus( $actionsTaken, $messages );
|
2013-01-08 14:52:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Constructs a Status object as returned by executeFilterActions() from the list of
|
|
|
|
* actions taken and the corresponding list of messages.
|
|
|
|
*
|
|
|
|
* @param array[] $actionsTaken associative array mapping each filter to the list if
|
|
|
|
* actions taken because of that filter.
|
|
|
|
* @param array[] $messages a list if arrays, where each array contains a message key
|
|
|
|
* followed by any message parameters.
|
|
|
|
*
|
|
|
|
* @return Status
|
|
|
|
*/
|
|
|
|
protected static function buildStatus( array $actionsTaken, array $messages ) {
|
|
|
|
$status = Status::newGood( $actionsTaken );
|
|
|
|
|
|
|
|
foreach ( $messages as $msg ) {
|
|
|
|
call_user_func_array( array( $status, 'fatal' ), $msg );
|
|
|
|
}
|
|
|
|
|
|
|
|
return $status;
|
2009-01-23 19:23:19 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $vars AbuseFilterVariableHolder
|
2012-09-02 11:07:02 +00:00
|
|
|
* @param $title Title
|
2014-11-07 12:21:45 +00:00
|
|
|
* @param string $group The filter's group (as defined in $wgAbuseFilterValidGroups)
|
2016-05-15 15:35:33 +00:00
|
|
|
* @param User $user The user performing the action; defaults to $wgUser
|
2016-06-13 11:53:50 +00:00
|
|
|
* @param string $mode Use 'execute' to run filters and log or 'stash' to only cache matches
|
2013-01-08 14:52:49 +00:00
|
|
|
* @return Status
|
2012-03-11 20:40:04 +00:00
|
|
|
*/
|
2016-06-13 11:53:50 +00:00
|
|
|
public static function filterAction(
|
|
|
|
$vars, $title, $group = 'default', $user = null, $mode = 'execute'
|
|
|
|
) {
|
2012-05-08 07:48:19 +00:00
|
|
|
global $wgUser, $wgTitle, $wgRequest;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-09-30 18:37:52 +00:00
|
|
|
$context = RequestContext::getMain();
|
|
|
|
$oldContextTitle = $context->getTitle();
|
|
|
|
|
|
|
|
$oldWgTitle = $wgTitle;
|
2016-07-15 14:03:05 +00:00
|
|
|
|
2010-08-19 21:12:09 +00:00
|
|
|
if ( !$wgTitle ) {
|
2009-03-16 23:02:18 +00:00
|
|
|
$wgTitle = SpecialPage::getTitleFor( 'AbuseFilter' );
|
2010-08-19 21:12:09 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2016-05-15 15:35:33 +00:00
|
|
|
if ( !$user ) {
|
|
|
|
$user = $wgUser;
|
|
|
|
}
|
|
|
|
|
2016-06-28 22:50:38 +00:00
|
|
|
$logger = LoggerFactory::getInstance( 'StashEdit' );
|
|
|
|
$statsd = MediaWikiServices::getInstance()->getStatsdDataFactory();
|
|
|
|
|
2009-03-05 02:43:05 +00:00
|
|
|
// Add vars from extensions
|
2015-08-06 17:15:42 +00:00
|
|
|
Hooks::run( 'AbuseFilter-filterAction', array( &$vars, $title ) );
|
2009-03-19 02:05:58 +00:00
|
|
|
$vars->setVar( 'context', 'filter' );
|
2009-04-01 03:59:58 +00:00
|
|
|
$vars->setVar( 'timestamp', time() );
|
2016-06-28 22:50:38 +00:00
|
|
|
|
2016-06-13 11:53:50 +00:00
|
|
|
// Get the stash key based on the relevant "input" variables
|
|
|
|
$cache = ObjectCache::getLocalClusterInstance();
|
|
|
|
$stashKey = self::getStashKey( $cache, $vars, $group );
|
2016-06-28 22:50:38 +00:00
|
|
|
$isForEdit = ( $vars->getVar( 'action' )->toString() === 'edit' );
|
2016-06-13 11:53:50 +00:00
|
|
|
|
|
|
|
$filter_matched = false;
|
2016-06-28 22:50:38 +00:00
|
|
|
if ( $mode === 'execute' && $isForEdit ) {
|
2016-06-13 11:53:50 +00:00
|
|
|
// Check the filter edit stash results first
|
2016-06-28 22:50:38 +00:00
|
|
|
$cacheData = $cache->get( $stashKey );
|
|
|
|
if ( $cacheData ) {
|
|
|
|
$filter_matched = $cacheData['matches'];
|
|
|
|
// Merge in any tags to apply to recent changes entries
|
|
|
|
self::bufferTagsToSetByAction( $cacheData['tags'] );
|
|
|
|
}
|
2016-06-13 11:53:50 +00:00
|
|
|
}
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2016-06-13 11:53:50 +00:00
|
|
|
if ( is_array( $filter_matched ) ) {
|
2016-08-30 03:37:14 +00:00
|
|
|
if ( $isForEdit && $mode !== 'stash' ) {
|
2016-06-28 22:50:38 +00:00
|
|
|
$logger->info( __METHOD__ . ": cache hit for '$title' (key $stashKey)." );
|
|
|
|
$statsd->increment( 'abusefilter.check-stash.hit' );
|
|
|
|
}
|
2016-06-13 11:53:50 +00:00
|
|
|
} else {
|
|
|
|
$filter_matched = self::checkAllFilters( $vars, $group );
|
2016-08-30 03:37:14 +00:00
|
|
|
if ( $isForEdit && $mode !== 'stash' ) {
|
2016-06-28 22:50:38 +00:00
|
|
|
$logger->info( __METHOD__ . ": cache miss for '$title' (key $stashKey)." );
|
|
|
|
$statsd->increment( 'abusefilter.check-stash.miss' );
|
|
|
|
}
|
2016-06-13 11:53:50 +00:00
|
|
|
}
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2016-06-13 11:53:50 +00:00
|
|
|
if ( $mode === 'stash' ) {
|
|
|
|
// Save the filter stash result and do nothing further
|
2016-06-28 22:50:38 +00:00
|
|
|
$cacheData = [ 'matches' => $filter_matched, 'tags' => self::$tagsToSet ];
|
|
|
|
$cache->set( $stashKey, $cacheData, $cache::TTL_MINUTE );
|
2016-09-26 22:20:58 +00:00
|
|
|
$logger->debug( __METHOD__ . ": cache store for '$title' (key $stashKey)." );
|
2016-06-13 11:53:50 +00:00
|
|
|
$statsd->increment( 'abusefilter.check-stash.store' );
|
|
|
|
|
|
|
|
return Status::newGood();
|
|
|
|
}
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2012-02-10 23:41:05 +00:00
|
|
|
$matched_filters = array_keys( array_filter( $filter_matched ) );
|
|
|
|
|
|
|
|
if ( count( $matched_filters ) == 0 ) {
|
2013-09-30 18:37:52 +00:00
|
|
|
$status = Status::newGood();
|
|
|
|
} else {
|
2016-06-13 11:53:50 +00:00
|
|
|
$status = self::executeFilterActions( $matched_filters, $title, $vars );
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2013-09-30 18:37:52 +00:00
|
|
|
$actions_taken = $status->value; // getValue() was introduced only in 1.20
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-09-30 18:37:52 +00:00
|
|
|
$action = $vars->getVar( 'ACTION' )->toString();
|
2013-01-08 14:52:49 +00:00
|
|
|
|
2016-02-03 21:07:25 +00:00
|
|
|
// If $wgUser isn't safe to load (e.g. a failure during
|
|
|
|
// AbortAutoAccount), create a dummy anonymous user instead.
|
2016-05-15 15:35:33 +00:00
|
|
|
$user = $user->isSafeToLoad() ? $user : new User;
|
2016-02-03 21:07:25 +00:00
|
|
|
|
2013-09-30 18:37:52 +00:00
|
|
|
// Create a template
|
|
|
|
$log_template = array(
|
2016-02-03 21:07:25 +00:00
|
|
|
'afl_user' => $user->getId(),
|
|
|
|
'afl_user_text' => $user->getName(),
|
2016-06-13 11:53:50 +00:00
|
|
|
'afl_timestamp' => wfGetDB( DB_SLAVE )->timestamp( wfTimestampNow() ),
|
2013-09-30 18:37:52 +00:00
|
|
|
'afl_namespace' => $title->getNamespace(),
|
|
|
|
'afl_title' => $title->getDBkey(),
|
|
|
|
'afl_ip' => $wgRequest->getIP()
|
|
|
|
);
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2013-09-30 18:37:52 +00:00
|
|
|
// Hack to avoid revealing IPs of people creating accounts
|
2016-02-03 21:07:25 +00:00
|
|
|
if ( !$user->getId() && ( $action == 'createaccount' || $action == 'autocreateaccount' ) ) {
|
2013-09-30 18:37:52 +00:00
|
|
|
$log_template['afl_user_text'] = $vars->getVar( 'accountname' )->toString();
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-09-30 18:37:52 +00:00
|
|
|
self::addLogEntries( $actions_taken, $log_template, $action, $vars, $group );
|
|
|
|
}
|
2009-02-02 23:30:48 +00:00
|
|
|
|
2013-09-30 18:37:52 +00:00
|
|
|
// Bug 53498: If we screwed around with $wgTitle, reset it so the title
|
|
|
|
// is correctly picked up from the request later. Do the same for the
|
|
|
|
// main RequestContext, because that might have picked up the bogus
|
|
|
|
// title from $wgTitle.
|
|
|
|
if ( $wgTitle !== $oldWgTitle ) {
|
|
|
|
$wgTitle = $oldWgTitle;
|
|
|
|
}
|
2014-07-20 20:04:37 +00:00
|
|
|
if ( $context->getTitle() !== $oldContextTitle && $oldContextTitle instanceof Title ) {
|
2013-09-30 18:37:52 +00:00
|
|
|
$context->setTitle( $oldContextTitle );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-01-08 14:52:49 +00:00
|
|
|
return $status;
|
2009-01-23 19:23:19 +00:00
|
|
|
}
|
|
|
|
|
2016-06-28 22:50:38 +00:00
|
|
|
/**
|
|
|
|
* @param string $id Filter ID (integer or "global-<integer>")
|
|
|
|
* @return stdClass|null DB row
|
|
|
|
*/
|
2016-07-15 21:55:13 +00:00
|
|
|
public static function getFilter( $id ) {
|
2016-06-28 22:50:38 +00:00
|
|
|
global $wgAbuseFilterCentralDB;
|
|
|
|
|
|
|
|
if ( !isset( self::$filterCache[$id] ) ) {
|
2017-01-02 11:42:06 +00:00
|
|
|
$globalIndex = self::decodeGlobalName( $id );
|
|
|
|
if ( $globalIndex ) {
|
2016-06-28 22:50:38 +00:00
|
|
|
// Global wiki filter
|
|
|
|
if ( !$wgAbuseFilterCentralDB ) {
|
|
|
|
return null; // not enabled
|
|
|
|
}
|
|
|
|
|
2017-01-02 11:42:06 +00:00
|
|
|
$id = $globalIndex;
|
2016-06-28 22:50:38 +00:00
|
|
|
$lb = wfGetLB( $wgAbuseFilterCentralDB );
|
|
|
|
$dbr = $lb->getConnectionRef( DB_SLAVE, array(), $wgAbuseFilterCentralDB );
|
|
|
|
} else {
|
|
|
|
// Local wiki filter
|
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
|
|
|
}
|
|
|
|
|
|
|
|
$row = $dbr->selectRow( 'abuse_filter', '*', [ 'af_id' => $id ], __METHOD__ );
|
|
|
|
self::$filterCache[$id] = $row ?: null;
|
|
|
|
}
|
|
|
|
|
|
|
|
return self::$filterCache[$id];
|
|
|
|
}
|
|
|
|
|
2016-06-13 11:53:50 +00:00
|
|
|
/**
|
|
|
|
* @param BagOStuff $cache
|
|
|
|
* @param $vars AbuseFilterVariableHolder
|
|
|
|
* @param string $group The filter's group (as defined in $wgAbuseFilterValidGroups)
|
|
|
|
*
|
|
|
|
* @return string
|
|
|
|
*/
|
|
|
|
private static function getStashKey(
|
|
|
|
BagOStuff $cache, AbuseFilterVariableHolder $vars, $group
|
|
|
|
) {
|
2016-06-28 20:46:01 +00:00
|
|
|
$inputVars = $vars->exportNonLazyVars();
|
2016-06-13 11:53:50 +00:00
|
|
|
// Exclude noisy fields that have superficial changes
|
|
|
|
unset( $inputVars['old_html'] );
|
|
|
|
unset( $inputVars['new_html'] );
|
|
|
|
unset( $inputVars['user_age'] );
|
|
|
|
unset( $inputVars['timestamp'] );
|
2016-07-15 14:03:05 +00:00
|
|
|
unset( $inputVars['_VIEWS'] );
|
2016-06-13 11:53:50 +00:00
|
|
|
ksort( $inputVars );
|
|
|
|
$hash = md5( serialize( $inputVars ) );
|
|
|
|
|
|
|
|
return ObjectCache::getLocalClusterInstance()->makeKey(
|
|
|
|
'abusefilter',
|
|
|
|
'check-stash',
|
|
|
|
$group,
|
2016-06-28 22:50:38 +00:00
|
|
|
$hash,
|
|
|
|
'v1'
|
2016-06-13 11:53:50 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $actions_taken
|
|
|
|
* @param $log_template
|
|
|
|
* @param $action
|
|
|
|
* @param $vars AbuseFilterVariableHolder
|
2014-11-07 12:21:45 +00:00
|
|
|
* @param string $group The filter's group (as defined in $wgAbuseFilterValidGroups)
|
2012-03-11 20:40:04 +00:00
|
|
|
* @return mixed
|
|
|
|
*/
|
2015-09-28 18:03:35 +00:00
|
|
|
public static function addLogEntries( $actions_taken, $log_template, $action,
|
|
|
|
$vars, $group = 'default'
|
|
|
|
) {
|
2008-06-27 06:18:51 +00:00
|
|
|
$dbw = wfGetDB( DB_MASTER );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-30 06:12:12 +00:00
|
|
|
$central_log_template = array(
|
2009-10-07 13:57:06 +00:00
|
|
|
'afl_wiki' => wfWikiID(),
|
2009-03-30 06:12:12 +00:00
|
|
|
);
|
2009-01-23 19:23:19 +00:00
|
|
|
|
|
|
|
$log_rows = array();
|
2009-03-30 06:12:12 +00:00
|
|
|
$central_log_rows = array();
|
|
|
|
$logged_local_filters = array();
|
|
|
|
$logged_global_filters = array();
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $actions_taken as $filter => $actions ) {
|
2009-03-30 06:12:12 +00:00
|
|
|
$globalIndex = self::decodeGlobalName( $filter );
|
2009-01-23 19:23:19 +00:00
|
|
|
$thisLog = $log_template;
|
|
|
|
$thisLog['afl_filter'] = $filter;
|
|
|
|
$thisLog['afl_action'] = $action;
|
2009-01-23 19:23:44 +00:00
|
|
|
$thisLog['afl_actions'] = implode( ',', $actions );
|
2009-01-23 19:23:19 +00:00
|
|
|
|
|
|
|
// Don't log if we were only throttling.
|
2009-02-13 01:40:57 +00:00
|
|
|
if ( $thisLog['afl_actions'] != 'throttle' ) {
|
2009-01-23 19:23:19 +00:00
|
|
|
$log_rows[] = $thisLog;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2010-08-19 21:12:09 +00:00
|
|
|
if ( !$globalIndex ) {
|
2012-11-07 13:45:58 +00:00
|
|
|
$logged_local_filters[] = $filter;
|
2010-08-19 21:12:09 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-30 06:12:12 +00:00
|
|
|
// Global logging
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( $globalIndex ) {
|
2012-03-02 22:02:40 +00:00
|
|
|
$title = Title::makeTitle( $thisLog['afl_namespace'], $thisLog['afl_title'] );
|
2009-03-30 06:12:12 +00:00
|
|
|
$centralLog = $thisLog + $central_log_template;
|
|
|
|
$centralLog['afl_filter'] = $globalIndex;
|
|
|
|
$centralLog['afl_title'] = $title->getPrefixedText();
|
|
|
|
$centralLog['afl_namespace'] = 0;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-30 06:12:12 +00:00
|
|
|
$central_log_rows[] = $centralLog;
|
|
|
|
$logged_global_filters[] = $globalIndex;
|
|
|
|
}
|
2008-09-21 13:08:10 +00:00
|
|
|
}
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( !count( $log_rows ) ) {
|
2008-09-21 13:08:10 +00:00
|
|
|
return;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-30 06:12:12 +00:00
|
|
|
// Only store the var dump if we're actually going to add log rows.
|
|
|
|
$var_dump = self::storeVarDump( $vars );
|
|
|
|
$var_dump = "stored-text:$var_dump"; // To distinguish from stuff stored directly
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
wfProfileIn( __METHOD__ . '-hitstats' );
|
|
|
|
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash = ObjectCache::getMainStashInstance();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-02-13 01:40:57 +00:00
|
|
|
// Increment trigger counter
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash->incr( self::filterMatchesKey() );
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2012-02-10 23:41:05 +00:00
|
|
|
$local_log_ids = array();
|
2012-12-15 17:14:42 +00:00
|
|
|
global $wgAbuseFilterNotifications, $wgAbuseFilterNotificationsPrivate;
|
2013-10-14 19:39:35 +00:00
|
|
|
foreach ( $log_rows as $data ) {
|
2012-04-14 10:10:35 +00:00
|
|
|
$data['afl_var_dump'] = $var_dump;
|
|
|
|
$data['afl_id'] = $dbw->nextSequenceValue( 'abuse_filter_log_afl_id_seq' );
|
|
|
|
$dbw->insert( 'abuse_filter_log', $data, __METHOD__ );
|
2012-07-24 17:55:58 +00:00
|
|
|
$local_log_ids[] = $dbw->insertId();
|
2012-04-14 10:10:35 +00:00
|
|
|
if ( $data['afl_id'] === null ) {
|
|
|
|
$data['afl_id'] = $dbw->insertId();
|
|
|
|
}
|
2016-12-21 14:56:32 +00:00
|
|
|
// Give grep a chance to find the usages:
|
|
|
|
// logentry-abusefilter-hit
|
2013-10-26 07:01:14 +00:00
|
|
|
$entry = new ManualLogEntry( 'abusefilter', 'hit' );
|
|
|
|
// Construct a user object
|
|
|
|
$user = User::newFromId( $data['afl_user'] );
|
|
|
|
$user->setName( $data['afl_user_text'] );
|
|
|
|
$entry->setPerformer( $user );
|
|
|
|
// Set action target
|
|
|
|
$entry->setTarget( Title::makeTitle( $data['afl_namespace'], $data['afl_title'] ) );
|
|
|
|
// Additional info
|
|
|
|
$entry->setParameters( array(
|
2015-09-28 18:03:35 +00:00
|
|
|
'action' => $data['afl_action'],
|
|
|
|
'filter' => $data['afl_filter'],
|
2013-10-26 07:01:14 +00:00
|
|
|
'actions' => $data['afl_actions'],
|
2015-09-28 18:03:35 +00:00
|
|
|
'log' => $data['afl_id'],
|
2013-10-26 07:01:14 +00:00
|
|
|
) );
|
|
|
|
|
|
|
|
// Send data to CheckUser if installed and we
|
|
|
|
// aren't already sending a notification to recentchanges
|
|
|
|
// Requires MW 1.23+
|
|
|
|
if ( is_callable( 'CheckUserHooks::updateCheckUserData' )
|
|
|
|
&& is_callable( 'ManualLogEntry::getRecentChange' )
|
2015-09-28 18:03:35 +00:00
|
|
|
&& strpos( $wgAbuseFilterNotifications, 'rc' ) === false
|
|
|
|
) {
|
2013-10-26 07:01:14 +00:00
|
|
|
$rc = $entry->getRecentChange();
|
|
|
|
CheckUserHooks::updateCheckUserData( $rc );
|
|
|
|
}
|
|
|
|
|
2012-04-14 10:10:35 +00:00
|
|
|
if ( $wgAbuseFilterNotifications !== false ) {
|
2012-12-15 17:14:42 +00:00
|
|
|
if ( self::filterHidden( $data['afl_filter'] ) && !$wgAbuseFilterNotificationsPrivate ) {
|
2012-12-13 10:29:16 +00:00
|
|
|
continue;
|
|
|
|
}
|
2012-04-14 10:10:35 +00:00
|
|
|
$entry->publish( 0, $wgAbuseFilterNotifications );
|
|
|
|
}
|
|
|
|
}
|
2009-02-13 01:40:57 +00:00
|
|
|
|
2013-08-28 21:30:59 +00:00
|
|
|
$method = __METHOD__;
|
|
|
|
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( count( $logged_local_filters ) ) {
|
2009-03-30 06:12:12 +00:00
|
|
|
// Update hit-counter.
|
2013-08-28 21:30:59 +00:00
|
|
|
$dbw->onTransactionPreCommitOrIdle(
|
2015-09-28 18:03:35 +00:00
|
|
|
function () use ( $dbw, $logged_local_filters, $method ) {
|
2013-08-28 21:30:59 +00:00
|
|
|
$dbw->update( 'abuse_filter',
|
|
|
|
array( 'af_hit_count=af_hit_count+1' ),
|
|
|
|
array( 'af_id' => $logged_local_filters ),
|
|
|
|
$method
|
|
|
|
);
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
);
|
2009-03-30 06:12:12 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-02-10 23:41:05 +00:00
|
|
|
$global_log_ids = array();
|
|
|
|
|
2009-03-30 06:12:12 +00:00
|
|
|
// Global stuff
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( count( $logged_global_filters ) ) {
|
2009-03-30 06:12:12 +00:00
|
|
|
$vars->computeDBVars();
|
2013-01-07 00:02:41 +00:00
|
|
|
$global_var_dump = self::storeVarDump( $vars, true );
|
2009-03-30 06:12:12 +00:00
|
|
|
$global_var_dump = "stored-text:$global_var_dump";
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $central_log_rows as $index => $data ) {
|
2009-03-30 06:12:12 +00:00
|
|
|
$central_log_rows[$index]['afl_var_dump'] = $global_var_dump;
|
|
|
|
}
|
|
|
|
|
|
|
|
global $wgAbuseFilterCentralDB;
|
|
|
|
$fdb = wfGetDB( DB_MASTER, array(), $wgAbuseFilterCentralDB );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2015-09-28 18:03:35 +00:00
|
|
|
foreach ( $central_log_rows as $row ) {
|
2012-02-10 23:41:05 +00:00
|
|
|
$fdb->insert( 'abuse_filter_log', $row, __METHOD__ );
|
|
|
|
$global_log_ids[] = $dbw->insertId();
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-08-28 21:30:59 +00:00
|
|
|
$fdb->onTransactionPreCommitOrIdle(
|
2015-09-28 18:03:35 +00:00
|
|
|
function () use ( $fdb, $logged_global_filters, $method ) {
|
2013-08-28 21:30:59 +00:00
|
|
|
$fdb->update( 'abuse_filter',
|
|
|
|
array( 'af_hit_count=af_hit_count+1' ),
|
|
|
|
array( 'af_id' => $logged_global_filters ),
|
|
|
|
$method
|
|
|
|
);
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
);
|
2009-03-30 06:12:12 +00:00
|
|
|
}
|
|
|
|
|
2012-02-10 23:41:05 +00:00
|
|
|
$vars->setVar( 'global_log_ids', $global_log_ids );
|
|
|
|
$vars->setVar( 'local_log_ids', $local_log_ids );
|
|
|
|
|
2009-02-13 01:40:57 +00:00
|
|
|
// Check for emergency disabling.
|
2016-04-13 18:06:24 +00:00
|
|
|
$total = $stash->get( AbuseFilter::filterUsedKey( $group ) );
|
2012-11-07 13:45:58 +00:00
|
|
|
self::checkEmergencyDisable( $group, $logged_local_filters, $total );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
wfProfileOut( __METHOD__ . '-hitstats' );
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Store a var dump to External Storage or the text table
|
|
|
|
* Some of this code is stolen from Revision::insertOn and friends
|
2011-08-24 22:11:52 +00:00
|
|
|
*
|
2013-01-07 00:02:41 +00:00
|
|
|
* @param $vars AbuseFilterVariableHolder
|
2011-08-24 22:11:52 +00:00
|
|
|
* @param $global bool
|
|
|
|
*
|
|
|
|
* @return int
|
2009-10-07 13:57:06 +00:00
|
|
|
*/
|
2009-03-30 06:12:12 +00:00
|
|
|
public static function storeVarDump( $vars, $global = false ) {
|
2010-08-19 21:12:09 +00:00
|
|
|
global $wgCompressRevisions;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-01-07 00:02:41 +00:00
|
|
|
// Get all variables yet set and compute old and new wikitext if not yet done
|
|
|
|
// as those are needed for the diff view on top of the abuse log pages
|
|
|
|
$vars = $vars->dumpAllVars( array( 'old_wikitext', 'new_wikitext' ) );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-01-07 00:02:41 +00:00
|
|
|
// Vars is an array with native PHP data types (non-objects) now
|
|
|
|
$text = serialize( $vars );
|
|
|
|
$flags = array( 'nativeDataArray' );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2010-02-13 14:10:36 +00:00
|
|
|
if ( $wgCompressRevisions ) {
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( function_exists( 'gzdeflate' ) ) {
|
2009-04-24 03:27:14 +00:00
|
|
|
$text = gzdeflate( $text );
|
|
|
|
$flags[] = 'gzip';
|
|
|
|
}
|
2009-02-27 03:06:19 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-02-27 03:06:19 +00:00
|
|
|
// Store to ES if applicable
|
2009-03-30 06:12:12 +00:00
|
|
|
global $wgDefaultExternalStore, $wgAbuseFilterCentralDB;
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( $wgDefaultExternalStore ) {
|
2010-08-19 21:12:09 +00:00
|
|
|
if ( $global ) {
|
2009-03-30 06:12:12 +00:00
|
|
|
$text = ExternalStore::insertToForeignDefault( $text, $wgAbuseFilterCentralDB );
|
2010-08-19 21:12:09 +00:00
|
|
|
} else {
|
2009-03-30 06:12:12 +00:00
|
|
|
$text = ExternalStore::insertToDefault( $text );
|
2010-08-19 21:12:09 +00:00
|
|
|
}
|
2009-02-27 03:06:19 +00:00
|
|
|
$flags[] = 'external';
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
if ( !$text ) {
|
2009-02-27 03:06:19 +00:00
|
|
|
// Not mission-critical, just return nothing
|
|
|
|
return null;
|
2009-04-17 08:08:35 +00:00
|
|
|
}
|
2009-02-27 03:06:19 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-02-27 03:06:19 +00:00
|
|
|
// Store to text table
|
2010-08-19 21:12:09 +00:00
|
|
|
if ( $global ) {
|
2009-03-30 06:12:12 +00:00
|
|
|
$dbw = wfGetDB( DB_MASTER, array(), $wgAbuseFilterCentralDB );
|
2010-08-19 21:12:09 +00:00
|
|
|
} else {
|
2009-03-30 06:12:12 +00:00
|
|
|
$dbw = wfGetDB( DB_MASTER );
|
2010-08-19 21:12:09 +00:00
|
|
|
}
|
2009-11-27 15:36:30 +00:00
|
|
|
$old_id = $dbw->nextSequenceValue( 'text_old_id_seq' );
|
2009-02-27 03:06:19 +00:00
|
|
|
$dbw->insert( 'text',
|
|
|
|
array(
|
2015-09-28 18:03:35 +00:00
|
|
|
'old_id' => $old_id,
|
|
|
|
'old_text' => $text,
|
2009-02-27 03:06:19 +00:00
|
|
|
'old_flags' => implode( ',', $flags ),
|
|
|
|
), __METHOD__
|
|
|
|
);
|
2015-09-28 18:03:35 +00:00
|
|
|
|
2015-02-04 18:25:21 +00:00
|
|
|
return $dbw->insertId();
|
2009-02-27 03:06:19 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve a var dump from External Storage or the text table
|
|
|
|
* Some of this code is stolen from Revision::loadText et al
|
2011-08-24 22:11:52 +00:00
|
|
|
*
|
|
|
|
* @param $stored_dump
|
|
|
|
*
|
2012-07-24 13:50:01 +00:00
|
|
|
* @return object|AbuseFilterVariableHolder|bool
|
2009-10-07 13:57:06 +00:00
|
|
|
*/
|
2009-02-27 03:06:19 +00:00
|
|
|
public static function loadVarDump( $stored_dump ) {
|
|
|
|
// Back-compat
|
2015-03-21 23:13:02 +00:00
|
|
|
if ( substr( $stored_dump, 0, strlen( 'stored-text:' ) ) !== 'stored-text:' ) {
|
|
|
|
$data = unserialize( $stored_dump );
|
|
|
|
if ( is_array( $data ) ) {
|
|
|
|
$vh = new AbuseFilterVariableHolder;
|
|
|
|
foreach ( $data as $name => $value ) {
|
|
|
|
$vh->setVar( $name, $value );
|
|
|
|
}
|
2015-09-28 18:03:35 +00:00
|
|
|
|
2015-03-21 23:13:02 +00:00
|
|
|
return $vh;
|
|
|
|
} else {
|
|
|
|
return $data;
|
|
|
|
}
|
2009-02-27 03:06:19 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-02-27 03:06:19 +00:00
|
|
|
$text_id = substr( $stored_dump, strlen( 'stored-text:' ) );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-02-27 03:06:19 +00:00
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-02-27 03:06:19 +00:00
|
|
|
$text_row = $dbr->selectRow(
|
2009-10-07 13:57:06 +00:00
|
|
|
'text',
|
|
|
|
array( 'old_text', 'old_flags' ),
|
|
|
|
array( 'old_id' => $text_id ),
|
|
|
|
__METHOD__
|
|
|
|
);
|
|
|
|
|
|
|
|
if ( !$text_row ) {
|
2009-02-27 03:06:19 +00:00
|
|
|
return new AbuseFilterVariableHolder;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-02-27 03:06:19 +00:00
|
|
|
$flags = explode( ',', $text_row->old_flags );
|
|
|
|
$text = $text_row->old_text;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-02-27 03:06:19 +00:00
|
|
|
if ( in_array( 'external', $flags ) ) {
|
|
|
|
$text = ExternalStore::fetchFromURL( $text );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-02-27 03:06:19 +00:00
|
|
|
if ( in_array( 'gzip', $flags ) ) {
|
|
|
|
$text = gzinflate( $text );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-02-27 03:06:19 +00:00
|
|
|
$obj = unserialize( $text );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-01-07 00:02:41 +00:00
|
|
|
if ( in_array( 'nativeDataArray', $flags ) ) {
|
|
|
|
$vars = $obj;
|
|
|
|
$obj = new AbuseFilterVariableHolder();
|
2015-09-28 18:03:35 +00:00
|
|
|
foreach ( $vars as $key => $value ) {
|
2013-01-07 00:02:41 +00:00
|
|
|
$obj->setVar( $key, $value );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-02-27 03:06:19 +00:00
|
|
|
return $obj;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
2012-03-11 21:01:29 +00:00
|
|
|
* @param $action string
|
|
|
|
* @param $parameters array
|
|
|
|
* @param $title Title
|
2012-03-11 20:40:04 +00:00
|
|
|
* @param $vars AbuseFilterVariableHolder
|
|
|
|
* @param $rule_desc
|
2013-06-30 17:32:31 +00:00
|
|
|
* @param $rule_number int|string
|
2013-01-08 14:52:49 +00:00
|
|
|
*
|
|
|
|
* @return array|null a message describing the action that was taken,
|
|
|
|
* or null if no action was taken. The message is given as an array
|
|
|
|
* containing the message key followed by any message parameters.
|
|
|
|
*
|
|
|
|
* @note: Returning the message as an array instead of a Message object is
|
|
|
|
* needed for compatibility with MW 1.20: we will be constructing a
|
|
|
|
* Status object from these messages, and before 1.21, Status did
|
|
|
|
* not accept Message objects to be added directly.
|
2012-03-11 20:40:04 +00:00
|
|
|
*/
|
2009-02-07 09:34:11 +00:00
|
|
|
public static function takeConsequenceAction( $action, $parameters, $title,
|
2015-09-28 18:03:35 +00:00
|
|
|
$vars, $rule_desc, $rule_number ) {
|
2012-05-08 07:48:19 +00:00
|
|
|
global $wgAbuseFilterCustomActionsHandlers, $wgRequest;
|
2011-12-28 00:26:13 +00:00
|
|
|
|
2013-01-08 14:52:49 +00:00
|
|
|
$message = null;
|
|
|
|
|
2009-10-07 13:57:06 +00:00
|
|
|
switch ( $action ) {
|
2008-06-27 06:18:51 +00:00
|
|
|
case 'disallow':
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( strlen( $parameters[0] ) ) {
|
2013-06-30 17:32:31 +00:00
|
|
|
$message = array( $parameters[0], $rule_desc, $rule_number );
|
2008-06-27 06:18:51 +00:00
|
|
|
} else {
|
|
|
|
// Generic message.
|
2013-01-08 14:52:49 +00:00
|
|
|
$message = array(
|
2012-09-02 11:07:02 +00:00
|
|
|
'abusefilter-disallowed',
|
2013-06-30 17:32:31 +00:00
|
|
|
$rule_desc,
|
|
|
|
$rule_number
|
2013-01-08 14:52:49 +00:00
|
|
|
);
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
|
|
|
break;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2008-06-27 06:18:51 +00:00
|
|
|
case 'block':
|
2016-03-14 17:17:23 +00:00
|
|
|
global $wgAbuseFilterBlockDuration, $wgAbuseFilterAnonBlockDuration, $wgUser;
|
2013-02-22 23:20:29 +00:00
|
|
|
if ( $wgUser->isAnon() && $wgAbuseFilterAnonBlockDuration !== null ) {
|
2015-09-28 18:03:35 +00:00
|
|
|
// The user isn't logged in and the anon block duration
|
|
|
|
// doesn't default to $wgAbuseFilterBlockDuration.
|
2013-02-22 23:20:29 +00:00
|
|
|
$expiry = $wgAbuseFilterAnonBlockDuration;
|
|
|
|
} else {
|
|
|
|
$expiry = $wgAbuseFilterBlockDuration;
|
|
|
|
}
|
|
|
|
|
2016-03-14 17:17:23 +00:00
|
|
|
self::doAbuseFilterBlock(
|
|
|
|
array(
|
|
|
|
'desc' => $rule_desc,
|
|
|
|
'number' => $rule_number
|
|
|
|
),
|
|
|
|
$wgUser->getName(),
|
|
|
|
$expiry,
|
|
|
|
true
|
|
|
|
);
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-01-08 14:52:49 +00:00
|
|
|
$message = array(
|
2012-09-02 11:07:02 +00:00
|
|
|
'abusefilter-blocked-display',
|
2013-06-30 17:32:31 +00:00
|
|
|
$rule_desc,
|
|
|
|
$rule_number
|
2013-01-08 14:52:49 +00:00
|
|
|
);
|
2008-06-27 06:18:51 +00:00
|
|
|
break;
|
2008-09-18 13:01:50 +00:00
|
|
|
case 'rangeblock':
|
2016-03-14 17:17:23 +00:00
|
|
|
self::doAbuseFilterBlock(
|
|
|
|
array(
|
|
|
|
'desc' => $rule_desc,
|
|
|
|
'number' => $rule_number
|
|
|
|
),
|
|
|
|
IP::sanitizeRange( $wgRequest->getIP() . '/16' ),
|
|
|
|
'1 week',
|
|
|
|
false
|
|
|
|
);
|
2008-09-18 13:01:50 +00:00
|
|
|
|
2013-01-08 14:52:49 +00:00
|
|
|
$message = array(
|
2012-09-02 11:07:02 +00:00
|
|
|
'abusefilter-blocked-display',
|
2013-06-30 17:32:31 +00:00
|
|
|
$rule_desc,
|
|
|
|
$rule_number
|
2013-01-08 14:52:49 +00:00
|
|
|
);
|
2008-09-18 13:01:50 +00:00
|
|
|
break;
|
2008-06-27 06:18:51 +00:00
|
|
|
case 'degroup':
|
|
|
|
global $wgUser;
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( !$wgUser->isAnon() ) {
|
2008-08-02 13:51:29 +00:00
|
|
|
// Remove all groups from the user. Ouch.
|
|
|
|
$groups = $wgUser->getGroups();
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $groups as $group ) {
|
2008-08-02 13:51:29 +00:00
|
|
|
$wgUser->removeGroup( $group );
|
|
|
|
}
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2013-01-08 14:52:49 +00:00
|
|
|
$message = array(
|
2012-09-02 11:07:02 +00:00
|
|
|
'abusefilter-degrouped',
|
2013-06-30 17:32:31 +00:00
|
|
|
$rule_desc,
|
|
|
|
$rule_number
|
2013-01-08 14:52:49 +00:00
|
|
|
);
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2009-01-26 18:52:41 +00:00
|
|
|
// Don't log it if there aren't any groups being removed!
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( !count( $groups ) ) {
|
2009-01-26 18:52:41 +00:00
|
|
|
break;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2008-08-02 13:51:29 +00:00
|
|
|
// Log it.
|
|
|
|
$log = new LogPage( 'rights' );
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2008-08-02 13:51:29 +00:00
|
|
|
$log->addEntry( 'rights',
|
|
|
|
$wgUser->getUserPage(),
|
2015-09-28 18:03:35 +00:00
|
|
|
wfMessage(
|
|
|
|
'abusefilter-degroupreason',
|
|
|
|
$rule_desc,
|
|
|
|
$rule_number
|
|
|
|
)->inContentLanguage()->text(),
|
2008-08-02 13:51:29 +00:00
|
|
|
array(
|
|
|
|
implode( ', ', $groups ),
|
2012-01-20 12:53:02 +00:00
|
|
|
''
|
2009-10-07 13:57:06 +00:00
|
|
|
),
|
|
|
|
self::getFilterUser()
|
|
|
|
);
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2008-06-27 06:18:51 +00:00
|
|
|
break;
|
|
|
|
case 'blockautopromote':
|
2015-05-21 21:54:30 +00:00
|
|
|
global $wgUser;
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( !$wgUser->isAnon() ) {
|
2010-02-13 14:10:36 +00:00
|
|
|
$blockPeriod = (int)mt_rand( 3 * 86400, 7 * 86400 ); // Block for 3-7 days.
|
2015-05-21 21:54:30 +00:00
|
|
|
ObjectCache::getMainStashInstance()->set(
|
|
|
|
self::autoPromoteBlockKey( $wgUser ), true, $blockPeriod
|
|
|
|
);
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2013-01-08 14:52:49 +00:00
|
|
|
$message = array(
|
2012-09-02 11:07:02 +00:00
|
|
|
'abusefilter-autopromote-blocked',
|
2013-06-30 17:32:31 +00:00
|
|
|
$rule_desc,
|
|
|
|
$rule_number
|
2013-01-08 14:52:49 +00:00
|
|
|
);
|
2008-08-02 13:51:29 +00:00
|
|
|
}
|
2008-06-27 06:18:51 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'flag':
|
|
|
|
// Do nothing. Here for completeness.
|
|
|
|
break;
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2009-01-28 19:08:18 +00:00
|
|
|
case 'tag':
|
|
|
|
// Mark with a tag on recentchanges.
|
|
|
|
global $wgUser;
|
|
|
|
|
|
|
|
$actionID = implode( '-', array(
|
2015-09-28 18:03:35 +00:00
|
|
|
$title->getPrefixedText(), $wgUser->getName(),
|
|
|
|
$vars->getVar( 'ACTION' )->toString()
|
|
|
|
) );
|
2009-01-28 19:08:18 +00:00
|
|
|
|
2016-06-28 22:50:38 +00:00
|
|
|
self::bufferTagsToSetByAction( [ $actionID => $parameters ] );
|
2009-01-28 19:08:18 +00:00
|
|
|
break;
|
|
|
|
default:
|
2015-09-28 18:03:35 +00:00
|
|
|
if ( isset( $wgAbuseFilterCustomActionsHandlers[$action] ) ) {
|
2011-11-06 01:15:55 +00:00
|
|
|
$custom_function = $wgAbuseFilterCustomActionsHandlers[$action];
|
2015-09-28 18:03:35 +00:00
|
|
|
if ( is_callable( $custom_function ) ) {
|
|
|
|
$msg = call_user_func(
|
|
|
|
$custom_function,
|
|
|
|
$action,
|
|
|
|
$parameters,
|
|
|
|
$title,
|
|
|
|
$vars,
|
|
|
|
$rule_desc,
|
|
|
|
$rule_number
|
|
|
|
);
|
2011-11-06 01:15:55 +00:00
|
|
|
}
|
2015-09-28 18:03:35 +00:00
|
|
|
if ( isset( $msg ) ) {
|
2013-01-08 14:52:49 +00:00
|
|
|
$message = array( $msg );
|
2011-11-06 01:15:55 +00:00
|
|
|
}
|
2011-11-06 21:05:03 +00:00
|
|
|
} else {
|
|
|
|
wfDebugLog( 'AbuseFilter', "Unrecognised action $action" );
|
2011-11-06 01:15:55 +00:00
|
|
|
}
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-01-08 14:52:49 +00:00
|
|
|
return $message;
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2016-06-28 22:50:38 +00:00
|
|
|
/**
|
|
|
|
* @param array[] $tagsByAction Map of (integer => string[])
|
|
|
|
*/
|
2016-07-27 20:22:39 +00:00
|
|
|
private static function bufferTagsToSetByAction( array $tagsByAction ) {
|
2016-06-28 22:50:38 +00:00
|
|
|
foreach ( $tagsByAction as $actionID => $tags ) {
|
|
|
|
if ( !isset( self::$tagsToSet[$actionID] ) ) {
|
|
|
|
self::$tagsToSet[$actionID] = $tags;
|
|
|
|
} else {
|
|
|
|
self::$tagsToSet[$actionID] = array_merge( self::$tagsToSet[$actionID], $tags );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-03-14 17:17:23 +00:00
|
|
|
/**
|
|
|
|
* Perform a block by the AbuseFilter system user
|
|
|
|
* @param array $rule should have 'desc' and 'number'
|
|
|
|
* @param string $target
|
|
|
|
* @param string $expiry
|
|
|
|
* @param bool $isAutoBlock
|
|
|
|
*/
|
|
|
|
protected static function doAbuseFilterBlock( array $rule, $target, $expiry, $isAutoBlock ) {
|
|
|
|
$filterUser = AbuseFilter::getFilterUser();
|
|
|
|
$reason = wfMessage(
|
|
|
|
'abusefilter-blockreason',
|
|
|
|
$rule['desc'], $rule['number']
|
|
|
|
)->inContentLanguage()->text();
|
|
|
|
|
|
|
|
$block = new Block();
|
|
|
|
$block->setTarget( $target );
|
|
|
|
$block->setBlocker( $filterUser );
|
|
|
|
$block->mReason = $reason;
|
|
|
|
$block->isHardblock( false );
|
|
|
|
$block->isAutoblocking( $isAutoBlock );
|
|
|
|
$block->prevents( 'createaccount', true );
|
|
|
|
$block->prevents( 'editownusertalk', false );
|
|
|
|
$block->mExpiry = SpecialBlock::parseExpiryInput( $expiry );
|
|
|
|
|
|
|
|
$success = $block->insert();
|
|
|
|
|
|
|
|
if ( $success ) {
|
|
|
|
// Log it only if the block was successful
|
|
|
|
$logParams = array();
|
|
|
|
$logParams['5::duration'] = ( $block->mExpiry === 'infinity' )
|
|
|
|
? 'indefinite'
|
|
|
|
: $expiry;
|
2016-03-31 15:42:30 +00:00
|
|
|
$flags = array( 'nocreate' );
|
2016-03-14 17:17:23 +00:00
|
|
|
if ( !$block->isAutoblocking() && !IP::isIPAddress( $target ) ) {
|
|
|
|
// Conditionally added same as SpecialBlock
|
|
|
|
$flags[] = 'noautoblock';
|
|
|
|
}
|
|
|
|
$logParams['6::flags'] = implode( ',', $flags );
|
|
|
|
|
|
|
|
$logEntry = new ManualLogEntry( 'block', 'block' );
|
|
|
|
$logEntry->setTarget( Title::makeTitle( NS_USER, $target ) );
|
|
|
|
$logEntry->setComment( $reason );
|
|
|
|
$logEntry->setPerformer( $filterUser );
|
|
|
|
$logEntry->setParameters( $logParams );
|
|
|
|
$blockIds = array_merge( array( $success['id'] ), $success['autoIds'] );
|
|
|
|
$logEntry->setRelations( array( 'ipb_id' => $blockIds ) );
|
|
|
|
$logEntry->publish( $logEntry->insert() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $throttleId
|
|
|
|
* @param $types
|
|
|
|
* @param $title
|
|
|
|
* @param $rateCount
|
|
|
|
* @param $ratePeriod
|
2012-09-02 11:07:02 +00:00
|
|
|
* @param $global bool
|
2012-03-11 20:40:04 +00:00
|
|
|
* @return bool
|
|
|
|
*/
|
2015-09-28 18:03:35 +00:00
|
|
|
public static function isThrottled( $throttleId, $types, $title, $rateCount,
|
|
|
|
$ratePeriod, $global = false
|
|
|
|
) {
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash = ObjectCache::getMainStashInstance();
|
2012-08-01 21:29:06 +00:00
|
|
|
$key = self::throttleKey( $throttleId, $types, $title, $global );
|
2016-04-13 18:06:24 +00:00
|
|
|
$count = intval( $stash->get( $key ) );
|
2009-02-03 23:44:47 +00:00
|
|
|
|
|
|
|
wfDebugLog( 'AbuseFilter', "Got value $count for throttle key $key\n" );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
if ( $count > 0 ) {
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash->incr( $key );
|
2009-02-03 23:44:47 +00:00
|
|
|
$count++;
|
|
|
|
wfDebugLog( 'AbuseFilter', "Incremented throttle key $key" );
|
2008-06-27 06:18:51 +00:00
|
|
|
} else {
|
2009-02-03 23:44:47 +00:00
|
|
|
wfDebugLog( 'AbuseFilter', "Added throttle key $key with value 1" );
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash->add( $key, 1, $ratePeriod );
|
2009-02-03 23:44:47 +00:00
|
|
|
$count = 1;
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-02-03 23:44:47 +00:00
|
|
|
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( $count > $rateCount ) {
|
2009-02-03 23:44:47 +00:00
|
|
|
wfDebugLog( 'AbuseFilter', "Throttle $key hit value $count -- maximum is $rateCount." );
|
2015-09-28 18:03:35 +00:00
|
|
|
|
2009-02-03 23:44:47 +00:00
|
|
|
return true; // THROTTLED
|
|
|
|
}
|
|
|
|
|
|
|
|
wfDebugLog( 'AbuseFilter', "Throttle $key not hit!" );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2008-06-27 06:18:51 +00:00
|
|
|
return false; // NOT THROTTLED
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $type
|
|
|
|
* @param $title Title
|
2013-10-15 12:27:10 +00:00
|
|
|
* @return int|string
|
2012-03-11 20:40:04 +00:00
|
|
|
*/
|
2008-06-27 06:18:51 +00:00
|
|
|
public static function throttleIdentifier( $type, $title ) {
|
2012-05-08 07:48:19 +00:00
|
|
|
global $wgUser, $wgRequest;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
switch ( $type ) {
|
2008-06-27 06:18:51 +00:00
|
|
|
case 'ip':
|
2012-05-08 07:48:19 +00:00
|
|
|
$identifier = $wgRequest->getIP();
|
2008-06-27 06:18:51 +00:00
|
|
|
break;
|
|
|
|
case 'user':
|
|
|
|
$identifier = $wgUser->getId();
|
|
|
|
break;
|
|
|
|
case 'range':
|
2012-05-08 07:48:19 +00:00
|
|
|
$identifier = substr( IP::toHex( $wgRequest->getIP() ), 0, 4 );
|
2008-06-27 06:18:51 +00:00
|
|
|
break;
|
|
|
|
case 'creationdate':
|
|
|
|
$reg = $wgUser->getRegistration();
|
2009-10-07 13:57:06 +00:00
|
|
|
$identifier = $reg - ( $reg % 86400 );
|
2008-06-27 06:18:51 +00:00
|
|
|
break;
|
|
|
|
case 'editcount':
|
|
|
|
// Hack for detecting different single-purpose accounts.
|
|
|
|
$identifier = $wgUser->getEditCount();
|
|
|
|
break;
|
|
|
|
case 'site':
|
2013-10-15 12:27:10 +00:00
|
|
|
$identifier = 1;
|
|
|
|
break;
|
2008-06-27 06:18:51 +00:00
|
|
|
case 'page':
|
2013-10-15 12:27:10 +00:00
|
|
|
$identifier = $title->getPrefixedText();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
$identifier = 0;
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2008-06-27 06:18:51 +00:00
|
|
|
return $identifier;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $throttleId
|
|
|
|
* @param $type
|
|
|
|
* @param $title Title
|
2012-09-02 11:07:02 +00:00
|
|
|
* @param $global bool
|
2012-03-11 20:40:04 +00:00
|
|
|
* @return String
|
|
|
|
*/
|
2015-09-28 18:03:35 +00:00
|
|
|
public static function throttleKey( $throttleId, $type, $title, $global = false ) {
|
2009-10-07 13:57:06 +00:00
|
|
|
$types = explode( ',', $type );
|
|
|
|
|
2008-06-27 06:18:51 +00:00
|
|
|
$identifiers = array();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $types as $subtype ) {
|
2008-06-27 06:18:51 +00:00
|
|
|
$identifiers[] = self::throttleIdentifier( $subtype, $title );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-09-17 23:52:05 +00:00
|
|
|
$identifier = sha1( implode( ':', $identifiers ) );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-08-01 21:29:06 +00:00
|
|
|
global $wgAbuseFilterIsCentral, $wgAbuseFilterCentralDB;
|
|
|
|
|
|
|
|
if ( $global && !$wgAbuseFilterIsCentral ) {
|
|
|
|
list ( $globalSite, $globalPrefix ) = wfSplitWikiID( $wgAbuseFilterCentralDB );
|
2015-09-28 18:03:35 +00:00
|
|
|
|
2012-08-01 21:29:06 +00:00
|
|
|
return wfForeignMemcKey(
|
|
|
|
$globalSite, $globalPrefix,
|
|
|
|
'abusefilter', 'throttle', $throttleId, $type, $identifier );
|
|
|
|
}
|
|
|
|
|
2008-06-27 06:18:51 +00:00
|
|
|
return wfMemcKey( 'abusefilter', 'throttle', $throttleId, $type, $identifier );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-08-03 21:55:35 +00:00
|
|
|
/**
|
2014-11-07 12:21:45 +00:00
|
|
|
* @param string $group The filter's group (as defined in $wgAbuseFilterValidGroups)
|
2012-08-03 21:55:35 +00:00
|
|
|
* @return String
|
|
|
|
*/
|
|
|
|
public static function getGlobalRulesKey( $group ) {
|
|
|
|
global $wgAbuseFilterIsCentral, $wgAbuseFilterCentralDB;
|
|
|
|
|
|
|
|
if ( !$wgAbuseFilterIsCentral ) {
|
|
|
|
list ( $globalSite, $globalPrefix ) = wfSplitWikiID( $wgAbuseFilterCentralDB );
|
|
|
|
|
|
|
|
return wfForeignMemcKey(
|
|
|
|
$globalSite, $globalPrefix,
|
|
|
|
'abusefilter', 'rules', $group
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return wfMemcKey( 'abusefilter', 'rules', $group );
|
|
|
|
}
|
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $user User
|
|
|
|
* @return String
|
|
|
|
*/
|
2008-06-27 06:18:51 +00:00
|
|
|
public static function autoPromoteBlockKey( $user ) {
|
|
|
|
return wfMemcKey( 'abusefilter', 'block-autopromote', $user->getId() );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
2012-08-03 21:55:35 +00:00
|
|
|
* Update statistics, and disable filters which are over-blocking.
|
2012-03-11 20:40:04 +00:00
|
|
|
* @param $filters
|
2014-11-07 12:21:45 +00:00
|
|
|
* @param string $group The filter's group (as defined in $wgAbuseFilterValidGroups)
|
2012-03-11 20:40:04 +00:00
|
|
|
*/
|
2012-11-07 13:45:58 +00:00
|
|
|
public static function recordStats( $filters, $group = 'default' ) {
|
2016-04-13 18:06:24 +00:00
|
|
|
global $wgAbuseFilterConditionLimit;
|
|
|
|
|
|
|
|
$stash = ObjectCache::getMainStashInstance();
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-01-23 19:23:19 +00:00
|
|
|
// Figure out if we've triggered overflows and blocks.
|
2009-10-07 13:57:06 +00:00
|
|
|
$overflow_triggered = ( self::$condCount > $wgAbuseFilterConditionLimit );
|
2009-01-23 19:23:19 +00:00
|
|
|
|
|
|
|
// Store some keys...
|
2008-07-17 02:43:45 +00:00
|
|
|
$overflow_key = self::filterLimitReachedKey();
|
2012-11-07 13:45:58 +00:00
|
|
|
$total_key = self::filterUsedKey( $group );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2016-04-13 18:06:24 +00:00
|
|
|
$total = $stash->get( $total_key );
|
2008-07-17 02:43:45 +00:00
|
|
|
|
2009-01-23 19:23:19 +00:00
|
|
|
$storage_period = self::$statsStoragePeriod;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
if ( !$total || $total > 10000 ) {
|
2014-11-07 12:15:28 +00:00
|
|
|
// This is for if the total doesn't exist, or has gone past 10,000.
|
2009-10-07 13:57:06 +00:00
|
|
|
// Recreate all the keys at the same time, so they expire together.
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash->set( $total_key, 0, $storage_period );
|
|
|
|
$stash->set( $overflow_key, 0, $storage_period );
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $filters as $filter => $matched ) {
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash->set( self::filterMatchesKey( $filter ), 0, $storage_period );
|
2008-07-17 02:43:45 +00:00
|
|
|
}
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash->set( self::filterMatchesKey(), 0, $storage_period );
|
2008-07-17 02:43:45 +00:00
|
|
|
}
|
2009-01-23 19:23:19 +00:00
|
|
|
|
|
|
|
// Increment total
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash->incr( $total_key );
|
2009-01-23 19:23:19 +00:00
|
|
|
|
|
|
|
// Increment overflow counter, if our condition limit overflowed
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( $overflow_triggered ) {
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash->incr( $overflow_key );
|
2008-07-17 02:43:45 +00:00
|
|
|
}
|
2009-01-23 19:23:19 +00:00
|
|
|
}
|
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
2014-11-07 12:21:45 +00:00
|
|
|
* @param string $group The filter's group (as defined in $wgAbuseFilterValidGroups)
|
2012-03-11 20:40:04 +00:00
|
|
|
* @param $filters
|
|
|
|
* @param $total
|
|
|
|
*/
|
2012-11-07 13:45:58 +00:00
|
|
|
public static function checkEmergencyDisable( $group, $filters, $total ) {
|
2009-02-07 09:34:11 +00:00
|
|
|
global $wgAbuseFilterEmergencyDisableThreshold, $wgAbuseFilterEmergencyDisableCount,
|
2016-04-13 18:06:24 +00:00
|
|
|
$wgAbuseFilterEmergencyDisableAge;
|
2009-01-23 19:23:19 +00:00
|
|
|
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash = ObjectCache::getMainStashInstance();
|
2012-11-07 13:45:58 +00:00
|
|
|
foreach ( $filters as $filter ) {
|
2012-10-23 13:14:34 +00:00
|
|
|
// determine emergency disable values for this action
|
2015-09-28 18:03:35 +00:00
|
|
|
$emergencyDisableThreshold =
|
|
|
|
self::getEmergencyValue( $wgAbuseFilterEmergencyDisableThreshold, $group );
|
|
|
|
$filterEmergencyDisableCount =
|
|
|
|
self::getEmergencyValue( $wgAbuseFilterEmergencyDisableCount, $group );
|
|
|
|
$emergencyDisableAge =
|
|
|
|
self::getEmergencyValue( $wgAbuseFilterEmergencyDisableAge, $group );
|
2012-10-23 13:14:34 +00:00
|
|
|
|
2009-02-13 18:30:34 +00:00
|
|
|
// Increment counter
|
2016-04-13 18:06:24 +00:00
|
|
|
$matchCount = $stash->get( self::filterMatchesKey( $filter ) );
|
2009-02-13 18:30:34 +00:00
|
|
|
|
|
|
|
// Handle missing keys...
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( !$matchCount ) {
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash->set( self::filterMatchesKey( $filter ), 1, self::$statsStoragePeriod );
|
2009-02-13 18:30:34 +00:00
|
|
|
} else {
|
2016-04-13 18:06:24 +00:00
|
|
|
$stash->incr( self::filterMatchesKey( $filter ) );
|
2009-02-13 18:30:34 +00:00
|
|
|
}
|
|
|
|
$matchCount++;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-02-13 18:30:34 +00:00
|
|
|
// Figure out if the filter is subject to being deleted.
|
2016-06-28 22:50:38 +00:00
|
|
|
$filter_age = wfTimestamp( TS_UNIX, self::getFilter( $filter )->af_timestamp );
|
2012-10-23 13:14:34 +00:00
|
|
|
$throttle_exempt_time = $filter_age + $emergencyDisableAge;
|
2009-02-13 18:30:34 +00:00
|
|
|
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( $total && $throttle_exempt_time > time()
|
2012-10-23 13:14:34 +00:00
|
|
|
&& $matchCount > $filterEmergencyDisableCount
|
2015-09-28 18:03:35 +00:00
|
|
|
&& ( $matchCount / $total ) > $emergencyDisableThreshold
|
|
|
|
) {
|
2009-02-13 18:30:34 +00:00
|
|
|
// More than $wgAbuseFilterEmergencyDisableCount matches,
|
2012-10-23 13:14:34 +00:00
|
|
|
// constituting more than $emergencyDisableThreshold
|
2009-02-13 18:30:34 +00:00
|
|
|
// (a fraction) of last few edits. Disable it.
|
2016-07-22 06:16:12 +00:00
|
|
|
DeferredUpdates::addUpdate(
|
|
|
|
new AutoCommitUpdate(
|
|
|
|
wfGetDB( DB_MASTER ),
|
|
|
|
__METHOD__,
|
|
|
|
function ( IDatabase $dbw, $fname ) use ( $filter ) {
|
|
|
|
$dbw->update( 'abuse_filter',
|
|
|
|
array( 'af_throttled' => 1 ),
|
|
|
|
array( 'af_id' => $filter ),
|
|
|
|
$fname
|
|
|
|
);
|
|
|
|
}
|
|
|
|
)
|
|
|
|
);
|
2008-07-17 02:43:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-10-23 13:14:34 +00:00
|
|
|
/**
|
|
|
|
* @param array $emergencyValue
|
2014-11-07 12:21:45 +00:00
|
|
|
* @param string $group The filter's group (as defined in $wgAbuseFilterValidGroups)
|
2012-10-23 13:14:34 +00:00
|
|
|
* @return mixed
|
|
|
|
*/
|
2012-11-07 13:45:58 +00:00
|
|
|
public static function getEmergencyValue( array $emergencyValue, $group ) {
|
|
|
|
return isset( $emergencyValue[$group] ) ? $emergencyValue[$group] : $emergencyValue['default'];
|
2012-10-23 13:14:34 +00:00
|
|
|
}
|
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @return String
|
|
|
|
*/
|
2008-07-17 02:43:45 +00:00
|
|
|
public static function filterLimitReachedKey() {
|
|
|
|
return wfMemcKey( 'abusefilter', 'stats', 'overflow' );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
2012-11-07 13:45:58 +00:00
|
|
|
* @param string $group The filter's group (as defined in $wgAbuseFilterValidGroups)
|
2012-03-11 20:40:04 +00:00
|
|
|
* @return String
|
|
|
|
*/
|
2012-11-07 13:45:58 +00:00
|
|
|
public static function filterUsedKey( $group = null ) {
|
|
|
|
return wfMemcKey( 'abusefilter', 'stats', 'total', $group );
|
2008-07-17 02:43:45 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $filter
|
|
|
|
* @return String
|
|
|
|
*/
|
2008-07-17 02:43:45 +00:00
|
|
|
public static function filterMatchesKey( $filter = null ) {
|
|
|
|
return wfMemcKey( 'abusefilter', 'stats', 'matches', $filter );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @return User
|
|
|
|
*/
|
2008-07-09 07:02:13 +00:00
|
|
|
public static function getFilterUser() {
|
2015-09-17 15:31:51 +00:00
|
|
|
$username = wfMessage( 'abusefilter-blocker' )->inContentLanguage()->text();
|
|
|
|
if ( method_exists( 'User', 'newSystemUser' ) ) {
|
|
|
|
$user = User::newSystemUser( $username, array( 'steal' => true ) );
|
2008-07-09 07:02:13 +00:00
|
|
|
} else {
|
2015-09-17 15:31:51 +00:00
|
|
|
$user = User::newFromName( $username );
|
|
|
|
$user->load();
|
|
|
|
if ( $user->getId() && $user->mPassword == '' ) {
|
|
|
|
// Already set up.
|
|
|
|
return $user;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Not set up. Create it.
|
|
|
|
if ( !$user->getId() ) {
|
|
|
|
print 'Trying to create account -- user id is ' . $user->getId();
|
|
|
|
$user->addToDatabase();
|
|
|
|
$user->saveSettings();
|
|
|
|
// Increment site_stats.ss_users
|
|
|
|
$ssu = new SiteStatsUpdate( 0, 0, 0, 0, 1 );
|
|
|
|
$ssu->doUpdate();
|
|
|
|
} else {
|
|
|
|
// Take over the account
|
|
|
|
$user->setPassword( null );
|
|
|
|
$user->setEmail( null );
|
|
|
|
$user->saveSettings();
|
|
|
|
}
|
2008-07-09 07:02:13 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2010-08-19 21:12:09 +00:00
|
|
|
// Promote user so it doesn't look too crazy.
|
2008-07-09 07:02:13 +00:00
|
|
|
$user->addGroup( 'sysop' );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2008-07-09 07:02:13 +00:00
|
|
|
return $user;
|
|
|
|
}
|
2009-01-23 22:49:13 +00:00
|
|
|
|
2012-02-09 17:00:34 +00:00
|
|
|
/**
|
|
|
|
* @param $rules String
|
|
|
|
* @param $textName String
|
|
|
|
* @param $addResultDiv Boolean
|
|
|
|
* @param $canEdit Boolean
|
2012-03-11 20:40:04 +00:00
|
|
|
* @return string
|
2012-02-09 17:00:34 +00:00
|
|
|
*/
|
2009-04-01 04:34:21 +00:00
|
|
|
static function buildEditBox( $rules, $textName = 'wpFilterRules', $addResultDiv = true,
|
2015-09-28 18:03:35 +00:00
|
|
|
$canEdit = true ) {
|
2009-01-23 22:49:13 +00:00
|
|
|
global $wgOut;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-02-09 17:00:34 +00:00
|
|
|
$textareaAttrib = array( 'dir' => 'ltr' ); # Rules are in English
|
|
|
|
if ( !$canEdit ) {
|
|
|
|
$textareaAttrib['readonly'] = 'readonly';
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-04-01 04:34:21 +00:00
|
|
|
global $wgUser;
|
|
|
|
$noTestAttrib = array();
|
|
|
|
if ( !$wgUser->isAllowed( 'abusefilter-modify' ) ) {
|
|
|
|
$noTestAttrib['disabled'] = 'disabled';
|
|
|
|
$addResultDiv = false;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
$rules = rtrim( $rules ) . "\n";
|
2014-01-11 14:16:47 +00:00
|
|
|
$rules = Xml::textarea( $textName, $rules, 40, 15, $textareaAttrib );
|
2009-01-23 22:49:13 +00:00
|
|
|
|
2012-02-10 00:26:39 +00:00
|
|
|
if ( $canEdit ) {
|
|
|
|
$dropDown = self::getBuilderValues();
|
|
|
|
// Generate builder drop-down
|
|
|
|
$builder = '';
|
2009-01-23 22:49:13 +00:00
|
|
|
|
2012-09-02 11:07:02 +00:00
|
|
|
$builder .= Xml::option( wfMessage( 'abusefilter-edit-builder-select' )->text() );
|
2009-01-23 22:49:13 +00:00
|
|
|
|
2012-02-10 00:26:39 +00:00
|
|
|
foreach ( $dropDown as $group => $values ) {
|
2013-08-18 06:35:16 +00:00
|
|
|
// Give grep a chance to find the usages:
|
|
|
|
// abusefilter-edit-builder-group-op-arithmetic, abusefilter-edit-builder-group-op-comparison,
|
|
|
|
// abusefilter-edit-builder-group-op-bool, abusefilter-edit-builder-group-misc,
|
|
|
|
// abusefilter-edit-builder-group-funcs, abusefilter-edit-builder-group-vars
|
2009-02-07 09:34:11 +00:00
|
|
|
$builder .=
|
2012-02-10 00:26:39 +00:00
|
|
|
Xml::openElement(
|
|
|
|
'optgroup',
|
2012-09-02 11:07:02 +00:00
|
|
|
array( 'label' => wfMessage( "abusefilter-edit-builder-group-$group" )->text() )
|
2009-02-07 09:34:11 +00:00
|
|
|
) . "\n";
|
2009-01-23 22:49:13 +00:00
|
|
|
|
2012-02-10 00:26:39 +00:00
|
|
|
foreach ( $values as $content => $name ) {
|
|
|
|
$builder .=
|
|
|
|
Xml::option(
|
2012-09-02 11:07:02 +00:00
|
|
|
wfMessage( "abusefilter-edit-builder-$group-$name" )->text(),
|
2012-02-10 00:26:39 +00:00
|
|
|
$content
|
|
|
|
) . "\n";
|
|
|
|
}
|
2009-01-23 22:49:13 +00:00
|
|
|
|
2012-02-10 00:26:39 +00:00
|
|
|
$builder .= Xml::closeElement( 'optgroup' ) . "\n";
|
|
|
|
}
|
2009-01-23 22:49:13 +00:00
|
|
|
|
2012-02-10 00:26:39 +00:00
|
|
|
$rules .=
|
|
|
|
Xml::tags(
|
|
|
|
'select',
|
|
|
|
array( 'id' => 'wpFilterBuilder', ),
|
|
|
|
$builder
|
|
|
|
) . ' ';
|
|
|
|
|
|
|
|
// Add syntax checking
|
|
|
|
$rules .= Xml::element( 'input',
|
|
|
|
array(
|
|
|
|
'type' => 'button',
|
2012-09-02 11:07:02 +00:00
|
|
|
'value' => wfMessage( 'abusefilter-edit-check' )->text(),
|
2012-02-10 00:26:39 +00:00
|
|
|
'id' => 'mw-abusefilter-syntaxcheck'
|
|
|
|
) + $noTestAttrib );
|
|
|
|
}
|
2009-01-29 22:44:31 +00:00
|
|
|
|
2016-01-06 20:17:41 +00:00
|
|
|
if ( $addResultDiv ) {
|
2010-02-13 14:10:36 +00:00
|
|
|
$rules .= Xml::element( 'div',
|
|
|
|
array( 'id' => 'mw-abusefilter-syntaxresult', 'style' => 'display: none;' ),
|
2010-05-30 17:33:59 +00:00
|
|
|
' ' );
|
2016-01-06 20:17:41 +00:00
|
|
|
}
|
2009-01-23 22:49:13 +00:00
|
|
|
|
|
|
|
// Add script
|
2011-08-26 20:12:34 +00:00
|
|
|
$wgOut->addModules( 'ext.abuseFilter.edit' );
|
|
|
|
self::$editboxName = $textName;
|
2009-01-23 22:49:13 +00:00
|
|
|
|
|
|
|
return $rules;
|
|
|
|
}
|
2009-01-26 22:31:02 +00:00
|
|
|
|
2009-10-07 13:57:06 +00:00
|
|
|
/**
|
|
|
|
* Each version is expected to be an array( $row, $actions )
|
|
|
|
* Returns an array of fields that are different.
|
2011-08-24 22:11:52 +00:00
|
|
|
*
|
|
|
|
* @param $version_1
|
|
|
|
* @param $version_2
|
|
|
|
*
|
|
|
|
* @return array
|
2009-10-07 13:57:06 +00:00
|
|
|
*/
|
2009-01-26 22:31:02 +00:00
|
|
|
static function compareVersions( $version_1, $version_2 ) {
|
2009-10-07 13:57:06 +00:00
|
|
|
$compareFields = array(
|
|
|
|
'af_public_comments',
|
|
|
|
'af_pattern',
|
|
|
|
'af_comments',
|
|
|
|
'af_deleted',
|
|
|
|
'af_enabled',
|
2009-03-30 06:12:12 +00:00
|
|
|
'af_hidden',
|
|
|
|
'af_global',
|
2012-05-06 06:44:45 +00:00
|
|
|
'af_group',
|
2009-02-07 09:34:11 +00:00
|
|
|
);
|
2009-01-26 22:31:02 +00:00
|
|
|
$differences = array();
|
|
|
|
|
2009-10-07 13:57:06 +00:00
|
|
|
list( $row1, $actions1 ) = $version_1;
|
|
|
|
list( $row2, $actions2 ) = $version_2;
|
2009-01-26 22:31:02 +00:00
|
|
|
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $compareFields as $field ) {
|
2012-09-27 10:10:10 +00:00
|
|
|
if ( !isset( $row2->$field ) || $row1->$field != $row2->$field ) {
|
2009-01-26 22:31:02 +00:00
|
|
|
$differences[] = $field;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-03 18:01:56 +00:00
|
|
|
global $wgAbuseFilterActions;
|
|
|
|
foreach ( array_filter( $wgAbuseFilterActions ) as $action => $_ ) {
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( !isset( $actions1[$action] ) && !isset( $actions2[$action] ) ) {
|
2009-01-26 22:31:02 +00:00
|
|
|
// They're both unset
|
2009-10-07 13:57:06 +00:00
|
|
|
} elseif ( isset( $actions1[$action] ) && isset( $actions2[$action] ) ) {
|
2009-01-26 22:31:02 +00:00
|
|
|
// They're both set.
|
2010-02-13 14:10:36 +00:00
|
|
|
if ( array_diff( $actions1[$action]['parameters'],
|
2015-09-28 18:03:35 +00:00
|
|
|
$actions2[$action]['parameters'] ) ) {
|
2009-01-26 22:31:02 +00:00
|
|
|
// Different parameters
|
|
|
|
$differences[] = 'actions';
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// One's unset, one's set.
|
|
|
|
$differences[] = 'actions';
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return array_unique( $differences );
|
|
|
|
}
|
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $row
|
|
|
|
* @return array
|
|
|
|
*/
|
2009-01-26 22:31:02 +00:00
|
|
|
static function translateFromHistory( $row ) {
|
2010-08-19 21:12:09 +00:00
|
|
|
# Translate into an abuse_filter row with some black magic.
|
|
|
|
# This is ever so slightly evil!
|
2009-01-26 22:31:02 +00:00
|
|
|
$af_row = new StdClass;
|
|
|
|
|
2009-02-07 09:34:11 +00:00
|
|
|
foreach ( self::$history_mappings as $af_col => $afh_col ) {
|
2009-01-26 22:31:02 +00:00
|
|
|
$af_row->$af_col = $row->$afh_col;
|
|
|
|
}
|
|
|
|
|
2010-08-19 21:12:09 +00:00
|
|
|
# Process flags
|
2009-01-26 22:31:02 +00:00
|
|
|
|
|
|
|
$af_row->af_deleted = 0;
|
|
|
|
$af_row->af_hidden = 0;
|
|
|
|
$af_row->af_enabled = 0;
|
|
|
|
|
2009-10-07 13:57:06 +00:00
|
|
|
$flags = explode( ',', $row->afh_flags );
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $flags as $flag ) {
|
2009-01-26 22:31:02 +00:00
|
|
|
$col_name = "af_$flag";
|
|
|
|
$af_row->$col_name = 1;
|
|
|
|
}
|
|
|
|
|
2010-08-19 21:12:09 +00:00
|
|
|
# Process actions
|
2009-10-07 13:57:06 +00:00
|
|
|
$actions_raw = unserialize( $row->afh_actions );
|
2009-01-26 22:31:02 +00:00
|
|
|
$actions_output = array();
|
2013-02-11 22:30:42 +00:00
|
|
|
if ( is_array( $actions_raw ) ) {
|
|
|
|
foreach ( $actions_raw as $action => $parameters ) {
|
|
|
|
$actions_output[$action] = array(
|
|
|
|
'action' => $action,
|
|
|
|
'parameters' => $parameters
|
|
|
|
);
|
|
|
|
}
|
2009-01-26 22:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return array( $af_row, $actions_output );
|
|
|
|
}
|
2009-01-28 01:26:38 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $action string
|
|
|
|
* @return String
|
|
|
|
*/
|
2009-01-28 01:26:38 +00:00
|
|
|
static function getActionDisplay( $action ) {
|
2013-08-18 06:35:16 +00:00
|
|
|
// Give grep a chance to find the usages:
|
|
|
|
// abusefilter-action-tag, abusefilter-action-throttle, abusefilter-action-warn,
|
|
|
|
// abusefilter-action-blockautopromote, abusefilter-action-block, abusefilter-action-degroup,
|
|
|
|
// abusefilter-action-rangeblock, abusefilter-action-disallow
|
2012-09-02 11:07:02 +00:00
|
|
|
$display = wfMessage( "abusefilter-action-$action" )->text();
|
|
|
|
$display = wfMessage( "abusefilter-action-$action", $display )->isDisabled() ? $action : $display;
|
2015-09-28 18:03:35 +00:00
|
|
|
|
2009-01-28 01:26:38 +00:00
|
|
|
return $display;
|
|
|
|
}
|
2009-01-28 23:54:41 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $row
|
|
|
|
* @return AbuseFilterVariableHolder|null
|
|
|
|
*/
|
2009-01-28 23:54:41 +00:00
|
|
|
public static function getVarsFromRCRow( $row ) {
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( $row->rc_this_oldid ) {
|
2009-01-28 23:54:41 +00:00
|
|
|
// It's an edit.
|
2009-03-19 02:05:58 +00:00
|
|
|
$vars = self::getEditVarsFromRCRow( $row );
|
2009-01-28 23:54:41 +00:00
|
|
|
} elseif ( $row->rc_log_type == 'move' ) {
|
2009-03-19 02:05:58 +00:00
|
|
|
$vars = self::getMoveVarsFromRCRow( $row );
|
2009-01-28 23:54:41 +00:00
|
|
|
} elseif ( $row->rc_log_type == 'newusers' ) {
|
2009-03-19 02:05:58 +00:00
|
|
|
$vars = self::getCreateVarsFromRCRow( $row );
|
2009-03-19 06:18:41 +00:00
|
|
|
} else {
|
|
|
|
return null;
|
2009-01-28 23:54:41 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( $vars ) {
|
2009-03-22 02:12:51 +00:00
|
|
|
$vars->setVar( 'context', 'generated' );
|
2013-01-07 17:36:31 +00:00
|
|
|
$vars->setVar( 'timestamp', wfTimestamp( TS_UNIX, $row->rc_timestamp ) );
|
2009-04-01 03:59:58 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-19 02:05:58 +00:00
|
|
|
return $vars;
|
2009-01-28 23:54:41 +00:00
|
|
|
}
|
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $row
|
|
|
|
* @return AbuseFilterVariableHolder
|
|
|
|
*/
|
2009-01-28 23:54:41 +00:00
|
|
|
public static function getCreateVarsFromRCRow( $row ) {
|
2009-03-22 02:46:05 +00:00
|
|
|
$vars = new AbuseFilterVariableHolder;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2015-09-28 18:03:35 +00:00
|
|
|
$vars->setVar( 'ACTION', ( $row->rc_log_action == 'autocreate' ) ?
|
|
|
|
'autocreateaccount' :
|
|
|
|
'createaccount' );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-22 02:46:05 +00:00
|
|
|
$name = Title::makeTitle( $row->rc_namespace, $row->rc_title )->getText();
|
2011-08-24 00:29:26 +00:00
|
|
|
// Add user data if the account was created by a registered user
|
|
|
|
if ( $row->rc_user && $name != $row->rc_user_text ) {
|
|
|
|
$user = User::newFromName( $row->rc_user_text );
|
2013-03-06 06:21:55 +00:00
|
|
|
$vars->addHolders( self::generateUserVars( $user ) );
|
2011-08-24 00:29:26 +00:00
|
|
|
}
|
|
|
|
|
2009-03-22 02:46:05 +00:00
|
|
|
$vars->setVar( 'accountname', $name );
|
2015-09-28 18:03:35 +00:00
|
|
|
|
2009-01-28 23:54:41 +00:00
|
|
|
return $vars;
|
|
|
|
}
|
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $row
|
|
|
|
* @return AbuseFilterVariableHolder
|
|
|
|
*/
|
2009-01-28 23:54:41 +00:00
|
|
|
public static function getEditVarsFromRCRow( $row ) {
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars = new AbuseFilterVariableHolder;
|
2009-01-28 23:54:41 +00:00
|
|
|
$title = Title::makeTitle( $row->rc_namespace, $row->rc_title );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2010-08-19 21:12:09 +00:00
|
|
|
if ( $row->rc_user ) {
|
2009-02-26 12:15:14 +00:00
|
|
|
$user = User::newFromName( $row->rc_user_text );
|
2010-08-19 21:12:09 +00:00
|
|
|
} else {
|
2009-02-25 04:31:53 +00:00
|
|
|
$user = new User;
|
|
|
|
$user->setName( $row->rc_user_text );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-03-06 06:21:55 +00:00
|
|
|
$vars->addHolders(
|
|
|
|
self::generateUserVars( $user ),
|
|
|
|
self::generateTitleVars( $title, 'ARTICLE' )
|
|
|
|
);
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars->setVar( 'ACTION', 'edit' );
|
|
|
|
$vars->setVar( 'SUMMARY', $row->rc_comment );
|
2009-01-28 23:54:41 +00:00
|
|
|
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars->setLazyLoadVar( 'new_wikitext', 'revision-text-by-id',
|
|
|
|
array( 'revid' => $row->rc_this_oldid ) );
|
2009-01-28 23:54:41 +00:00
|
|
|
|
2009-10-07 13:57:06 +00:00
|
|
|
if ( $row->rc_last_oldid ) {
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars->setLazyLoadVar( 'old_wikitext', 'revision-text-by-id',
|
|
|
|
array( 'revid' => $row->rc_last_oldid ) );
|
2009-01-28 23:54:41 +00:00
|
|
|
} else {
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars->setVar( 'old_wikitext', '' );
|
2009-01-28 23:54:41 +00:00
|
|
|
}
|
|
|
|
|
2013-03-06 06:21:55 +00:00
|
|
|
$vars->addHolders( self::getEditVars( $title ) );
|
2009-01-28 23:54:41 +00:00
|
|
|
|
|
|
|
return $vars;
|
|
|
|
}
|
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $row
|
|
|
|
* @return AbuseFilterVariableHolder
|
|
|
|
*/
|
2009-01-28 23:54:41 +00:00
|
|
|
public static function getMoveVarsFromRCRow( $row ) {
|
2010-08-19 21:12:09 +00:00
|
|
|
if ( $row->rc_user ) {
|
2009-02-25 04:31:53 +00:00
|
|
|
$user = User::newFromId( $row->rc_user );
|
2010-08-19 21:12:09 +00:00
|
|
|
} else {
|
2009-02-25 04:31:53 +00:00
|
|
|
$user = new User;
|
|
|
|
$user->setName( $row->rc_user_text );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2013-12-31 03:30:49 +00:00
|
|
|
$params = array_values( DatabaseLogEntry::newFromRow( $row )->getParameters() );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-01-28 23:54:41 +00:00
|
|
|
$oldTitle = Title::makeTitle( $row->rc_namespace, $row->rc_title );
|
2016-04-04 18:27:51 +00:00
|
|
|
$newTitle = Title::newFromText( $params[0] );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars = AbuseFilterVariableHolder::merge(
|
|
|
|
AbuseFilter::generateUserVars( $user ),
|
2009-01-28 23:54:41 +00:00
|
|
|
AbuseFilter::generateTitleVars( $oldTitle, 'MOVED_FROM' ),
|
2009-02-26 12:15:14 +00:00
|
|
|
AbuseFilter::generateTitleVars( $newTitle, 'MOVED_TO' )
|
|
|
|
);
|
2009-01-28 23:54:41 +00:00
|
|
|
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars->setVar( 'SUMMARY', $row->rc_comment );
|
|
|
|
$vars->setVar( 'ACTION', 'move' );
|
2009-01-28 23:54:41 +00:00
|
|
|
|
|
|
|
return $vars;
|
|
|
|
}
|
|
|
|
|
2011-02-10 17:25:25 +00:00
|
|
|
/**
|
2013-04-01 09:04:03 +00:00
|
|
|
* @param Title $title
|
|
|
|
* @param null|Page $page
|
2011-02-10 17:25:25 +00:00
|
|
|
* @return AbuseFilterVariableHolder
|
|
|
|
*/
|
2013-01-04 15:37:56 +00:00
|
|
|
public static function getEditVars( $title, Page $page = null ) {
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars = new AbuseFilterVariableHolder;
|
2009-01-28 23:54:41 +00:00
|
|
|
|
2013-01-04 15:37:56 +00:00
|
|
|
// NOTE: $page may end up remaining null, e.g. if $title points to a special page.
|
2013-06-05 01:52:29 +00:00
|
|
|
if ( !$page && $title instanceof Title && $title->canExist() ) {
|
2013-01-04 15:37:56 +00:00
|
|
|
$page = WikiPage::factory( $title );
|
|
|
|
}
|
|
|
|
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars->setLazyLoadVar( 'edit_diff', 'diff',
|
2009-03-04 01:56:21 +00:00
|
|
|
array( 'oldtext-var' => 'old_wikitext', 'newtext-var' => 'new_wikitext' ) );
|
2013-10-01 07:07:50 +00:00
|
|
|
$vars->setLazyLoadVar( 'edit_diff_pst', 'diff',
|
|
|
|
array( 'oldtext-var' => 'old_wikitext', 'newtext-var' => 'new_pst' ) );
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars->setLazyLoadVar( 'new_size', 'length', array( 'length-var' => 'new_wikitext' ) );
|
|
|
|
$vars->setLazyLoadVar( 'old_size', 'length', array( 'length-var' => 'old_wikitext' ) );
|
|
|
|
$vars->setLazyLoadVar( 'edit_delta', 'subtract',
|
|
|
|
array( 'val1-var' => 'new_size', 'val2-var' => 'old_size' ) );
|
2009-01-28 23:54:41 +00:00
|
|
|
|
|
|
|
// Some more specific/useful details about the changes.
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars->setLazyLoadVar( 'added_lines', 'diff-split',
|
|
|
|
array( 'diff-var' => 'edit_diff', 'line-prefix' => '+' ) );
|
|
|
|
$vars->setLazyLoadVar( 'removed_lines', 'diff-split',
|
|
|
|
array( 'diff-var' => 'edit_diff', 'line-prefix' => '-' ) );
|
2013-10-01 07:07:50 +00:00
|
|
|
$vars->setLazyLoadVar( 'added_lines_pst', 'diff-split',
|
|
|
|
array( 'diff-var' => 'edit_diff_pst', 'line-prefix' => '+' ) );
|
2009-02-26 12:15:14 +00:00
|
|
|
|
|
|
|
// Links
|
|
|
|
$vars->setLazyLoadVar( 'added_links', 'link-diff-added',
|
|
|
|
array( 'oldlink-var' => 'old_links', 'newlink-var' => 'all_links' ) );
|
|
|
|
$vars->setLazyLoadVar( 'removed_links', 'link-diff-removed',
|
|
|
|
array( 'oldlink-var' => 'old_links', 'newlink-var' => 'all_links' ) );
|
|
|
|
$vars->setLazyLoadVar( 'new_text', 'strip-html',
|
|
|
|
array( 'html-var' => 'new_html' ) );
|
|
|
|
$vars->setLazyLoadVar( 'old_text', 'strip-html',
|
|
|
|
array( 'html-var' => 'old_html' ) );
|
2009-01-28 23:54:41 +00:00
|
|
|
|
2013-02-14 11:39:51 +00:00
|
|
|
if ( $title instanceof Title ) {
|
|
|
|
$vars->setLazyLoadVar( 'all_links', 'links-from-wikitext',
|
|
|
|
array(
|
|
|
|
'namespace' => $title->getNamespace(),
|
|
|
|
'title' => $title->getText(),
|
|
|
|
'text-var' => 'new_wikitext',
|
|
|
|
'article' => $page
|
|
|
|
) );
|
|
|
|
$vars->setLazyLoadVar( 'old_links', 'links-from-wikitext-or-database',
|
|
|
|
array(
|
|
|
|
'namespace' => $title->getNamespace(),
|
|
|
|
'title' => $title->getText(),
|
|
|
|
'text-var' => 'old_wikitext'
|
|
|
|
) );
|
2013-04-24 14:53:12 +00:00
|
|
|
$vars->setLazyLoadVar( 'new_pst', 'parse-wikitext',
|
|
|
|
array(
|
|
|
|
'namespace' => $title->getNamespace(),
|
|
|
|
'title' => $title->getText(),
|
|
|
|
'wikitext-var' => 'new_wikitext',
|
|
|
|
'article' => $page,
|
|
|
|
'pst' => true,
|
|
|
|
) );
|
2013-02-14 11:39:51 +00:00
|
|
|
$vars->setLazyLoadVar( 'new_html', 'parse-wikitext',
|
|
|
|
array(
|
|
|
|
'namespace' => $title->getNamespace(),
|
|
|
|
'title' => $title->getText(),
|
|
|
|
'wikitext-var' => 'new_wikitext',
|
|
|
|
'article' => $page
|
|
|
|
) );
|
|
|
|
$vars->setLazyLoadVar( 'old_html', 'parse-wikitext-nonedit',
|
|
|
|
array(
|
|
|
|
'namespace' => $title->getNamespace(),
|
|
|
|
'title' => $title->getText(),
|
|
|
|
'wikitext-var' => 'old_wikitext'
|
|
|
|
) );
|
|
|
|
}
|
|
|
|
|
2009-01-28 23:54:41 +00:00
|
|
|
return $vars;
|
|
|
|
}
|
2009-01-29 22:44:31 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
2016-09-17 07:03:42 +00:00
|
|
|
* @param AbuseFilterVariableHolder $vars
|
|
|
|
* @param IContextSource $context
|
2012-03-11 20:40:04 +00:00
|
|
|
* @return string
|
|
|
|
*/
|
2016-09-17 07:03:42 +00:00
|
|
|
public static function buildVarDumpTable( $vars, IContextSource $context ) {
|
2009-02-26 12:15:14 +00:00
|
|
|
// Export all values
|
2010-08-19 21:12:09 +00:00
|
|
|
if ( $vars instanceof AbuseFilterVariableHolder ) {
|
2009-02-26 12:15:14 +00:00
|
|
|
$vars = $vars->exportAllVars();
|
2010-08-19 21:12:09 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-01-29 22:44:31 +00:00
|
|
|
$output = '';
|
|
|
|
|
|
|
|
// I don't want to change the names of the pre-existing messages
|
|
|
|
// describing the variables, nor do I want to rewrite them, so I'm just
|
|
|
|
// mapping the variable names to builder messages with a pre-existing array.
|
2009-03-05 02:43:05 +00:00
|
|
|
$variableMessageMappings = self::getBuilderValues();
|
|
|
|
$variableMessageMappings = $variableMessageMappings['vars'];
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
$output .=
|
|
|
|
Xml::openElement( 'table', array( 'class' => 'mw-abuselog-details' ) ) .
|
|
|
|
Xml::openElement( 'tbody' ) .
|
2009-02-07 09:34:11 +00:00
|
|
|
"\n";
|
|
|
|
|
2009-10-07 13:57:06 +00:00
|
|
|
$header =
|
2016-09-17 07:03:42 +00:00
|
|
|
Xml::element( 'th', null, $context->msg( 'abusefilter-log-details-var' )->text() ) .
|
|
|
|
Xml::element( 'th', null, $context->msg( 'abusefilter-log-details-val' )->text() );
|
2009-01-30 15:40:59 +00:00
|
|
|
$output .= Xml::tags( 'tr', null, $header ) . "\n";
|
2009-01-29 22:44:31 +00:00
|
|
|
|
2012-07-24 16:34:28 +00:00
|
|
|
if ( !count( $vars ) ) {
|
|
|
|
$output .= Xml::closeElement( 'tbody' ) . Xml::closeElement( 'table' );
|
2015-09-28 18:03:35 +00:00
|
|
|
|
2012-07-24 16:34:28 +00:00
|
|
|
return $output;
|
|
|
|
}
|
|
|
|
|
2009-01-29 22:44:31 +00:00
|
|
|
// Now, build the body of the table.
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $vars as $key => $value ) {
|
2009-10-07 13:57:06 +00:00
|
|
|
$key = strtolower( $key );
|
|
|
|
|
|
|
|
if ( !empty( $variableMessageMappings[$key] ) ) {
|
2009-01-29 22:44:31 +00:00
|
|
|
$mapping = $variableMessageMappings[$key];
|
2016-09-17 07:03:42 +00:00
|
|
|
$keyDisplay = $context->msg( "abusefilter-edit-builder-vars-$mapping" )->parse() .
|
|
|
|
' ' . Xml::element( 'code', null, $context->msg( 'parentheses', $key )->text() );
|
2009-01-29 22:44:31 +00:00
|
|
|
} else {
|
2012-09-07 08:47:14 +00:00
|
|
|
$keyDisplay = Xml::element( 'code', null, $key );
|
2009-01-29 22:44:31 +00:00
|
|
|
}
|
|
|
|
|
2016-01-06 20:17:41 +00:00
|
|
|
if ( is_null( $value ) ) {
|
2009-01-30 15:40:59 +00:00
|
|
|
$value = '';
|
2016-01-06 20:17:41 +00:00
|
|
|
}
|
2015-08-11 12:02:59 +00:00
|
|
|
$value = Xml::element( 'div', array( 'class' => 'mw-abuselog-var-value' ), $value, false );
|
2009-01-29 22:44:31 +00:00
|
|
|
|
2009-10-07 13:57:06 +00:00
|
|
|
$trow =
|
2010-02-13 14:10:36 +00:00
|
|
|
Xml::tags( 'td', array( 'class' => 'mw-abuselog-var' ), $keyDisplay ) .
|
2009-02-07 09:34:11 +00:00
|
|
|
Xml::tags( 'td', array( 'class' => 'mw-abuselog-var-value' ), $value );
|
2010-02-13 14:10:36 +00:00
|
|
|
$output .=
|
2009-10-07 13:57:06 +00:00
|
|
|
Xml::tags( 'tr',
|
2009-02-07 09:34:11 +00:00
|
|
|
array( 'class' => "mw-abuselog-details-$key mw-abuselog-value" ), $trow
|
|
|
|
) . "\n";
|
2009-01-29 22:44:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
$output .= Xml::closeElement( 'tbody' ) . Xml::closeElement( 'table' );
|
2015-09-28 18:03:35 +00:00
|
|
|
|
2009-01-29 22:44:31 +00:00
|
|
|
return $output;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $page
|
|
|
|
* @param $type
|
|
|
|
* @param $title Title
|
|
|
|
* @param $sk Skin
|
|
|
|
* @param $args array
|
2012-12-22 18:50:55 +00:00
|
|
|
* @param $filterWikilinks bool
|
2012-03-11 20:40:04 +00:00
|
|
|
* @return String
|
|
|
|
*/
|
2012-12-22 18:50:55 +00:00
|
|
|
static function modifyActionText( $page, $type, $title, $sk, $args, $filterWikilinks ) {
|
2009-03-11 07:12:42 +00:00
|
|
|
list( $history_id, $filter_id ) = $args;
|
2015-09-28 18:03:35 +00:00
|
|
|
$details_title = SpecialPage::getTitleFor(
|
|
|
|
'AbuseFilter',
|
|
|
|
"history/$filter_id/diff/prev/$history_id"
|
|
|
|
);
|
2012-12-22 18:50:55 +00:00
|
|
|
if ( !$filterWikilinks ) { // Plaintext? Bug 43105
|
2015-09-28 18:03:35 +00:00
|
|
|
return wfMessage(
|
|
|
|
'abusefilter-log-entry-modify',
|
|
|
|
'[[' . $title->getFullText() . ']]',
|
|
|
|
'[[' . $details_title->getFullText() . ']]' )->text();
|
2012-12-22 18:50:55 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2016-12-04 15:13:16 +00:00
|
|
|
$linkRenderer = MediaWikiServices::getInstance()->getLinkRenderer();
|
|
|
|
$filter_link = $linkRenderer->makeLink( $title );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-09-02 11:07:02 +00:00
|
|
|
$details_text = wfMessage( 'abusefilter-log-detailslink' )->parse();
|
2016-12-04 15:13:16 +00:00
|
|
|
$details_link = $linkRenderer->makeLink( $details_title, new HtmlArmor( $details_text ) );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-09-02 11:07:02 +00:00
|
|
|
return wfMessage( 'abusefilter-log-entry-modify' )
|
|
|
|
->rawParams( $filter_link, $details_link )->parse();
|
2009-03-11 07:12:42 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $action
|
|
|
|
* @param $parameters
|
|
|
|
* @return String
|
|
|
|
*/
|
2009-03-12 05:04:39 +00:00
|
|
|
static function formatAction( $action, $parameters ) {
|
2013-10-15 12:35:03 +00:00
|
|
|
/** @var $wgLang Language */
|
2009-03-12 05:04:39 +00:00
|
|
|
global $wgLang;
|
2010-02-13 14:10:36 +00:00
|
|
|
if ( count( $parameters ) == 0 ) {
|
2009-03-12 05:04:39 +00:00
|
|
|
$displayAction = AbuseFilter::getActionDisplay( $action );
|
|
|
|
} else {
|
|
|
|
$displayAction = AbuseFilter::getActionDisplay( $action ) .
|
2012-09-02 11:07:02 +00:00
|
|
|
wfMessage( 'colon-separator' )->escaped() .
|
2015-09-28 18:03:35 +00:00
|
|
|
$wgLang->semicolonList( $parameters );
|
2009-03-12 05:04:39 +00:00
|
|
|
}
|
2015-09-28 18:03:35 +00:00
|
|
|
|
2009-03-12 05:04:39 +00:00
|
|
|
return $displayAction;
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $value array
|
|
|
|
* @return string
|
|
|
|
*/
|
2009-03-12 05:04:39 +00:00
|
|
|
static function formatFlags( $value ) {
|
2013-10-15 12:35:03 +00:00
|
|
|
/** @var $wgLang Language */
|
2009-03-12 05:04:39 +00:00
|
|
|
global $wgLang;
|
|
|
|
$flags = array_filter( explode( ',', $value ) );
|
|
|
|
$flags_display = array();
|
2010-02-13 14:10:36 +00:00
|
|
|
foreach ( $flags as $flag ) {
|
2012-09-02 11:07:02 +00:00
|
|
|
$flags_display[] = wfMessage( "abusefilter-history-$flag" )->text();
|
2009-03-12 05:04:39 +00:00
|
|
|
}
|
2015-09-28 18:03:35 +00:00
|
|
|
|
2009-03-12 05:04:39 +00:00
|
|
|
return $wgLang->commaList( $flags_display );
|
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2012-03-11 20:40:04 +00:00
|
|
|
/**
|
|
|
|
* @param $filterID
|
|
|
|
* @return bool|mixed|string
|
|
|
|
*/
|
2009-03-30 06:12:12 +00:00
|
|
|
static function getGlobalFilterDescription( $filterID ) {
|
|
|
|
global $wgAbuseFilterCentralDB;
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2010-08-19 21:12:09 +00:00
|
|
|
if ( !$wgAbuseFilterCentralDB ) {
|
2012-03-11 20:40:04 +00:00
|
|
|
return '';
|
2010-08-19 21:12:09 +00:00
|
|
|
}
|
2009-10-07 13:57:06 +00:00
|
|
|
|
2009-03-30 06:12:12 +00:00
|
|
|
$fdb = wfGetDB( DB_SLAVE, array(), $wgAbuseFilterCentralDB );
|
2009-10-07 13:57:06 +00:00
|
|
|
|
|
|
|
return $fdb->selectField(
|
|
|
|
'abuse_filter',
|
|
|
|
'af_public_comments',
|
|
|
|
array( 'af_id' => $filterID ),
|
|
|
|
__METHOD__
|
|
|
|
);
|
2009-03-30 06:12:12 +00:00
|
|
|
}
|
2012-05-06 06:44:45 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Gives either the user-specified name for a group,
|
|
|
|
* or spits the input back out
|
2014-11-07 12:21:45 +00:00
|
|
|
* @param string $group The filter's group (as defined in $wgAbuseFilterValidGroups)
|
2012-05-06 06:44:45 +00:00
|
|
|
* @return String A name for that filter group, or the input.
|
|
|
|
*/
|
2015-09-28 18:03:35 +00:00
|
|
|
static function nameGroup( $group ) {
|
2013-08-18 06:35:16 +00:00
|
|
|
// Give grep a chance to find the usages: abusefilter-group-default
|
2012-05-06 06:44:45 +00:00
|
|
|
$msg = "abusefilter-group-$group";
|
2015-09-28 18:03:35 +00:00
|
|
|
|
|
|
|
return wfMessage( $msg )->exists() ? wfMessage( $msg )->escaped() : $group;
|
2012-05-06 06:44:45 +00:00
|
|
|
}
|
2012-11-20 15:16:58 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Look up some text of a revision from its revision id
|
|
|
|
*
|
|
|
|
* Note that this is really *some* text, we do not make *any* guarantee
|
|
|
|
* that this text will be even close to what the user actually sees, or
|
|
|
|
* that the form is fit for any intended purpose.
|
|
|
|
*
|
|
|
|
* Note also that if the revision for any reason is not an Revision
|
|
|
|
* the function returns with an empty string.
|
|
|
|
*
|
|
|
|
* @param Revision $revision a valid revision
|
|
|
|
* @param $audience Integer: one of:
|
|
|
|
* Revision::FOR_PUBLIC to be displayed to all users
|
|
|
|
* Revision::FOR_THIS_USER to be displayed to the given user
|
|
|
|
* Revision::RAW get the text regardless of permissions
|
|
|
|
* @return string|null the content of the revision as some kind of string,
|
2015-09-28 18:03:35 +00:00
|
|
|
* or an empty string if it can not be found
|
2012-11-20 15:16:58 +00:00
|
|
|
*/
|
2013-01-04 15:37:56 +00:00
|
|
|
static function revisionToString( $revision, $audience = Revision::FOR_THIS_USER ) {
|
2012-11-20 15:16:58 +00:00
|
|
|
if ( !$revision instanceof Revision ) {
|
|
|
|
return '';
|
|
|
|
}
|
2015-11-23 10:00:25 +00:00
|
|
|
|
|
|
|
$content = $revision->getContent( $audience );
|
|
|
|
if ( $content === null ) {
|
|
|
|
return '';
|
2012-11-20 15:16:58 +00:00
|
|
|
}
|
2015-11-23 10:00:25 +00:00
|
|
|
$result = self::contentToString( $content );
|
|
|
|
|
2012-11-20 15:16:58 +00:00
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2013-01-04 15:37:56 +00:00
|
|
|
/**
|
|
|
|
* Converts the given Content object to a string.
|
|
|
|
*
|
|
|
|
* This uses Content::getNativeData() if $content is an instance of TextContent,
|
|
|
|
* or Content::getTextForSearchIndex() otherwise.
|
|
|
|
*
|
|
|
|
* The hook 'AbuseFilter::contentToString' can be used to override this
|
|
|
|
* behavior.
|
|
|
|
*
|
|
|
|
* @param Content $content
|
|
|
|
*
|
|
|
|
* @return string a suitable string representation of the content.
|
|
|
|
*/
|
|
|
|
static function contentToString( Content $content ) {
|
|
|
|
$text = null;
|
|
|
|
|
2015-08-06 17:15:42 +00:00
|
|
|
if ( Hooks::run( 'AbuseFilter-contentToString', array( $content, &$text ) ) ) {
|
2013-01-04 15:37:56 +00:00
|
|
|
$text = $content instanceof TextContent
|
2015-09-28 18:03:35 +00:00
|
|
|
? $content->getNativeData()
|
|
|
|
: $content->getTextForSearchIndex();
|
2013-01-04 15:37:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ( is_string( $text ) ) {
|
|
|
|
// bug 20310
|
|
|
|
// XXX: Is this really needed? Should we rather apply PST?
|
|
|
|
$text = str_replace( "\r\n", "\n", $text );
|
|
|
|
} else {
|
|
|
|
$text = '';
|
|
|
|
}
|
|
|
|
|
|
|
|
return $text;
|
|
|
|
}
|
|
|
|
|
2012-12-30 03:59:33 +00:00
|
|
|
/*
|
|
|
|
* Get the history ID of the first change to a given filter
|
|
|
|
*
|
|
|
|
* @param $filterId integer: Filter id
|
|
|
|
* @return integer|bool
|
|
|
|
*/
|
|
|
|
public static function getFirstFilterChange( $filterID ) {
|
|
|
|
static $firstChanges = array();
|
|
|
|
|
2015-09-28 18:03:35 +00:00
|
|
|
if ( !isset( $firstChanges[$filterID] ) ) {
|
2012-12-30 03:59:33 +00:00
|
|
|
$dbr = wfGetDB( DB_SLAVE );
|
|
|
|
$row = $dbr->selectRow(
|
|
|
|
'abuse_filter_history',
|
|
|
|
'afh_id',
|
|
|
|
array(
|
|
|
|
'afh_filter' => $filterID,
|
|
|
|
),
|
|
|
|
__METHOD__,
|
|
|
|
array( 'ORDER BY' => 'afh_timestamp ASC' )
|
|
|
|
);
|
|
|
|
$firstChanges[$filterID] = $row->afh_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $firstChanges[$filterID];
|
|
|
|
}
|
2008-06-27 06:18:51 +00:00
|
|
|
}
|