1f64dbc90SAndreas Gohr<?php 2*ab9790caSAndreas Gohruse dokuwiki\PassHash; 30489c64bSMoisés Braga Ribeirouse dokuwiki\Utf8\Sort; 40489c64bSMoisés Braga Ribeiro 5f64dbc90SAndreas Gohr/** 6f64dbc90SAndreas Gohr * DokuWiki Plugin authpdo (Auth Component) 7f64dbc90SAndreas Gohr * 8f64dbc90SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 9f64dbc90SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 10f64dbc90SAndreas Gohr */ 11f64dbc90SAndreas Gohr 120d586afdSAndreas Gohr/** 130d586afdSAndreas Gohr * Class auth_plugin_authpdo 140d586afdSAndreas Gohr */ 153213bf4eSAndreas Gohrclass auth_plugin_authpdo extends DokuWiki_Auth_Plugin 163213bf4eSAndreas Gohr{ 17f64dbc90SAndreas Gohr 18f64dbc90SAndreas Gohr /** @var PDO */ 19f64dbc90SAndreas Gohr protected $pdo; 20f64dbc90SAndreas Gohr 210cec3e2aSAndreas Gohr /** @var null|array The list of all groups */ 22*ab9790caSAndreas Gohr protected $groupcache; 230cec3e2aSAndreas Gohr 24f64dbc90SAndreas Gohr /** 25f64dbc90SAndreas Gohr * Constructor. 26f64dbc90SAndreas Gohr */ 273213bf4eSAndreas Gohr public function __construct() 283213bf4eSAndreas Gohr { 29f64dbc90SAndreas Gohr parent::__construct(); // for compatibility 30f64dbc90SAndreas Gohr 31f64dbc90SAndreas Gohr if (!class_exists('PDO')) { 323213bf4eSAndreas Gohr $this->debugMsg('PDO extension for PHP not found.', -1, __LINE__); 33f64dbc90SAndreas Gohr $this->success = false; 34f64dbc90SAndreas Gohr return; 35f64dbc90SAndreas Gohr } 36f64dbc90SAndreas Gohr 37f64dbc90SAndreas Gohr if (!$this->getConf('dsn')) { 383213bf4eSAndreas Gohr $this->debugMsg('No DSN specified', -1, __LINE__); 39f64dbc90SAndreas Gohr $this->success = false; 40f64dbc90SAndreas Gohr return; 41f64dbc90SAndreas Gohr } 42f64dbc90SAndreas Gohr 43f64dbc90SAndreas Gohr try { 44f64dbc90SAndreas Gohr $this->pdo = new PDO( 45f64dbc90SAndreas Gohr $this->getConf('dsn'), 46f64dbc90SAndreas Gohr $this->getConf('user'), 470cc11d97SAndreas Gohr conf_decodeString($this->getConf('pass')), 48*ab9790caSAndreas Gohr [ 4970a89417SAndreas Gohr PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // always fetch as array 5070a89417SAndreas Gohr PDO::ATTR_EMULATE_PREPARES => true, // emulating prepares allows us to reuse param names 515de3a6a5SAndreas Gohr PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // we want exceptions, not error codes 52*ab9790caSAndreas Gohr ] 53f64dbc90SAndreas Gohr ); 54f64dbc90SAndreas Gohr } catch (PDOException $e) { 553213bf4eSAndreas Gohr $this->debugMsg($e); 56c27579a6SAndreas Gohr msg($this->getLang('connectfail'), -1); 57f64dbc90SAndreas Gohr $this->success = false; 58f64dbc90SAndreas Gohr return; 59f64dbc90SAndreas Gohr } 60f64dbc90SAndreas Gohr 610d586afdSAndreas Gohr // can Users be created? 623213bf4eSAndreas Gohr $this->cando['addUser'] = $this->checkConfig( 63*ab9790caSAndreas Gohr ['select-user', 'select-user-groups', 'select-groups', 'insert-user', 'insert-group', 'join-group'] 640d586afdSAndreas Gohr ); 65f64dbc90SAndreas Gohr 660d586afdSAndreas Gohr // can Users be deleted? 673213bf4eSAndreas Gohr $this->cando['delUser'] = $this->checkConfig( 68*ab9790caSAndreas Gohr ['select-user', 'select-user-groups', 'select-groups', 'leave-group', 'delete-user'] 690d586afdSAndreas Gohr ); 700d586afdSAndreas Gohr 710d586afdSAndreas Gohr // can login names be changed? 723213bf4eSAndreas Gohr $this->cando['modLogin'] = $this->checkConfig( 73*ab9790caSAndreas Gohr ['select-user', 'select-user-groups', 'update-user-login'] 740d586afdSAndreas Gohr ); 750d586afdSAndreas Gohr 760d586afdSAndreas Gohr // can passwords be changed? 773213bf4eSAndreas Gohr $this->cando['modPass'] = $this->checkConfig( 78*ab9790caSAndreas Gohr ['select-user', 'select-user-groups', 'update-user-pass'] 790d586afdSAndreas Gohr ); 800d586afdSAndreas Gohr 81ad4d5631SAndreas Gohr // can real names be changed? 823213bf4eSAndreas Gohr $this->cando['modName'] = $this->checkConfig( 83*ab9790caSAndreas Gohr ['select-user', 'select-user-groups', 'update-user-info:name'] 84ad4d5631SAndreas Gohr ); 85ad4d5631SAndreas Gohr 86ad4d5631SAndreas Gohr // can real email be changed? 873213bf4eSAndreas Gohr $this->cando['modMail'] = $this->checkConfig( 88*ab9790caSAndreas Gohr ['select-user', 'select-user-groups', 'update-user-info:mail'] 890d586afdSAndreas Gohr ); 900d586afdSAndreas Gohr 910d586afdSAndreas Gohr // can groups be changed? 923213bf4eSAndreas Gohr $this->cando['modGroups'] = $this->checkConfig( 93*ab9790caSAndreas Gohr ['select-user', 'select-user-groups', 'select-groups', 'leave-group', 'join-group', 'insert-group'] 940d586afdSAndreas Gohr ); 950d586afdSAndreas Gohr 960d586afdSAndreas Gohr // can a filtered list of users be retrieved? 973213bf4eSAndreas Gohr $this->cando['getUsers'] = $this->checkConfig( 98*ab9790caSAndreas Gohr ['list-users'] 990d586afdSAndreas Gohr ); 1000d586afdSAndreas Gohr 1010d586afdSAndreas Gohr // can the number of users be retrieved? 1023213bf4eSAndreas Gohr $this->cando['getUserCount'] = $this->checkConfig( 103*ab9790caSAndreas Gohr ['count-users'] 1040d586afdSAndreas Gohr ); 1050d586afdSAndreas Gohr 1060d586afdSAndreas Gohr // can a list of available groups be retrieved? 1073213bf4eSAndreas Gohr $this->cando['getGroups'] = $this->checkConfig( 108*ab9790caSAndreas Gohr ['select-groups'] 1090d586afdSAndreas Gohr ); 1100d586afdSAndreas Gohr 111f64dbc90SAndreas Gohr $this->success = true; 112f64dbc90SAndreas Gohr } 113f64dbc90SAndreas Gohr 114f64dbc90SAndreas Gohr /** 115f64dbc90SAndreas Gohr * Check user+password 116f64dbc90SAndreas Gohr * 117f64dbc90SAndreas Gohr * @param string $user the user name 118f64dbc90SAndreas Gohr * @param string $pass the clear text password 119f64dbc90SAndreas Gohr * @return bool 120f64dbc90SAndreas Gohr */ 1213213bf4eSAndreas Gohr public function checkPass($user, $pass) 1223213bf4eSAndreas Gohr { 123f64dbc90SAndreas Gohr 1243213bf4eSAndreas Gohr $userdata = $this->selectUser($user); 125397d62a2SAndreas Gohr if ($userdata == false) return false; 126f64dbc90SAndreas Gohr 127397d62a2SAndreas Gohr // password checking done in SQL? 128*ab9790caSAndreas Gohr if ($this->checkConfig(['check-pass'])) { 129397d62a2SAndreas Gohr $userdata['clear'] = $pass; 130397d62a2SAndreas Gohr $userdata['hash'] = auth_cryptPassword($pass); 1313213bf4eSAndreas Gohr $result = $this->query($this->getConf('check-pass'), $userdata); 132397d62a2SAndreas Gohr if ($result === false) return false; 133397d62a2SAndreas Gohr return (count($result) == 1); 134397d62a2SAndreas Gohr } 135397d62a2SAndreas Gohr 136397d62a2SAndreas Gohr // we do password checking on our own 137397d62a2SAndreas Gohr if (isset($userdata['hash'])) { 138f64dbc90SAndreas Gohr // hashed password 139*ab9790caSAndreas Gohr $passhash = new PassHash(); 140397d62a2SAndreas Gohr return $passhash->verify_hash($pass, $userdata['hash']); 141f64dbc90SAndreas Gohr } else { 142f64dbc90SAndreas Gohr // clear text password in the database O_o 143d5c0422fSAndreas Gohr return ($pass === $userdata['clear']); 144f64dbc90SAndreas Gohr } 145f64dbc90SAndreas Gohr } 146f64dbc90SAndreas Gohr 147f64dbc90SAndreas Gohr /** 148f64dbc90SAndreas Gohr * Return user info 149f64dbc90SAndreas Gohr * 150f64dbc90SAndreas Gohr * Returns info about the given user needs to contain 151f64dbc90SAndreas Gohr * at least these fields: 152f64dbc90SAndreas Gohr * 153f64dbc90SAndreas Gohr * name string full name of the user 154f64dbc90SAndreas Gohr * mail string email addres of the user 155f64dbc90SAndreas Gohr * grps array list of groups the user is in 156f64dbc90SAndreas Gohr * 157f64dbc90SAndreas Gohr * @param string $user the user name 158f64dbc90SAndreas Gohr * @param bool $requireGroups whether or not the returned data must include groups 159358942b5SAndreas Gohr * @return array|bool containing user data or false 160f64dbc90SAndreas Gohr */ 1613213bf4eSAndreas Gohr public function getUserData($user, $requireGroups = true) 1623213bf4eSAndreas Gohr { 1633213bf4eSAndreas Gohr $data = $this->selectUser($user); 164f64dbc90SAndreas Gohr if ($data == false) return false; 165f64dbc90SAndreas Gohr 16670a89417SAndreas Gohr if (isset($data['hash'])) unset($data['hash']); 16770a89417SAndreas Gohr if (isset($data['clean'])) unset($data['clean']); 168f64dbc90SAndreas Gohr 16970a89417SAndreas Gohr if ($requireGroups) { 1703213bf4eSAndreas Gohr $data['grps'] = $this->selectUserGroups($data); 1715de3a6a5SAndreas Gohr if ($data['grps'] === false) return false; 172f64dbc90SAndreas Gohr } 173f64dbc90SAndreas Gohr 174f64dbc90SAndreas Gohr return $data; 175f64dbc90SAndreas Gohr } 176f64dbc90SAndreas Gohr 177f64dbc90SAndreas Gohr /** 178f64dbc90SAndreas Gohr * Create a new User [implement only where required/possible] 179f64dbc90SAndreas Gohr * 180f64dbc90SAndreas Gohr * Returns false if the user already exists, null when an error 181f64dbc90SAndreas Gohr * occurred and true if everything went well. 182f64dbc90SAndreas Gohr * 183f64dbc90SAndreas Gohr * The new user HAS TO be added to the default group by this 184f64dbc90SAndreas Gohr * function! 185f64dbc90SAndreas Gohr * 186f64dbc90SAndreas Gohr * Set addUser capability when implemented 187f64dbc90SAndreas Gohr * 188f64dbc90SAndreas Gohr * @param string $user 1895de3a6a5SAndreas Gohr * @param string $clear 190f64dbc90SAndreas Gohr * @param string $name 191f64dbc90SAndreas Gohr * @param string $mail 192f64dbc90SAndreas Gohr * @param null|array $grps 193f64dbc90SAndreas Gohr * @return bool|null 194f64dbc90SAndreas Gohr */ 1953213bf4eSAndreas Gohr public function createUser($user, $clear, $name, $mail, $grps = null) 1963213bf4eSAndreas Gohr { 1975de3a6a5SAndreas Gohr global $conf; 1985de3a6a5SAndreas Gohr 1995de3a6a5SAndreas Gohr if (($info = $this->getUserData($user, false)) !== false) { 2005de3a6a5SAndreas Gohr msg($this->getLang('userexists'), -1); 2015de3a6a5SAndreas Gohr return false; // user already exists 2025de3a6a5SAndreas Gohr } 2035de3a6a5SAndreas Gohr 2045de3a6a5SAndreas Gohr // prepare data 205*ab9790caSAndreas Gohr if ($grps == null) $grps = []; 20607a11e2aSAndreas Gohr array_unshift($grps, $conf['defaultgroup']); 2075de3a6a5SAndreas Gohr $grps = array_unique($grps); 2085de3a6a5SAndreas Gohr $hash = auth_cryptPassword($clear); 209*ab9790caSAndreas Gohr $userdata = ['user' => $user, 'clear' => $clear, 'hash' => $hash, 'name' => $name, 'mail' => $mail]; 2105de3a6a5SAndreas Gohr 2115de3a6a5SAndreas Gohr // action protected by transaction 2125de3a6a5SAndreas Gohr $this->pdo->beginTransaction(); 2135de3a6a5SAndreas Gohr { 2145de3a6a5SAndreas Gohr // insert the user 2153213bf4eSAndreas Gohr $ok = $this->query($this->getConf('insert-user'), $userdata); 2165de3a6a5SAndreas Gohr if ($ok === false) goto FAIL; 2175de3a6a5SAndreas Gohr $userdata = $this->getUserData($user, false); 2185de3a6a5SAndreas Gohr if ($userdata === false) goto FAIL; 2195de3a6a5SAndreas Gohr 2205de3a6a5SAndreas Gohr // create all groups that do not exist, the refetch the groups 2213213bf4eSAndreas Gohr $allgroups = $this->selectGroups(); 2225de3a6a5SAndreas Gohr foreach ($grps as $group) { 2235de3a6a5SAndreas Gohr if (!isset($allgroups[$group])) { 2246459f496SAndreas Gohr $ok = $this->addGroup($group); 2255de3a6a5SAndreas Gohr if ($ok === false) goto FAIL; 2265de3a6a5SAndreas Gohr } 2275de3a6a5SAndreas Gohr } 2283213bf4eSAndreas Gohr $allgroups = $this->selectGroups(); 2295de3a6a5SAndreas Gohr 2305de3a6a5SAndreas Gohr // add user to the groups 2315de3a6a5SAndreas Gohr foreach ($grps as $group) { 2323213bf4eSAndreas Gohr $ok = $this->joinGroup($userdata, $allgroups[$group]); 2335de3a6a5SAndreas Gohr if ($ok === false) goto FAIL; 2345de3a6a5SAndreas Gohr } 2355de3a6a5SAndreas Gohr } 2365de3a6a5SAndreas Gohr $this->pdo->commit(); 2375de3a6a5SAndreas Gohr return true; 2385de3a6a5SAndreas Gohr 2395de3a6a5SAndreas Gohr // something went wrong, rollback 2405de3a6a5SAndreas Gohr FAIL: 2415de3a6a5SAndreas Gohr $this->pdo->rollBack(); 2423213bf4eSAndreas Gohr $this->debugMsg('Transaction rolled back', 0, __LINE__); 243c27579a6SAndreas Gohr msg($this->getLang('writefail'), -1); 2445de3a6a5SAndreas Gohr return null; // return error 2455de3a6a5SAndreas Gohr } 246f64dbc90SAndreas Gohr 247f64dbc90SAndreas Gohr /** 2484fb8dfabSAndreas Gohr * Modify user data 249f64dbc90SAndreas Gohr * 250f64dbc90SAndreas Gohr * @param string $user nick of the user to be changed 251f64dbc90SAndreas Gohr * @param array $changes array of field/value pairs to be changed (password will be clear text) 252f64dbc90SAndreas Gohr * @return bool 253f64dbc90SAndreas Gohr */ 2543213bf4eSAndreas Gohr public function modifyUser($user, $changes) 2553213bf4eSAndreas Gohr { 2564fb8dfabSAndreas Gohr // secure everything in transaction 2574fb8dfabSAndreas Gohr $this->pdo->beginTransaction(); 2584fb8dfabSAndreas Gohr { 2594fb8dfabSAndreas Gohr $olddata = $this->getUserData($user); 2604fb8dfabSAndreas Gohr $oldgroups = $olddata['grps']; 2614fb8dfabSAndreas Gohr unset($olddata['grps']); 2624fb8dfabSAndreas Gohr 2634fb8dfabSAndreas Gohr // changing the user name? 2644fb8dfabSAndreas Gohr if (isset($changes['user'])) { 2654fb8dfabSAndreas Gohr if ($this->getUserData($changes['user'], false)) goto FAIL; 2664fb8dfabSAndreas Gohr $params = $olddata; 2674fb8dfabSAndreas Gohr $params['newlogin'] = $changes['user']; 2684fb8dfabSAndreas Gohr 2693213bf4eSAndreas Gohr $ok = $this->query($this->getConf('update-user-login'), $params); 2704fb8dfabSAndreas Gohr if ($ok === false) goto FAIL; 2714fb8dfabSAndreas Gohr } 2724fb8dfabSAndreas Gohr 2734fb8dfabSAndreas Gohr // changing the password? 2744fb8dfabSAndreas Gohr if (isset($changes['pass'])) { 2754fb8dfabSAndreas Gohr $params = $olddata; 2764fb8dfabSAndreas Gohr $params['clear'] = $changes['pass']; 2774fb8dfabSAndreas Gohr $params['hash'] = auth_cryptPassword($changes['pass']); 2784fb8dfabSAndreas Gohr 2793213bf4eSAndreas Gohr $ok = $this->query($this->getConf('update-user-pass'), $params); 2804fb8dfabSAndreas Gohr if ($ok === false) goto FAIL; 2814fb8dfabSAndreas Gohr } 2824fb8dfabSAndreas Gohr 2834fb8dfabSAndreas Gohr // changing info? 2844fb8dfabSAndreas Gohr if (isset($changes['mail']) || isset($changes['name'])) { 2854fb8dfabSAndreas Gohr $params = $olddata; 2864fb8dfabSAndreas Gohr if (isset($changes['mail'])) $params['mail'] = $changes['mail']; 2874fb8dfabSAndreas Gohr if (isset($changes['name'])) $params['name'] = $changes['name']; 2884fb8dfabSAndreas Gohr 2893213bf4eSAndreas Gohr $ok = $this->query($this->getConf('update-user-info'), $params); 2904fb8dfabSAndreas Gohr if ($ok === false) goto FAIL; 2914fb8dfabSAndreas Gohr } 2924fb8dfabSAndreas Gohr 2934fb8dfabSAndreas Gohr // changing groups? 2944fb8dfabSAndreas Gohr if (isset($changes['grps'])) { 2953213bf4eSAndreas Gohr $allgroups = $this->selectGroups(); 2964fb8dfabSAndreas Gohr 2974fb8dfabSAndreas Gohr // remove membership for previous groups 2984fb8dfabSAndreas Gohr foreach ($oldgroups as $group) { 299358942b5SAndreas Gohr if (!in_array($group, $changes['grps']) && isset($allgroups[$group])) { 3003213bf4eSAndreas Gohr $ok = $this->leaveGroup($olddata, $allgroups[$group]); 3014fb8dfabSAndreas Gohr if ($ok === false) goto FAIL; 3024fb8dfabSAndreas Gohr } 3034fb8dfabSAndreas Gohr } 3044fb8dfabSAndreas Gohr 3054fb8dfabSAndreas Gohr // create all new groups that are missing 3064fb8dfabSAndreas Gohr $added = 0; 3074fb8dfabSAndreas Gohr foreach ($changes['grps'] as $group) { 3084fb8dfabSAndreas Gohr if (!isset($allgroups[$group])) { 3096459f496SAndreas Gohr $ok = $this->addGroup($group); 3104fb8dfabSAndreas Gohr if ($ok === false) goto FAIL; 3114fb8dfabSAndreas Gohr $added++; 3124fb8dfabSAndreas Gohr } 3134fb8dfabSAndreas Gohr } 3144fb8dfabSAndreas Gohr // reload group info 3153213bf4eSAndreas Gohr if ($added > 0) $allgroups = $this->selectGroups(); 3164fb8dfabSAndreas Gohr 3174fb8dfabSAndreas Gohr // add membership for new groups 3184fb8dfabSAndreas Gohr foreach ($changes['grps'] as $group) { 3194fb8dfabSAndreas Gohr if (!in_array($group, $oldgroups)) { 3203213bf4eSAndreas Gohr $ok = $this->joinGroup($olddata, $allgroups[$group]); 3214fb8dfabSAndreas Gohr if ($ok === false) goto FAIL; 3224fb8dfabSAndreas Gohr } 3234fb8dfabSAndreas Gohr } 3244fb8dfabSAndreas Gohr } 3254fb8dfabSAndreas Gohr 3264fb8dfabSAndreas Gohr } 3274fb8dfabSAndreas Gohr $this->pdo->commit(); 3284fb8dfabSAndreas Gohr return true; 3294fb8dfabSAndreas Gohr 3304fb8dfabSAndreas Gohr // something went wrong, rollback 3314fb8dfabSAndreas Gohr FAIL: 3324fb8dfabSAndreas Gohr $this->pdo->rollBack(); 3333213bf4eSAndreas Gohr $this->debugMsg('Transaction rolled back', 0, __LINE__); 334c27579a6SAndreas Gohr msg($this->getLang('writefail'), -1); 3354fb8dfabSAndreas Gohr return false; // return error 3364fb8dfabSAndreas Gohr } 337f64dbc90SAndreas Gohr 338f64dbc90SAndreas Gohr /** 339e19be516SAndreas Gohr * Delete one or more users 340f64dbc90SAndreas Gohr * 341f64dbc90SAndreas Gohr * Set delUser capability when implemented 342f64dbc90SAndreas Gohr * 343f64dbc90SAndreas Gohr * @param array $users 344f64dbc90SAndreas Gohr * @return int number of users deleted 345f64dbc90SAndreas Gohr */ 3463213bf4eSAndreas Gohr public function deleteUsers($users) 3473213bf4eSAndreas Gohr { 348e19be516SAndreas Gohr $count = 0; 349e19be516SAndreas Gohr foreach ($users as $user) { 3503213bf4eSAndreas Gohr if ($this->deleteUser($user)) $count++; 351e19be516SAndreas Gohr } 352e19be516SAndreas Gohr return $count; 353e19be516SAndreas Gohr } 354f64dbc90SAndreas Gohr 355f64dbc90SAndreas Gohr /** 356f64dbc90SAndreas Gohr * Bulk retrieval of user data [implement only where required/possible] 357f64dbc90SAndreas Gohr * 358f64dbc90SAndreas Gohr * Set getUsers capability when implemented 359f64dbc90SAndreas Gohr * 360f64dbc90SAndreas Gohr * @param int $start index of first user to be returned 361f64dbc90SAndreas Gohr * @param int $limit max number of users to be returned 362f64dbc90SAndreas Gohr * @param array $filter array of field/pattern pairs, null for no filter 363f64dbc90SAndreas Gohr * @return array list of userinfo (refer getUserData for internal userinfo details) 364f64dbc90SAndreas Gohr */ 3653213bf4eSAndreas Gohr public function retrieveUsers($start = 0, $limit = -1, $filter = null) 3663213bf4eSAndreas Gohr { 3676459f496SAndreas Gohr if ($limit < 0) $limit = 10000; // we don't support no limit 368*ab9790caSAndreas Gohr if (is_null($filter)) $filter = []; 3696459f496SAndreas Gohr 37012c7f5c3SAndreas Gohr if (isset($filter['grps'])) $filter['group'] = $filter['grps']; 371*ab9790caSAndreas Gohr foreach (['user', 'name', 'mail', 'group'] as $key) { 3726459f496SAndreas Gohr if (!isset($filter[$key])) { 3736459f496SAndreas Gohr $filter[$key] = '%'; 3746459f496SAndreas Gohr } else { 3756459f496SAndreas Gohr $filter[$key] = '%' . $filter[$key] . '%'; 3766459f496SAndreas Gohr } 3776459f496SAndreas Gohr } 37814119d44SAndreas Gohr $filter['start'] = (int)$start; 37914119d44SAndreas Gohr $filter['end'] = (int)$start + $limit; 38014119d44SAndreas Gohr $filter['limit'] = (int)$limit; 3816459f496SAndreas Gohr 3823213bf4eSAndreas Gohr $result = $this->query($this->getConf('list-users'), $filter); 383*ab9790caSAndreas Gohr if (!$result) return []; 384*ab9790caSAndreas Gohr $users = []; 38588ca2487SPhy if (is_array($result)) { 3866459f496SAndreas Gohr foreach ($result as $row) { 3876459f496SAndreas Gohr if (!isset($row['user'])) { 38831a58abaSAndreas Gohr $this->debugMsg("list-users statement did not return 'user' attribute", -1, __LINE__); 389*ab9790caSAndreas Gohr return []; 3906459f496SAndreas Gohr } 3913e2a8145SAndreas Gohr $users[] = $this->getUserData($row['user']); 3926459f496SAndreas Gohr } 39388ca2487SPhy } else { 39431a58abaSAndreas Gohr $this->debugMsg("list-users statement did not return a list of result", -1, __LINE__); 39588ca2487SPhy } 3966459f496SAndreas Gohr return $users; 3976459f496SAndreas Gohr } 398f64dbc90SAndreas Gohr 399f64dbc90SAndreas Gohr /** 400f64dbc90SAndreas Gohr * Return a count of the number of user which meet $filter criteria 401f64dbc90SAndreas Gohr * 402f64dbc90SAndreas Gohr * @param array $filter array of field/pattern pairs, empty array for no filter 403f64dbc90SAndreas Gohr * @return int 404f64dbc90SAndreas Gohr */ 405*ab9790caSAndreas Gohr public function getUserCount($filter = []) 4063213bf4eSAndreas Gohr { 407*ab9790caSAndreas Gohr if (is_null($filter)) $filter = []; 4086459f496SAndreas Gohr 40912c7f5c3SAndreas Gohr if (isset($filter['grps'])) $filter['group'] = $filter['grps']; 410*ab9790caSAndreas Gohr foreach (['user', 'name', 'mail', 'group'] as $key) { 4116459f496SAndreas Gohr if (!isset($filter[$key])) { 4126459f496SAndreas Gohr $filter[$key] = '%'; 4136459f496SAndreas Gohr } else { 4146459f496SAndreas Gohr $filter[$key] = '%' . $filter[$key] . '%'; 4156459f496SAndreas Gohr } 4166459f496SAndreas Gohr } 4176459f496SAndreas Gohr 4183213bf4eSAndreas Gohr $result = $this->query($this->getConf('count-users'), $filter); 4196459f496SAndreas Gohr if (!$result || !isset($result[0]['count'])) { 4203213bf4eSAndreas Gohr $this->debugMsg("Statement did not return 'count' attribute", -1, __LINE__); 4216459f496SAndreas Gohr } 422f3c1c207SAndreas Gohr return (int)$result[0]['count']; 4236459f496SAndreas Gohr } 424f64dbc90SAndreas Gohr 425f64dbc90SAndreas Gohr /** 4266459f496SAndreas Gohr * Create a new group with the given name 427f64dbc90SAndreas Gohr * 428f64dbc90SAndreas Gohr * @param string $group 429f64dbc90SAndreas Gohr * @return bool 430f64dbc90SAndreas Gohr */ 4313213bf4eSAndreas Gohr public function addGroup($group) 4323213bf4eSAndreas Gohr { 4336459f496SAndreas Gohr $sql = $this->getConf('insert-group'); 4346459f496SAndreas Gohr 435*ab9790caSAndreas Gohr $result = $this->query($sql, [':group' => $group]); 4363213bf4eSAndreas Gohr $this->clearGroupCache(); 4376459f496SAndreas Gohr if ($result === false) return false; 4386459f496SAndreas Gohr return true; 4396459f496SAndreas Gohr } 440f64dbc90SAndreas Gohr 441f64dbc90SAndreas Gohr /** 4425de3a6a5SAndreas Gohr * Retrieve groups 443f64dbc90SAndreas Gohr * 444f64dbc90SAndreas Gohr * Set getGroups capability when implemented 445f64dbc90SAndreas Gohr * 446f64dbc90SAndreas Gohr * @param int $start 447f64dbc90SAndreas Gohr * @param int $limit 448f64dbc90SAndreas Gohr * @return array 449f64dbc90SAndreas Gohr */ 4503213bf4eSAndreas Gohr public function retrieveGroups($start = 0, $limit = 0) 4513213bf4eSAndreas Gohr { 4523213bf4eSAndreas Gohr $groups = array_keys($this->selectGroups()); 453*ab9790caSAndreas Gohr if ($groups === false) return []; 454f64dbc90SAndreas Gohr 4555de3a6a5SAndreas Gohr if (!$limit) { 4565de3a6a5SAndreas Gohr return array_splice($groups, $start); 4575de3a6a5SAndreas Gohr } else { 4585de3a6a5SAndreas Gohr return array_splice($groups, $start, $limit); 459f64dbc90SAndreas Gohr } 460f64dbc90SAndreas Gohr } 461f64dbc90SAndreas Gohr 462f64dbc90SAndreas Gohr /** 463f64dbc90SAndreas Gohr * Select data of a specified user 464f64dbc90SAndreas Gohr * 4655de3a6a5SAndreas Gohr * @param string $user the user name 4665de3a6a5SAndreas Gohr * @return bool|array user data, false on error 467f64dbc90SAndreas Gohr */ 4683213bf4eSAndreas Gohr protected function selectUser($user) 4693213bf4eSAndreas Gohr { 470f64dbc90SAndreas Gohr $sql = $this->getConf('select-user'); 471f64dbc90SAndreas Gohr 472*ab9790caSAndreas Gohr $result = $this->query($sql, [':user' => $user]); 47370a89417SAndreas Gohr if (!$result) return false; 474f64dbc90SAndreas Gohr 47570a89417SAndreas Gohr if (count($result) > 1) { 4763213bf4eSAndreas Gohr $this->debugMsg('Found more than one matching user', -1, __LINE__); 477f64dbc90SAndreas Gohr return false; 478f64dbc90SAndreas Gohr } 479f64dbc90SAndreas Gohr 480f64dbc90SAndreas Gohr $data = array_shift($result); 481f64dbc90SAndreas Gohr $dataok = true; 482f64dbc90SAndreas Gohr 483f64dbc90SAndreas Gohr if (!isset($data['user'])) { 4843213bf4eSAndreas Gohr $this->debugMsg("Statement did not return 'user' attribute", -1, __LINE__); 485f64dbc90SAndreas Gohr $dataok = false; 486f64dbc90SAndreas Gohr } 487*ab9790caSAndreas Gohr if (!isset($data['hash']) && !isset($data['clear']) && !$this->checkConfig(['check-pass'])) { 4883213bf4eSAndreas Gohr $this->debugMsg("Statement did not return 'clear' or 'hash' attribute", -1, __LINE__); 489f64dbc90SAndreas Gohr $dataok = false; 490f64dbc90SAndreas Gohr } 491f64dbc90SAndreas Gohr if (!isset($data['name'])) { 4923213bf4eSAndreas Gohr $this->debugMsg("Statement did not return 'name' attribute", -1, __LINE__); 493f64dbc90SAndreas Gohr $dataok = false; 494f64dbc90SAndreas Gohr } 495f64dbc90SAndreas Gohr if (!isset($data['mail'])) { 4963213bf4eSAndreas Gohr $this->debugMsg("Statement did not return 'mail' attribute", -1, __LINE__); 497f64dbc90SAndreas Gohr $dataok = false; 498f64dbc90SAndreas Gohr } 499f64dbc90SAndreas Gohr 500f64dbc90SAndreas Gohr if (!$dataok) return false; 501f64dbc90SAndreas Gohr return $data; 502f64dbc90SAndreas Gohr } 503f64dbc90SAndreas Gohr 504f64dbc90SAndreas Gohr /** 505e19be516SAndreas Gohr * Delete a user after removing all their group memberships 506e19be516SAndreas Gohr * 507e19be516SAndreas Gohr * @param string $user 508e19be516SAndreas Gohr * @return bool true when the user was deleted 509e19be516SAndreas Gohr */ 5103213bf4eSAndreas Gohr protected function deleteUser($user) 5113213bf4eSAndreas Gohr { 512e19be516SAndreas Gohr $this->pdo->beginTransaction(); 513e19be516SAndreas Gohr { 514e19be516SAndreas Gohr $userdata = $this->getUserData($user); 515e19be516SAndreas Gohr if ($userdata === false) goto FAIL; 5163213bf4eSAndreas Gohr $allgroups = $this->selectGroups(); 517e19be516SAndreas Gohr 518e19be516SAndreas Gohr // remove group memberships (ignore errors) 519e19be516SAndreas Gohr foreach ($userdata['grps'] as $group) { 520358942b5SAndreas Gohr if (isset($allgroups[$group])) { 5213213bf4eSAndreas Gohr $this->leaveGroup($userdata, $allgroups[$group]); 522e19be516SAndreas Gohr } 523358942b5SAndreas Gohr } 524e19be516SAndreas Gohr 5253213bf4eSAndreas Gohr $ok = $this->query($this->getConf('delete-user'), $userdata); 526e19be516SAndreas Gohr if ($ok === false) goto FAIL; 527e19be516SAndreas Gohr } 528e19be516SAndreas Gohr $this->pdo->commit(); 529e19be516SAndreas Gohr return true; 530e19be516SAndreas Gohr 531e19be516SAndreas Gohr FAIL: 532e19be516SAndreas Gohr $this->pdo->rollBack(); 533e19be516SAndreas Gohr return false; 534e19be516SAndreas Gohr } 535e19be516SAndreas Gohr 536e19be516SAndreas Gohr /** 53770a89417SAndreas Gohr * Select all groups of a user 53870a89417SAndreas Gohr * 53970a89417SAndreas Gohr * @param array $userdata The userdata as returned by _selectUser() 5405de3a6a5SAndreas Gohr * @return array|bool list of group names, false on error 54170a89417SAndreas Gohr */ 5423213bf4eSAndreas Gohr protected function selectUserGroups($userdata) 5433213bf4eSAndreas Gohr { 54470a89417SAndreas Gohr global $conf; 54570a89417SAndreas Gohr $sql = $this->getConf('select-user-groups'); 5463213bf4eSAndreas Gohr $result = $this->query($sql, $userdata); 5475de3a6a5SAndreas Gohr if ($result === false) return false; 54870a89417SAndreas Gohr 549*ab9790caSAndreas Gohr $groups = [$conf['defaultgroup']]; // always add default config 55088ca2487SPhy if (is_array($result)) { 5515de3a6a5SAndreas Gohr foreach ($result as $row) { 5525de3a6a5SAndreas Gohr if (!isset($row['group'])) { 55331a58abaSAndreas Gohr $this->debugMsg("No 'group' field returned in select-user-groups statement", -1, __LINE__); 5545de3a6a5SAndreas Gohr return false; 5555de3a6a5SAndreas Gohr } 55670a89417SAndreas Gohr $groups[] = $row['group']; 55770a89417SAndreas Gohr } 55888ca2487SPhy } else { 55931a58abaSAndreas Gohr $this->debugMsg("select-user-groups statement did not return a list of result", -1, __LINE__); 56088ca2487SPhy } 56170a89417SAndreas Gohr 56270a89417SAndreas Gohr $groups = array_unique($groups); 5630489c64bSMoisés Braga Ribeiro Sort::sort($groups); 56470a89417SAndreas Gohr return $groups; 56570a89417SAndreas Gohr } 56670a89417SAndreas Gohr 56770a89417SAndreas Gohr /** 5685de3a6a5SAndreas Gohr * Select all available groups 5695de3a6a5SAndreas Gohr * 5705de3a6a5SAndreas Gohr * @return array|bool list of all available groups and their properties 5715de3a6a5SAndreas Gohr */ 5723213bf4eSAndreas Gohr protected function selectGroups() 5733213bf4eSAndreas Gohr { 5740cec3e2aSAndreas Gohr if ($this->groupcache) return $this->groupcache; 5750cec3e2aSAndreas Gohr 5765de3a6a5SAndreas Gohr $sql = $this->getConf('select-groups'); 5773213bf4eSAndreas Gohr $result = $this->query($sql); 5785de3a6a5SAndreas Gohr if ($result === false) return false; 5795de3a6a5SAndreas Gohr 580*ab9790caSAndreas Gohr $groups = []; 58188ca2487SPhy if (is_array($result)) { 5825de3a6a5SAndreas Gohr foreach ($result as $row) { 5835de3a6a5SAndreas Gohr if (!isset($row['group'])) { 5843213bf4eSAndreas Gohr $this->debugMsg("No 'group' field returned from select-groups statement", -1, __LINE__); 5855de3a6a5SAndreas Gohr return false; 5865de3a6a5SAndreas Gohr } 5875de3a6a5SAndreas Gohr 5885de3a6a5SAndreas Gohr // relayout result with group name as key 5895de3a6a5SAndreas Gohr $group = $row['group']; 5905de3a6a5SAndreas Gohr $groups[$group] = $row; 5915de3a6a5SAndreas Gohr } 59288ca2487SPhy } else { 59331a58abaSAndreas Gohr $this->debugMsg("select-groups statement did not return a list of result", -1, __LINE__); 59488ca2487SPhy } 5955de3a6a5SAndreas Gohr 5960489c64bSMoisés Braga Ribeiro Sort::ksort($groups); 5975de3a6a5SAndreas Gohr return $groups; 5985de3a6a5SAndreas Gohr } 5995de3a6a5SAndreas Gohr 6005de3a6a5SAndreas Gohr /** 6010cec3e2aSAndreas Gohr * Remove all entries from the group cache 6020cec3e2aSAndreas Gohr */ 6033213bf4eSAndreas Gohr protected function clearGroupCache() 6043213bf4eSAndreas Gohr { 6050cec3e2aSAndreas Gohr $this->groupcache = null; 6060cec3e2aSAndreas Gohr } 6070cec3e2aSAndreas Gohr 6080cec3e2aSAndreas Gohr /** 6094fb8dfabSAndreas Gohr * Adds the user to the group 6105de3a6a5SAndreas Gohr * 6115de3a6a5SAndreas Gohr * @param array $userdata all the user data 6125de3a6a5SAndreas Gohr * @param array $groupdata all the group data 6135de3a6a5SAndreas Gohr * @return bool 6145de3a6a5SAndreas Gohr */ 6153213bf4eSAndreas Gohr protected function joinGroup($userdata, $groupdata) 6163213bf4eSAndreas Gohr { 6175de3a6a5SAndreas Gohr $data = array_merge($userdata, $groupdata); 6185de3a6a5SAndreas Gohr $sql = $this->getConf('join-group'); 6193213bf4eSAndreas Gohr $result = $this->query($sql, $data); 6205de3a6a5SAndreas Gohr if ($result === false) return false; 6215de3a6a5SAndreas Gohr return true; 6225de3a6a5SAndreas Gohr } 6235de3a6a5SAndreas Gohr 6245de3a6a5SAndreas Gohr /** 6254fb8dfabSAndreas Gohr * Removes the user from the group 6264fb8dfabSAndreas Gohr * 6274fb8dfabSAndreas Gohr * @param array $userdata all the user data 6284fb8dfabSAndreas Gohr * @param array $groupdata all the group data 6294fb8dfabSAndreas Gohr * @return bool 6304fb8dfabSAndreas Gohr */ 6313213bf4eSAndreas Gohr protected function leaveGroup($userdata, $groupdata) 6323213bf4eSAndreas Gohr { 6334fb8dfabSAndreas Gohr $data = array_merge($userdata, $groupdata); 6344fb8dfabSAndreas Gohr $sql = $this->getConf('leave-group'); 6353213bf4eSAndreas Gohr $result = $this->query($sql, $data); 6364fb8dfabSAndreas Gohr if ($result === false) return false; 6374fb8dfabSAndreas Gohr return true; 6384fb8dfabSAndreas Gohr } 6394fb8dfabSAndreas Gohr 6404fb8dfabSAndreas Gohr /** 64170a89417SAndreas Gohr * Executes a query 64270a89417SAndreas Gohr * 64370a89417SAndreas Gohr * @param string $sql The SQL statement to execute 64470a89417SAndreas Gohr * @param array $arguments Named parameters to be used in the statement 645f695c447SAndreas Gohr * @return array|int|bool The result as associative array for SELECTs, affected rows for others, false on error 64670a89417SAndreas Gohr */ 647*ab9790caSAndreas Gohr protected function query($sql, $arguments = []) 6483213bf4eSAndreas Gohr { 649f695c447SAndreas Gohr $sql = trim($sql); 6505de3a6a5SAndreas Gohr if (empty($sql)) { 6513213bf4eSAndreas Gohr $this->debugMsg('No SQL query given', -1, __LINE__); 6525de3a6a5SAndreas Gohr return false; 6535de3a6a5SAndreas Gohr } 6545de3a6a5SAndreas Gohr 65514119d44SAndreas Gohr // execute 656*ab9790caSAndreas Gohr $params = []; 65714119d44SAndreas Gohr $sth = $this->pdo->prepare($sql); 65888ca2487SPhy $result = false; 65914119d44SAndreas Gohr try { 66014119d44SAndreas Gohr // prepare parameters - we only use those that exist in the SQL 66170a89417SAndreas Gohr foreach ($arguments as $key => $value) { 66270a89417SAndreas Gohr if (is_array($value)) continue; 66370a89417SAndreas Gohr if (is_object($value)) continue; 66470a89417SAndreas Gohr if ($key[0] != ':') $key = ":$key"; // prefix with colon if needed 665*ab9790caSAndreas Gohr if (strpos($sql, (string) $key) === false) continue; // skip if parameter is missing 66614119d44SAndreas Gohr 66714119d44SAndreas Gohr if (is_int($value)) { 66814119d44SAndreas Gohr $sth->bindValue($key, $value, PDO::PARAM_INT); 66914119d44SAndreas Gohr } else { 67014119d44SAndreas Gohr $sth->bindValue($key, $value); 67114119d44SAndreas Gohr } 672f6cd8a7fSphjanderson $params[$key] = $value; //remember for debugging 67370a89417SAndreas Gohr } 67470a89417SAndreas Gohr 67514119d44SAndreas Gohr $sth->execute(); 6766a1b9bfeSPhy // only report last line's result 6776a1b9bfeSPhy $hasnextrowset = true; 6786a1b9bfeSPhy $currentsql = $sql; 6796a1b9bfeSPhy while ($hasnextrowset) { 6806a1b9bfeSPhy if (strtolower(substr($currentsql, 0, 6)) == 'select') { 68170a89417SAndreas Gohr $result = $sth->fetchAll(); 682f695c447SAndreas Gohr } else { 683f695c447SAndreas Gohr $result = $sth->rowCount(); 684f695c447SAndreas Gohr } 6856a1b9bfeSPhy $semi_pos = strpos($currentsql, ';'); 6866a1b9bfeSPhy if ($semi_pos) { 6876a1b9bfeSPhy $currentsql = trim(substr($currentsql, $semi_pos + 1)); 6886a1b9bfeSPhy } 6896a1b9bfeSPhy try { 6906a1b9bfeSPhy $hasnextrowset = $sth->nextRowset(); // run next rowset 6916a1b9bfeSPhy } catch (PDOException $rowset_e) { 6926a1b9bfeSPhy $hasnextrowset = false; // driver does not support multi-rowset, should be executed in one time 6936a1b9bfeSPhy } 6946a1b9bfeSPhy } 6955de3a6a5SAndreas Gohr } catch (Exception $e) { 6964fb8dfabSAndreas Gohr // report the caller's line 6974fb8dfabSAndreas Gohr $trace = debug_backtrace(); 6984fb8dfabSAndreas Gohr $line = $trace[0]['line']; 6993213bf4eSAndreas Gohr $dsql = $this->debugSQL($sql, $params, !defined('DOKU_UNITTEST')); 7003213bf4eSAndreas Gohr $this->debugMsg($e, -1, $line); 7013213bf4eSAndreas Gohr $this->debugMsg("SQL: <pre>$dsql</pre>", -1, $line); 7021600c7ccSAndreas Gohr } 70370a89417SAndreas Gohr $sth->closeCursor(); 70470a89417SAndreas Gohr 7055de3a6a5SAndreas Gohr return $result; 7065de3a6a5SAndreas Gohr } 70770a89417SAndreas Gohr 70870a89417SAndreas Gohr /** 709f64dbc90SAndreas Gohr * Wrapper around msg() but outputs only when debug is enabled 710f64dbc90SAndreas Gohr * 711f64dbc90SAndreas Gohr * @param string|Exception $message 712f64dbc90SAndreas Gohr * @param int $err 713f64dbc90SAndreas Gohr * @param int $line 714f64dbc90SAndreas Gohr */ 7153213bf4eSAndreas Gohr protected function debugMsg($message, $err = 0, $line = 0) 7163213bf4eSAndreas Gohr { 717f64dbc90SAndreas Gohr if (!$this->getConf('debug')) return; 718f64dbc90SAndreas Gohr if (is_a($message, 'Exception')) { 719f64dbc90SAndreas Gohr $err = -1; 720f64dbc90SAndreas Gohr $msg = $message->getMessage(); 7214fb8dfabSAndreas Gohr if (!$line) $line = $message->getLine(); 722f64dbc90SAndreas Gohr } else { 723f64dbc90SAndreas Gohr $msg = $message; 724f64dbc90SAndreas Gohr } 725f64dbc90SAndreas Gohr 726f64dbc90SAndreas Gohr if (defined('DOKU_UNITTEST')) { 727f64dbc90SAndreas Gohr printf("\n%s, %s:%d\n", $msg, __FILE__, $line); 728f64dbc90SAndreas Gohr } else { 729f64dbc90SAndreas Gohr msg('authpdo: ' . $msg, $err, $line, __FILE__); 730f64dbc90SAndreas Gohr } 731f64dbc90SAndreas Gohr } 7325de3a6a5SAndreas Gohr 7335de3a6a5SAndreas Gohr /** 7345de3a6a5SAndreas Gohr * Check if the given config strings are set 7355de3a6a5SAndreas Gohr * 7365de3a6a5SAndreas Gohr * @param string[] $keys 7375de3a6a5SAndreas Gohr * @return bool 73831a58abaSAndreas Gohr * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 73931a58abaSAndreas Gohr * 7405de3a6a5SAndreas Gohr */ 7413213bf4eSAndreas Gohr protected function checkConfig($keys) 7423213bf4eSAndreas Gohr { 7435de3a6a5SAndreas Gohr foreach ($keys as $key) { 744ad4d5631SAndreas Gohr $params = explode(':', $key); 745ad4d5631SAndreas Gohr $key = array_shift($params); 746ad4d5631SAndreas Gohr $sql = trim($this->getConf($key)); 747ad4d5631SAndreas Gohr 748ad4d5631SAndreas Gohr // check if sql is set 749ad4d5631SAndreas Gohr if (!$sql) return false; 750ad4d5631SAndreas Gohr // check if needed params are there 751ad4d5631SAndreas Gohr foreach ($params as $param) { 752ad4d5631SAndreas Gohr if (strpos($sql, ":$param") === false) return false; 753ad4d5631SAndreas Gohr } 7545de3a6a5SAndreas Gohr } 7555de3a6a5SAndreas Gohr 7565de3a6a5SAndreas Gohr return true; 7575de3a6a5SAndreas Gohr } 7585de3a6a5SAndreas Gohr 7595de3a6a5SAndreas Gohr /** 7605de3a6a5SAndreas Gohr * create an approximation of the SQL string with parameters replaced 7615de3a6a5SAndreas Gohr * 7625de3a6a5SAndreas Gohr * @param string $sql 7635de3a6a5SAndreas Gohr * @param array $params 7645de3a6a5SAndreas Gohr * @param bool $htmlescape Should the result be escaped for output in HTML? 7655de3a6a5SAndreas Gohr * @return string 7665de3a6a5SAndreas Gohr */ 7673213bf4eSAndreas Gohr protected function debugSQL($sql, $params, $htmlescape = true) 7683213bf4eSAndreas Gohr { 7695de3a6a5SAndreas Gohr foreach ($params as $key => $val) { 7705de3a6a5SAndreas Gohr if (is_int($val)) { 7715de3a6a5SAndreas Gohr $val = $this->pdo->quote($val, PDO::PARAM_INT); 7725de3a6a5SAndreas Gohr } elseif (is_bool($val)) { 7735de3a6a5SAndreas Gohr $val = $this->pdo->quote($val, PDO::PARAM_BOOL); 7745de3a6a5SAndreas Gohr } elseif (is_null($val)) { 7755de3a6a5SAndreas Gohr $val = 'NULL'; 7765de3a6a5SAndreas Gohr } else { 7775de3a6a5SAndreas Gohr $val = $this->pdo->quote($val); 7785de3a6a5SAndreas Gohr } 7795de3a6a5SAndreas Gohr $sql = str_replace($key, $val, $sql); 7805de3a6a5SAndreas Gohr } 7815de3a6a5SAndreas Gohr if ($htmlescape) $sql = hsc($sql); 7825de3a6a5SAndreas Gohr return $sql; 7835de3a6a5SAndreas Gohr } 784f64dbc90SAndreas Gohr} 785f64dbc90SAndreas Gohr 786f64dbc90SAndreas Gohr// vim:ts=4:sw=4:et: 787