# Serialized record version
define( 'MW_USER_VERSION', 4 );
+# Some punctuation to prevent editing from broken text-mangling proxies.
+# FIXME: this is embedded unescaped into HTML attributes in various
+# places, so we can't safely include ' or " even though we really should.
+define( 'EDIT_TOKEN_SUFFIX', '\\' );
+
+/**
+ * Thrown by User::setPassword() on error
+ */
+class PasswordError extends MWException {
+ // NOP
+}
+
/**
*
* @package MediaWiki
'editwidth',
'watchcreations',
'watchdefault',
+ 'watchmoves',
+ 'watchdeletion',
'minordefault',
'previewontop',
'previewonfirst',
'externaldiff',
'showjumplinks',
'uselivepreview',
- 'autopatrol',
'forceeditsummary',
'watchlisthideown',
'watchlisthidebots',
+ 'watchlisthideminor',
+ 'ccmeonemails',
);
/**
* @return bool
*/
static function isIP( $name ) {
- return preg_match("/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/",$name);
+ return preg_match('/^\d{1,3}\.\d{1,3}\.\d{1,3}\.(?:xxx|\d{1,3})$/',$name);
/*return preg_match("/^
(?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
(?:[01]?\d{1,2}|2(:?[0-4]\d|5[0-5]))\.
return false;
}
- $dbr =& wfGetDB( DB_SLAVE );
+ $dbr =& wfGetDB( DB_MASTER );
$s = $dbr->selectRow( 'user', '*', array( 'user_id' => $this->mId ), __METHOD__ );
if ( $s !== false ) {
}
function inSorbsBlacklist( $ip ) {
- global $wgEnableSorbs;
+ global $wgEnableSorbs, $wgSorbsUrl;
+
return $wgEnableSorbs &&
- $this->inDnsBlacklist( $ip, 'http.dnsbl.sorbs.net.' );
+ $this->inDnsBlacklist( $ip, $wgSorbsUrl );
}
function inDnsBlacklist( $ip, $base ) {
$found = false;
$host = '';
+ $m = array();
if ( preg_match( '/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/', $ip, $m ) ) {
# Make hostname
for ( $i=4; $i>=1; $i-- ) {
* @public
*/
function pingLimiter( $action='edit' ) {
+
+ # Call the 'PingLimiter' hook
+ $result = false;
+ if( !wfRunHooks( 'PingLimiter', array( &$this, $action, $result ) ) ) {
+ return $result;
+ }
+
global $wgRateLimits, $wgRateLimitsExcludedGroups;
if( !isset( $wgRateLimits[$action] ) ) {
return false;
if( isset( $limits['ip'] ) ) {
$keys["mediawiki:limiter:$action:ip:$ip"] = $limits['ip'];
}
+ $matches = array();
if( isset( $limits['subnet'] ) && preg_match( '/^(\d+\.\d+\.\d+)\.\d+$/', $ip, $matches ) ) {
$subnet = $matches[1];
$keys["mediawiki:limiter:$action:subnet:$subnet"] = $limits['subnet'];
}
/**
- * Set the password and reset the random token
+ * Set the password and reset the random token
+ * Calls through to authentication plugin if necessary;
+ * will have no effect if the auth plugin refuses to
+ * pass the change through or if the legal password
+ * checks fail.
+ *
+ * As a special case, setting the password to null
+ * wipes it, so the account cannot be logged in until
+ * a new password is set, for instance via e-mail.
+ *
+ * @param string $str
+ * @throws PasswordError on failure
*/
function setPassword( $str ) {
+ global $wgAuth;
+
+ if( $str !== null ) {
+ if( !$wgAuth->allowPasswordChange() ) {
+ throw new PasswordError( wfMsg( 'password-change-forbidden' ) );
+ }
+
+ if( !$this->isValidPassword( $str ) ) {
+ global $wgMinimalPasswordLength;
+ throw new PasswordError( wfMsg( 'passwordtooshort',
+ $wgMinimalPasswordLength ) );
+ }
+ }
+
+ if( !$wgAuth->setPassword( $this, $str ) ) {
+ throw new PasswordError( wfMsg( 'externaldberror' ) );
+ }
+
$this->load();
$this->setToken();
- $this->mPassword = $this->encryptPassword( $str );
+
+ if( $str === null ) {
+ // Save an invalid hash...
+ $this->mPassword = '';
+ } else {
+ $this->mPassword = $this->encryptPassword( $str );
+ }
$this->mNewpassword = '';
- $this->mNewpassTime = NULL;
+ $this->mNewpassTime = null;
+
+ return true;
}
/**
/**
* @param string $oname The option to check
+ * @param string $defaultOverride A default value returned if the option does not exist
* @return string
*/
- function getOption( $oname ) {
+ function getOption( $oname, $defaultOverride = '' ) {
$this->load();
+
if ( is_null( $this->mOptions ) ) {
+ if($defaultOverride != '') {
+ return $defaultOverride;
+ }
$this->mOptions = User::getDefaultOptions();
}
+
if ( array_key_exists( $oname, $this->mOptions ) ) {
return trim( $this->mOptions[$oname] );
} else {
- return '';
+ return $defaultOverride;
}
}
* @todo FIXME : need to check the old failback system [AV]
*/
function &getSkin() {
- global $IP, $wgRequest;
+ global $wgRequest;
if ( ! isset( $this->mSkin ) ) {
wfProfileIn( __METHOD__ );
function clearNotification( &$title ) {
global $wgUser, $wgUseEnotif;
+ # Do nothing if the database is locked to writes
+ if( wfReadOnly() ) {
+ return;
+ }
+
if ($title->getNamespace() == NS_USER_TALK &&
$title->getText() == $this->getName() ) {
if (!wfRunHooks('UserClearNewTalkNotification', array(&$this)))
// any matching rows
if ( $watched ) {
$dbw =& wfGetDB( DB_MASTER );
- $success = $dbw->update( 'watchlist',
+ $dbw->update( 'watchlist',
array( /* SET */
'wl_notificationtimestamp' => NULL
), array( /* WHERE */
if( $currentUser != 0 ) {
$dbw =& wfGetDB( DB_MASTER );
- $success = $dbw->update( 'watchlist',
+ $dbw->update( 'watchlist',
array( /* SET */
'wl_notificationtimestamp' => NULL
), array( /* WHERE */
$this->mOptions = array();
$a = explode( "\n", $str );
foreach ( $a as $s ) {
+ $m = array();
if ( preg_match( "/^(.[^=]*)=(.*)$/", $s, $m ) ) {
$this->mOptions[$m[1]] = $m[2];
}
* Checks if a user with the given name exists, returns the ID
*/
function idForName() {
- $gotid = 0;
$s = trim( $this->getName() );
if ( 0 == strcmp( '', $s ) ) return 0;
'user_options' => $user->encodeOptions(),
'user_token' => $user->mToken,
'user_registration' => $dbw->timestamp( $user->mRegistration ),
+ 'user_editcount' => 0,
);
foreach ( $params as $name => $value ) {
$fields["user_$name"] = $value;
'user_options' => $this->encodeOptions(),
'user_token' => $this->mToken,
'user_registration' => $dbw->timestamp( $this->mRegistration ),
+ 'user_editcount' => 0,
), __METHOD__
);
$this->mId = $dbw->insertId();
return;
}
- if ( !$userblock->mEnableAutoblock ) {
- return;
- }
-
- # Check if this IP address is already blocked
- $ipblock = Block::newFromDB( wfGetIP() );
- if ( $ipblock ) {
- # If the user is already blocked. Then check if the autoblock would
- # exceed the user block. If it would exceed, then do nothing, else
- # prolong block time
- if ($userblock->mExpiry &&
- ($userblock->mExpiry < Block::getAutoblockExpiry($ipblock->mTimestamp))) {
- return;
- }
- # Just update the timestamp
- $ipblock->updateTimestamp();
- return;
- } else {
- $ipblock = new Block;
- }
-
- # Make a new block object with the desired properties
- wfDebug( "Autoblocking {$this->mName}@" . wfGetIP() . "\n" );
- $ipblock->mAddress = wfGetIP();
- $ipblock->mUser = 0;
- $ipblock->mBy = $userblock->mBy;
- $ipblock->mReason = wfMsg( 'autoblocker', $this->getName(), $userblock->mReason );
- $ipblock->mTimestamp = wfTimestampNow();
- $ipblock->mAuto = 1;
- # If the user is already blocked with an expiry date, we don't
- # want to pile on top of that!
- if($userblock->mExpiry) {
- $ipblock->mExpiry = min ( $userblock->mExpiry, Block::getAutoblockExpiry( $ipblock->mTimestamp ));
- } else {
- $ipblock->mExpiry = Block::getAutoblockExpiry( $ipblock->mTimestamp );
- }
-
- # Insert it
- $ipblock->insert();
+ $userblock->doAutoblock( wfGetIp() );
}
* @return string
*/
function getPageRenderingHash() {
- global $wgContLang, $wgUseDynamicDates;
+ global $wgContLang, $wgUseDynamicDates, $wgLang;
if( $this->mHash ){
return $this->mHash;
}
$confstr .= '!' . $this->getDatePreference();
}
$confstr .= '!' . ($this->getOption( 'numberheadings' ) ? '1' : '');
- $confstr .= '!' . $this->getOption( 'language' );
+ $confstr .= '!' . $wgLang->getCode();
$confstr .= '!' . $this->getOption( 'thumbsize' );
// add in language specific options, if any
$extra = $wgContLang->getExtraHashOptions();
* @return bool True if the given password is correct otherwise False.
*/
function checkPassword( $password ) {
- global $wgAuth, $wgMinimalPasswordLength;
+ global $wgAuth;
$this->load();
// Even though we stop people from creating passwords that
// to. Certain authentication plugins do NOT want to save
// domain passwords in a mysql database, so we should
// check this (incase $wgAuth->strict() is false).
- if( strlen( $password ) < $wgMinimalPasswordLength ) {
+ if( !$this->isValidPassword( $password ) ) {
return false;
}
$ep = $this->encryptPassword( $password );
if ( 0 == strcmp( $ep, $this->mPassword ) ) {
return true;
- } elseif ( ($this->mNewpassword != '') && (0 == strcmp( $ep, $this->mNewpassword )) ) {
- return true;
} elseif ( function_exists( 'iconv' ) ) {
# Some wikis were converted from ISO 8859-1 to UTF-8, the passwords can't be converted
# Check for this with iconv
}
return false;
}
+
+ /**
+ * Check if the given clear-text password matches the temporary password
+ * sent by e-mail for password reset operations.
+ * @return bool
+ */
+ function checkTemporaryPassword( $plaintext ) {
+ $hash = $this->encryptPassword( $plaintext );
+ return $hash === $this->mNewpassword;
+ }
/**
* Initialize (if necessary) and return a session token value
if( is_array( $salt ) ) {
$salt = implode( '|', $salt );
}
- return md5( $token . $salt );
+ return md5( $token . $salt ) . EDIT_TOKEN_SUFFIX;
}
/**
*/
function sendConfirmationMail() {
global $wgContLang;
+ $expiration = null; // gets passed-by-ref and defined in next line.
$url = $this->confirmationTokenUrl( $expiration );
return $this->sendMail( wfMsg( 'confirmemail_subject' ),
wfMsg( 'confirmemail_body',
return $confirmed;
}
}
+
+ /**
+ * Return true if there is an outstanding request for e-mail confirmation.
+ * @return bool
+ */
+ function isEmailConfirmationPending() {
+ global $wgEmailAuthentication;
+ return $wgEmailAuthentication &&
+ !$this->isEmailConfirmed() &&
+ $this->mEmailToken &&
+ $this->mEmailTokenExpires > wfTimestamp();
+ }
/**
* @param array $groups list of groups
return $text;
}
}
+
+ /**
+ * Increment the user's edit-count field.
+ * Will have no effect for anonymous users.
+ */
+ function incEditCount() {
+ if( !$this->isAnon() ) {
+ $dbw = wfGetDB( DB_MASTER );
+ $dbw->update( 'user',
+ array( 'user_editcount=user_editcount+1' ),
+ array( 'user_id' => $this->getId() ),
+ __METHOD__ );
+
+ // Lazy initialization check...
+ if( $dbw->affectedRows() == 0 ) {
+ // Pull from a slave to be less cruel to servers
+ // Accuracy isn't the point anyway here
+ $dbr = wfGetDB( DB_SLAVE );
+ $count = $dbr->selectField( 'revision',
+ 'COUNT(rev_user)',
+ array( 'rev_user' => $this->getId() ),
+ __METHOD__ );
+
+ // Now here's a goddamn hack...
+ if( $dbr !== $dbw ) {
+ // If we actually have a slave server, the count is
+ // at least one behind because the current transaction
+ // has not been committed and replicated.
+ $count++;
+ } else {
+ // But if DB_SLAVE is selecting the master, then the
+ // count we just read includes the revision that was
+ // just added in the working transaction.
+ }
+
+ $dbw->update( 'user',
+ array( 'user_editcount' => $count ),
+ array( 'user_id' => $this->getId() ),
+ __METHOD__ );
+ }
+ }
+ }
}
?>