From 3ff230f7a83037178c07da3e76b11633d3e76e3f Mon Sep 17 00:00:00 2001
From: Matthias Grimm <matthiasgrimm@users.sourceforge.net>
Date: Sun, 6 Nov 2005 14:03:03 +0100
Subject: [PATCH] mysql extension for OO auth system

This patch adds the mysql extension to the OO auth system. The SQL statements
are defined in conf/mysql.conf.php.example and needs to be adapted to the
local database. The set of statements work with the database structure described
in conf/mysql.conf.php.example.

This module is beta and heavy testing in different environments is recommended.

The documentation of the SQL statements is not complete yet

darcs-hash:20051106130303-4145d-28acb18584822f8d1eafa1d63e206f2e83f64009.gz
---
 conf/mysql.conf.php.example | 106 ++++++
 inc/auth/mysql.class.php    | 742 ++++++++++++++++++++++++++++++++++++
 inc/lang/de/lang.php        |  10 +-
 inc/lang/en/lang.php        |  10 +-
 4 files changed, 866 insertions(+), 2 deletions(-)
 create mode 100644 conf/mysql.conf.php.example
 create mode 100644 inc/auth/mysql.class.php

diff --git a/conf/mysql.conf.php.example b/conf/mysql.conf.php.example
new file mode 100644
index 000000000..1c70e794e
--- /dev/null
+++ b/conf/mysql.conf.php.example
@@ -0,0 +1,106 @@
+<?php
+/*
+  This is an example configuration for the mysql auth module.
+
+  This SQL statements are optimized for following table structure.
+  If you use a different one you have to change them accordingly.
+  See comments of every statement for details.
+
+  TABLE users
+      uid   login   pass   firstname   lastname   email
+	  
+  TABLE groups
+      gid   name
+	  
+  TABLE usergroup
+      uid   gid
+  
+  To use this configuration you have to copy them to local.php
+  or at least include this file in local.php.
+ */
+
+
+/* Normally password encryptionis done by DokuWiki (recommended) but for
+ * some reasons it might be usefull to let the database do the encryption.
+ * Set 'encryptPass' to '1' and the cleartext password is forwarded to
+ * the database, otherwise the encrypted one.
+ */
+$conf['auth']['mysql']['encryptPass'] = 0;
+
+/* Multiple table operations will be protected by locks. This array tolds
+ * the module which tables to lock. If you use any aliases for table names
+ * these array must also contain these aliases. Any unamed alias will cause
+ * a warning suring operation. See the example below.
+ */
+$conf['auth']['mysql']['TablesToLock']= array("users", "users AS u","groups", "groups AS g", "usergroup", "usergroup AS ug");
+
+/* This statement should return the database index of a given user name.
+ * The module will access the index with the name 'id' so a alias might be
+ * necessary.
+ * following patters will be replaced:
+ *   %u    user name 
+ */
+$conf['auth']['mysql']['getUserID']   = "SELECT uid AS id FROM users WHERE login='%u'";
+
+/* This statement should return the database index of a given group name.
+ * The module will access the index with the name 'id' so a alias might be
+ * necessary.
+ * following patters will be replaced:
+ *   %g    group name 
+ */
+$conf['auth']['mysql']['getGroupID']  = "SELECT gid AS id FROM groups WHERE name='%g'";
+
+/* This statement is used to grant or deny access to the wiki. The result should
+ * be a table with exact one line containing at least the password of the user.
+ * If the result table is empty or contains more than one row, access will be denied.
+ * The module access the password as 'pass' so a alias might be necessary.
+ * following patters will be replaced:
+ *   %u    user name
+ *   %p    encrypted or clear text password (depends on 'encryptPass')
+ *   %g    default group name 
+ */
+$conf['auth']['mysql']['checkPass']   = "SELECT pass
+                                         FROM usergroup AS ug
+                                         JOIN users AS u ON u.uid=ug.uid
+                                         JOIN groups AS g ON g.gid=ug.gid
+                                         WHERE login='%u'
+                                         AND name='%g'";
+
+$conf['auth']['mysql']['getGroups']   = "SELECT name as `group`
+                                         FROM groups g, users u, usergroup ug
+                                         WHERE u.uid = ug.uid
+                                         AND g.gid = ug.gid
+                                         AND u.login='%u'";
+$conf['auth']['mysql']['getUserInfo'] = "SELECT pass, CONCAT(firstname,' ',lastname) AS name, email AS mail
+                                         FROM users
+                                         WHERE login='%u'";
+$conf['auth']['mysql']['getUsers']    = "SELECT DISTINCT login AS user
+                                         FROM users AS u 
+                                         LEFT JOIN usergroup AS ug ON u.uid=ug.uid
+                                         LEFT JOIN groups AS g ON ug.gid=g.gid";
+
+$conf['auth']['mysql']['SortOrder']   = "ORDER BY login";
+$conf['auth']['mysql']['FilterLogin'] = "login LIKE '%s'";
+$conf['auth']['mysql']['FilterName']  = "CONCAT(firstname,' ',lastname) LIKE '%s'";
+$conf['auth']['mysql']['FilterEmail'] = "email LIKE '%s'";
+$conf['auth']['mysql']['FilterGroup'] = "name LIKE '%s'";
+
+$conf['auth']['mysql']['addUser']     = "INSERT INTO users
+                                         (login, pass, email, firstname, lastname)
+                                         VALUES ('%u', '%p', '%e',
+                                         SUBSTRING_INDEX('%n',' ', 1),
+                                         SUBSTRING_INDEX('%n',' ', -1))";
+$conf['auth']['mysql']['delUser']     = "DELETE FROM users
+                                         WHERE uid='%uid'";
+$conf['auth']['mysql']['addGroup']    = "INSERT INTO groups (name)
+                                         VALUES ('%g')";
+$conf['auth']['mysql']['delGroup']    = "DELETE FROM groups
+                                         WHERE gid='%gid'";
+$conf['auth']['mysql']['addUserGroup']= "INSERT INTO usergroup (uid, gid)
+                                         VALUES ('%uid', '%gid')";
+$conf['auth']['mysql']['delUserGroup']= "DELETE FROM usergroup
+                                         WHERE uid='%uid'
+                                         AND gid='%gid'";
+$conf['auth']['mysql']['delUserRefs'] = "DELETE FROM usergroup
+                                         WHERE uid='%uid'";
+?> 
diff --git a/inc/auth/mysql.class.php b/inc/auth/mysql.class.php
new file mode 100644
index 000000000..accb650eb
--- /dev/null
+++ b/inc/auth/mysql.class.php
@@ -0,0 +1,742 @@
+<?php
+/**
+ * MySQLP authentication backend
+ *
+ * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
+ * @author     Andreas Gohr <andi@splitbrain.org>
+ * @author     Chris Smith <chris@jalakai.co.uk>
+ * @author     Matthias Grimm <matthias.grimmm@sourceforge.net>
+*/
+ 
+define('DOKU_AUTH', dirname(__FILE__)); 
+require_once(DOKU_AUTH.'/basic.class.php');
+
+class auth_mysql extends auth_basic {
+   
+    var $dbcon        = 0;
+    var $cnf          = null;
+    var $defaultgroup = "";
+    
+    /**
+     * Constructor
+     *
+     * checks if the mysql interface is available, otherwise it will
+     * set the variable $success of the basis class to FALSE
+     *
+     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function auth_mysql() {
+      global $conf;
+      global $lang;
+      
+      if (method_exists($this, 'auth_basic'))
+        parent::auth_basic();
+        
+      if(!function_exists('mysql_connect')) {
+        msg($lang['noMySQL'],-1);
+        $this->success = false;
+      }
+      
+      $this->cnf          = $conf['auth']['mysql'];
+      $this->defaultgroup = $conf['defaultgroup'];
+    }
+    
+    /**
+     * [public function]
+     *
+     * Checks if the given user exists and the given plaintext password
+     * is correct. Furtheron it might be checked wether the user is
+     * member of the right group
+     *
+     * Depending on which SQL string is defined in the config, password
+     * checking is done here (getpass) or by the database (passcheck)
+     *
+     * @param  $user  user who would like access
+     * @param  $pass  user's clear text password to check
+     * @return bool
+     *
+     * @author  Andreas Gohr <andi@splitbrain.org>
+     * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */ 
+    function checkPass($user,$pass){
+      $rc  = false;
+      
+      if($this->openDB()) {
+        $sql    = str_replace('%u',addslashes($user),$this->cnf['checkPass']);
+        $sql    = str_replace('%p',addslashes($pass),$sql);
+        $sql    = str_replace('%g',addslashes($this->defaultgroup),$sql);
+        $result = $this->queryDB($sql);
+      
+        if($result !== false && count($result) == 1)
+          $rc = $cnf['encryptPass'] ? true : auth_verifyPassword($pass,$result[0]['pass']);
+		  
+        $this->closeDB();
+      }
+      return $rc;
+    }
+
+    /**
+     * [public function]
+     *
+     * Returns info about the given user needs to contain
+     * at least these fields:
+     *   name  string  full name of the user
+     *   mail  string  email addres of the user
+     *   grps  array   list of groups the user is in
+     *
+     * @param $user   user's nick to get data for 
+     *
+     * @author  Andreas Gohr <andi@splitbrain.org>
+     * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function getUserData($user){
+      if($this->openDB()) {
+        $this->lockTables("READ");
+        $info = $this->getUserInfo($user);
+        $this->unlockTables();
+        $this->closeDB();
+      } else
+        $info = false;
+      return $info;
+    }
+
+    /**
+     * [public function]
+     *
+     * Create a new User. Returns false if the user already exists,
+     * null when an error occured and the cleartext password of the
+     * new user if everything went well.
+     * 
+     * The new user will be added to the default group by this
+     * function if grps are not specified (default behaviour).
+     *
+     * @param $user  nick of the user
+     * @param $pwd   clear text password
+     * @param $name  full name of the user
+     * @param $mail  email address
+     * @param $grps  array of groups the user should become member of
+     *
+     * @author  Andreas Gohr <andi@splitbrain.org>
+     * @author  Chris Smith <chris@jalakai.co.uk>
+     * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function createUser($user,$pwd,$name,$mail,$grps=null){
+      if($this->openDB()) {
+        if (($info = $this->getUserInfo($user)) !== false)
+          return false;  // user already exists
+
+        // set defaultgroup if no groups were given
+        if ($grps == null)
+          $grps = array($this->defaultgroup);
+ 
+        $this->lockTables("WRITE");
+        $rc = $this->addUser($user,$pwd,$name,$mail,$grps);
+        $this->unlockTables();
+        $this->closeDB();
+        if ($rc) return $pwd;
+      }
+      return null;  // return error
+    }
+   
+    /**
+     * [public function]
+     *
+     * Modify user data
+     *
+     * @param   $user      nick of the user to be changed
+     * @param   $changes   array of field/value pairs to be changed (password will be clear text)
+     * @return  bool
+     *
+     * @todo  Modifications are done through deleting and recreating the user.
+     *        This might be suboptimal and dangerous. Using UPDATE seems the
+     *        better way.
+     *
+     * @author  Chris Smith <chris@jalakai.co.uk>
+     * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function modifyUser($user, $changes) {
+      $rc = false;
+      
+      if (!is_array($changes) || !count($changes))
+        return true;  // nothing to change
+        
+      if($this->openDB()) {
+        $this->lockTables("WRITE");
+        if (($info = $this->getUserInfo($user)) !== false) {
+          foreach ($changes as $field => $value)
+            $info[$field] = $value;  // update user record
+
+          $rc = $this->delUser($user);   // remove user from database
+          if ($rc)
+            $rc = $this->addUser($user,$info['pass'],$info['name'],$info['mail'],$info['grps']);
+          if (!$rc)
+            msg($lang['modUserFailed'], -1);
+        }  
+        $this->unlockTables();
+        $this->closeDB();
+      }
+      return $rc;
+    }
+
+    /**
+     * [public function]
+     *
+     * Remove one or more users from the list of registered users
+     *
+     * @param   array  $users   array of users to be deleted
+     * @return  int             the number of users deleted
+     *
+     * @author  Christopher Smith <chris@jalakai.co.uk>
+     * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function deleteUsers($users) {
+      $count = 0;
+	  
+      if($this->openDB()) {
+        if (is_array($users) && !empty($users)) {
+          $this->lockTables("WRITE");
+          foreach ($users as $user) {
+            if ($this->delUser($user))
+              $count++;
+          }
+          $this->unlockTables();
+        }
+        $this->closeDB();
+      }
+      return $count;
+    }
+ 
+    /**
+     * [public function]
+     *
+     * Counts users which meet certain $filter criteria.
+     *
+     * @param  array  $filter  filter criteria in item/pattern pairs
+     * @return count of found users.
+     *
+     * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function getUserCount($filter=array()) {
+      $rc = 0;
+      
+      if($this->openDB()) {
+        $sql = $this->createSQLFilter($this->cnf['getUsers'], $filter);
+        $result = $this->queryDB($sql);
+        if ($result)
+            $rc = count($result);
+        $this->closeDB();
+      }
+      return $rc;
+    }
+    
+    /**
+     * [public function]
+     *
+     * Bulk retrieval of user data.
+     *
+     * @param   start     index of first user to be returned
+     * @param   limit     max number of users to be returned
+     * @param   filter    array of field/pattern pairs
+     * @return  array of userinfo (refer getUserData for internal userinfo details)
+     *
+     * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function retrieveUsers($start=0,$limit=0,$filter=array()) {
+      $out   = array();
+      $i     = 0;
+      $count = 0;
+      
+      if($this->openDB()) {
+        $this->lockTables("READ");
+        $sql = $this->createSQLFilter($this->cnf['getUsers'], $filter)." ".$this->cnf['SortOrder'];
+        $result = $this->queryDB($sql);
+        if ($result) {
+          foreach ($result as $user) {
+            if ($i++ >= $start) {
+              $info = $this->getUserInfo($user['user']);
+              if ($info) {
+                $out[$user['user']] = $info;
+                if (($limit > 0) && (++$count >= $limit)) break;
+              }
+            }
+          }
+        }
+        $this->unlockTables();
+        $this->closeDB();
+      }
+      return $out;
+    }
+
+    /**
+     * [public function]
+     *
+     * Give user membership of a group
+     * 
+     * @param   $user
+     * @param   $group  
+     * @return  bool
+     *
+     * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function joinGroup($user, $group) {
+      $rc = false;
+      
+      if($this->openDB()) {
+        $this->lockTables("WRITE");
+        $rc = addUserToGroup($user, $group);
+        $this->unlochTables();
+        $this->closeDB();
+      }
+      return $rc;
+    }
+
+    /**
+     * [public function]
+     *
+     * Remove user from a group
+     *
+     * @param   $user    user that leaves a group
+     * @param   $group   group to leave
+     * @return  bool
+     *
+     * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function leaveGroup($user, $group) {
+      $rc = false;
+      
+      if($this->openDB()) {
+        $this->lockTables("WRITE");
+        
+        $uid = $this->getUserID($user);
+        if ($uid) {
+          $gid = $this->getGroupID($group);
+          if ($gid) {
+            $sql = str_replace('%uid',addslashes($uid),$this->cnf['delUserGroup']);
+            $sql = str_replace('%u'  ,addslashes($user),$sql);
+            $sql = str_replace('%gid',addslashes($gid),$sql);
+            $sql = str_replace('%g'  ,addslashes($group),$sql);
+            $rc  = $this->modifyDB($sql) == 0 ? true : false;
+          }
+        }
+        $this->unlochTables();
+        $this->closeDB();
+      }
+      return $rc;
+    }
+ 
+    /**
+     * Adds a user to a group. If $force is set to '1' the group will be
+     * created if it not already existed.
+     *
+     * The database connection must already be established. Otherwise
+     * this function does nothing and returns 'false'. It is strongly
+     * recommended to call this function only after all participating
+     * tables (group and usergroup) have been locked.
+     *
+     * @param   $user    user to add to a group
+     * @param   $group   name of the group
+     * @param   $force   '1' create missing groups
+     * @return  bool     'true' on success, 'false' on error
+     *
+     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function addUserToGroup($user, $group, $force=0) {
+      $newgroup = 0;
+        
+      if($this->dbcon) {
+        $uid = $this->getUserID($user);
+        if ($uid) {
+          $gid = $this->getGroupID($group);
+          if (!$gid) {
+            if ($force) {  // create missing groups
+              $sql = str_replace('%g',addslashes($group),$this->cnf['addGroup']);
+              $gid = $this->modifyDB($sql);
+              $newgroup = 1;  // group newly created
+            }
+            if (!$gid) return false; // group didm't exist and can't be created
+          }
+        
+          $sql = str_replace('%uid',addslashes($uid),$this->cnf['addUserGroup']);
+          $sql = str_replace('%u'  ,addslashes($user),$sql);
+          $sql = str_replace('%gid',addslashes($gid),$sql);
+          $sql = str_replace('%g'  ,addslashes($group),$sql);
+          if ($this->modifyDB($sql) !== false) return true;
+
+          if ($newgroup) { // remove previously created group on error
+            $sql = str_replace('%gid',addslashes($gid),$this->cnf['delGroup']);
+            $sql = str_replace('%g'  ,addslashes($group),$sql);
+            $this->modifyDB($sql);
+          }
+        }
+      }
+      return false;
+    }
+
+    /**
+     * Retrieves a list of groups the user is a member off.
+     *
+     * The database connection must already be established
+     * for this function to work. Otherwise it will return
+     * 'false'.
+     *
+     * @param  $user  user whose groups should be listed
+     * @return bool   false on error
+     * @return array  array containing all groups on success
+     *
+     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function getGroups($user) {
+      $groups = array();
+      
+      if($this->dbcon) {
+        $sql = str_replace('%u',addslashes($user),$this->cnf['getGroups']);
+        $result = $this->queryDB($sql);
+        
+        if(count($result)) {
+          foreach($result as $row)
+            $groups[] = $row['group'];
+        }
+        return $groups;
+      }
+      return false;
+    }
+
+    /**
+     * Retrieves the user id of a given user name
+     * 
+     * The database connection must already be established
+     * for this function to work. Otherwise it will return
+     * 'false'.
+     *
+     * @param  $user   user whose id is desired
+     * @return user id
+     *
+     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function getUserID($user) {
+      if($this->dbcon) {
+        $sql = str_replace('%u',addslashes($user),$this->cnf['getUserID']);
+        $result = $this->queryDB($sql);
+        return $result === false ? false : $result[0]['id'];
+      }
+      return false;
+    }
+ 
+    /**
+     * Adds a new User to the database.
+     *
+     * The database connection must already be established
+     * for this function to work. Otherwise it will return
+     * 'false'.
+     *
+     * @param  $user  nick of the user
+     * @param  $pwd   clear text password
+     * @param  $name  full name of the user
+     * @param  $mail  email address
+     * @param  $grps  array of groups the user should become member of
+     * @return bool
+     *
+     * @author  Andreas Gohr <andi@splitbrain.org>
+     * @author  Chris Smith <chris@jalakai.co.uk>
+     * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function addUser($user,$pwd,$name,$mail,$grps){
+      if($this->dbcon && is_array($grps)) {
+        $_pwd = $this->cnf['encryptPass'] ? $pwd : auth_cryptPassword($pwd);
+        $sql = str_replace('%u'  ,addslashes($user),$this->cnf['addUser']);
+        $sql = str_replace('%p'  ,addslashes($_pwd),$sql);
+        $sql = str_replace('%n'  ,addslashes($name),$sql);
+        $sql = str_replace('%e'  ,addslashes($mail),$sql);  
+        $uid = $this->modifyDB($sql);
+      
+        if ($uid) {
+          foreach($grps as $group) {
+            $gid = $this->addUserToGroup($user, $group, 1);
+            if ($gid === false) break;
+          }
+          
+          if ($gid) return true;
+          else {
+            /* remove the new user and all group relations if a group can't
+             * be assigned. Newly created groups will remain in the database
+             * and won't be removed. This might create orphaned groups but
+             * is not a big issue so we ignore this problem here.
+             */
+            $this->delUser($user);
+            $text = str_replace('%u' ,addslashes($user),$this->cnf['joinGroupFailed']);
+            $text = str_replace('%g' ,addslashes($group),$text);
+            msg($text, -1);
+          }
+        }
+      }
+      return false;
+    }
+ 
+    /**
+     * Deletes a given user and all his group references.
+     * 
+     * The database connection must already be established
+     * for this function to work. Otherwise it will return
+     * 'false'.
+     *
+     * @param  $user   user whose id is desired
+     * @return bool
+     *
+     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function delUser($user) {
+      if($this->dbcon) {
+        $uid = $this->getUserID($user);
+        if ($uid) {
+          $sql = str_replace('%uid',addslashes($uid),$this->cnf['delUser']);
+          $sql = str_replace('%u',  addslashes($user),$sql);
+          $this->modifyDB($sql);
+          $sql = str_replace('%uid',addslashes($uid),$this->cnf['delUserRefs']);
+          $this->modifyDB($sql);
+          return true;
+        }
+      }
+      return false;
+    }
+
+    /**
+     * getUserInfo
+     *
+     * Gets the data for a specific user The database connection
+     * must already be established for this function to work.
+     * Otherwise it will return 'false'.
+     *
+     * @param  $user  user's nick to get data for
+     * @return bool   false on error
+     * @return array  user info on success
+     *
+     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function getUserInfo($user){
+      $sql = str_replace('%u',addslashes($user),$this->cnf['getUserInfo']);
+      $result = $this->queryDB($sql);
+      if(count($result)) {
+        $info = $result[0];
+        $info['grps'] = $this->getGroups($user);
+        return $info;
+      }
+      return false;
+    }
+
+    /**
+     * Retrieves the group id of a given group name
+     * 
+     * The database connection must already be established
+     * for this function to work. Otherwise it will return
+     * 'false'.
+     *
+     * @param  $group   group name which id is desired
+     * @return group id
+     *
+     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function getGroupID($group) {
+      if($this->dbcon) {
+        $sql = str_replace('%g',addslashes($group),$this->cnf['getGroupID']);
+        $result = $this->queryDB($sql);
+        return $result === false ? false : $result[0]['id'];
+      }
+      return false;
+    }
+    
+    /**
+     * Opens a connection to a database and saves the handle for further
+     * usage in the object. The successful call to this functions is
+     * essential for most functions in this object.
+     * 
+     * @return bool
+     *
+     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function openDB() {
+      global $lang;
+      
+      if ($this->dbcon == 0) {
+        $con = @mysql_connect ($this->cnf['server'], $this->cnf['user'], $this->cnf['password']);   
+
+        if ($con) {
+          if ((mysql_select_db($this->cnf['database'], $con))) {
+            $this->dbcon = $con; 
+            return true;   // connection and database sucessfully opened
+          } else {
+            $text = str_replace('%d',addslashes($this->cnf['database']),$lang['noDatabase']);
+            msg($text, -1);
+            mysql_close ($con);
+          }   
+        } else {
+          $text = str_replace('%u',addslashes($this->cnf['user']),$lang['noConnect']);
+          $text = str_replace('%s',addslashes($this->cnf['server']),$text);
+          msg($text, -1);
+        }
+        return false;  // connection failed
+      }
+      return true;  // connection already open
+    }
+  
+    /**
+     * Closes a database connection.
+     *
+     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function closeDB() {
+      if ($this->dbcon) {
+        mysql_close ($this->dbcon);
+        $this->dbcon = 0;
+      }
+    }
+ 
+    /**
+     * Sends a SQL query to the database and transforms the result into
+     * an associative array.
+     * 
+     * This function is only able to handle queries that returns a 
+     * table such as SELECT.
+     *
+     * @param $query  SQL string that contains the query
+     * @return array with the result table
+     *
+     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */	 
+    function queryDB($query) {
+      if ($this->dbcon) {
+        $result = @mysql_query($query,$this->dbcon);
+        if ($result) {
+          while (($t = mysql_fetch_assoc($result)) !== false)
+            $resultarray[]=$t;
+          mysql_free_result ($result);
+          return $resultarray;
+        }
+        msg('MySQL: '.mysql_error($this->dbcon), -1);
+      }
+      return false;
+    }
+ 
+    /**
+     * Sends a SQL query to the database
+     * 
+     * This function is only able to handle queries that returns
+     * either nothing or an id value such as INPUT, DELETE, UPDATE, etc.
+     *
+     * @param $query  SQL string that contains the query
+     * @return insert id or 0, false on error
+     *
+     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */	 
+    function modifyDB($query) {
+      if ($this->dbcon) {
+        $result = @mysql_query($query,$this->dbcon);
+        if ($result) {
+          $rc = mysql_insert_id($this->dbcon); //give back ID on insert
+          if ($rc !== false) return $rc;
+        }
+        msg('MySQL: '.mysql_error($this->dbcon), -1);
+      }
+      return false;
+    }
+  
+    /**
+     * Locked a list of tables for exclusive access so that modifications
+     * to the database can't be disturbed by other threads. The list
+     * could be set with $conf['auth']['mysql']['TablesToLock'] = array()
+     *
+     * If aliases for tables are used in SQL statements, also this aliases
+     * must be locked. For eg. you use a table 'user' and the alias 'u' in
+     * some sql queries, the array must looks like this (order is important):
+     *   array("user", "user AS u");
+     *
+     * MySQL V3 is not able to handle transactions with COMMIT/ROLLBACK
+     * so that this functionality is simulated by this function. Nevertheless
+     * it is not as powerful as transactions, it is a good compromise in safty.
+     *
+     * @param $mode  could be 'READ' or 'WRITE'
+     *
+     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */	 
+    function lockTables($mode) {
+      if ($this->dbcon) {
+        if (is_array($this->cnf['TablesToLock']) && !empty($this->cnf['TablesToLock'])) {
+          if ($mode == "READ" || $mode == "WRITE") {
+            $sql = "LOCK TABLES ";
+            $cnt = 0;
+            foreach ($this->cnf['TablesToLock'] as $table) {
+              if ($cnt++ != 0) $sql .= ", ";
+              $sql .= "$table $mode";
+            }
+            $this->modifyDB($sql);
+            return true;
+          }
+        }
+      }
+      return false;
+    }
+
+    /**
+     * Unlock locked tables. All existing locks of this thread will be
+     * abrogated.
+     *
+     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */	 
+    function unlockTables() {
+      if ($this->dbcon) {
+        $this->modifyDB("UNLOCK TABLES");
+        return true;
+      }
+      return false;
+    }
+	
+    /**
+     * Transforms the filter settings in an filter string for a SQL database
+     * The database connection must already be established, otherwise the
+     * original SQL string without filter criteria will be returned.
+     *
+     * @param  $sql     SQL string to which the $filter criteria should be added
+     * @param  $filter  array of filter criteria as pairs of item and pattern
+     * @return SQL string with attached $filter criteria on success
+     * @return the original SQL string on error.
+     *
+     * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net>
+     */
+    function createSQLFilter($sql, $filter) {
+      $SQLfilter = "";
+      $cnt = 0;
+        
+      if ($this->dbcon) {
+        foreach ($filter as $item => $pattern) {
+          $tmp = addslashes('%'.mysql_real_escape_string($pattern, $this->dbcon).'%');
+          if ($item == 'user') {
+            if ($cnt++ > 0) $SQLfilter .= " AND ";
+            $SQLfilter .= str_replace('%s',$tmp,$this->cnf['FilterLogin']);
+          } else if ($item == 'name') {
+            if ($cnt++ > 0) $SQLfilter .= " AND ";
+            $SQLfilter .= str_replace('%s',$tmp,$this->cnf['FilterName']);
+          } else if ($item == 'mail') {
+            if ($cnt++ > 0) $SQLfilter .= " AND ";
+            $SQLfilter .= str_replace('%s',$tmp,$this->cnf['FilterEmail']);
+          } else if ($item == 'grps') {
+            if ($cnt++ > 0) $SQLfilter .= " AND ";
+            $SQLfilter .= str_replace('%s',$tmp,$this->cnf['FilterGroup']);
+          }
+        }
+        
+        // we have to check SQLfilter here and must not use $cnt because if
+        // any of cnf['Filter????'] is not defined, a malformed SQL string
+        // would be generated.
+        
+        if (strlen($SQLfilter)) {
+          $glue = strpos(strtolower($sql),"where") ? " AND " : " WHERE ";
+          $sql = $sql.$glue.$SQLfilter;
+        }
+      }
+
+      return $sql;
+    }
+    
+}
+
+//Setup VIM: ex: et ts=2 enc=utf-8 :
+
diff --git a/inc/lang/de/lang.php b/inc/lang/de/lang.php
index 5213d8b0d..a62175837 100644
--- a/inc/lang/de/lang.php
+++ b/inc/lang/de/lang.php
@@ -191,6 +191,14 @@ $lang['subscribe_noaddress']= 'Weil Deine E-Mail-Adresse fehlt, kannst Du leider
 $lang['unsubscribe_success']= 'Das Abonnement von %s für die Seite %s wurde aufgelöst';
 $lang['unsubscribe_error']  = 'Das Abonnement von %s für die Seite %s konnte nicht aufgelöst werden';
 
