r41226 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r41225‎ | r41226 | r41227 >
Date:14:50, 24 September 2008
Author:btongminh
Status:old
Tags:
Comment:
Importing internally used AuthPlugin NssMySQLAuth
Modified paths:
  • /trunk/extensions/NssMySQLAuth/Md5crypt.php (added) (history)
  • /trunk/extensions/NssMySQLAuth/NssMySQLAuth.php (added) (history)
  • /trunk/extensions/NssMySQLAuth/NssMySQLAuthPlugin.php (added) (history)
  • /trunk/extensions/NssMySQLAuth/SpecialAccountManager.php (added) (history)
  • /trunk/extensions/NssMySQLAuth/libnss-mysql.cfg (added) (history)
  • /trunk/extensions/NssMySQLAuth/tables.sql (added) (history)

Diff [purge]

Index: trunk/extensions/NssMySQLAuth/NssMySQLAuthPlugin.php
@@ -0,0 +1,149 @@
 2+<?php
 3+
 4+/*
 5+ * A plugin to authenticate against a libnss-mysql database
 6+ *
 7+ * Copyright 2008 - Bryan Tong Minh / Delft Aerospace Rocket Engineering
 8+ * Licensed under the terms of the GNU General Public License, version 2
 9+ * or any later version.
 10+ *
 11+ */
 12+
 13+class NssMySQLAuthPlugin extends AuthPlugin {
 14+ static function initialize() {
 15+ global $wgAuth, $wgHooks;
 16+ global $wgNssMySQLAuthDB;
 17+ $wgAuth = new self( $wgNssMySQLAuthDB );
 18+
 19+ $wgHooks['UserEffectiveGroups'][] = array( $wgAuth, 'onUserEffectiveGroups' );
 20+ $wgHooks['UserGetEmail'][] = array( $wgAuth, 'onUserGetEmail' );
 21+ $wgHooks['UserSetEmail'][] = array( $wgAuth, 'onUserSetEmail' );
 22+ }
 23+
 24+ function __construct( $wikiName = false ) {
 25+ $this->wikiName = $wikiName;
 26+ $this->users = array();
 27+ }
 28+
 29+ function getDB( $db = DB_LAST ) {
 30+ return wfGetDB( $db, array(), $this->wikiName );
 31+ }
 32+
 33+ function userExists( $username ) {
 34+ if( isset( $this->users[$username] ))
 35+ return $this->users[$username];
 36+
 37+ $dbr = $this->getDB( DB_READ );
 38+ return $this->users[$username] =
 39+ false !== $dbr->select(
 40+ 'passwd', 1, array( 'pwd_name' => $username ),
 41+ __METHOD__
 42+ );
 43+ }
 44+
 45+ function authenticate( $username, $password ) {
 46+ $dbr = $this->getDB( DB_READ );
 47+ $res = $dbr->selectRow(
 48+ 'passwd',
 49+ array( 'pwd_name', 'pwd_password' ),
 50+ array( 'pwd_name' => $username ),
 51+ __METHOD__
 52+ );
 53+ if( $res === false ) return false;
 54+
 55+ return Md5crypt::encryptPassword( $password, $res->pwd_password )
 56+ == $res->pwd_password;
 57+ }
 58+
 59+ function updateUser( &$user ) {
 60+ $dbr = $this->getDB( DB_READ );
 61+ $res = $dbr->selectRow(
 62+ 'passwd',
 63+ array( 'pwd_email' ),
 64+ array( 'pwd_name' => $user->getName() ),
 65+ __METHOD__
 66+ );
 67+
 68+ if( $res === false ) return true;
 69+
 70+ $user->setEmail( $res->pwd_email );
 71+ return true;
 72+ }
 73+
 74+ function autoCreate() {
 75+ return true;
 76+ }
 77+ function setPassword( $user, $password ) {
 78+ $encryptedPassword = Md5crypt::encryptPassword( $password );
 79+ $dbw = $this->getDB( DB_WRITE );
 80+ return true == $dbw->update(
 81+ 'passwd',
 82+ array(
 83+ 'pwd_password' => $encryptedPassword,
 84+ 'pwd_password_lastchange' => wfTimestamp( TS_UNIX ),
 85+ ),
 86+ array( 'pwd_name' => $user->getName() ),
 87+ __METHOD__
 88+ );
 89+ }
 90+
 91+ function updateExternalDB( $user ) {
 92+ // Email updated via hook
 93+ return true;
 94+ }
 95+
 96+ function canCreateAccounts() {
 97+ return false;
 98+ }
 99+
 100+ function addUser( $user, $password, $email='', $realname='' ) {
 101+ return false;
 102+ }
 103+
 104+ function strict() {
 105+ return false;
 106+ }
 107+
 108+ function onUserEffectiveGroups( &$user, &$groups ) {
 109+ if( !$this->userExists( $user->getName() ) )
 110+ return true;
 111+
 112+ $dbr = $this->getDB( DB_READ );
 113+ $res = $dbr->select(
 114+ array( 'passwd', 'group_membership' ),
 115+ array( 'gm_group' ),
 116+ array( 'pwd_uid = gm_user', 'pwd_name' => $user->getName() ),
 117+ __METHOD__
 118+ );
 119+ while( $row = $res->fetchObject() )
 120+ $groups[] = $row->gm_group;
 121+
 122+ return true;
 123+ }
 124+
 125+ function onUserGetEmail( $user, &$address ) {
 126+ if( !$this->userExists( $user->getName() ) )
 127+ return true;
 128+
 129+ $dbr = $this->getDB( DB_READ );
 130+ $row = $dbr->selectRow( 'passwd' , 'pwd_email',
 131+ array( 'pwd_name' => $user->getName() ) );
 132+ if( $row ) $address = $row->pwd_email;
 133+ return true;
 134+
 135+ }
 136+
 137+ function onUserSetEmail( $user, &$address ) {
 138+ if( !$this->userExists( $user->getName() ) )
 139+ return true;
 140+
 141+ $dbw = $this->getDB( DB_WRITE );
 142+ return true == $dbw->update(
 143+ 'passwd',
 144+ array( 'pwd_email' => $address ),
 145+ array( 'pwd_name' => $user->getName() ),
 146+ __METHOD__
 147+ );
 148+ }
 149+
 150+}
