xref: /dokuwiki/lib/plugins/authpdo/auth.php (revision 9c952d3bf926c58a14a76de8937bcd2e4e7686d6)
1f64dbc90SAndreas Gohr<?php
2d4f83172SAndreas Gohr
38553d24dSAndreas Gohruse dokuwiki\Extension\AuthPlugin;
4ab9790caSAndreas Gohruse dokuwiki\PassHash;
50489c64bSMoisés Braga Ribeirouse dokuwiki\Utf8\Sort;
60489c64bSMoisés Braga Ribeiro
7f64dbc90SAndreas Gohr/**
8f64dbc90SAndreas Gohr * DokuWiki Plugin authpdo (Auth Component)
9f64dbc90SAndreas Gohr *
10f64dbc90SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
11f64dbc90SAndreas Gohr * @author  Andreas Gohr <andi@splitbrain.org>
12f64dbc90SAndreas Gohr */
13f64dbc90SAndreas Gohr
140d586afdSAndreas Gohr/**
150d586afdSAndreas Gohr * Class auth_plugin_authpdo
160d586afdSAndreas Gohr */
178553d24dSAndreas Gohrclass auth_plugin_authpdo extends AuthPlugin
183213bf4eSAndreas Gohr{
19f64dbc90SAndreas Gohr    /** @var PDO */
20f64dbc90SAndreas Gohr    protected $pdo;
21f64dbc90SAndreas Gohr
220cec3e2aSAndreas Gohr    /** @var null|array The list of all groups */
23ab9790caSAndreas Gohr    protected $groupcache;
240cec3e2aSAndreas Gohr
25f64dbc90SAndreas Gohr    /**
26f64dbc90SAndreas Gohr     * Constructor.
27f64dbc90SAndreas Gohr     */
283213bf4eSAndreas Gohr    public function __construct()
293213bf4eSAndreas Gohr    {
30f64dbc90SAndreas Gohr        parent::__construct(); // for compatibility
31f64dbc90SAndreas Gohr
32f64dbc90SAndreas Gohr        if (!class_exists('PDO')) {
333213bf4eSAndreas Gohr            $this->debugMsg('PDO extension for PHP not found.', -1, __LINE__);
34f64dbc90SAndreas Gohr            $this->success = false;
35f64dbc90SAndreas Gohr            return;
36f64dbc90SAndreas Gohr        }
37f64dbc90SAndreas Gohr
38f64dbc90SAndreas Gohr        if (!$this->getConf('dsn')) {
393213bf4eSAndreas Gohr            $this->debugMsg('No DSN specified', -1, __LINE__);
40f64dbc90SAndreas Gohr            $this->success = false;
41f64dbc90SAndreas Gohr            return;
42f64dbc90SAndreas Gohr        }
43f64dbc90SAndreas Gohr
44f64dbc90SAndreas Gohr        try {
45f64dbc90SAndreas Gohr            $this->pdo = new PDO(
46f64dbc90SAndreas Gohr                $this->getConf('dsn'),
47f64dbc90SAndreas Gohr                $this->getConf('user'),
480cc11d97SAndreas Gohr                conf_decodeString($this->getConf('pass')),
49ab9790caSAndreas Gohr                [
5070a89417SAndreas Gohr                    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // always fetch as array
5170a89417SAndreas Gohr                    PDO::ATTR_EMULATE_PREPARES => true, // emulating prepares allows us to reuse param names
525de3a6a5SAndreas Gohr                    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // we want exceptions, not error codes
53ab9790caSAndreas Gohr                ]
54f64dbc90SAndreas Gohr            );
55f64dbc90SAndreas Gohr        } catch (PDOException $e) {
563213bf4eSAndreas Gohr            $this->debugMsg($e);
57c27579a6SAndreas Gohr            msg($this->getLang('connectfail'), -1);
58f64dbc90SAndreas Gohr            $this->success = false;
59f64dbc90SAndreas Gohr            return;
60f64dbc90SAndreas Gohr        }
61f64dbc90SAndreas Gohr
620d586afdSAndreas Gohr        // can Users be created?
633213bf4eSAndreas Gohr        $this->cando['addUser'] = $this->checkConfig(
64ab9790caSAndreas Gohr            ['select-user', 'select-user-groups', 'select-groups', 'insert-user', 'insert-group', 'join-group']
650d586afdSAndreas Gohr        );
66f64dbc90SAndreas Gohr
670d586afdSAndreas Gohr        // can Users be deleted?
683213bf4eSAndreas Gohr        $this->cando['delUser'] = $this->checkConfig(
69ab9790caSAndreas Gohr            ['select-user', 'select-user-groups', 'select-groups', 'leave-group', 'delete-user']
700d586afdSAndreas Gohr        );
710d586afdSAndreas Gohr
720d586afdSAndreas Gohr        // can login names be changed?
733213bf4eSAndreas Gohr        $this->cando['modLogin'] = $this->checkConfig(
74ab9790caSAndreas Gohr            ['select-user', 'select-user-groups', 'update-user-login']
750d586afdSAndreas Gohr        );
760d586afdSAndreas Gohr
770d586afdSAndreas Gohr        // can passwords be changed?
783213bf4eSAndreas Gohr        $this->cando['modPass'] = $this->checkConfig(
79ab9790caSAndreas Gohr            ['select-user', 'select-user-groups', 'update-user-pass']
800d586afdSAndreas Gohr        );
810d586afdSAndreas Gohr
82ad4d5631SAndreas Gohr        // can real names be changed?
833213bf4eSAndreas Gohr        $this->cando['modName'] = $this->checkConfig(
84ab9790caSAndreas Gohr            ['select-user', 'select-user-groups', 'update-user-info:name']
85ad4d5631SAndreas Gohr        );
86ad4d5631SAndreas Gohr
87ad4d5631SAndreas Gohr        // can real email be changed?
883213bf4eSAndreas Gohr        $this->cando['modMail'] = $this->checkConfig(
89ab9790caSAndreas Gohr            ['select-user', 'select-user-groups', 'update-user-info:mail']
900d586afdSAndreas Gohr        );
910d586afdSAndreas Gohr
920d586afdSAndreas Gohr        // can groups be changed?
933213bf4eSAndreas Gohr        $this->cando['modGroups'] = $this->checkConfig(
94ab9790caSAndreas Gohr            ['select-user', 'select-user-groups', 'select-groups', 'leave-group', 'join-group', 'insert-group']
950d586afdSAndreas Gohr        );
960d586afdSAndreas Gohr
970d586afdSAndreas Gohr        // can a filtered list of users be retrieved?
983213bf4eSAndreas Gohr        $this->cando['getUsers'] = $this->checkConfig(
99ab9790caSAndreas Gohr            ['list-users']
1000d586afdSAndreas Gohr        );
1010d586afdSAndreas Gohr
1020d586afdSAndreas Gohr        // can the number of users be retrieved?
1033213bf4eSAndreas Gohr        $this->cando['getUserCount'] = $this->checkConfig(
104ab9790caSAndreas Gohr            ['count-users']
1050d586afdSAndreas Gohr        );
1060d586afdSAndreas Gohr
1070d586afdSAndreas Gohr        // can a list of available groups be retrieved?
1083213bf4eSAndreas Gohr        $this->cando['getGroups'] = $this->checkConfig(
109ab9790caSAndreas Gohr            ['select-groups']
1100d586afdSAndreas Gohr        );
1110d586afdSAndreas Gohr
112f64dbc90SAndreas Gohr        $this->success = true;
113f64dbc90SAndreas Gohr    }
114f64dbc90SAndreas Gohr
115f64dbc90SAndreas Gohr    /**
116f64dbc90SAndreas Gohr     * Check user+password
117f64dbc90SAndreas Gohr     *
118f64dbc90SAndreas Gohr     * @param string $user the user name
119f64dbc90SAndreas Gohr     * @param string $pass the clear text password
120f64dbc90SAndreas Gohr     * @return  bool
121f64dbc90SAndreas Gohr     */
1223213bf4eSAndreas Gohr    public function checkPass($user, $pass)
1233213bf4eSAndreas Gohr    {
124f64dbc90SAndreas Gohr
1253213bf4eSAndreas Gohr        $userdata = $this->selectUser($user);
126*9c952d3bSAndreas Gohr        if ($userdata === false) {
127*9c952d3bSAndreas Gohr            auth_cryptPassword('dummy'); // run a crypt op to prevent timing attacks
128*9c952d3bSAndreas Gohr            return false;
129*9c952d3bSAndreas Gohr        }
130f64dbc90SAndreas Gohr
131397d62a2SAndreas Gohr        // password checking done in SQL?
132ab9790caSAndreas Gohr        if ($this->checkConfig(['check-pass'])) {
133397d62a2SAndreas Gohr            $userdata['clear'] = $pass;
134397d62a2SAndreas Gohr            $userdata['hash'] = auth_cryptPassword($pass);
1353213bf4eSAndreas Gohr            $result = $this->query($this->getConf('check-pass'), $userdata);
136397d62a2SAndreas Gohr            if ($result === false) return false;
137397d62a2SAndreas Gohr            return (count($result) == 1);
138397d62a2SAndreas Gohr        }
139397d62a2SAndreas Gohr
140397d62a2SAndreas Gohr        // we do password checking on our own
141397d62a2SAndreas Gohr        if (isset($userdata['hash'])) {
142f64dbc90SAndreas Gohr            // hashed password
143ab9790caSAndreas Gohr            $passhash = new PassHash();
144397d62a2SAndreas Gohr            return $passhash->verify_hash($pass, $userdata['hash']);
145f64dbc90SAndreas Gohr        } else {
146f64dbc90SAndreas Gohr            // clear text password in the database O_o
147d5c0422fSAndreas Gohr            return ($pass === $userdata['clear']);
148f64dbc90SAndreas Gohr        }
149f64dbc90SAndreas Gohr    }
150f64dbc90SAndreas Gohr
151f64dbc90SAndreas Gohr    /**
152f64dbc90SAndreas Gohr     * Return user info
153f64dbc90SAndreas Gohr     *
154f64dbc90SAndreas Gohr     * Returns info about the given user needs to contain
155f64dbc90SAndreas Gohr     * at least these fields:
156f64dbc90SAndreas Gohr     *
157f64dbc90SAndreas Gohr     * name string  full name of the user
158f64dbc90SAndreas Gohr     * mail string  email addres of the user
159f64dbc90SAndreas Gohr     * grps array   list of groups the user is in
160f64dbc90SAndreas Gohr     *
161f64dbc90SAndreas Gohr     * @param string $user the user name
162f64dbc90SAndreas Gohr     * @param bool $requireGroups whether or not the returned data must include groups
163358942b5SAndreas Gohr     * @return array|bool containing user data or false
164f64dbc90SAndreas Gohr     */
1653213bf4eSAndreas Gohr    public function getUserData($user, $requireGroups = true)
1663213bf4eSAndreas Gohr    {
1673213bf4eSAndreas Gohr        $data = $this->selectUser($user);
168f64dbc90SAndreas Gohr        if ($data == false) return false;
169f64dbc90SAndreas Gohr
17070a89417SAndreas Gohr        if (isset($data['hash'])) unset($data['hash']);
17170a89417SAndreas Gohr        if (isset($data['clean'])) unset($data['clean']);
172f64dbc90SAndreas Gohr
17370a89417SAndreas Gohr        if ($requireGroups) {
1743213bf4eSAndreas Gohr            $data['grps'] = $this->selectUserGroups($data);
1755de3a6a5SAndreas Gohr            if ($data['grps'] === false) return false;
176f64dbc90SAndreas Gohr        }
177f64dbc90SAndreas Gohr
178f64dbc90SAndreas Gohr        return $data;
179f64dbc90SAndreas Gohr    }
180f64dbc90SAndreas Gohr
181f64dbc90SAndreas Gohr    /**
182f64dbc90SAndreas Gohr     * Create a new User [implement only where required/possible]
183f64dbc90SAndreas Gohr     *
184f64dbc90SAndreas Gohr     * Returns false if the user already exists, null when an error
185f64dbc90SAndreas Gohr     * occurred and true if everything went well.
186f64dbc90SAndreas Gohr     *
187f64dbc90SAndreas Gohr     * The new user HAS TO be added to the default group by this
188f64dbc90SAndreas Gohr     * function!
189f64dbc90SAndreas Gohr     *
190f64dbc90SAndreas Gohr     * Set addUser capability when implemented
191f64dbc90SAndreas Gohr     *
192f64dbc90SAndreas Gohr     * @param string $user
1935de3a6a5SAndreas Gohr     * @param string $clear
194f64dbc90SAndreas Gohr     * @param string $name
195f64dbc90SAndreas Gohr     * @param string $mail
196f64dbc90SAndreas Gohr     * @param null|array $grps
197f64dbc90SAndreas Gohr     * @return bool|null
198f64dbc90SAndreas Gohr     */
1993213bf4eSAndreas Gohr    public function createUser($user, $clear, $name, $mail, $grps = null)
2003213bf4eSAndreas Gohr    {
2015de3a6a5SAndreas Gohr        global $conf;
2025de3a6a5SAndreas Gohr
2035de3a6a5SAndreas Gohr        if (($info = $this->getUserData($user, false)) !== false) {
2045de3a6a5SAndreas Gohr            msg($this->getLang('userexists'), -1);
2055de3a6a5SAndreas Gohr            return false; // user already exists
2065de3a6a5SAndreas Gohr        }
2075de3a6a5SAndreas Gohr
2085de3a6a5SAndreas Gohr        // prepare data
209ab9790caSAndreas Gohr        if ($grps == null) $grps = [];
21007a11e2aSAndreas Gohr        array_unshift($grps, $conf['defaultgroup']);
2115de3a6a5SAndreas Gohr        $grps = array_unique($grps);
2125de3a6a5SAndreas Gohr        $hash = auth_cryptPassword($clear);
213ab9790caSAndreas Gohr        $userdata = ['user' => $user, 'clear' => $clear, 'hash' => $hash, 'name' => $name, 'mail' => $mail];
2145de3a6a5SAndreas Gohr
2155de3a6a5SAndreas Gohr        // action protected by transaction
2165de3a6a5SAndreas Gohr        $this->pdo->beginTransaction();
2175de3a6a5SAndreas Gohr        {
2185de3a6a5SAndreas Gohr            // insert the user
2193213bf4eSAndreas Gohr            $ok = $this->query($this->getConf('insert-user'), $userdata);
2205de3a6a5SAndreas Gohr            if ($ok === false) goto FAIL;
2215de3a6a5SAndreas Gohr            $userdata = $this->getUserData($user, false);
2225de3a6a5SAndreas Gohr            if ($userdata === false) goto FAIL;
2235de3a6a5SAndreas Gohr
2245de3a6a5SAndreas Gohr            // create all groups that do not exist, the refetch the groups
2253213bf4eSAndreas Gohr            $allgroups = $this->selectGroups();
2265de3a6a5SAndreas Gohr        foreach ($grps as $group) {
2275de3a6a5SAndreas Gohr            if (!isset($allgroups[$group])) {
2286459f496SAndreas Gohr                $ok = $this->addGroup($group);
2295de3a6a5SAndreas Gohr                if ($ok === false) goto FAIL;
2305de3a6a5SAndreas Gohr            }
2315de3a6a5SAndreas Gohr        }
2323213bf4eSAndreas Gohr            $allgroups = $this->selectGroups();
2335de3a6a5SAndreas Gohr
2345de3a6a5SAndreas Gohr            // add user to the groups
2355de3a6a5SAndreas Gohr        foreach ($grps as $group) {
2363213bf4eSAndreas Gohr            $ok = $this->joinGroup($userdata, $allgroups[$group]);
2375de3a6a5SAndreas Gohr            if ($ok === false) goto FAIL;
2385de3a6a5SAndreas Gohr        }
2395de3a6a5SAndreas Gohr        }
2405de3a6a5SAndreas Gohr        $this->pdo->commit();
2415de3a6a5SAndreas Gohr        return true;
2425de3a6a5SAndreas Gohr
2435de3a6a5SAndreas Gohr        // something went wrong, rollback
2445de3a6a5SAndreas Gohr        FAIL:
2455de3a6a5SAndreas Gohr        $this->pdo->rollBack();
2463213bf4eSAndreas Gohr        $this->debugMsg('Transaction rolled back', 0, __LINE__);
247c27579a6SAndreas Gohr        msg($this->getLang('writefail'), -1);
2485de3a6a5SAndreas Gohr        return null; // return error
2495de3a6a5SAndreas Gohr    }
250f64dbc90SAndreas Gohr
251f64dbc90SAndreas Gohr    /**
2524fb8dfabSAndreas Gohr     * Modify user data
253f64dbc90SAndreas Gohr     *
254f64dbc90SAndreas Gohr     * @param string $user nick of the user to be changed
255f64dbc90SAndreas Gohr     * @param array $changes array of field/value pairs to be changed (password will be clear text)
256f64dbc90SAndreas Gohr     * @return  bool
257f64dbc90SAndreas Gohr     */
2583213bf4eSAndreas Gohr    public function modifyUser($user, $changes)
2593213bf4eSAndreas Gohr    {
2604fb8dfabSAndreas Gohr        // secure everything in transaction
2614fb8dfabSAndreas Gohr        $this->pdo->beginTransaction();
2624fb8dfabSAndreas Gohr        {
2634fb8dfabSAndreas Gohr            $olddata = $this->getUserData($user);
2644fb8dfabSAndreas Gohr            $oldgroups = $olddata['grps'];
2654fb8dfabSAndreas Gohr            unset($olddata['grps']);
2664fb8dfabSAndreas Gohr
2674fb8dfabSAndreas Gohr            // changing the user name?
2684fb8dfabSAndreas Gohr        if (isset($changes['user'])) {
2694fb8dfabSAndreas Gohr            if ($this->getUserData($changes['user'], false)) goto FAIL;
2704fb8dfabSAndreas Gohr            $params = $olddata;
2714fb8dfabSAndreas Gohr            $params['newlogin'] = $changes['user'];
2724fb8dfabSAndreas Gohr
2733213bf4eSAndreas Gohr            $ok = $this->query($this->getConf('update-user-login'), $params);
2744fb8dfabSAndreas Gohr            if ($ok === false) goto FAIL;
2754fb8dfabSAndreas Gohr        }
2764fb8dfabSAndreas Gohr
2774fb8dfabSAndreas Gohr            // changing the password?
2784fb8dfabSAndreas Gohr        if (isset($changes['pass'])) {
2794fb8dfabSAndreas Gohr            $params = $olddata;
2804fb8dfabSAndreas Gohr            $params['clear'] = $changes['pass'];
2814fb8dfabSAndreas Gohr            $params['hash'] = auth_cryptPassword($changes['pass']);
2824fb8dfabSAndreas Gohr
2833213bf4eSAndreas Gohr            $ok = $this->query($this->getConf('update-user-pass'), $params);
2844fb8dfabSAndreas Gohr            if ($ok === false) goto FAIL;
2854fb8dfabSAndreas Gohr        }
2864fb8dfabSAndreas Gohr
2874fb8dfabSAndreas Gohr            // changing info?
2884fb8dfabSAndreas Gohr        if (isset($changes['mail']) || isset($changes['name'])) {
2894fb8dfabSAndreas Gohr            $params = $olddata;
2904fb8dfabSAndreas Gohr            if (isset($changes['mail'])) $params['mail'] = $changes['mail'];
2914fb8dfabSAndreas Gohr            if (isset($changes['name'])) $params['name'] = $changes['name'];
2924fb8dfabSAndreas Gohr
2933213bf4eSAndreas Gohr            $ok = $this->query($this->getConf('update-user-info'), $params);
2944fb8dfabSAndreas Gohr            if ($ok === false) goto FAIL;
2954fb8dfabSAndreas Gohr        }
2964fb8dfabSAndreas Gohr
2974fb8dfabSAndreas Gohr            // changing groups?
2984fb8dfabSAndreas Gohr        if (isset($changes['grps'])) {
2993213bf4eSAndreas Gohr            $allgroups = $this->selectGroups();
3004fb8dfabSAndreas Gohr
3014fb8dfabSAndreas Gohr            // remove membership for previous groups
3024fb8dfabSAndreas Gohr            foreach ($oldgroups as $group) {
303358942b5SAndreas Gohr                if (!in_array($group, $changes['grps']) && isset($allgroups[$group])) {
3043213bf4eSAndreas Gohr                    $ok = $this->leaveGroup($olddata, $allgroups[$group]);
3054fb8dfabSAndreas Gohr                    if ($ok === false) goto FAIL;
3064fb8dfabSAndreas Gohr                }
3074fb8dfabSAndreas Gohr            }
3084fb8dfabSAndreas Gohr
3094fb8dfabSAndreas Gohr            // create all new groups that are missing
3104fb8dfabSAndreas Gohr            $added = 0;
3114fb8dfabSAndreas Gohr            foreach ($changes['grps'] as $group) {
3124fb8dfabSAndreas Gohr                if (!isset($allgroups[$group])) {
3136459f496SAndreas Gohr                    $ok = $this->addGroup($group);
3144fb8dfabSAndreas Gohr                    if ($ok === false) goto FAIL;
3154fb8dfabSAndreas Gohr                    $added++;
3164fb8dfabSAndreas Gohr                }
3174fb8dfabSAndreas Gohr            }
3184fb8dfabSAndreas Gohr            // reload group info
3193213bf4eSAndreas Gohr            if ($added > 0) $allgroups = $this->selectGroups();
3204fb8dfabSAndreas Gohr
3214fb8dfabSAndreas Gohr            // add membership for new groups
3224fb8dfabSAndreas Gohr            foreach ($changes['grps'] as $group) {
3234fb8dfabSAndreas Gohr                if (!in_array($group, $oldgroups)) {
3243213bf4eSAndreas Gohr                    $ok = $this->joinGroup($olddata, $allgroups[$group]);
3254fb8dfabSAndreas Gohr                    if ($ok === false) goto FAIL;
3264fb8dfabSAndreas Gohr                }
3274fb8dfabSAndreas Gohr            }
3284fb8dfabSAndreas Gohr        }
3294fb8dfabSAndreas Gohr
3304fb8dfabSAndreas Gohr        }
3314fb8dfabSAndreas Gohr        $this->pdo->commit();
3324fb8dfabSAndreas Gohr        return true;
3334fb8dfabSAndreas Gohr
3344fb8dfabSAndreas Gohr        // something went wrong, rollback
3354fb8dfabSAndreas Gohr        FAIL:
3364fb8dfabSAndreas Gohr        $this->pdo->rollBack();
3373213bf4eSAndreas Gohr        $this->debugMsg('Transaction rolled back', 0, __LINE__);
338c27579a6SAndreas Gohr        msg($this->getLang('writefail'), -1);
3394fb8dfabSAndreas Gohr        return false; // return error
3404fb8dfabSAndreas Gohr    }
341f64dbc90SAndreas Gohr
342f64dbc90SAndreas Gohr    /**
343e19be516SAndreas Gohr     * Delete one or more users
344f64dbc90SAndreas Gohr     *
345f64dbc90SAndreas Gohr     * Set delUser capability when implemented
346f64dbc90SAndreas Gohr     *
347f64dbc90SAndreas Gohr     * @param array $users
348f64dbc90SAndreas Gohr     * @return  int    number of users deleted
349f64dbc90SAndreas Gohr     */
3503213bf4eSAndreas Gohr    public function deleteUsers($users)
3513213bf4eSAndreas Gohr    {
352e19be516SAndreas Gohr        $count = 0;
353e19be516SAndreas Gohr        foreach ($users as $user) {
3543213bf4eSAndreas Gohr            if ($this->deleteUser($user)) $count++;
355e19be516SAndreas Gohr        }
356e19be516SAndreas Gohr        return $count;
357e19be516SAndreas Gohr    }
358f64dbc90SAndreas Gohr
359f64dbc90SAndreas Gohr    /**
360f64dbc90SAndreas Gohr     * Bulk retrieval of user data [implement only where required/possible]
361f64dbc90SAndreas Gohr     *
362f64dbc90SAndreas Gohr     * Set getUsers capability when implemented
363f64dbc90SAndreas Gohr     *
364f64dbc90SAndreas Gohr     * @param int $start index of first user to be returned
365f64dbc90SAndreas Gohr     * @param int $limit max number of users to be returned
366f64dbc90SAndreas Gohr     * @param array $filter array of field/pattern pairs, null for no filter
367f64dbc90SAndreas Gohr     * @return  array list of userinfo (refer getUserData for internal userinfo details)
368f64dbc90SAndreas Gohr     */
3693213bf4eSAndreas Gohr    public function retrieveUsers($start = 0, $limit = -1, $filter = null)
3703213bf4eSAndreas Gohr    {
3716459f496SAndreas Gohr        if ($limit < 0) $limit = 10000; // we don't support no limit
372ab9790caSAndreas Gohr        if (is_null($filter)) $filter = [];
3736459f496SAndreas Gohr
37412c7f5c3SAndreas Gohr        if (isset($filter['grps'])) $filter['group'] = $filter['grps'];
375ab9790caSAndreas Gohr        foreach (['user', 'name', 'mail', 'group'] as $key) {
3766459f496SAndreas Gohr            if (!isset($filter[$key])) {
3776459f496SAndreas Gohr                $filter[$key] = '%';
3786459f496SAndreas Gohr            } else {
3796459f496SAndreas Gohr                $filter[$key] = '%' . $filter[$key] . '%';
3806459f496SAndreas Gohr            }
3816459f496SAndreas Gohr        }
38214119d44SAndreas Gohr        $filter['start'] = (int)$start;
38314119d44SAndreas Gohr        $filter['end'] = (int)$start + $limit;
38414119d44SAndreas Gohr        $filter['limit'] = (int)$limit;
3856459f496SAndreas Gohr
3863213bf4eSAndreas Gohr        $result = $this->query($this->getConf('list-users'), $filter);
387ab9790caSAndreas Gohr        if (!$result) return [];
388ab9790caSAndreas Gohr        $users = [];
38988ca2487SPhy        if (is_array($result)) {
3906459f496SAndreas Gohr            foreach ($result as $row) {
3916459f496SAndreas Gohr                if (!isset($row['user'])) {
39231a58abaSAndreas Gohr                    $this->debugMsg("list-users statement did not return 'user' attribute", -1, __LINE__);
393ab9790caSAndreas Gohr                    return [];
3946459f496SAndreas Gohr                }
3953e2a8145SAndreas Gohr                $users[] = $this->getUserData($row['user']);
3966459f496SAndreas Gohr            }
39788ca2487SPhy        } else {
39831a58abaSAndreas Gohr            $this->debugMsg("list-users statement did not return a list of result", -1, __LINE__);
39988ca2487SPhy        }
4006459f496SAndreas Gohr        return $users;
4016459f496SAndreas Gohr    }
402f64dbc90SAndreas Gohr
403f64dbc90SAndreas Gohr    /**
404f64dbc90SAndreas Gohr     * Return a count of the number of user which meet $filter criteria
405f64dbc90SAndreas Gohr     *
406f64dbc90SAndreas Gohr     * @param array $filter array of field/pattern pairs, empty array for no filter
407f64dbc90SAndreas Gohr     * @return int
408f64dbc90SAndreas Gohr     */
409ab9790caSAndreas Gohr    public function getUserCount($filter = [])
4103213bf4eSAndreas Gohr    {
411ab9790caSAndreas Gohr        if (is_null($filter)) $filter = [];
4126459f496SAndreas Gohr
41312c7f5c3SAndreas Gohr        if (isset($filter['grps'])) $filter['group'] = $filter['grps'];
414ab9790caSAndreas Gohr        foreach (['user', 'name', 'mail', 'group'] as $key) {
4156459f496SAndreas Gohr            if (!isset($filter[$key])) {
4166459f496SAndreas Gohr                $filter[$key] = '%';
4176459f496SAndreas Gohr            } else {
4186459f496SAndreas Gohr                $filter[$key] = '%' . $filter[$key] . '%';
4196459f496SAndreas Gohr            }
4206459f496SAndreas Gohr        }
4216459f496SAndreas Gohr
4223213bf4eSAndreas Gohr        $result = $this->query($this->getConf('count-users'), $filter);
4236459f496SAndreas Gohr        if (!$result || !isset($result[0]['count'])) {
4243213bf4eSAndreas Gohr            $this->debugMsg("Statement did not return 'count' attribute", -1, __LINE__);
4256459f496SAndreas Gohr        }
426f3c1c207SAndreas Gohr        return (int)$result[0]['count'];
4276459f496SAndreas Gohr    }
428f64dbc90SAndreas Gohr
429f64dbc90SAndreas Gohr    /**
4306459f496SAndreas Gohr     * Create a new group with the given name
431f64dbc90SAndreas Gohr     *
432f64dbc90SAndreas Gohr     * @param string $group
433f64dbc90SAndreas Gohr     * @return bool
434f64dbc90SAndreas Gohr     */
4353213bf4eSAndreas Gohr    public function addGroup($group)
4363213bf4eSAndreas Gohr    {
4376459f496SAndreas Gohr        $sql = $this->getConf('insert-group');
4386459f496SAndreas Gohr
439ab9790caSAndreas Gohr        $result = $this->query($sql, [':group' => $group]);
4403213bf4eSAndreas Gohr        $this->clearGroupCache();
4416459f496SAndreas Gohr        if ($result === false) return false;
4426459f496SAndreas Gohr        return true;
4436459f496SAndreas Gohr    }
444f64dbc90SAndreas Gohr
445f64dbc90SAndreas Gohr    /**
4465de3a6a5SAndreas Gohr     * Retrieve groups
447f64dbc90SAndreas Gohr     *
448f64dbc90SAndreas Gohr     * Set getGroups capability when implemented
449f64dbc90SAndreas Gohr     *
450f64dbc90SAndreas Gohr     * @param int $start
451f64dbc90SAndreas Gohr     * @param int $limit
452f64dbc90SAndreas Gohr     * @return  array
453f64dbc90SAndreas Gohr     */
4543213bf4eSAndreas Gohr    public function retrieveGroups($start = 0, $limit = 0)
4553213bf4eSAndreas Gohr    {
4563213bf4eSAndreas Gohr        $groups = array_keys($this->selectGroups());
457ab9790caSAndreas Gohr        if ($groups === false) return [];
458f64dbc90SAndreas Gohr
4595de3a6a5SAndreas Gohr        if (!$limit) {
4605de3a6a5SAndreas Gohr            return array_splice($groups, $start);
4615de3a6a5SAndreas Gohr        } else {
4625de3a6a5SAndreas Gohr            return array_splice($groups, $start, $limit);
463f64dbc90SAndreas Gohr        }
464f64dbc90SAndreas Gohr    }
465f64dbc90SAndreas Gohr
466f64dbc90SAndreas Gohr    /**
467f64dbc90SAndreas Gohr     * Select data of a specified user
468f64dbc90SAndreas Gohr     *
4695de3a6a5SAndreas Gohr     * @param string $user the user name
4705de3a6a5SAndreas Gohr     * @return bool|array user data, false on error
471f64dbc90SAndreas Gohr     */
4723213bf4eSAndreas Gohr    protected function selectUser($user)
4733213bf4eSAndreas Gohr    {
474f64dbc90SAndreas Gohr        $sql = $this->getConf('select-user');
475f64dbc90SAndreas Gohr
476ab9790caSAndreas Gohr        $result = $this->query($sql, [':user' => $user]);
47770a89417SAndreas Gohr        if (!$result) return false;
478f64dbc90SAndreas Gohr
47970a89417SAndreas Gohr        if (count($result) > 1) {
4803213bf4eSAndreas Gohr            $this->debugMsg('Found more than one matching user', -1, __LINE__);
481f64dbc90SAndreas Gohr            return false;
482f64dbc90SAndreas Gohr        }
483f64dbc90SAndreas Gohr
484f64dbc90SAndreas Gohr        $data = array_shift($result);
485f64dbc90SAndreas Gohr        $dataok = true;
486f64dbc90SAndreas Gohr
487f64dbc90SAndreas Gohr        if (!isset($data['user'])) {
4883213bf4eSAndreas Gohr            $this->debugMsg("Statement did not return 'user' attribute", -1, __LINE__);
489f64dbc90SAndreas Gohr            $dataok = false;
490f64dbc90SAndreas Gohr        }
491ab9790caSAndreas Gohr        if (!isset($data['hash']) && !isset($data['clear']) && !$this->checkConfig(['check-pass'])) {
4923213bf4eSAndreas Gohr            $this->debugMsg("Statement did not return 'clear' or 'hash' attribute", -1, __LINE__);
493f64dbc90SAndreas Gohr            $dataok = false;
494f64dbc90SAndreas Gohr        }
495f64dbc90SAndreas Gohr        if (!isset($data['name'])) {
4963213bf4eSAndreas Gohr            $this->debugMsg("Statement did not return 'name' attribute", -1, __LINE__);
497f64dbc90SAndreas Gohr            $dataok = false;
498f64dbc90SAndreas Gohr        }
499f64dbc90SAndreas Gohr        if (!isset($data['mail'])) {
5003213bf4eSAndreas Gohr            $this->debugMsg("Statement did not return 'mail' attribute", -1, __LINE__);
501f64dbc90SAndreas Gohr            $dataok = false;
502f64dbc90SAndreas Gohr        }
503f64dbc90SAndreas Gohr
504f64dbc90SAndreas Gohr        if (!$dataok) return false;
505f64dbc90SAndreas Gohr        return $data;
506f64dbc90SAndreas Gohr    }
507f64dbc90SAndreas Gohr
508f64dbc90SAndreas Gohr    /**
509e19be516SAndreas Gohr     * Delete a user after removing all their group memberships
510e19be516SAndreas Gohr     *
511e19be516SAndreas Gohr     * @param string $user
512e19be516SAndreas Gohr     * @return bool true when the user was deleted
513e19be516SAndreas Gohr     */
5143213bf4eSAndreas Gohr    protected function deleteUser($user)
5153213bf4eSAndreas Gohr    {
516e19be516SAndreas Gohr        $this->pdo->beginTransaction();
517e19be516SAndreas Gohr        {
518e19be516SAndreas Gohr            $userdata = $this->getUserData($user);
519e19be516SAndreas Gohr            if ($userdata === false) goto FAIL;
5203213bf4eSAndreas Gohr            $allgroups = $this->selectGroups();
521e19be516SAndreas Gohr
522e19be516SAndreas Gohr            // remove group memberships (ignore errors)
523e19be516SAndreas Gohr        foreach ($userdata['grps'] as $group) {
524358942b5SAndreas Gohr            if (isset($allgroups[$group])) {
5253213bf4eSAndreas Gohr                $this->leaveGroup($userdata, $allgroups[$group]);
526e19be516SAndreas Gohr            }
527358942b5SAndreas Gohr        }
528e19be516SAndreas Gohr
5293213bf4eSAndreas Gohr            $ok = $this->query($this->getConf('delete-user'), $userdata);
530e19be516SAndreas Gohr            if ($ok === false) goto FAIL;
531e19be516SAndreas Gohr        }
532e19be516SAndreas Gohr        $this->pdo->commit();
533e19be516SAndreas Gohr        return true;
534e19be516SAndreas Gohr
535e19be516SAndreas Gohr        FAIL:
536e19be516SAndreas Gohr        $this->pdo->rollBack();
537e19be516SAndreas Gohr        return false;
538e19be516SAndreas Gohr    }
539e19be516SAndreas Gohr
540e19be516SAndreas Gohr    /**
54170a89417SAndreas Gohr     * Select all groups of a user
54270a89417SAndreas Gohr     *
54370a89417SAndreas Gohr     * @param array $userdata The userdata as returned by _selectUser()
5445de3a6a5SAndreas Gohr     * @return array|bool list of group names, false on error
54570a89417SAndreas Gohr     */
5463213bf4eSAndreas Gohr    protected function selectUserGroups($userdata)
5473213bf4eSAndreas Gohr    {
54870a89417SAndreas Gohr        global $conf;
54970a89417SAndreas Gohr        $sql = $this->getConf('select-user-groups');
5503213bf4eSAndreas Gohr        $result = $this->query($sql, $userdata);
5515de3a6a5SAndreas Gohr        if ($result === false) return false;
55270a89417SAndreas Gohr
553ab9790caSAndreas Gohr        $groups = [$conf['defaultgroup']]; // always add default config
55488ca2487SPhy        if (is_array($result)) {
5555de3a6a5SAndreas Gohr            foreach ($result as $row) {
5565de3a6a5SAndreas Gohr                if (!isset($row['group'])) {
55731a58abaSAndreas Gohr                    $this->debugMsg("No 'group' field returned in select-user-groups statement", -1, __LINE__);
5585de3a6a5SAndreas Gohr                    return false;
5595de3a6a5SAndreas Gohr                }
56070a89417SAndreas Gohr                $groups[] = $row['group'];
56170a89417SAndreas Gohr            }
56288ca2487SPhy        } else {
56331a58abaSAndreas Gohr            $this->debugMsg("select-user-groups statement did not return a list of result", -1, __LINE__);
56488ca2487SPhy        }
56570a89417SAndreas Gohr
56670a89417SAndreas Gohr        $groups = array_unique($groups);
5670489c64bSMoisés Braga Ribeiro        Sort::sort($groups);
56870a89417SAndreas Gohr        return $groups;
56970a89417SAndreas Gohr    }
57070a89417SAndreas Gohr
57170a89417SAndreas Gohr    /**
5725de3a6a5SAndreas Gohr     * Select all available groups
5735de3a6a5SAndreas Gohr     *
5745de3a6a5SAndreas Gohr     * @return array|bool list of all available groups and their properties
5755de3a6a5SAndreas Gohr     */
5763213bf4eSAndreas Gohr    protected function selectGroups()
5773213bf4eSAndreas Gohr    {
5780cec3e2aSAndreas Gohr        if ($this->groupcache) return $this->groupcache;
5790cec3e2aSAndreas Gohr
5805de3a6a5SAndreas Gohr        $sql = $this->getConf('select-groups');
5813213bf4eSAndreas Gohr        $result = $this->query($sql);
5825de3a6a5SAndreas Gohr        if ($result === false) return false;
5835de3a6a5SAndreas Gohr
584ab9790caSAndreas Gohr        $groups = [];
58588ca2487SPhy        if (is_array($result)) {
5865de3a6a5SAndreas Gohr            foreach ($result as $row) {
5875de3a6a5SAndreas Gohr                if (!isset($row['group'])) {
5883213bf4eSAndreas Gohr                    $this->debugMsg("No 'group' field returned from select-groups statement", -1, __LINE__);
5895de3a6a5SAndreas Gohr                    return false;
5905de3a6a5SAndreas Gohr                }
5915de3a6a5SAndreas Gohr
5925de3a6a5SAndreas Gohr                // relayout result with group name as key
5935de3a6a5SAndreas Gohr                $group = $row['group'];
5945de3a6a5SAndreas Gohr                $groups[$group] = $row;
5955de3a6a5SAndreas Gohr            }
59688ca2487SPhy        } else {
59731a58abaSAndreas Gohr            $this->debugMsg("select-groups statement did not return a list of result", -1, __LINE__);
59888ca2487SPhy        }
5995de3a6a5SAndreas Gohr
6000489c64bSMoisés Braga Ribeiro        Sort::ksort($groups);
6015de3a6a5SAndreas Gohr        return $groups;
6025de3a6a5SAndreas Gohr    }
6035de3a6a5SAndreas Gohr
6045de3a6a5SAndreas Gohr    /**
6050cec3e2aSAndreas Gohr     * Remove all entries from the group cache
6060cec3e2aSAndreas Gohr     */
6073213bf4eSAndreas Gohr    protected function clearGroupCache()
6083213bf4eSAndreas Gohr    {
6090cec3e2aSAndreas Gohr        $this->groupcache = null;
6100cec3e2aSAndreas Gohr    }
6110cec3e2aSAndreas Gohr
6120cec3e2aSAndreas Gohr    /**
6134fb8dfabSAndreas Gohr     * Adds the user to the group
6145de3a6a5SAndreas Gohr     *
6155de3a6a5SAndreas Gohr     * @param array $userdata all the user data
6165de3a6a5SAndreas Gohr     * @param array $groupdata all the group data
6175de3a6a5SAndreas Gohr     * @return bool
6185de3a6a5SAndreas Gohr     */
6193213bf4eSAndreas Gohr    protected function joinGroup($userdata, $groupdata)
6203213bf4eSAndreas Gohr    {
6215de3a6a5SAndreas Gohr        $data = array_merge($userdata, $groupdata);
6225de3a6a5SAndreas Gohr        $sql = $this->getConf('join-group');
6233213bf4eSAndreas Gohr        $result = $this->query($sql, $data);
6245de3a6a5SAndreas Gohr        if ($result === false) return false;
6255de3a6a5SAndreas Gohr        return true;
6265de3a6a5SAndreas Gohr    }
6275de3a6a5SAndreas Gohr
6285de3a6a5SAndreas Gohr    /**
6294fb8dfabSAndreas Gohr     * Removes the user from the group
6304fb8dfabSAndreas Gohr     *
6314fb8dfabSAndreas Gohr     * @param array $userdata all the user data
6324fb8dfabSAndreas Gohr     * @param array $groupdata all the group data
6334fb8dfabSAndreas Gohr     * @return bool
6344fb8dfabSAndreas Gohr     */
6353213bf4eSAndreas Gohr    protected function leaveGroup($userdata, $groupdata)
6363213bf4eSAndreas Gohr    {
6374fb8dfabSAndreas Gohr        $data = array_merge($userdata, $groupdata);
6384fb8dfabSAndreas Gohr        $sql = $this->getConf('leave-group');
6393213bf4eSAndreas Gohr        $result = $this->query($sql, $data);
6404fb8dfabSAndreas Gohr        if ($result === false) return false;
6414fb8dfabSAndreas Gohr        return true;
6424fb8dfabSAndreas Gohr    }
6434fb8dfabSAndreas Gohr
6444fb8dfabSAndreas Gohr    /**
64570a89417SAndreas Gohr     * Executes a query
64670a89417SAndreas Gohr     *
64770a89417SAndreas Gohr     * @param string $sql The SQL statement to execute
64870a89417SAndreas Gohr     * @param array $arguments Named parameters to be used in the statement
649f695c447SAndreas Gohr     * @return array|int|bool The result as associative array for SELECTs, affected rows for others, false on error
65070a89417SAndreas Gohr     */
651ab9790caSAndreas Gohr    protected function query($sql, $arguments = [])
6523213bf4eSAndreas Gohr    {
653f695c447SAndreas Gohr        $sql = trim($sql);
6545de3a6a5SAndreas Gohr        if (empty($sql)) {
6553213bf4eSAndreas Gohr            $this->debugMsg('No SQL query given', -1, __LINE__);
6565de3a6a5SAndreas Gohr            return false;
6575de3a6a5SAndreas Gohr        }
6585de3a6a5SAndreas Gohr
65914119d44SAndreas Gohr        // execute
660ab9790caSAndreas Gohr        $params = [];
66114119d44SAndreas Gohr        $sth = $this->pdo->prepare($sql);
66288ca2487SPhy        $result = false;
66314119d44SAndreas Gohr        try {
66414119d44SAndreas Gohr            // prepare parameters - we only use those that exist in the SQL
66570a89417SAndreas Gohr            foreach ($arguments as $key => $value) {
66670a89417SAndreas Gohr                if (is_array($value)) continue;
66770a89417SAndreas Gohr                if (is_object($value)) continue;
66870a89417SAndreas Gohr                if ($key[0] != ':') $key = ":$key"; // prefix with colon if needed
669ab9790caSAndreas Gohr                if (strpos($sql, (string) $key) === false) continue; // skip if parameter is missing
67014119d44SAndreas Gohr
67114119d44SAndreas Gohr                if (is_int($value)) {
67214119d44SAndreas Gohr                    $sth->bindValue($key, $value, PDO::PARAM_INT);
67314119d44SAndreas Gohr                } else {
67414119d44SAndreas Gohr                    $sth->bindValue($key, $value);
67514119d44SAndreas Gohr                }
676f6cd8a7fSphjanderson                $params[$key] = $value; //remember for debugging
67770a89417SAndreas Gohr            }
67870a89417SAndreas Gohr
67914119d44SAndreas Gohr            $sth->execute();
6806a1b9bfeSPhy            // only report last line's result
6816a1b9bfeSPhy            $hasnextrowset = true;
6826a1b9bfeSPhy            $currentsql = $sql;
6836a1b9bfeSPhy            while ($hasnextrowset) {
6841b2deed9Sfiwswe                if (str_starts_with(strtolower($currentsql), 'select')) {
68570a89417SAndreas Gohr                    $result = $sth->fetchAll();
686f695c447SAndreas Gohr                } else {
687f695c447SAndreas Gohr                    $result = $sth->rowCount();
688f695c447SAndreas Gohr                }
6896a1b9bfeSPhy                $semi_pos = strpos($currentsql, ';');
6906a1b9bfeSPhy                if ($semi_pos) {
6916a1b9bfeSPhy                    $currentsql = trim(substr($currentsql, $semi_pos + 1));
6926a1b9bfeSPhy                }
6936a1b9bfeSPhy                try {
6946a1b9bfeSPhy                    $hasnextrowset = $sth->nextRowset(); // run next rowset
6956a1b9bfeSPhy                } catch (PDOException $rowset_e) {
6966a1b9bfeSPhy                    $hasnextrowset = false; // driver does not support multi-rowset, should be executed in one time
6976a1b9bfeSPhy                }
6986a1b9bfeSPhy            }
6995de3a6a5SAndreas Gohr        } catch (Exception $e) {
7004fb8dfabSAndreas Gohr            // report the caller's line
7014fb8dfabSAndreas Gohr            $trace = debug_backtrace();
7024fb8dfabSAndreas Gohr            $line = $trace[0]['line'];
7033213bf4eSAndreas Gohr            $dsql = $this->debugSQL($sql, $params, !defined('DOKU_UNITTEST'));
7043213bf4eSAndreas Gohr            $this->debugMsg($e, -1, $line);
7053213bf4eSAndreas Gohr            $this->debugMsg("SQL: <pre>$dsql</pre>", -1, $line);
7061600c7ccSAndreas Gohr        }
70770a89417SAndreas Gohr        $sth->closeCursor();
70870a89417SAndreas Gohr
7095de3a6a5SAndreas Gohr        return $result;
7105de3a6a5SAndreas Gohr    }
71170a89417SAndreas Gohr
71270a89417SAndreas Gohr    /**
713f64dbc90SAndreas Gohr     * Wrapper around msg() but outputs only when debug is enabled
714f64dbc90SAndreas Gohr     *
715f64dbc90SAndreas Gohr     * @param string|Exception $message
716f64dbc90SAndreas Gohr     * @param int $err
717f64dbc90SAndreas Gohr     * @param int $line
718f64dbc90SAndreas Gohr     */
7193213bf4eSAndreas Gohr    protected function debugMsg($message, $err = 0, $line = 0)
7203213bf4eSAndreas Gohr    {
721f64dbc90SAndreas Gohr        if (!$this->getConf('debug')) return;
722f64dbc90SAndreas Gohr        if (is_a($message, 'Exception')) {
723f64dbc90SAndreas Gohr            $err = -1;
724f64dbc90SAndreas Gohr            $msg = $message->getMessage();
7254fb8dfabSAndreas Gohr            if (!$line) $line = $message->getLine();
726f64dbc90SAndreas Gohr        } else {
727f64dbc90SAndreas Gohr            $msg = $message;
728f64dbc90SAndreas Gohr        }
729f64dbc90SAndreas Gohr
730f64dbc90SAndreas Gohr        if (defined('DOKU_UNITTEST')) {
731f64dbc90SAndreas Gohr            printf("\n%s, %s:%d\n", $msg, __FILE__, $line);
732f64dbc90SAndreas Gohr        } else {
733f64dbc90SAndreas Gohr            msg('authpdo: ' . $msg, $err, $line, __FILE__);
734f64dbc90SAndreas Gohr        }
735f64dbc90SAndreas Gohr    }
7365de3a6a5SAndreas Gohr
7375de3a6a5SAndreas Gohr    /**
7385de3a6a5SAndreas Gohr     * Check if the given config strings are set
7395de3a6a5SAndreas Gohr     *
7405de3a6a5SAndreas Gohr     * @param string[] $keys
7415de3a6a5SAndreas Gohr     * @return  bool
74231a58abaSAndreas Gohr     * @author  Matthias Grimm <matthiasgrimm@users.sourceforge.net>
74331a58abaSAndreas Gohr     *
7445de3a6a5SAndreas Gohr     */
7453213bf4eSAndreas Gohr    protected function checkConfig($keys)
7463213bf4eSAndreas Gohr    {
7475de3a6a5SAndreas Gohr        foreach ($keys as $key) {
748ad4d5631SAndreas Gohr            $params = explode(':', $key);
749ad4d5631SAndreas Gohr            $key = array_shift($params);
750ad4d5631SAndreas Gohr            $sql = trim($this->getConf($key));
751ad4d5631SAndreas Gohr
752ad4d5631SAndreas Gohr            // check if sql is set
753ad4d5631SAndreas Gohr            if (!$sql) return false;
754ad4d5631SAndreas Gohr            // check if needed params are there
755ad4d5631SAndreas Gohr            foreach ($params as $param) {
756ad4d5631SAndreas Gohr                if (strpos($sql, ":$param") === false) return false;
757ad4d5631SAndreas Gohr            }
7585de3a6a5SAndreas Gohr        }
7595de3a6a5SAndreas Gohr
7605de3a6a5SAndreas Gohr        return true;
7615de3a6a5SAndreas Gohr    }
7625de3a6a5SAndreas Gohr
7635de3a6a5SAndreas Gohr    /**
7645de3a6a5SAndreas Gohr     * create an approximation of the SQL string with parameters replaced
7655de3a6a5SAndreas Gohr     *
7665de3a6a5SAndreas Gohr     * @param string $sql
7675de3a6a5SAndreas Gohr     * @param array $params
7685de3a6a5SAndreas Gohr     * @param bool $htmlescape Should the result be escaped for output in HTML?
7695de3a6a5SAndreas Gohr     * @return string
7705de3a6a5SAndreas Gohr     */
7713213bf4eSAndreas Gohr    protected function debugSQL($sql, $params, $htmlescape = true)
7723213bf4eSAndreas Gohr    {
7735de3a6a5SAndreas Gohr        foreach ($params as $key => $val) {
7745de3a6a5SAndreas Gohr            if (is_int($val)) {
7755de3a6a5SAndreas Gohr                $val = $this->pdo->quote($val, PDO::PARAM_INT);
7765de3a6a5SAndreas Gohr            } elseif (is_bool($val)) {
7775de3a6a5SAndreas Gohr                $val = $this->pdo->quote($val, PDO::PARAM_BOOL);
7785de3a6a5SAndreas Gohr            } elseif (is_null($val)) {
7795de3a6a5SAndreas Gohr                $val = 'NULL';
7805de3a6a5SAndreas Gohr            } else {
7815de3a6a5SAndreas Gohr                $val = $this->pdo->quote($val);
7825de3a6a5SAndreas Gohr            }
7835de3a6a5SAndreas Gohr            $sql = str_replace($key, $val, $sql);
7845de3a6a5SAndreas Gohr        }
7855de3a6a5SAndreas Gohr        if ($htmlescape) $sql = hsc($sql);
7865de3a6a5SAndreas Gohr        return $sql;
7875de3a6a5SAndreas Gohr    }
788f64dbc90SAndreas Gohr}
789f64dbc90SAndreas Gohr
790f64dbc90SAndreas Gohr// vim:ts=4:sw=4:et:
791