-$lang['authmodfailed'] = 'Benutzerüberprüfung nicht möglich. Bitte wenden Sie sich an den Systembetreuer.';
+/* auth.class lanuage support */
+$lang['authmodfailed']   = 'Benutzerüberprüfung nicht möglich. Bitte wenden Sie sich an den Systembetreuer.';
+
+/* mysql.class language support */
+$lang['noMySQL']         = "MySQL Erweiterung für PHP nicht gefunden. Bitte informieren Sie ihren Wiki Admin.";
+$lang['noDatabase']      = "MySQL: Zugriff auf Datenbank '%d' fehlgeschlagen. Bitte informieren Sie ihren Wiki Admin.";
+$lang['noConnect']       = "MySQL: Datenbank '%u@%s' nicht erreichbar. Bitte informieren Sie ihren Wiki Admin.";
+$lang['joinGroupFailed'] = "Konto für '%u' nicht angelegt; Benutzer konnte nicht der Gruppe '%g' zugeordnet werden.";
+$lang['modUserFailed']   = "Benutzerdaten können nicht geändert werden. Bitte informieren Sie ihren Wiki Admin.";
 
 //Setup VIM: ex: et ts=2 enc=utf-8 :
diff --git a/inc/lang/en/lang.php b/inc/lang/en/lang.php
index f5c1f1372..d686f0c23 100644
--- a/inc/lang/en/lang.php
+++ b/inc/lang/en/lang.php
@@ -189,6 +189,14 @@ $lang['subscribe_noaddress']= 'There is no address associated with your login, y
 $lang['unsubscribe_success']= 'Removed %s from subscription list for %s';
 $lang['unsubscribe_error']  = 'Error removing %s from subscription list for %s';
 
-$lang['authmodfailed'] = 'User authentification not possible. Please inform your system administrator.';
+/* auth.class lanuage support */
+$lang['authmodfailed']   = 'User authentification not possible. Please inform your Wiki Admin.';
+
+/* mysql.class language support */
+$lang['noMySQL']         = "MySQL extension for PHP not found. Please inform your Wiki Admin.";
+$lang['noDatabase']      = "MySQL: Can't access Database '%d'. Please inform you Wiki Admin.";
+$lang['noConnect']       = "MySQL: Can't connect to '%u@%s'. Please inform your Wiki Admin.";
+$lang['joinGroupFailed'] = "Account for '%u' not created because it can't be added to group '%g'.";
+$lang['modUserFailed']   = "Unable to modify user data. Please inform your Wiki Admin";
 
 //Setup VIM: ex: et ts=2 enc=utf-8 :
-- 
GitLab