Property changes on: trunk/extensions/NssMySQLAuth/NssMySQLAuthPlugin.php
___________________________________________________________________
Added: svn:eol-style
1151 + native
Index: trunk/extensions/NssMySQLAuth/NssMySQLAuth.php
@@ -0,0 +1,29 @@
 2+<?php
 3+
 4+/*
 5+ * A plugin to authenticate against a libnss-mysql database
 6+ *
 7+ * Copyright 2008 - Bryan Tong Minh / Delft Aerospace Rocket Engineering
 8+ * Licensed under the terms of the GNU General Public License, version 2
 9+ * or any later version.
 10+ *
 11+ */
 12+
 13+ ### READ BEFORE USING ###
 14+ /*
 15+ * This plugin allows authentication against an libnss-mysql database and thus
 16+ * allows the use of the same login for MediaWiki as for shell.
 17+ *
 18+ */
 19+
 20+
 21+$wgAutoloadClasses['NssMySQLAuthPlugin'] = dirname( __FILE__ ) . '/NssMySQLAuthPlugin.php';
 22+$wgAutoloadClasses['Md5crypt'] = dirname( __FILE__ ) . '/Md5crypt.php';
 23+$wgAutoloadClasses['SpecialAccountManager'] = dirname( __FILE__ ) . '/SpecialAccountManager.php';
 24+$wgSpecialPages['AccountManager'] = 'SpecialAccountManager';
 25+
 26+$wgNssMySQLAuthDB = false;
 27+
 28+$wgExtensionFunctions[] = array( 'NssMySQLAuthPlugin', 'initialize' );
 29+
 30+$wgUserProperties = array( 'address', 'city' );
