Mereged updates from DokuWiki 38
[sudaraka-org:dokuwiki-mods.git] / inc / auth / mysql.class.php
1 <?php
2 /**
3  * MySQLP authentication backend
4  *
5  * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
6  * @author     Andreas Gohr <andi@splitbrain.org>
7  * @author     Chris Smith <chris@jalakai.co.uk>
8  * @author     Matthias Grimm <matthias.grimmm@sourceforge.net>
9  */
10
11 class auth_mysql extends auth_basic {
12
13     var $dbcon        = 0;
14     var $dbver        = 0;    // database version
15     var $dbrev        = 0;    // database revision
16     var $dbsub        = 0;    // database subrevision
17     var $cnf          = null;
18     var $defaultgroup = "";
19
20     /**
21      * Constructor
22      *
23      * checks if the mysql interface is available, otherwise it will
24      * set the variable $success of the basis class to false
25      *
26      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
27      */
28     function __construct() {
29         global $conf;
30         $this->cnf = $conf['auth']['mysql'];
31
32         if (method_exists($this, 'auth_basic')){
33             parent::__construct();
34         }
35
36         if(!function_exists('mysql_connect')) {
37             if ($this->cnf['debug']){
38                 msg("MySQL err: PHP MySQL extension not found.",-1,__LINE__,__FILE__);
39             }
40             $this->success = false;
41             return;
42         }
43
44         // default to UTF-8, you rarely want something else
45         if(!isset($this->cnf['charset'])) $this->cnf['charset'] = 'utf8';
46
47         $this->defaultgroup = $conf['defaultgroup'];
48
49         // set capabilities based upon config strings set
50         if (empty($this->cnf['server']) || empty($this->cnf['user']) ||
51             !isset($this->cnf['password']) || empty($this->cnf['database'])){
52
53             if ($this->cnf['debug']){
54                 msg("MySQL err: insufficient configuration.",-1,__LINE__,__FILE__);
55             }
56             $this->success = false;
57             return;
58         }
59
60         $this->cando['addUser']      = $this->_chkcnf(array(
61                                             'getUserInfo',
62                                             'getGroups',
63                                             'addUser',
64                                             'getUserID',
65                                             'getGroupID',
66                                             'addGroup',
67                                             'addUserGroup'),true);
68         $this->cando['delUser']      = $this->_chkcnf(array(
69                                             'getUserID',
70                                             'delUser',
71                                             'delUserRefs'),true);
72         $this->cando['modLogin']     = $this->_chkcnf(array(
73                                             'getUserID',
74                                             'updateUser',
75                                             'UpdateTarget'),true);
76         $this->cando['modPass']      = $this->cando['modLogin'];
77         $this->cando['modName']      = $this->cando['modLogin'];
78         $this->cando['modMail']      = $this->cando['modLogin'];
79         $this->cando['modGroups']    = $this->_chkcnf(array(
80                                             'getUserID',
81                                             'getGroups',
82                                             'getGroupID',
83                                             'addGroup',
84                                             'addUserGroup',
85                                             'delGroup',
86                                             'getGroupID',
87                                             'delUserGroup'),true);
88         /* getGroups is not yet supported
89            $this->cando['getGroups']    = $this->_chkcnf(array('getGroups',
90            'getGroupID'),false); */
91         $this->cando['getUsers']     = $this->_chkcnf(array(
92                                             'getUsers',
93                                             'getUserInfo',
94                                             'getGroups'),false);
95         $this->cando['getUserCount'] = $this->_chkcnf(array('getUsers'),false);
96     }
97
98     /**
99      * Check if the given config strings are set
100      *
101      * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
102      * @return  bool
103      */
104     function _chkcnf($keys, $wop=false){
105         foreach ($keys as $key){
106             if (empty($this->cnf[$key])) return false;
107         }
108
109         /* write operation and lock array filled with tables names? */
110         if ($wop && (!is_array($this->cnf['TablesToLock']) ||
111                     !count($this->cnf['TablesToLock']))){
112             return false;
113         }
114
115         return true;
116     }
117
118     /**
119      * Checks if the given user exists and the given plaintext password
120      * is correct. Furtheron it might be checked wether the user is
121      * member of the right group
122      *
123      * Depending on which SQL string is defined in the config, password
124      * checking is done here (getpass) or by the database (passcheck)
125      *
126      * @param  $user  user who would like access
127      * @param  $pass  user's clear text password to check
128      * @return bool
129      *
130      * @author  Andreas Gohr <andi@splitbrain.org>
131      * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
132      */
133     function checkPass($user,$pass){
134         $rc  = false;
135
136         if($this->_openDB()) {
137             $sql    = str_replace('%{user}',$this->_escape($user),$this->cnf['checkPass']);
138             $sql    = str_replace('%{pass}',$this->_escape($pass),$sql);
139             $sql    = str_replace('%{dgroup}',$this->_escape($this->defaultgroup),$sql);
140             $result = $this->_queryDB($sql);
141
142             if($result !== false && count($result) == 1) {
143                 if($this->cnf['forwardClearPass'] == 1)
144                     $rc = true;
145                 else
146                     $rc = auth_verifyPassword($pass,$result[0]['pass']);
147             }
148             $this->_closeDB();
149         }
150         return $rc;
151     }
152
153     /**
154      * [public function]
155      *
156      * Returns info about the given user needs to contain
157      * at least these fields:
158      *   name  string  full name of the user
159      *   mail  string  email addres of the user
160      *   grps  array   list of groups the user is in
161      *
162      * @param $user   user's nick to get data for
163      *
164      * @author  Andreas Gohr <andi@splitbrain.org>
165      * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
166      */
167     function getUserData($user){
168         if($this->_openDB()) {
169             $this->_lockTables("READ");
170             $info = $this->_getUserInfo($user);
171             $this->_unlockTables();
172             $this->_closeDB();
173         } else
174             $info = false;
175         return $info;
176     }
177
178     /**
179      * [public function]
180      *
181      * Create a new User. Returns false if the user already exists,
182      * null when an error occurred and true if everything went well.
183      *
184      * The new user will be added to the default group by this
185      * function if grps are not specified (default behaviour).
186      *
187      * @param $user  nick of the user
188      * @param $pwd   clear text password
189      * @param $name  full name of the user
190      * @param $mail  email address
191      * @param $grps  array of groups the user should become member of
192      *
193      * @author  Andreas Gohr <andi@splitbrain.org>
194      * @author  Chris Smith <chris@jalakai.co.uk>
195      * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
196      */
197     function createUser($user,$pwd,$name,$mail,$grps=null){
198         if($this->_openDB()) {
199             if (($info = $this->_getUserInfo($user)) !== false)
200                 return false;  // user already exists
201
202             // set defaultgroup if no groups were given
203             if ($grps == null)
204                 $grps = array($this->defaultgroup);
205
206             $this->_lockTables("WRITE");
207             $pwd = $this->cnf['forwardClearPass'] ? $pwd : auth_cryptPassword($pwd);
208             $rc = $this->_addUser($user,$pwd,$name,$mail,$grps);
209             $this->_unlockTables();
210             $this->_closeDB();
211             if ($rc) return true;
212         }
213         return null;  // return error
214     }
215
216     /**
217      * Modify user data [public function]
218      *
219      * An existing user dataset will be modified. Changes are given in an array.
220      *
221      * The dataset update will be rejected if the user name should be changed
222      * to an already existing one.
223      *
224      * The password must be provides unencrypted. Pasword cryption is done
225      * automatically if configured.
226      *
227      * If one or more groups could't be updated, an error would be set. In
228      * this case the dataset might already be changed and we can't rollback
229      * the changes. Transactions would be really usefull here.
230      *
231      * modifyUser() may be called without SQL statements defined that are
232      * needed to change group membership (for example if only the user profile
233      * should be modified). In this case we asure that we don't touch groups
234      * even $changes['grps'] is set by mistake.
235      *
236      * @param   $user     nick of the user to be changed
237      * @param   $changes  array of field/value pairs to be changed (password
238      *                    will be clear text)
239      * @return  bool      true on success, false on error
240      *
241      * @author  Chris Smith <chris@jalakai.co.uk>
242      * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
243      */
244     function modifyUser($user, $changes) {
245         $rc = false;
246
247         if (!is_array($changes) || !count($changes))
248             return true;  // nothing to change
249
250         if($this->_openDB()) {
251             $this->_lockTables("WRITE");
252
253             if (($uid = $this->_getUserID($user))) {
254                 $rc = $this->_updateUserInfo($changes, $uid);
255
256                 if ($rc && isset($changes['grps']) && $this->cando['modGroups']) {
257                     $groups = $this->_getGroups($user);
258                     $grpadd = array_diff($changes['grps'], $groups);
259                     $grpdel = array_diff($groups, $changes['grps']);
260
261                     foreach($grpadd as $group)
262                         if (($this->_addUserToGroup($user, $group, 1)) == false)
263                             $rc = false;
264
265                     foreach($grpdel as $group)
266                         if (($this->_delUserFromGroup($user, $group)) == false)
267                             $rc = false;
268                 }
269             }
270
271             $this->_unlockTables();
272             $this->_closeDB();
273         }
274         return $rc;
275     }
276
277     /**
278      * [public function]
279      *
280      * Remove one or more users from the list of registered users
281      *
282      * @param   array  $users   array of users to be deleted
283      * @return  int             the number of users deleted
284      *
285      * @author  Christopher Smith <chris@jalakai.co.uk>
286      * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
287      */
288     function deleteUsers($users) {
289         $count = 0;
290
291         if($this->_openDB()) {
292             if (is_array($users) && count($users)) {
293                 $this->_lockTables("WRITE");
294                 foreach ($users as $user) {
295                     if ($this->_delUser($user))
296                         $count++;
297                 }
298                 $this->_unlockTables();
299             }
300             $this->_closeDB();
301         }
302         return $count;
303     }
304
305     /**
306      * [public function]
307      *
308      * Counts users which meet certain $filter criteria.
309      *
310      * @param  array  $filter  filter criteria in item/pattern pairs
311      * @return count of found users.
312      *
313      * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
314      */
315     function getUserCount($filter=array()) {
316         $rc = 0;
317
318         if($this->_openDB()) {
319             $sql = $this->_createSQLFilter($this->cnf['getUsers'], $filter);
320
321             if ($this->dbver >= 4) {
322                 $sql = substr($sql, 6);  /* remove 'SELECT' or 'select' */
323                 $sql = "SELECT SQL_CALC_FOUND_ROWS".$sql." LIMIT 1";
324                 $this->_queryDB($sql);
325                 $result = $this->_queryDB("SELECT FOUND_ROWS()");
326                 $rc = $result[0]['FOUND_ROWS()'];
327             } else if (($result = $this->_queryDB($sql)))
328                 $rc = count($result);
329
330             $this->_closeDB();
331         }
332         return $rc;
333     }
334
335     /**
336      * Bulk retrieval of user data. [public function]
337      *
338      * @param   first     index of first user to be returned
339      * @param   limit     max number of users to be returned
340      * @param   filter    array of field/pattern pairs
341      * @return  array of userinfo (refer getUserData for internal userinfo details)
342      *
343      * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
344      */
345     function retrieveUsers($first=0,$limit=10,$filter=array()) {
346         $out   = array();
347
348         if($this->_openDB()) {
349             $this->_lockTables("READ");
350             $sql  = $this->_createSQLFilter($this->cnf['getUsers'], $filter);
351             $sql .= " ".$this->cnf['SortOrder']." LIMIT $first, $limit";
352             $result = $this->_queryDB($sql);
353
354             if (!empty($result)) {
355                 foreach ($result as $user)
356                     if (($info = $this->_getUserInfo($user['user'])))
357                         $out[$user['user']] = $info;
358             }
359
360             $this->_unlockTables();
361             $this->_closeDB();
362         }
363         return $out;
364     }
365
366     /**
367      * Give user membership of a group [public function]
368      *
369      * @param   $user
370      * @param   $group
371      * @return  bool    true on success, false on error
372      *
373      * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
374      */
375     function joinGroup($user, $group) {
376         $rc = false;
377
378         if ($this->_openDB()) {
379             $this->_lockTables("WRITE");
380             $rc  = $this->_addUserToGroup($user, $group);
381             $this->_unlockTables();
382             $this->_closeDB();
383         }
384         return $rc;
385     }
386
387     /**
388      * Remove user from a group [public function]
389      *
390      * @param   $user    user that leaves a group
391      * @param   $group   group to leave
392      * @return  bool
393      *
394      * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
395      */
396     function leaveGroup($user, $group) {
397         $rc = false;
398
399         if ($this->_openDB()) {
400             $this->_lockTables("WRITE");
401             $uid = $this->_getUserID($user);
402             $rc  = $this->_delUserFromGroup($user, $group);
403             $this->_unlockTables();
404             $this->_closeDB();
405         }
406         return $rc;
407     }
408
409     /**
410      * MySQL is case-insensitive
411      */
412     function isCaseSensitive(){
413         return false;
414     }
415
416     /**
417      * Adds a user to a group.
418      *
419      * If $force is set to '1' non existing groups would be created.
420      *
421      * The database connection must already be established. Otherwise
422      * this function does nothing and returns 'false'. It is strongly
423      * recommended to call this function only after all participating
424      * tables (group and usergroup) have been locked.
425      *
426      * @param   $user    user to add to a group
427      * @param   $group   name of the group
428      * @param   $force   '1' create missing groups
429      * @return  bool     'true' on success, 'false' on error
430      *
431      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
432      */
433     function _addUserToGroup($user, $group, $force=0) {
434         $newgroup = 0;
435
436         if (($this->dbcon) && ($user)) {
437             $gid = $this->_getGroupID($group);
438             if (!$gid) {
439                 if ($force) {  // create missing groups
440                     $sql = str_replace('%{group}',$this->_escape($group),$this->cnf['addGroup']);
441                     $gid = $this->_modifyDB($sql);
442                     $newgroup = 1;  // group newly created
443                 }
444                 if (!$gid) return false; // group didn't exist and can't be created
445             }
446
447             $sql = $this->cnf['addUserGroup'];
448             if(strpos($sql,'%{uid}') !== false){
449                 $uid = $this->_getUserID($user);
450                 $sql = str_replace('%{uid}',  $this->_escape($uid),$sql);
451             }
452             $sql = str_replace('%{user}', $this->_escape($user),$sql);
453             $sql = str_replace('%{gid}',  $this->_escape($gid),$sql);
454             $sql = str_replace('%{group}',$this->_escape($group),$sql);
455             if ($this->_modifyDB($sql) !== false) return true;
456
457             if ($newgroup) { // remove previously created group on error
458                 $sql = str_replace('%{gid}',  $this->_escape($gid),$this->cnf['delGroup']);
459                 $sql = str_replace('%{group}',$this->_escape($group),$sql);
460                 $this->_modifyDB($sql);
461             }
462         }
463         return false;
464     }
465
466     /**
467      * Remove user from a group
468      *
469      * @param   $user    user that leaves a group
470      * @param   $group   group to leave
471      * @return  bool     true on success, false on error
472      *
473      * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
474      */
475     function _delUserFromGroup($user, $group) {
476         $rc = false;
477
478         if (($this->dbcon) && ($user)) {
479             $sql = $this->cnf['delUserGroup'];
480             if(strpos($sql,'%{uid}') !== false){
481                 $uid = $this->_getUserID($user);
482                 $sql = str_replace('%{uid}',  $this->_escape($uid),$sql);
483             }
484             $gid = $this->_getGroupID($group);
485             if ($gid) {
486                 $sql = str_replace('%{user}', $this->_escape($user),$sql);
487                 $sql = str_replace('%{gid}',  $this->_escape($gid),$sql);
488                 $sql = str_replace('%{group}',$this->_escape($group),$sql);
489                 $rc  = $this->_modifyDB($sql) == 0 ? true : false;
490             }
491         }
492         return $rc;
493     }
494
495     /**
496      * Retrieves a list of groups the user is a member off.
497      *
498      * The database connection must already be established
499      * for this function to work. Otherwise it will return
500      * 'false'.
501      *
502      * @param  $user  user whose groups should be listed
503      * @return bool   false on error
504      * @return array  array containing all groups on success
505      *
506      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
507      */
508     function _getGroups($user) {
509         $groups = array();
510
511         if($this->dbcon) {
512             $sql = str_replace('%{user}',$this->_escape($user),$this->cnf['getGroups']);
513             $result = $this->_queryDB($sql);
514
515             if($result !== false && count($result)) {
516                 foreach($result as $row)
517                     $groups[] = $row['group'];
518             }
519             return $groups;
520         }
521         return false;
522     }
523
524     /**
525      * Retrieves the user id of a given user name
526      *
527      * The database connection must already be established
528      * for this function to work. Otherwise it will return
529      * 'false'.
530      *
531      * @param  $user   user whose id is desired
532      * @return user id
533      *
534      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
535      */
536     function _getUserID($user) {
537         if($this->dbcon) {
538             $sql = str_replace('%{user}',$this->_escape($user),$this->cnf['getUserID']);
539             $result = $this->_queryDB($sql);
540             return $result === false ? false : $result[0]['id'];
541         }
542         return false;
543     }
544
545     /**
546      * Adds a new User to the database.
547      *
548      * The database connection must already be established
549      * for this function to work. Otherwise it will return
550      * 'false'.
551      *
552      * @param  $user  login of the user
553      * @param  $pwd   encrypted password
554      * @param  $name  full name of the user
555      * @param  $mail  email address
556      * @param  $grps  array of groups the user should become member of
557      * @return bool
558      *
559      * @author  Andreas Gohr <andi@splitbrain.org>
560      * @author  Chris Smith <chris@jalakai.co.uk>
561      * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
562      */
563     function _addUser($user,$pwd,$name,$mail,$grps){
564         if($this->dbcon && is_array($grps)) {
565             $sql = str_replace('%{user}', $this->_escape($user),$this->cnf['addUser']);
566             $sql = str_replace('%{pass}', $this->_escape($pwd),$sql);
567             $sql = str_replace('%{name}', $this->_escape($name),$sql);
568             $sql = str_replace('%{email}',$this->_escape($mail),$sql);
569             $uid = $this->_modifyDB($sql);
570
571             if ($uid) {
572                 foreach($grps as $group) {
573                     $gid = $this->_addUserToGroup($user, $group, 1);
574                     if ($gid === false) break;
575                 }
576
577                 if ($gid) return true;
578                 else {
579                     /* remove the new user and all group relations if a group can't
580                      * be assigned. Newly created groups will remain in the database
581                      * and won't be removed. This might create orphaned groups but
582                      * is not a big issue so we ignore this problem here.
583                      */
584                     $this->_delUser($user);
585                     if ($this->cnf['debug'])
586                         msg ("MySQL err: Adding user '$user' to group '$group' failed.",-1,__LINE__,__FILE__);
587                 }
588             }
589         }
590         return false;
591     }
592
593     /**
594      * Deletes a given user and all his group references.
595      *
596      * The database connection must already be established
597      * for this function to work. Otherwise it will return
598      * 'false'.
599      *
600      * @param  $user   user whose id is desired
601      * @return bool
602      *
603      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
604      */
605     function _delUser($user) {
606         if($this->dbcon) {
607             $uid = $this->_getUserID($user);
608             if ($uid) {
609                 $sql = str_replace('%{uid}',$this->_escape($uid),$this->cnf['delUserRefs']);
610                 $this->_modifyDB($sql);
611                 $sql = str_replace('%{uid}',$this->_escape($uid),$this->cnf['delUser']);
612                 $sql = str_replace('%{user}',  $this->_escape($user),$sql);
613                 $this->_modifyDB($sql);
614                 return true;
615             }
616         }
617         return false;
618     }
619
620     /**
621      * getUserInfo
622      *
623      * Gets the data for a specific user The database connection
624      * must already be established for this function to work.
625      * Otherwise it will return 'false'.
626      *
627      * @param  $user  user's nick to get data for
628      * @return bool   false on error
629      * @return array  user info on success
630      *
631      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
632      */
633     function _getUserInfo($user){
634         $sql = str_replace('%{user}',$this->_escape($user),$this->cnf['getUserInfo']);
635         $result = $this->_queryDB($sql);
636         if($result !== false && count($result)) {
637             $info = $result[0];
638             $info['grps'] = $this->_getGroups($user);
639             return $info;
640         }
641         return false;
642     }
643
644     /**
645      * Updates the user info in the database
646      *
647      * Update a user data structure in the database according changes
648      * given in an array. The user name can only be changes if it didn't
649      * exists already. If the new user name exists the update procedure
650      * will be aborted. The database keeps unchanged.
651      *
652      * The database connection has already to be established for this
653      * function to work. Otherwise it will return 'false'.
654      *
655      * The password will be crypted if necessary.
656      *
657      * @param  $changes  array of items to change as pairs of item and value
658      * @param  $uid      user id of dataset to change, must be unique in DB
659      * @return true on success or false on error
660      *
661      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
662      */
663     function _updateUserInfo($changes, $uid) {
664         $sql  = $this->cnf['updateUser']." ";
665         $cnt = 0;
666         $err = 0;
667
668         if($this->dbcon) {
669             foreach ($changes as $item => $value) {
670                 if ($item == 'user') {
671                     if (($this->_getUserID($changes['user']))) {
672                         $err = 1; /* new username already exists */
673                         break;    /* abort update */
674                     }
675                     if ($cnt++ > 0) $sql .= ", ";
676                     $sql .= str_replace('%{user}',$value,$this->cnf['UpdateLogin']);
677                 } else if ($item == 'name') {
678                     if ($cnt++ > 0) $sql .= ", ";
679                     $sql .= str_replace('%{name}',$value,$this->cnf['UpdateName']);
680                 } else if ($item == 'pass') {
681                     if (!$this->cnf['forwardClearPass'])
682                         $value = auth_cryptPassword($value);
683                     if ($cnt++ > 0) $sql .= ", ";
684                     $sql .= str_replace('%{pass}',$value,$this->cnf['UpdatePass']);
685                 } else if ($item == 'mail') {
686                     if ($cnt++ > 0) $sql .= ", ";
687                     $sql .= str_replace('%{email}',$value,$this->cnf['UpdateEmail']);
688                 }
689             }
690
691             if ($err == 0) {
692                 if ($cnt > 0) {
693                     $sql .= " ".str_replace('%{uid}', $uid, $this->cnf['UpdateTarget']);
694                     if(get_class($this) == 'auth_mysql') $sql .= " LIMIT 1"; //some PgSQL inheritance comp.
695                     $this->_modifyDB($sql);
696                 }
697                 return true;
698             }
699         }
700         return false;
701     }
702
703     /**
704      * Retrieves the group id of a given group name
705      *
706      * The database connection must already be established
707      * for this function to work. Otherwise it will return
708      * 'false'.
709      *
710      * @param  $group   group name which id is desired
711      * @return group id
712      *
713      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
714      */
715     function _getGroupID($group) {
716         if($this->dbcon) {
717             $sql = str_replace('%{group}',$this->_escape($group),$this->cnf['getGroupID']);
718             $result = $this->_queryDB($sql);
719             return $result === false ? false : $result[0]['id'];
720         }
721         return false;
722     }
723
724     /**
725      * Opens a connection to a database and saves the handle for further
726      * usage in the object. The successful call to this functions is
727      * essential for most functions in this object.
728      *
729      * @return bool
730      *
731      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
732      */
733     function _openDB() {
734         if (!$this->dbcon) {
735             $con = @mysql_connect ($this->cnf['server'], $this->cnf['user'], $this->cnf['password']);
736             if ($con) {
737                 if ((mysql_select_db($this->cnf['database'], $con))) {
738                     if ((preg_match("/^(\d+)\.(\d+)\.(\d+).*/", mysql_get_server_info ($con), $result)) == 1) {
739                         $this->dbver = $result[1];
740                         $this->dbrev = $result[2];
741                         $this->dbsub = $result[3];
742                     }
743                     $this->dbcon = $con;
744                     if(!empty($this->cnf['charset'])){
745                         mysql_query('SET CHARACTER SET "' . $this->cnf['charset'] . '"', $con);
746                     }
747                     return true;   // connection and database successfully opened
748                 } else {
749                     mysql_close ($con);
750                     if ($this->cnf['debug'])
751                         msg("MySQL err: No access to database {$this->cnf['database']}.",-1,__LINE__,__FILE__);
752                 }
753             } else if ($this->cnf['debug'])
754                 msg ("MySQL err: Connection to {$this->cnf['user']}@{$this->cnf['server']} not possible.",
755                         -1,__LINE__,__FILE__);
756
757             return false;  // connection failed
758         }
759         return true;  // connection already open
760     }
761
762     /**
763      * Closes a database connection.
764      *
765      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
766      */
767     function _closeDB() {
768         if ($this->dbcon) {
769             mysql_close ($this->dbcon);
770             $this->dbcon = 0;
771         }
772     }
773
774     /**
775      * Sends a SQL query to the database and transforms the result into
776      * an associative array.
777      *
778      * This function is only able to handle queries that returns a
779      * table such as SELECT.
780      *
781      * @param $query  SQL string that contains the query
782      * @return array with the result table
783      *
784      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
785      */
786     function _queryDB($query) {
787         if($this->cnf['debug'] >= 2){
788             msg('MySQL query: '.hsc($query),0,__LINE__,__FILE__);
789         }
790
791         $resultarray = array();
792         if ($this->dbcon) {
793             $result = @mysql_query($query,$this->dbcon);
794             if ($result) {
795                 while (($t = mysql_fetch_assoc($result)) !== false)
796                     $resultarray[]=$t;
797                 mysql_free_result ($result);
798                 return $resultarray;
799             }
800             if ($this->cnf['debug'])
801                 msg('MySQL err: '.mysql_error($this->dbcon),-1,__LINE__,__FILE__);
802         }
803         return false;
804     }
805
806     /**
807      * Sends a SQL query to the database
808      *
809      * This function is only able to handle queries that returns
810      * either nothing or an id value such as INPUT, DELETE, UPDATE, etc.
811      *
812      * @param $query  SQL string that contains the query
813      * @return insert id or 0, false on error
814      *
815      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
816      */
817     function _modifyDB($query) {
818         if ($this->dbcon) {
819             $result = @mysql_query($query,$this->dbcon);
820             if ($result) {
821                 $rc = mysql_insert_id($this->dbcon); //give back ID on insert
822                 if ($rc !== false) return $rc;
823             }
824             if ($this->cnf['debug'])
825                 msg('MySQL err: '.mysql_error($this->dbcon),-1,__LINE__,__FILE__);
826         }
827         return false;
828     }
829
830     /**
831      * Locked a list of tables for exclusive access so that modifications
832      * to the database can't be disturbed by other threads. The list
833      * could be set with $conf['auth']['mysql']['TablesToLock'] = array()
834      *
835      * If aliases for tables are used in SQL statements, also this aliases
836      * must be locked. For eg. you use a table 'user' and the alias 'u' in
837      * some sql queries, the array must looks like this (order is important):
838      *   array("user", "user AS u");
839      *
840      * MySQL V3 is not able to handle transactions with COMMIT/ROLLBACK
841      * so that this functionality is simulated by this function. Nevertheless
842      * it is not as powerful as transactions, it is a good compromise in safty.
843      *
844      * @param $mode  could be 'READ' or 'WRITE'
845      *
846      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
847      */
848     function _lockTables($mode) {
849         if ($this->dbcon) {
850             if (is_array($this->cnf['TablesToLock']) && !empty($this->cnf['TablesToLock'])) {
851                 if ($mode == "READ" || $mode == "WRITE") {
852                     $sql = "LOCK TABLES ";
853                     $cnt = 0;
854                     foreach ($this->cnf['TablesToLock'] as $table) {
855                         if ($cnt++ != 0) $sql .= ", ";
856                         $sql .= "$table $mode";
857                     }
858                     $this->_modifyDB($sql);
859                     return true;
860                 }
861             }
862         }
863         return false;
864     }
865
866     /**
867      * Unlock locked tables. All existing locks of this thread will be
868      * abrogated.
869      *
870      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
871      */
872     function _unlockTables() {
873         if ($this->dbcon) {
874             $this->_modifyDB("UNLOCK TABLES");
875             return true;
876         }
877         return false;
878     }
879
880     /**
881      * Transforms the filter settings in an filter string for a SQL database
882      * The database connection must already be established, otherwise the
883      * original SQL string without filter criteria will be returned.
884      *
885      * @param  $sql     SQL string to which the $filter criteria should be added
886      * @param  $filter  array of filter criteria as pairs of item and pattern
887      * @return SQL string with attached $filter criteria on success
888      * @return the original SQL string on error.
889      *
890      * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
891      */
892     function _createSQLFilter($sql, $filter) {
893         $SQLfilter = "";
894         $cnt = 0;
895
896         if ($this->dbcon) {
897             foreach ($filter as $item => $pattern) {
898                 $tmp = '%'.$this->_escape($pattern).'%';
899                 if ($item == 'user') {
900                     if ($cnt++ > 0) $SQLfilter .= " AND ";
901                     $SQLfilter .= str_replace('%{user}',$tmp,$this->cnf['FilterLogin']);
902                 } else if ($item == 'name') {
903                     if ($cnt++ > 0) $SQLfilter .= " AND ";
904                     $SQLfilter .= str_replace('%{name}',$tmp,$this->cnf['FilterName']);
905                 } else if ($item == 'mail') {
906                     if ($cnt++ > 0) $SQLfilter .= " AND ";
907                     $SQLfilter .= str_replace('%{email}',$tmp,$this->cnf['FilterEmail']);
908                 } else if ($item == 'grps') {
909                     if ($cnt++ > 0) $SQLfilter .= " AND ";
910                     $SQLfilter .= str_replace('%{group}',$tmp,$this->cnf['FilterGroup']);
911                 }
912             }
913
914             // we have to check SQLfilter here and must not use $cnt because if
915             // any of cnf['Filter????'] is not defined, a malformed SQL string
916             // would be generated.
917
918             if (strlen($SQLfilter)) {
919                 $glue = strpos(strtolower($sql),"where") ? " AND " : " WHERE ";
920                 $sql = $sql.$glue.$SQLfilter;
921             }
922         }
923
924         return $sql;
925     }
926
927     /**
928      * Escape a string for insertion into the database
929      *
930      * @author Andreas Gohr <andi@splitbrain.org>
931      * @param  string  $string The string to escape
932      * @param  boolean $like   Escape wildcard chars as well?
933      */
934     function _escape($string,$like=false){
935         if($this->dbcon){
936             $string = mysql_real_escape_string($string, $this->dbcon);
937         }else{
938             $string = addslashes($string);
939         }
940         if($like){
941             $string = addcslashes($string,'%_');
942         }
943         return $string;
944     }
945 }
946
947 //Setup VIM: ex: et ts=2 :