Merge branch 'w51_MDL-37125_m25_yui380' of git://github.com/skodak/moodle
[moodle.git] / auth / db / auth.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Authentication Plugin: External Database Authentication
19  *
20  * Checks against an external database.
21  *
22  * @package    auth_db
23  * @author     Martin Dougiamas
24  * @license    http://www.gnu.org/copyleft/gpl.html GNU Public License
25  */
27 defined('MOODLE_INTERNAL') || die();
29 require_once($CFG->libdir.'/authlib.php');
31 /**
32  * External database authentication plugin.
33  */
34 class auth_plugin_db extends auth_plugin_base {
36     /**
37      * Constructor.
38      */
39     function __construct() {
40         global $CFG;
41         require_once($CFG->libdir.'/adodb/adodb.inc.php');
43         $this->authtype = 'db';
44         $this->config = get_config('auth/db');
45         if (empty($this->config->extencoding)) {
46             $this->config->extencoding = 'utf-8';
47         }
48     }
50     /**
51      * Returns true if the username and password work and false if they are
52      * wrong or don't exist.
53      *
54      * @param string $username The username
55      * @param string $password The password
56      * @return bool Authentication success or failure.
57      */
58     function user_login($username, $password) {
59         global $CFG, $DB;
61         $extusername = textlib::convert($username, 'utf-8', $this->config->extencoding);
62         $extpassword = textlib::convert($password, 'utf-8', $this->config->extencoding);
64         if ($this->is_internal()) {
65             // Lookup username externally, but resolve
66             // password locally -- to support backend that
67             // don't track passwords.
69             if (isset($this->config->removeuser) and $this->config->removeuser == AUTH_REMOVEUSER_KEEP) {
70                 // No need to connect to external database in this case because users are never removed and we verify password locally.
71                 if ($user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype))) {
72                     return validate_internal_user_password($user, $password);
73                 } else {
74                     return false;
75                 }
76             }
78             $authdb = $this->db_init();
80             $rs = $authdb->Execute("SELECT *
81                                       FROM {$this->config->table}
82                                      WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'");
83             if (!$rs) {
84                 $authdb->Close();
85                 debugging(get_string('auth_dbcantconnect','auth_db'));
86                 return false;
87             }
89             if (!$rs->EOF) {
90                 $rs->Close();
91                 $authdb->Close();
92                 // User exists externally - check username/password internally.
93                 if ($user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype))) {
94                     return validate_internal_user_password($user, $password);
95                 }
96             } else {
97                 $rs->Close();
98                 $authdb->Close();
99                 // User does not exist externally.
100                 return false;
101             }
103         } else {
104             // Normal case: use external db for both usernames and passwords.
106             $authdb = $this->db_init();
108             if ($this->config->passtype === 'md5') {   // Re-format password accordingly.
109                 $extpassword = md5($extpassword);
110             } else if ($this->config->passtype === 'sha1') {
111                 $extpassword = sha1($extpassword);
112             }
114             $rs = $authdb->Execute("SELECT *
115                                       FROM {$this->config->table}
116                                      WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'
117                                            AND {$this->config->fieldpass} = '".$this->ext_addslashes($extpassword)."'");
118             if (!$rs) {
119                 $authdb->Close();
120                 debugging(get_string('auth_dbcantconnect','auth_db'));
121                 return false;
122             }
124             if (!$rs->EOF) {
125                 $rs->Close();
126                 $authdb->Close();
127                 return true;
128             } else {
129                 $rs->Close();
130                 $authdb->Close();
131                 return false;
132             }
134         }
135     }
137     function db_init() {
138         // Connect to the external database (forcing new connection).
139         $authdb = ADONewConnection($this->config->type);
140         if (!empty($this->config->debugauthdb)) {
141             $authdb->debug = true;
142             ob_start(); //Start output buffer to allow later use of the page headers.
143         }
144         $authdb->Connect($this->config->host, $this->config->user, $this->config->pass, $this->config->name, true);
145         $authdb->SetFetchMode(ADODB_FETCH_ASSOC);
146         if (!empty($this->config->setupsql)) {
147             $authdb->Execute($this->config->setupsql);
148         }
150         return $authdb;
151     }
153     /**
154      * Returns user attribute mappings between moodle and ldap.
155      *
156      * @return array
157      */
158     function db_attributes() {
159         $moodleattributes = array();
160         foreach ($this->userfields as $field) {
161             if (!empty($this->config->{"field_map_$field"})) {
162                 $moodleattributes[$field] = $this->config->{"field_map_$field"};
163             }
164         }
165         $moodleattributes['username'] = $this->config->fielduser;
166         return $moodleattributes;
167     }
169     /**
170      * Reads any other information for a user from external database,
171      * then returns it in an array.
172      *
173      * @param string $username
174      * @return array
175      */
176     function get_userinfo($username) {
177         global $CFG;
179         $extusername = textlib::convert($username, 'utf-8', $this->config->extencoding);
181         $authdb = $this->db_init();
183         // Array to map local fieldnames we want, to external fieldnames.
184         $selectfields = $this->db_attributes();
186         $result = array();
187         // If at least one field is mapped from external db, get that mapped data.
188         if ($selectfields) {
189             $select = array();
190             foreach ($selectfields as $localname=>$externalname) {
191                 $select[] = "$externalname AS $localname";
192             }
193             $select = implode(', ', $select);
194             $sql = "SELECT $select
195                       FROM {$this->config->table}
196                      WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."'";
197             if ($rs = $authdb->Execute($sql)) {
198                 if (!$rs->EOF) {
199                     $fields_obj = $rs->FetchObj();
200                     $fields_obj = (object)array_change_key_case((array)$fields_obj , CASE_LOWER);
201                     foreach ($selectfields as $localname=>$externalname) {
202                         $result[$localname] = textlib::convert($fields_obj->{$localname}, $this->config->extencoding, 'utf-8');
203                      }
204                  }
205                  $rs->Close();
206             }
207         }
208         $authdb->Close();
209         return $result;
210     }
212     /**
213      * Change a user's password.
214      *
215      * @param  stdClass  $user      User table object
216      * @param  string  $newpassword Plaintext password
217      * @return bool                 True on success
218      */
219     function user_update_password($user, $newpassword) {
220         global $DB;
222         if ($this->is_internal()) {
223             $puser = $DB->get_record('user', array('id'=>$user->id), '*', MUST_EXIST);
224             if (update_internal_user_password($puser, $newpassword)) {
225                 $user->password = $puser->password;
226                 return true;
227             } else {
228                 return false;
229             }
230         } else {
231             // We should have never been called!
232             return false;
233         }
234     }
236     /**
237      * Synchronizes user from external db to moodle user table.
238      *
239      * Sync should be done by using idnumber attribute, not username.
240      * You need to pass firstsync parameter to function to fill in
241      * idnumbers if they don't exists in moodle user table.
242      *
243      * Syncing users removes (disables) users that don't exists anymore in external db.
244      * Creates new users and updates coursecreator status of users.
245      *
246      * This implementation is simpler but less scalable than the one found in the LDAP module.
247      *
248      * @param progress_trace $trace
249      * @param bool $do_updates  Optional: set to true to force an update of existing accounts
250      * @return int 0 means success, 1 means failure
251      */
252     function sync_users(progress_trace $trace, $do_updates=false) {
253         global $CFG, $DB;
255         // List external users.
256         $userlist = $this->get_userlist();
258         // Delete obsolete internal users.
259         if (!empty($this->config->removeuser)) {
261             $suspendselect = "";
262             if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
263                 $suspendselect = "AND u.suspended = 0";
264             }
266             // Find obsolete users.
267             if (count($userlist)) {
268                 list($notin_sql, $params) = $DB->get_in_or_equal($userlist, SQL_PARAMS_NAMED, 'u', false);
269                 $params['authtype'] = $this->authtype;
270                 $sql = "SELECT u.*
271                           FROM {user} u
272                          WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid $suspendselect AND u.username $notin_sql";
273             } else {
274                 $sql = "SELECT u.*
275                           FROM {user} u
276                          WHERE u.auth=:authtype AND u.deleted=0 AND u.mnethostid=:mnethostid $suspendselect";
277                 $params = array();
278                 $params['authtype'] = $this->authtype;
279             }
280             $params['mnethostid'] = $CFG->mnet_localhost_id;
281             $remove_users = $DB->get_records_sql($sql, $params);
283             if (!empty($remove_users)) {
284                 $trace->output(get_string('auth_dbuserstoremove','auth_db', count($remove_users)));
286                 foreach ($remove_users as $user) {
287                     if ($this->config->removeuser == AUTH_REMOVEUSER_FULLDELETE) {
288                         delete_user($user);
289                         $trace->output(get_string('auth_dbdeleteuser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)), 1);
290                     } else if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
291                         $updateuser = new stdClass();
292                         $updateuser->id   = $user->id;
293                         $updateuser->suspended = 1;
294                         $updateuser->timemodified = time();
295                         $DB->update_record('user', $updateuser);
296                         $trace->output(get_string('auth_dbsuspenduser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)), 1);
297                     }
298                 }
299             }
300             unset($remove_users);
301         }
303         if (!count($userlist)) {
304             // Exit right here, nothing else to do.
305             $trace->finished();
306             return 0;
307         }
309         // Update existing accounts.
310         if ($do_updates) {
311             // Narrow down what fields we need to update.
312             $all_keys = array_keys(get_object_vars($this->config));
313             $updatekeys = array();
314             foreach ($all_keys as $key) {
315                 if (preg_match('/^field_updatelocal_(.+)$/',$key, $match)) {
316                     if ($this->config->{$key} === 'onlogin') {
317                         array_push($updatekeys, $match[1]); // The actual key name.
318                     }
319                 }
320             }
321             unset($all_keys); unset($key);
323             // Only go ahead if we actually have fields to update locally.
324             if (!empty($updatekeys)) {
325                 list($in_sql, $params) = $DB->get_in_or_equal($userlist, SQL_PARAMS_NAMED, 'u', true);
326                 $params['authtype'] = $this->authtype;
327                 $sql = "SELECT u.id, u.username
328                           FROM {user} u
329                          WHERE u.auth=:authtype AND u.deleted=0 AND u.username {$in_sql}";
330                 if ($update_users = $DB->get_records_sql($sql, $params)) {
331                     $trace->output("User entries to update: ".count($update_users));
333                     foreach ($update_users as $user) {
334                         if ($this->update_user_record($user->username, $updatekeys)) {
335                             $trace->output(get_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id)), 1);
336                         } else {
337                             $trace->output(get_string('auth_dbupdatinguser', 'auth_db', array('name'=>$user->username, 'id'=>$user->id))." - ".get_string('skipped'), 1);
338                         }
339                     }
340                     unset($update_users);
341                 }
342             }
343         }
346         // Create missing accounts.
347         // NOTE: this is very memory intensive and generally inefficient.
348         $suspendselect = "";
349         if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
350             $suspendselect = "AND u.suspended = 0";
351         }
352         $sql = "SELECT u.id, u.username
353                   FROM {user} u
354                  WHERE u.auth=:authtype AND u.deleted='0' AND mnethostid=:mnethostid $suspendselect";
356         $users = $DB->get_records_sql($sql, array('authtype'=>$this->authtype, 'mnethostid'=>$CFG->mnet_localhost_id));
358         // Simplify down to usernames.
359         $usernames = array();
360         if (!empty($users)) {
361             foreach ($users as $user) {
362                 array_push($usernames, $user->username);
363             }
364             unset($users);
365         }
367         $add_users = array_diff($userlist, $usernames);
368         unset($usernames);
370         if (!empty($add_users)) {
371             $trace->output(get_string('auth_dbuserstoadd','auth_db',count($add_users)));
372             // Do not use transactions around this foreach, we want to skip problematic users, not revert everything.
373             foreach($add_users as $user) {
374                 $username = $user;
375                 if ($this->config->removeuser == AUTH_REMOVEUSER_SUSPEND) {
376                     if ($old_user = $DB->get_record('user', array('username'=>$username, 'deleted'=>0, 'suspended'=>1, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype))) {
377                         $DB->set_field('user', 'suspended', 0, array('id'=>$old_user->id));
378                         $trace->output(get_string('auth_dbreviveduser', 'auth_db', array('name'=>$username, 'id'=>$old_user->id)), 1);
379                         continue;
380                     }
381                 }
383                 // Do not try to undelete users here, instead select suspending if you ever expect users will reappear.
385                 // Prep a few params.
386                 $user = $this->get_userinfo_asobj($user);
387                 $user->username   = $username;
388                 $user->confirmed  = 1;
389                 $user->auth       = $this->authtype;
390                 $user->mnethostid = $CFG->mnet_localhost_id;
391                 if (empty($user->lang)) {
392                     $user->lang = $CFG->lang;
393                 }
394                 $user->timecreated = time();
395                 $user->timemodified = $user->timecreated;
396                 if ($collision = $DB->get_record_select('user', "username = :username AND mnethostid = :mnethostid AND auth <> :auth", array('username'=>$user->username, 'mnethostid'=>$CFG->mnet_localhost_id, 'auth'=>$this->authtype), 'id,username,auth')) {
397                     $trace->output(get_string('auth_dbinsertuserduplicate', 'auth_db', array('username'=>$user->username, 'auth'=>$collision->auth)), 1);
398                     continue;
399                 }
400                 try {
401                     $id = $DB->insert_record ('user', $user); // it is truly a new user
402                     $trace->output(get_string('auth_dbinsertuser', 'auth_db', array('name'=>$user->username, 'id'=>$id)), 1);
403                 } catch (moodle_exception $e) {
404                     $trace->output(get_string('auth_dbinsertusererror', 'auth_db', $user->username), 1);
405                     continue;
406                 }
407                 // If relevant, tag for password generation.
408                 if ($this->is_internal()) {
409                     set_user_preference('auth_forcepasswordchange', 1, $id);
410                     set_user_preference('create_password',          1, $id);
411                 }
412                 // Make sure user context is present.
413                 context_user::instance($id);
414             }
415             unset($add_users);
416         }
417         $trace->finished();
418         return 0;
419     }
421     function user_exists($username) {
423         // Init result value.
424         $result = false;
426         $extusername = textlib::convert($username, 'utf-8', $this->config->extencoding);
428         $authdb = $this->db_init();
430         $rs = $authdb->Execute("SELECT *
431                                   FROM {$this->config->table}
432                                  WHERE {$this->config->fielduser} = '".$this->ext_addslashes($extusername)."' ");
434         if (!$rs) {
435             print_error('auth_dbcantconnect','auth_db');
436         } else if (!$rs->EOF) {
437             // User exists externally.
438             $result = true;
439         }
441         $authdb->Close();
442         return $result;
443     }
446     function get_userlist() {
448         // Init result value.
449         $result = array();
451         $authdb = $this->db_init();
453         // Fetch userlist.
454         $rs = $authdb->Execute("SELECT {$this->config->fielduser} AS username
455                                   FROM {$this->config->table} ");
457         if (!$rs) {
458             print_error('auth_dbcantconnect','auth_db');
459         } else if (!$rs->EOF) {
460             while ($rec = $rs->FetchRow()) {
461                 $rec = (object)array_change_key_case((array)$rec , CASE_LOWER);
462                 array_push($result, $rec->username);
463             }
464         }
466         $authdb->Close();
467         return $result;
468     }
470     /**
471      * Reads user information from DB and return it in an object.
472      *
473      * @param string $username username
474      * @return array
475      */
476     function get_userinfo_asobj($username) {
477         $user_array = truncate_userinfo($this->get_userinfo($username));
478         $user = new stdClass();
479         foreach($user_array as $key=>$value) {
480             $user->{$key} = $value;
481         }
482         return $user;
483     }
485     /**
486      * will update a local user record from an external source.
487      * is a lighter version of the one in moodlelib -- won't do
488      * expensive ops such as enrolment.
489      *
490      * If you don't pass $updatekeys, there is a performance hit and
491      * values removed from DB won't be removed from moodle.
492      *
493      * @param string $username username
494      * @param bool $updatekeys
495      * @return stdClass
496      */
497     function update_user_record($username, $updatekeys=false) {
498         global $CFG, $DB;
500         //just in case check text case
501         $username = trim(textlib::strtolower($username));
503         // get the current user record
504         $user = $DB->get_record('user', array('username'=>$username, 'mnethostid'=>$CFG->mnet_localhost_id));
505         if (empty($user)) { // trouble
506             error_log("Cannot update non-existent user: $username");
507             print_error('auth_dbusernotexist','auth_db',$username);
508             die;
509         }
511         // Ensure userid is not overwritten.
512         $userid = $user->id;
513         $updated = false;
515         if ($newinfo = $this->get_userinfo($username)) {
516             $newinfo = truncate_userinfo($newinfo);
518             if (empty($updatekeys)) { // All keys? This does not support removing values.
519                 $updatekeys = array_keys($newinfo);
520             }
522             foreach ($updatekeys as $key) {
523                 if (isset($newinfo[$key])) {
524                     $value = $newinfo[$key];
525                 } else {
526                     $value = '';
527                 }
529                 if (!empty($this->config->{'field_updatelocal_' . $key})) {
530                     if (isset($user->{$key}) and $user->{$key} != $value) { // Only update if it's changed.
531                         $DB->set_field('user', $key, $value, array('id'=>$userid));
532                         $updated = true;
533                     }
534                 }
535             }
536         }
537         if ($updated) {
538             $DB->set_field('user', 'timemodified', time(), array('id'=>$userid));
539         }
540         return $DB->get_record('user', array('id'=>$userid, 'deleted'=>0));
541     }
543     /**
544      * Called when the user record is updated.
545      * Modifies user in external database. It takes olduser (before changes) and newuser (after changes)
546      * compares information saved modified information to external db.
547      *
548      * @param stdClass $olduser     Userobject before modifications
549      * @param stdClass $newuser     Userobject new modified userobject
550      * @return boolean result
551      *
552      */
553     function user_update($olduser, $newuser) {
554         if (isset($olduser->username) and isset($newuser->username) and $olduser->username != $newuser->username) {
555             error_log("ERROR:User renaming not allowed in ext db");
556             return false;
557         }
559         if (isset($olduser->auth) and $olduser->auth != $this->authtype) {
560             return true; // Just change auth and skip update.
561         }
563         $curruser = $this->get_userinfo($olduser->username);
564         if (empty($curruser)) {
565             error_log("ERROR:User $olduser->username found in ext db");
566             return false;
567         }
569         $extusername = textlib::convert($olduser->username, 'utf-8', $this->config->extencoding);
571         $authdb = $this->db_init();
573         $update = array();
574         foreach($curruser as $key=>$value) {
575             if ($key == 'username') {
576                 continue; // Skip this.
577             }
578             if (empty($this->config->{"field_updateremote_$key"})) {
579                 continue; // Remote update not requested.
580             }
581             if (!isset($newuser->$key)) {
582                 continue;
583             }
584             $nuvalue = $newuser->$key;
585             if ($nuvalue != $value) {
586                 $update[] = $this->config->{"field_map_$key"}."='".$this->ext_addslashes(textlib::convert($nuvalue, 'utf-8', $this->config->extencoding))."'";
587             }
588         }
589         if (!empty($update)) {
590             $authdb->Execute("UPDATE {$this->config->table}
591                                  SET ".implode(',', $update)."
592                                WHERE {$this->config->fielduser}='".$this->ext_addslashes($extusername)."'");
593         }
594         $authdb->Close();
595         return true;
596     }
598     /**
599      * A chance to validate form data, and last chance to
600      * do stuff before it is inserted in config_plugin
601      *
602      * @param stfdClass $form
603      * @param array $err errors
604      * @return void
605      */
606      function validate_form($form, &$err) {
607         if ($form->passtype === 'internal') {
608             $this->config->changepasswordurl = '';
609             set_config('changepasswordurl', '', 'auth/db');
610         }
611     }
613     function prevent_local_passwords() {
614         return !$this->is_internal();
615     }
617     /**
618      * Returns true if this authentication plugin is "internal".
619      *
620      * Internal plugins use password hashes from Moodle user table for authentication.
621      *
622      * @return bool
623      */
624     function is_internal() {
625         if (!isset($this->config->passtype)) {
626             return true;
627         }
628         return ($this->config->passtype === 'internal');
629     }
631     /**
632      * Indicates if moodle should automatically update internal user
633      * records with data from external sources using the information
634      * from auth_plugin_base::get_userinfo().
635      *
636      * @return bool true means automatically copy data from ext to user table
637      */
638     function is_synchronised_with_external() {
639         return true;
640     }
642     /**
643      * Returns true if this authentication plugin can change the user's
644      * password.
645      *
646      * @return bool
647      */
648     function can_change_password() {
649         return ($this->is_internal() or !empty($this->config->changepasswordurl));
650     }
652     /**
653      * Returns the URL for changing the user's pw, or empty if the default can
654      * be used.
655      *
656      * @return moodle_url
657      */
658     function change_password_url() {
659         if ($this->is_internal()) {
660             // Standard form.
661             return null;
662         } else {
663             // Use admin defined custom url.
664             return new moodle_url($this->config->changepasswordurl);
665         }
666     }
668     /**
669      * Returns true if plugin allows resetting of internal password.
670      *
671      * @return bool
672      */
673     function can_reset_password() {
674         return $this->is_internal();
675     }
677     /**
678      * Prints a form for configuring this authentication plugin.
679      *
680      * This function is called from admin/auth.php, and outputs a full page with
681      * a form for configuring this plugin.
682      *
683      * @param stdClass $config
684      * @param array $err errors
685      * @param array $user_fields
686      * @return void
687      */
688     function config_form($config, $err, $user_fields) {
689         include 'config.html';
690     }
692     /**
693      * Processes and stores configuration data for this authentication plugin.
694      *
695      * @param srdClass $config
696      * @return bool always true or exception
697      */
698     function process_config($config) {
699         // set to defaults if undefined
700         if (!isset($config->host)) {
701             $config->host = 'localhost';
702         }
703         if (!isset($config->type)) {
704             $config->type = 'mysql';
705         }
706         if (!isset($config->sybasequoting)) {
707             $config->sybasequoting = 0;
708         }
709         if (!isset($config->name)) {
710             $config->name = '';
711         }
712         if (!isset($config->user)) {
713             $config->user = '';
714         }
715         if (!isset($config->pass)) {
716             $config->pass = '';
717         }
718         if (!isset($config->table)) {
719             $config->table = '';
720         }
721         if (!isset($config->fielduser)) {
722             $config->fielduser = '';
723         }
724         if (!isset($config->fieldpass)) {
725             $config->fieldpass = '';
726         }
727         if (!isset($config->passtype)) {
728             $config->passtype = 'plaintext';
729         }
730         if (!isset($config->extencoding)) {
731             $config->extencoding = 'utf-8';
732         }
733         if (!isset($config->setupsql)) {
734             $config->setupsql = '';
735         }
736         if (!isset($config->debugauthdb)) {
737             $config->debugauthdb = 0;
738         }
739         if (!isset($config->removeuser)) {
740             $config->removeuser = AUTH_REMOVEUSER_KEEP;
741         }
742         if (!isset($config->changepasswordurl)) {
743             $config->changepasswordurl = '';
744         }
746         // Save settings.
747         set_config('host',          $config->host,          'auth/db');
748         set_config('type',          $config->type,          'auth/db');
749         set_config('sybasequoting', $config->sybasequoting, 'auth/db');
750         set_config('name',          $config->name,          'auth/db');
751         set_config('user',          $config->user,          'auth/db');
752         set_config('pass',          $config->pass,          'auth/db');
753         set_config('table',         $config->table,         'auth/db');
754         set_config('fielduser',     $config->fielduser,     'auth/db');
755         set_config('fieldpass',     $config->fieldpass,     'auth/db');
756         set_config('passtype',      $config->passtype,      'auth/db');
757         set_config('extencoding',   trim($config->extencoding), 'auth/db');
758         set_config('setupsql',      trim($config->setupsql),'auth/db');
759         set_config('debugauthdb',   $config->debugauthdb,   'auth/db');
760         set_config('removeuser',    $config->removeuser,    'auth/db');
761         set_config('changepasswordurl', trim($config->changepasswordurl), 'auth/db');
763         return true;
764     }
766     /**
767      * Add slashes, we can not use placeholders or system functions.
768      *
769      * @param string $text
770      * @return string
771      */
772     function ext_addslashes($text) {
773         if (empty($this->config->sybasequoting)) {
774             $text = str_replace('\\', '\\\\', $text);
775             $text = str_replace(array('\'', '"', "\0"), array('\\\'', '\\"', '\\0'), $text);
776         } else {
777             $text = str_replace("'", "''", $text);
778         }
779         return $text;
780     }