Property changes on: trunk/extensions/NssMySQLAuth/NssMySQLAuth.php
___________________________________________________________________
Added: svn:eol-style
131 + native
Index: trunk/extensions/NssMySQLAuth/tables.sql
@@ -0,0 +1,60 @@
 2+CREATE DATABASE nss_auth;
 3+
 4+USE nss_auth;
 5+
 6+CREATE TABLE passwd (
 7+ pwd_uid int not null,
 8+ pwd_name varchar(255),
 9+ pwd_password varbinary(255),
 10+ pwd_password_lastchange int not null,
 11+ pwd_gid int not null,
 12+ pwd_home varchar(255) default null,
 13+ pwd_shell varchar(255) default '/bin/sh',
 14+ pwd_active varchar(15) default 1,
 15+ pwd_email varchar(255) not null,
 16+
 17+ PRIMARY KEY (pwd_uid),
 18+ UNIQUE INDEX (pwd_name)
 19+) character set ascii collate ascii_general_ci;
 20+
 21+CREATE TABLE groups (
 22+ grp_gid int not null,
 23+ grp_name varchar(255),
 24+ grp_password varchar(255) not null,
 25+
 26+ PRIMARY KEY(grp_gid),
 27+ INDEX (grp_name)
 28+) character set ascii collate ascii_general_ci;
 29+
 30+CREATE TABLE group_membership (
 31+ gm_user int not null,
 32+ gm_group varchar(255),
 33+
 34+ PRIMARY KEY (gm_user, gm_group),
 35+ KEY (gm_group)
 36+) character set ascii collate ascii_general_ci;
 37+
 38+CREATE TABLE permission (
 39+ perm_user int not null,
 40+ perm_action varbinary(255),
 41+ perm_permission varbinary(255)
 42+) character set ascii collate ascii_general_ci;
 43+
 44+CREATE TABLE user_props (
 45+ up_user varchar(255),
 46+ up_timestamp binary(14),
 47+ up_name varchar(255),
 48+ up_value blob,
 49+
 50+ INDEX(up_name),
 51+ INDEX(up_user, up_timestamp)
 52+) character set ascii collate ascii_general_ci;
 53+
 54+GRANT USAGE ON nss_auth.* TO `nss-root`@`localhost` IDENTIFIED BY 'secretpassword';
 55+GRANT USAGE ON nss_auth.* TO `nss-user`@`localhost` IDENTIFIED BY 'publiclyviewablepassword';
 56+
 57+GRANT SELECT ON nss_auth.* TO `nss-root`@`localhost`;
 58+GRANT SELECT (pwd_uid, pwd_name, pwd_gid, pwd_home, pwd_shell, pwd_active) ON nss_auth.passwd TO `nss-user`@`localhost`;
 59+GRANT SELECT ON nss_auth.groups TO `nss-user`@`localhost`;
 60+GRANT SELECT ON nss_auth.group_membership TO `nss-user`@`localhost`;
 61+
Property changes on: trunk/extensions/NssMySQLAuth/tables.sql
___________________________________________________________________
Added: svn:eol-style
162 + native
Index: trunk/extensions/NssMySQLAuth/libnss-mysql.cfg
@@ -0,0 +1,16 @@
 2+getpwnam SELECT pwd_name, 'x', pwd_uid, pwd_gid, '', pwd_home, pwd_shell FROM passwd WHERE pwd_name = '%1$s' LIMIT 1
 3+getpwuid SELECT pwd_name, 'x', pwd_uid, pwd_gid, '', pwd_home, pwd_shell FROM passwd WHERE pwd_uid = '%1$u' LIMIT 1
 4+getspnam SELECT pwd_name, pwd_password, pwd_password_lastchange, 0, 3650000, 7, -1, -1, 0 FROM passwd WHERE pwd_name = '%1$s' LIMIT 1
 5+getpwent SELECT pwd_name, 'x', pwd_uid, pwd_gid, '', pwd_home, pwd_shell FROM passwd
 6+getspent SELECT pwd_name, pwd_password, pwd_password_lastchange, 0, 3650000, 7, -1, -1, 0 FROM passwd
 7+getgrnam SELECT grp_name, grp_password, grp_gid FROM groups WHERE grp_name = '%1$u' LIMIT 1
 8+getgrgid SELECT grp_name, grp_password, grp_gid FROM groups WHERE grp_gid = '%1$u' LIMIT 1
 9+getgrent SELECT grp_name, grp_password, grp_gid FROM groups
 10+memsbygid SELECT pwd_name FROM group_membership, groups, passwd WHERE gm_group = grp_name AND gm_user = pwd_uid AND grp_gid = '%1$u'
 11+gidsbymem SELECT grp_gid FROM group_membership, groups, passwd WHERE gm_group = grp_name AND gm_user = pwd_uid AND pwd_name = '%1$s'
 12+
 13+host localhost
 14+database nss_auth
 15+username nss-user
 16+password publiclyviewablepassword
 17+socket /var/run/mysqld/mysqld.sock
Property changes on: trunk/extensions/NssMySQLAuth/libnss-mysql.cfg
___________________________________________________________________
Added: svn:eol-style
118 + native
Index: trunk/extensions/NssMySQLAuth/Md5crypt.php
@@ -0,0 +1,145 @@
 2+<?php
 3+
 4+/*
 5+
 6+md5crypt.php5
 7+--------------
 8+Modified by
 9+ - Bryan Tong Minh
 10+
 11+Written by
 12+
 13+ - Dennis Riehle <selfhtml@riehle-web.com>
 14+
 15+Based on
 16+
 17+ - perl's Crypt::PasswdMD5 by Luis Munoz (lem@cantv.net)
 18+ - phyton's md5crypt.py by Michal Wallace http://www.sabren.com/
 19+ - /usr/src/libcrypt/crypt.c from FreeBSD 2.2.5-RELEASE
 20+
 21+Many thanks to
 22+
 23+ - Fabian Steiner <info@fabis-site.net>
 24+ without him this script would not work!!
 25+
 26+Version: 1.0 stable
 27+Last edit: Tue, 13 September 2005 13:49:28 GMT
 28+
 29+
 30+USAGE
 31+
 32+ $cryptedpassword = md5crypt_unix ($password [, $salt [, $magicstring ]);
 33+ $apachepassword = md5crypt_apache ($password [, $salt]);
 34+
 35+DESCRIPTION
 36+
 37+ unix_md5_crypt() provides a crypt()-compatible interface to the
 38+ rather new MD5-based crypt() function found in modern operating systems.
 39+ It's based on the implementation found on FreeBSD 2.2.[56]-RELEASE and
 40+ contains the following license in it:
 41+
 42+ "THE BEER-WARE LICENSE" (Revision 42):
 43+ <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
 44+ can do whatever you want with this stuff. If we meet some day, and you think
 45+ this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
 46+
 47+ apache_md5_crypt() provides a function compatible with Apache's
 48+ .htpasswd files. This was contributed by Bryan Hart <bryan@eai.com>.
 49+
 50+*/
 51+
 52+class Md5crypt {
 53+ private static $itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
 54+
 55+ static function to64($v, $n) {
 56+ $ret = '';
 57+
 58+ while(--$n >= 0) {
 59+ $ret .= self::$itoa64{$v & 0x3f};
 60+ $v = $v >> 6;
 61+ }
 62+ return $ret;
 63+ }
 64+
 65+ static function encryptPassword($pw, $salt = NULL, $Magic = '$1$') {
 66+ if($salt !== NULL) {
 67+ // Take care of the magic string if present
 68+ if(substr($salt, 0, strlen($Magic)) == $Magic) {
 69+ $salt = substr($salt, strlen($Magic), strlen($salt));
 70+ }
 71+ // Salt can have up to 8 characters
 72+ $parts = explode('$', $salt, 1);
 73+ $salt = substr($parts[0], 0, 8);
 74+ } else {
 75+ $salt = '';
 76+ mt_srand((double)(microtime() * 10000000));
 77+
 78+ while(strlen($salt) < 8) {
 79+ $salt .= self::$itoa64{mt_rand(0, strlen($itoa64))};
 80+ }
 81+ }
 82+
 83+ $ctx = $pw . $Magic . $salt;
 84+
 85+ $final = pack('H*', md5($pw . $salt . $pw));
 86+
 87+ for ($pl = strlen($pw); $pl > 0; $pl -= 16) {
 88+ $ctx .= substr($final, 0, ($pl > 16) ? 16 : $pl);
 89+ }
 90+
 91+ // Now the 'weird' xform
 92+ for($i = strlen($pw); $i; $i >>= 1) {
 93+ if($i & 1) { // This comes from the original version,
 94+ $ctx .= pack("C", 0); // where a memset() is done to $final
 95+ } else { // before this loop
 96+ $ctx .= $pw{0};
 97+ }
 98+ }
 99+
 100+ $final = pack('H*', md5($ctx)); // The following is supposed to make
 101+ // things run slower
 102+
 103+ for($i = 0; $i < 1000; $i++) {
 104+ $ctx1 = '';
 105+ if($i & 1) {
 106+ $ctx1 .= $pw;
 107+ } else {
 108+ $ctx1 .= substr($final, 0, 16);
 109+ }
 110+ if($i % 3) {
 111+ $ctx1 .= $salt;
 112+ }
 113+ if($i % 7) {
 114+ $ctx1 .= $pw;
 115+ }
 116+ if($i & 1) {
 117+ $ctx1 .= substr($final, 0, 16);
 118+ } else {
 119+ $ctx1 .= $pw;
 120+ }
 121+ $final = pack('H*', md5($ctx1));
 122+ }
 123+
 124+ // Final xform
 125+ $passwd = '';
 126+ $passwd .= self::to64((intval(ord($final{0})) << 16)
 127+ |(intval(ord($final{6})) << 8)
 128+ |(intval(ord($final{12}))),4);
 129+ $passwd .= self::to64((intval(ord($final{1})) << 16)
 130+ |(intval(ord($final{7})) << 8)
 131+ |(intval(ord($final{13}))), 4);
 132+ $passwd .= self::to64((intval(ord($final{2})) << 16)
 133+ |(intval(ord($final{8})) << 8)
 134+ |(intval(ord($final{14}))), 4);
 135+ $passwd .= self::to64((intval(ord($final{3})) << 16)
 136+ |(intval(ord($final{9})) << 8)
 137+ |(intval(ord($final{15}))), 4);
 138+ $passwd .= self::to64((intval(ord($final{4}) << 16)
 139+ |(intval(ord($final{10})) << 8)
 140+ |(intval(ord($final{5})))), 4);
 141+ $passwd .= self::to64((intval(ord($final{11}))), 2);
 142+
 143+ // Return the final string
 144+ return $Magic . $salt . '$' . $passwd;
 145+ }
 146+}
Property changes on: trunk/extensions/NssMySQLAuth/Md5crypt.php
___________________________________________________________________
Added: svn:eol-style
1147 + native
Index: trunk/extensions/NssMySQLAuth/SpecialAccountManager.php
@@ -0,0 +1,177 @@
 2+<?php
 3+
 4+class SpecialAccountManager extends SpecialPage {
 5+ function __construct() {
 6+ parent::__construct( 'AccountManager', 'accountmanager', false );
 7+ }
 8+
 9+ function execute() {
 10+ global $wgUser;
 11+ if( !$this->userCanExecute( $wgUser ) )
 12+ return $this->displayRestrictionError();
 13+
 14+ $this->users = UserProps::fetchAllUsers();
 15+ if( $this->processData() === true)
 16+ $this->showSuccess();
 17+ $this->constructForm();
 18+ }
 19+
 20+ function showSuccess() {
 21+ global $wgOut;
 22+ $wgOut->addHTML( Xml::element('p', array(), 'Your changes have been successfully updated' ) );
 23+ }
 24+
 25+ function constructForm() {
 26+ global $wgOut, $wgScript;
 27+ global $wgUserProperties;
 28+
 29+ // TODO: wfMsg etc.
 30+ $wgOut->addHTML( Xml::openElement( 'form', array(
 31+ 'action' => $wgScript,
 32+ 'method' => 'post' )
 33+ ) );
 34+
 35+ $wgOut->addHTML("<table id=\"userprops\" border=\"1\" width=\"100%\">\n\t<tr>".
 36+ "<th>Username</th><th>Email</th>");
 37+ foreach( $wgUserProperties as $i )
 38+ $wgOut->addHTML( Xml::element( 'th', null, $i ) );
 39+ $wgOut->addHTML("</tr>\n\n");
 40+
 41+ foreach( $this->users as $user ) {
 42+ $name = $user->getName();
 43+ $row = "\t<tr>";
 44+ $row .= Xml::element( 'td', null, $name );
 45+ $row .= Xml::element( 'td', null, $user->getEmail() );
 46+
 47+ $props = $user->getProps();
 48+ foreach( $wgUserProperties as $key ) {
 49+ $value = isset( $wgUserProperties[$key] ) ?
 50+ $wgUserProperties[$key] : '';
 51+ $row .= "<td>".Xml::input(
 52+ "am-{$name}-{$key}",
 53+ false, $value
 54+ )."</td>";
 55+ }
 56+ $row .= "</tr>\n";
 57+ $wgOut->addHTML( $row );
 58+ }
 59+
 60+ $wgOut->addHTML( "</table>\n" );
 61+ $wgOut->addHTML( "<div id=\"userprops-submit\">\n".
 62+ Xml::hidden( 'title', 'Special:AccountManager' ).
 63+ Xml::hidden( 'action', 'submit' ).
 64+ Xml::element( 'input', array(
 65+ 'type' => 'submit',
 66+ 'value' => 'Save changes'
 67+ ) ).
 68+ "</div>\n</form>"
 69+ );
 70+ }
 71+
 72+ function processData() {
 73+ global $wgRequest, $wgUserProperties;
 74+ if( !$wgRequest->wasPosted() || $wgRequest->getVal('action') != 'submit' )
 75+ return;
 76+
 77+ $post = $wgRequest->getValues();
 78+ foreach( $post as $key => $value ) {
 79+ if( substr( $key, 0, 3 ) != 'am-' )
 80+ continue;
 81+ $parts = explode( '-', $key, 3 );
 82+ if( count( $parts ) != 3 )
 83+ continue;
 84+
 85+ $username = $parts[1];
 86+ $keyname = $parts[2];
 87+
 88+ if( !isset( $this->users[$username] ) )
 89+ continue;
 90+ if( !isset( $wgUserProperties[$key] ) )
 91+ continue;
 92+
 93+ $this->users[$username]->set( $keyname, $value );
 94+ }
 95+
 96+ foreach( $this->users as $user )
 97+ $user->update();
 98+ return true;
 99+ }
 100+}
 101+
 102+class UserProps {
 103+ static function fetchAllUsers() {
 104+ $users = array();
 105+ $res = self::select();
 106+ while( $row = $res->fetchObject() ) {
 107+ if( !isset( $users[$row->pwd_name] ) )
 108+ $users[$row->pwd_name] = new self( $row->pwd_name, $row->pwd_email );
 109+ $users[$row->pwd_name]->setInternal($row->up_name, $row->up_value);
 110+ }
 111+ $res->free();
 112+ return $users;
 113+ }
 114+ function __construct( $username, $email = null ) {
 115+ $this->username = $username;
 116+ $this->props = null;
 117+ $this->email = $email;
 118+ }
 119+ function getProps() {
 120+ return $this->props;
 121+ }
 122+ function getName() {
 123+ return $this->username;
 124+ }
 125+ function getEmail() {
 126+ return $this->email;
 127+ }
 128+ function setEmail( $email ) {
 129+ $this->email = $email;
 130+ }
 131+
 132+ static function select($username = null) {
 133+ global $wgAuth;
 134+ $dbr = $wgAuth->getDB( DB_READ );
 135+ $join = is_null( $username ) ? 'RIGHT JOIN' : 'JOIN';
 136+ $where = is_null( $username ) ? array() : array( 'up_user' => $username );
 137+
 138+ return $dbr->select(
 139+ array( 'user_props', 'passwd' ),
 140+ array( 'up_name', 'up_value', 'pwd_name', 'pwd_email' ),
 141+ $where,
 142+ __METHOD__,
 143+ array( 'ORDER BY' => 'up_timestamp DESC', 'DISTINCT' ),
 144+ array( 'passwd' => array( $join, 'pwd_name = up_user' ) )
 145+ );
 146+ }
 147+
 148+ function set($name, $value) {
 149+ $this->props[$name] = $value;
 150+ }
 151+ function setInternal($name, $value) {
 152+ if( is_null( $this->props ) ) {
 153+ $this->props = array();
 154+ $this->old_props = array();
 155+ }
 156+ $this->old_props[$name] = $this->props[$name] = $value;
 157+ }
 158+
 159+ function update() {
 160+ $diff = array_diff_assoc($this->props, $this->old_props);
 161+ if( !count( $diff ) ) return;
 162+
 163+ global $wgAuth;
 164+ $dbw = $wgAuth->getDB( DB_WRITE );
 165+ $timestamp = $dbw->timestamp();
 166+
 167+ $insert = array();
 168+ foreach( $diff as $key => $value )
 169+ $insert[] = array(
 170+ 'up_timestamp' => $timestamp,
 171+ 'up_user' => $this->username,
 172+ 'up_name' => $key,
 173+ 'up_value' => $value,
 174+ );
 175+
 176+ $dbw->insert( 'user_props', $insert, __METHOD__ );
 177+ }
 178+}
Property changes on: trunk/extensions/NssMySQLAuth/SpecialAccountManager.php
___________________________________________________________________
Added: svn:eol-style
1179 + native