1f64dbc90SAndreas Gohr<?php 2f64dbc90SAndreas Gohr/** 3f64dbc90SAndreas Gohr * DokuWiki Plugin authpdo (Auth Component) 4f64dbc90SAndreas Gohr * 5f64dbc90SAndreas Gohr * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html 6f64dbc90SAndreas Gohr * @author Andreas Gohr <andi@splitbrain.org> 7f64dbc90SAndreas Gohr */ 8f64dbc90SAndreas Gohr 90d586afdSAndreas Gohr/** 100d586afdSAndreas Gohr * Class auth_plugin_authpdo 110d586afdSAndreas Gohr */ 12*3213bf4eSAndreas Gohrclass auth_plugin_authpdo extends DokuWiki_Auth_Plugin 13*3213bf4eSAndreas Gohr{ 14f64dbc90SAndreas Gohr 15f64dbc90SAndreas Gohr /** @var PDO */ 16f64dbc90SAndreas Gohr protected $pdo; 17f64dbc90SAndreas Gohr 180cec3e2aSAndreas Gohr /** @var null|array The list of all groups */ 190cec3e2aSAndreas Gohr protected $groupcache = null; 200cec3e2aSAndreas Gohr 21f64dbc90SAndreas Gohr /** 22f64dbc90SAndreas Gohr * Constructor. 23f64dbc90SAndreas Gohr */ 24*3213bf4eSAndreas Gohr public function __construct() 25*3213bf4eSAndreas Gohr { 26f64dbc90SAndreas Gohr parent::__construct(); // for compatibility 27f64dbc90SAndreas Gohr 28f64dbc90SAndreas Gohr if (!class_exists('PDO')) { 29*3213bf4eSAndreas Gohr $this->debugMsg('PDO extension for PHP not found.', -1, __LINE__); 30f64dbc90SAndreas Gohr $this->success = false; 31f64dbc90SAndreas Gohr return; 32f64dbc90SAndreas Gohr } 33f64dbc90SAndreas Gohr 34f64dbc90SAndreas Gohr if (!$this->getConf('dsn')) { 35*3213bf4eSAndreas Gohr $this->debugMsg('No DSN specified', -1, __LINE__); 36f64dbc90SAndreas Gohr $this->success = false; 37f64dbc90SAndreas Gohr return; 38f64dbc90SAndreas Gohr } 39f64dbc90SAndreas Gohr 40f64dbc90SAndreas Gohr try { 41f64dbc90SAndreas Gohr $this->pdo = new PDO( 42f64dbc90SAndreas Gohr $this->getConf('dsn'), 43f64dbc90SAndreas Gohr $this->getConf('user'), 440cc11d97SAndreas Gohr conf_decodeString($this->getConf('pass')), 45f64dbc90SAndreas Gohr array( 4670a89417SAndreas Gohr PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, // always fetch as array 4770a89417SAndreas Gohr PDO::ATTR_EMULATE_PREPARES => true, // emulating prepares allows us to reuse param names 485de3a6a5SAndreas Gohr PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, // we want exceptions, not error codes 49f64dbc90SAndreas Gohr ) 50f64dbc90SAndreas Gohr ); 51f64dbc90SAndreas Gohr } catch (PDOException $e) { 52*3213bf4eSAndreas Gohr $this->debugMsg($e); 53c27579a6SAndreas Gohr msg($this->getLang('connectfail'), -1); 54f64dbc90SAndreas Gohr $this->success = false; 55f64dbc90SAndreas Gohr return; 56f64dbc90SAndreas Gohr } 57f64dbc90SAndreas Gohr 580d586afdSAndreas Gohr // can Users be created? 59*3213bf4eSAndreas Gohr $this->cando['addUser'] = $this->checkConfig( 600d586afdSAndreas Gohr array( 610d586afdSAndreas Gohr 'select-user', 620d586afdSAndreas Gohr 'select-user-groups', 630d586afdSAndreas Gohr 'select-groups', 640d586afdSAndreas Gohr 'insert-user', 650d586afdSAndreas Gohr 'insert-group', 660d586afdSAndreas Gohr 'join-group' 670d586afdSAndreas Gohr ) 680d586afdSAndreas Gohr ); 69f64dbc90SAndreas Gohr 700d586afdSAndreas Gohr // can Users be deleted? 71*3213bf4eSAndreas Gohr $this->cando['delUser'] = $this->checkConfig( 720d586afdSAndreas Gohr array( 730d586afdSAndreas Gohr 'select-user', 740d586afdSAndreas Gohr 'select-user-groups', 750d586afdSAndreas Gohr 'select-groups', 767f89f089SAndreas Gohr 'leave-group', 777f89f089SAndreas Gohr 'delete-user' 780d586afdSAndreas Gohr ) 790d586afdSAndreas Gohr ); 800d586afdSAndreas Gohr 810d586afdSAndreas Gohr // can login names be changed? 82*3213bf4eSAndreas Gohr $this->cando['modLogin'] = $this->checkConfig( 830d586afdSAndreas Gohr array( 840d586afdSAndreas Gohr 'select-user', 850d586afdSAndreas Gohr 'select-user-groups', 860d586afdSAndreas Gohr 'update-user-login' 870d586afdSAndreas Gohr ) 880d586afdSAndreas Gohr ); 890d586afdSAndreas Gohr 900d586afdSAndreas Gohr // can passwords be changed? 91*3213bf4eSAndreas Gohr $this->cando['modPass'] = $this->checkConfig( 920d586afdSAndreas Gohr array( 930d586afdSAndreas Gohr 'select-user', 940d586afdSAndreas Gohr 'select-user-groups', 950d586afdSAndreas Gohr 'update-user-pass' 960d586afdSAndreas Gohr ) 970d586afdSAndreas Gohr ); 980d586afdSAndreas Gohr 99ad4d5631SAndreas Gohr // can real names be changed? 100*3213bf4eSAndreas Gohr $this->cando['modName'] = $this->checkConfig( 1010d586afdSAndreas Gohr array( 1020d586afdSAndreas Gohr 'select-user', 1030d586afdSAndreas Gohr 'select-user-groups', 104ad4d5631SAndreas Gohr 'update-user-info:name' 105ad4d5631SAndreas Gohr ) 106ad4d5631SAndreas Gohr ); 107ad4d5631SAndreas Gohr 108ad4d5631SAndreas Gohr // can real email be changed? 109*3213bf4eSAndreas Gohr $this->cando['modMail'] = $this->checkConfig( 110ad4d5631SAndreas Gohr array( 111ad4d5631SAndreas Gohr 'select-user', 112ad4d5631SAndreas Gohr 'select-user-groups', 113ad4d5631SAndreas Gohr 'update-user-info:mail' 1140d586afdSAndreas Gohr ) 1150d586afdSAndreas Gohr ); 1160d586afdSAndreas Gohr 1170d586afdSAndreas Gohr // can groups be changed? 118*3213bf4eSAndreas Gohr $this->cando['modGroups'] = $this->checkConfig( 1190d586afdSAndreas Gohr array( 1200d586afdSAndreas Gohr 'select-user', 1210d586afdSAndreas Gohr 'select-user-groups', 1220d586afdSAndreas Gohr 'select-groups', 1230d586afdSAndreas Gohr 'leave-group', 1240d586afdSAndreas Gohr 'join-group', 1250d586afdSAndreas Gohr 'insert-group' 1260d586afdSAndreas Gohr ) 1270d586afdSAndreas Gohr ); 1280d586afdSAndreas Gohr 1290d586afdSAndreas Gohr // can a filtered list of users be retrieved? 130*3213bf4eSAndreas Gohr $this->cando['getUsers'] = $this->checkConfig( 1310d586afdSAndreas Gohr array( 1320d586afdSAndreas Gohr 'list-users' 1330d586afdSAndreas Gohr ) 1340d586afdSAndreas Gohr ); 1350d586afdSAndreas Gohr 1360d586afdSAndreas Gohr // can the number of users be retrieved? 137*3213bf4eSAndreas Gohr $this->cando['getUserCount'] = $this->checkConfig( 1380d586afdSAndreas Gohr array( 1390d586afdSAndreas Gohr 'count-users' 1400d586afdSAndreas Gohr ) 1410d586afdSAndreas Gohr ); 1420d586afdSAndreas Gohr 1430d586afdSAndreas Gohr // can a list of available groups be retrieved? 144*3213bf4eSAndreas Gohr $this->cando['getGroups'] = $this->checkConfig( 1450d586afdSAndreas Gohr array( 1460d586afdSAndreas Gohr 'select-groups' 1470d586afdSAndreas Gohr ) 1480d586afdSAndreas Gohr ); 1490d586afdSAndreas Gohr 150f64dbc90SAndreas Gohr $this->success = true; 151f64dbc90SAndreas Gohr } 152f64dbc90SAndreas Gohr 153f64dbc90SAndreas Gohr /** 154f64dbc90SAndreas Gohr * Check user+password 155f64dbc90SAndreas Gohr * 156f64dbc90SAndreas Gohr * @param string $user the user name 157f64dbc90SAndreas Gohr * @param string $pass the clear text password 158f64dbc90SAndreas Gohr * @return bool 159f64dbc90SAndreas Gohr */ 160*3213bf4eSAndreas Gohr public function checkPass($user, $pass) 161*3213bf4eSAndreas Gohr { 162f64dbc90SAndreas Gohr 163*3213bf4eSAndreas Gohr $userdata = $this->selectUser($user); 164397d62a2SAndreas Gohr if ($userdata == false) return false; 165f64dbc90SAndreas Gohr 166397d62a2SAndreas Gohr // password checking done in SQL? 167*3213bf4eSAndreas Gohr if ($this->checkConfig(array('check-pass'))) { 168397d62a2SAndreas Gohr $userdata['clear'] = $pass; 169397d62a2SAndreas Gohr $userdata['hash'] = auth_cryptPassword($pass); 170*3213bf4eSAndreas Gohr $result = $this->query($this->getConf('check-pass'), $userdata); 171397d62a2SAndreas Gohr if ($result === false) return false; 172397d62a2SAndreas Gohr return (count($result) == 1); 173397d62a2SAndreas Gohr } 174397d62a2SAndreas Gohr 175397d62a2SAndreas Gohr // we do password checking on our own 176397d62a2SAndreas Gohr if (isset($userdata['hash'])) { 177f64dbc90SAndreas Gohr // hashed password 178f64dbc90SAndreas Gohr $passhash = new PassHash(); 179397d62a2SAndreas Gohr return $passhash->verify_hash($pass, $userdata['hash']); 180f64dbc90SAndreas Gohr } else { 181f64dbc90SAndreas Gohr // clear text password in the database O_o 182d5c0422fSAndreas Gohr return ($pass === $userdata['clear']); 183f64dbc90SAndreas Gohr } 184f64dbc90SAndreas Gohr } 185f64dbc90SAndreas Gohr 186f64dbc90SAndreas Gohr /** 187f64dbc90SAndreas Gohr * Return user info 188f64dbc90SAndreas Gohr * 189f64dbc90SAndreas Gohr * Returns info about the given user needs to contain 190f64dbc90SAndreas Gohr * at least these fields: 191f64dbc90SAndreas Gohr * 192f64dbc90SAndreas Gohr * name string full name of the user 193f64dbc90SAndreas Gohr * mail string email addres of the user 194f64dbc90SAndreas Gohr * grps array list of groups the user is in 195f64dbc90SAndreas Gohr * 196f64dbc90SAndreas Gohr * @param string $user the user name 197f64dbc90SAndreas Gohr * @param bool $requireGroups whether or not the returned data must include groups 198358942b5SAndreas Gohr * @return array|bool containing user data or false 199f64dbc90SAndreas Gohr */ 200*3213bf4eSAndreas Gohr public function getUserData($user, $requireGroups = true) 201*3213bf4eSAndreas Gohr { 202*3213bf4eSAndreas Gohr $data = $this->selectUser($user); 203f64dbc90SAndreas Gohr if ($data == false) return false; 204f64dbc90SAndreas Gohr 20570a89417SAndreas Gohr if (isset($data['hash'])) unset($data['hash']); 20670a89417SAndreas Gohr if (isset($data['clean'])) unset($data['clean']); 207f64dbc90SAndreas Gohr 20870a89417SAndreas Gohr if ($requireGroups) { 209*3213bf4eSAndreas Gohr $data['grps'] = $this->selectUserGroups($data); 2105de3a6a5SAndreas Gohr if ($data['grps'] === false) return false; 211f64dbc90SAndreas Gohr } 212f64dbc90SAndreas Gohr 213f64dbc90SAndreas Gohr return $data; 214f64dbc90SAndreas Gohr } 215f64dbc90SAndreas Gohr 216f64dbc90SAndreas Gohr /** 217f64dbc90SAndreas Gohr * Create a new User [implement only where required/possible] 218f64dbc90SAndreas Gohr * 219f64dbc90SAndreas Gohr * Returns false if the user already exists, null when an error 220f64dbc90SAndreas Gohr * occurred and true if everything went well. 221f64dbc90SAndreas Gohr * 222f64dbc90SAndreas Gohr * The new user HAS TO be added to the default group by this 223f64dbc90SAndreas Gohr * function! 224f64dbc90SAndreas Gohr * 225f64dbc90SAndreas Gohr * Set addUser capability when implemented 226f64dbc90SAndreas Gohr * 227f64dbc90SAndreas Gohr * @param string $user 2285de3a6a5SAndreas Gohr * @param string $clear 229f64dbc90SAndreas Gohr * @param string $name 230f64dbc90SAndreas Gohr * @param string $mail 231f64dbc90SAndreas Gohr * @param null|array $grps 232f64dbc90SAndreas Gohr * @return bool|null 233f64dbc90SAndreas Gohr */ 234*3213bf4eSAndreas Gohr public function createUser($user, $clear, $name, $mail, $grps = null) 235*3213bf4eSAndreas Gohr { 2365de3a6a5SAndreas Gohr global $conf; 2375de3a6a5SAndreas Gohr 2385de3a6a5SAndreas Gohr if (($info = $this->getUserData($user, false)) !== false) { 2395de3a6a5SAndreas Gohr msg($this->getLang('userexists'), -1); 2405de3a6a5SAndreas Gohr return false; // user already exists 2415de3a6a5SAndreas Gohr } 2425de3a6a5SAndreas Gohr 2435de3a6a5SAndreas Gohr // prepare data 2445de3a6a5SAndreas Gohr if ($grps == null) $grps = array(); 24507a11e2aSAndreas Gohr array_unshift($grps, $conf['defaultgroup']); 2465de3a6a5SAndreas Gohr $grps = array_unique($grps); 2475de3a6a5SAndreas Gohr $hash = auth_cryptPassword($clear); 2485de3a6a5SAndreas Gohr $userdata = compact('user', 'clear', 'hash', 'name', 'mail'); 2495de3a6a5SAndreas Gohr 2505de3a6a5SAndreas Gohr // action protected by transaction 2515de3a6a5SAndreas Gohr $this->pdo->beginTransaction(); 2525de3a6a5SAndreas Gohr { 2535de3a6a5SAndreas Gohr // insert the user 254*3213bf4eSAndreas Gohr $ok = $this->query($this->getConf('insert-user'), $userdata); 2555de3a6a5SAndreas Gohr if ($ok === false) goto FAIL; 2565de3a6a5SAndreas Gohr $userdata = $this->getUserData($user, false); 2575de3a6a5SAndreas Gohr if ($userdata === false) goto FAIL; 2585de3a6a5SAndreas Gohr 2595de3a6a5SAndreas Gohr // create all groups that do not exist, the refetch the groups 260*3213bf4eSAndreas Gohr $allgroups = $this->selectGroups(); 2615de3a6a5SAndreas Gohr foreach ($grps as $group) { 2625de3a6a5SAndreas Gohr if (!isset($allgroups[$group])) { 2636459f496SAndreas Gohr $ok = $this->addGroup($group); 2645de3a6a5SAndreas Gohr if ($ok === false) goto FAIL; 2655de3a6a5SAndreas Gohr } 2665de3a6a5SAndreas Gohr } 267*3213bf4eSAndreas Gohr $allgroups = $this->selectGroups(); 2685de3a6a5SAndreas Gohr 2695de3a6a5SAndreas Gohr // add user to the groups 2705de3a6a5SAndreas Gohr foreach ($grps as $group) { 271*3213bf4eSAndreas Gohr $ok = $this->joinGroup($userdata, $allgroups[$group]); 2725de3a6a5SAndreas Gohr if ($ok === false) goto FAIL; 2735de3a6a5SAndreas Gohr } 2745de3a6a5SAndreas Gohr } 2755de3a6a5SAndreas Gohr $this->pdo->commit(); 2765de3a6a5SAndreas Gohr return true; 2775de3a6a5SAndreas Gohr 2785de3a6a5SAndreas Gohr // something went wrong, rollback 2795de3a6a5SAndreas Gohr FAIL: 2805de3a6a5SAndreas Gohr $this->pdo->rollBack(); 281*3213bf4eSAndreas Gohr $this->debugMsg('Transaction rolled back', 0, __LINE__); 282c27579a6SAndreas Gohr msg($this->getLang('writefail'), -1); 2835de3a6a5SAndreas Gohr return null; // return error 2845de3a6a5SAndreas Gohr } 285f64dbc90SAndreas Gohr 286f64dbc90SAndreas Gohr /** 2874fb8dfabSAndreas Gohr * Modify user data 288f64dbc90SAndreas Gohr * 289f64dbc90SAndreas Gohr * @param string $user nick of the user to be changed 290f64dbc90SAndreas Gohr * @param array $changes array of field/value pairs to be changed (password will be clear text) 291f64dbc90SAndreas Gohr * @return bool 292f64dbc90SAndreas Gohr */ 293*3213bf4eSAndreas Gohr public function modifyUser($user, $changes) 294*3213bf4eSAndreas Gohr { 2954fb8dfabSAndreas Gohr // secure everything in transaction 2964fb8dfabSAndreas Gohr $this->pdo->beginTransaction(); 2974fb8dfabSAndreas Gohr { 2984fb8dfabSAndreas Gohr $olddata = $this->getUserData($user); 2994fb8dfabSAndreas Gohr $oldgroups = $olddata['grps']; 3004fb8dfabSAndreas Gohr unset($olddata['grps']); 3014fb8dfabSAndreas Gohr 3024fb8dfabSAndreas Gohr // changing the user name? 3034fb8dfabSAndreas Gohr if (isset($changes['user'])) { 3044fb8dfabSAndreas Gohr if ($this->getUserData($changes['user'], false)) goto FAIL; 3054fb8dfabSAndreas Gohr $params = $olddata; 3064fb8dfabSAndreas Gohr $params['newlogin'] = $changes['user']; 3074fb8dfabSAndreas Gohr 308*3213bf4eSAndreas Gohr $ok = $this->query($this->getConf('update-user-login'), $params); 3094fb8dfabSAndreas Gohr if ($ok === false) goto FAIL; 3104fb8dfabSAndreas Gohr } 3114fb8dfabSAndreas Gohr 3124fb8dfabSAndreas Gohr // changing the password? 3134fb8dfabSAndreas Gohr if (isset($changes['pass'])) { 3144fb8dfabSAndreas Gohr $params = $olddata; 3154fb8dfabSAndreas Gohr $params['clear'] = $changes['pass']; 3164fb8dfabSAndreas Gohr $params['hash'] = auth_cryptPassword($changes['pass']); 3174fb8dfabSAndreas Gohr 318*3213bf4eSAndreas Gohr $ok = $this->query($this->getConf('update-user-pass'), $params); 3194fb8dfabSAndreas Gohr if ($ok === false) goto FAIL; 3204fb8dfabSAndreas Gohr } 3214fb8dfabSAndreas Gohr 3224fb8dfabSAndreas Gohr // changing info? 3234fb8dfabSAndreas Gohr if (isset($changes['mail']) || isset($changes['name'])) { 3244fb8dfabSAndreas Gohr $params = $olddata; 3254fb8dfabSAndreas Gohr if (isset($changes['mail'])) $params['mail'] = $changes['mail']; 3264fb8dfabSAndreas Gohr if (isset($changes['name'])) $params['name'] = $changes['name']; 3274fb8dfabSAndreas Gohr 328*3213bf4eSAndreas Gohr $ok = $this->query($this->getConf('update-user-info'), $params); 3294fb8dfabSAndreas Gohr if ($ok === false) goto FAIL; 3304fb8dfabSAndreas Gohr } 3314fb8dfabSAndreas Gohr 3324fb8dfabSAndreas Gohr // changing groups? 3334fb8dfabSAndreas Gohr if (isset($changes['grps'])) { 334*3213bf4eSAndreas Gohr $allgroups = $this->selectGroups(); 3354fb8dfabSAndreas Gohr 3364fb8dfabSAndreas Gohr // remove membership for previous groups 3374fb8dfabSAndreas Gohr foreach ($oldgroups as $group) { 338358942b5SAndreas Gohr if (!in_array($group, $changes['grps']) && isset($allgroups[$group])) { 339*3213bf4eSAndreas Gohr $ok = $this->leaveGroup($olddata, $allgroups[$group]); 3404fb8dfabSAndreas Gohr if ($ok === false) goto FAIL; 3414fb8dfabSAndreas Gohr } 3424fb8dfabSAndreas Gohr } 3434fb8dfabSAndreas Gohr 3444fb8dfabSAndreas Gohr // create all new groups that are missing 3454fb8dfabSAndreas Gohr $added = 0; 3464fb8dfabSAndreas Gohr foreach ($changes['grps'] as $group) { 3474fb8dfabSAndreas Gohr if (!isset($allgroups[$group])) { 3486459f496SAndreas Gohr $ok = $this->addGroup($group); 3494fb8dfabSAndreas Gohr if ($ok === false) goto FAIL; 3504fb8dfabSAndreas Gohr $added++; 3514fb8dfabSAndreas Gohr } 3524fb8dfabSAndreas Gohr } 3534fb8dfabSAndreas Gohr // reload group info 354*3213bf4eSAndreas Gohr if ($added > 0) $allgroups = $this->selectGroups(); 3554fb8dfabSAndreas Gohr 3564fb8dfabSAndreas Gohr // add membership for new groups 3574fb8dfabSAndreas Gohr foreach ($changes['grps'] as $group) { 3584fb8dfabSAndreas Gohr if (!in_array($group, $oldgroups)) { 359*3213bf4eSAndreas Gohr $ok = $this->joinGroup($olddata, $allgroups[$group]); 3604fb8dfabSAndreas Gohr if ($ok === false) goto FAIL; 3614fb8dfabSAndreas Gohr } 3624fb8dfabSAndreas Gohr } 3634fb8dfabSAndreas Gohr } 3644fb8dfabSAndreas Gohr 3654fb8dfabSAndreas Gohr } 3664fb8dfabSAndreas Gohr $this->pdo->commit(); 3674fb8dfabSAndreas Gohr return true; 3684fb8dfabSAndreas Gohr 3694fb8dfabSAndreas Gohr // something went wrong, rollback 3704fb8dfabSAndreas Gohr FAIL: 3714fb8dfabSAndreas Gohr $this->pdo->rollBack(); 372*3213bf4eSAndreas Gohr $this->debugMsg('Transaction rolled back', 0, __LINE__); 373c27579a6SAndreas Gohr msg($this->getLang('writefail'), -1); 3744fb8dfabSAndreas Gohr return false; // return error 3754fb8dfabSAndreas Gohr } 376f64dbc90SAndreas Gohr 377f64dbc90SAndreas Gohr /** 378e19be516SAndreas Gohr * Delete one or more users 379f64dbc90SAndreas Gohr * 380f64dbc90SAndreas Gohr * Set delUser capability when implemented 381f64dbc90SAndreas Gohr * 382f64dbc90SAndreas Gohr * @param array $users 383f64dbc90SAndreas Gohr * @return int number of users deleted 384f64dbc90SAndreas Gohr */ 385*3213bf4eSAndreas Gohr public function deleteUsers($users) 386*3213bf4eSAndreas Gohr { 387e19be516SAndreas Gohr $count = 0; 388e19be516SAndreas Gohr foreach ($users as $user) { 389*3213bf4eSAndreas Gohr if ($this->deleteUser($user)) $count++; 390e19be516SAndreas Gohr } 391e19be516SAndreas Gohr return $count; 392e19be516SAndreas Gohr } 393f64dbc90SAndreas Gohr 394f64dbc90SAndreas Gohr /** 395f64dbc90SAndreas Gohr * Bulk retrieval of user data [implement only where required/possible] 396f64dbc90SAndreas Gohr * 397f64dbc90SAndreas Gohr * Set getUsers capability when implemented 398f64dbc90SAndreas Gohr * 399f64dbc90SAndreas Gohr * @param int $start index of first user to be returned 400f64dbc90SAndreas Gohr * @param int $limit max number of users to be returned 401f64dbc90SAndreas Gohr * @param array $filter array of field/pattern pairs, null for no filter 402f64dbc90SAndreas Gohr * @return array list of userinfo (refer getUserData for internal userinfo details) 403f64dbc90SAndreas Gohr */ 404*3213bf4eSAndreas Gohr public function retrieveUsers($start = 0, $limit = -1, $filter = null) 405*3213bf4eSAndreas Gohr { 4066459f496SAndreas Gohr if ($limit < 0) $limit = 10000; // we don't support no limit 4076459f496SAndreas Gohr if (is_null($filter)) $filter = array(); 4086459f496SAndreas Gohr 40912c7f5c3SAndreas Gohr if (isset($filter['grps'])) $filter['group'] = $filter['grps']; 4106459f496SAndreas Gohr foreach (array('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 } 41714119d44SAndreas Gohr $filter['start'] = (int) $start; 41814119d44SAndreas Gohr $filter['end'] = (int) $start + $limit; 41914119d44SAndreas Gohr $filter['limit'] = (int) $limit; 4206459f496SAndreas Gohr 421*3213bf4eSAndreas Gohr $result = $this->query($this->getConf('list-users'), $filter); 4226459f496SAndreas Gohr if (!$result) return array(); 4236459f496SAndreas Gohr $users = array(); 4246459f496SAndreas Gohr foreach ($result as $row) { 4256459f496SAndreas Gohr if (!isset($row['user'])) { 426*3213bf4eSAndreas Gohr $this->debugMsg("Statement did not return 'user' attribute", -1, __LINE__); 4276459f496SAndreas Gohr return array(); 4286459f496SAndreas Gohr } 4293e2a8145SAndreas Gohr $users[] = $this->getUserData($row['user']); 4306459f496SAndreas Gohr } 4316459f496SAndreas Gohr return $users; 4326459f496SAndreas Gohr } 433f64dbc90SAndreas Gohr 434f64dbc90SAndreas Gohr /** 435f64dbc90SAndreas Gohr * Return a count of the number of user which meet $filter criteria 436f64dbc90SAndreas Gohr * 437f64dbc90SAndreas Gohr * @param array $filter array of field/pattern pairs, empty array for no filter 438f64dbc90SAndreas Gohr * @return int 439f64dbc90SAndreas Gohr */ 440*3213bf4eSAndreas Gohr public function getUserCount($filter = array()) 441*3213bf4eSAndreas Gohr { 4426459f496SAndreas Gohr if (is_null($filter)) $filter = array(); 4436459f496SAndreas Gohr 44412c7f5c3SAndreas Gohr if (isset($filter['grps'])) $filter['group'] = $filter['grps']; 4456459f496SAndreas Gohr foreach (array('user', 'name', 'mail', 'group') as $key) { 4466459f496SAndreas Gohr if (!isset($filter[$key])) { 4476459f496SAndreas Gohr $filter[$key] = '%'; 4486459f496SAndreas Gohr } else { 4496459f496SAndreas Gohr $filter[$key] = '%' . $filter[$key] . '%'; 4506459f496SAndreas Gohr } 4516459f496SAndreas Gohr } 4526459f496SAndreas Gohr 453*3213bf4eSAndreas Gohr $result = $this->query($this->getConf('count-users'), $filter); 4546459f496SAndreas Gohr if (!$result || !isset($result[0]['count'])) { 455*3213bf4eSAndreas Gohr $this->debugMsg("Statement did not return 'count' attribute", -1, __LINE__); 4566459f496SAndreas Gohr } 457f3c1c207SAndreas Gohr return (int) $result[0]['count']; 4586459f496SAndreas Gohr } 459f64dbc90SAndreas Gohr 460f64dbc90SAndreas Gohr /** 4616459f496SAndreas Gohr * Create a new group with the given name 462f64dbc90SAndreas Gohr * 463f64dbc90SAndreas Gohr * @param string $group 464f64dbc90SAndreas Gohr * @return bool 465f64dbc90SAndreas Gohr */ 466*3213bf4eSAndreas Gohr public function addGroup($group) 467*3213bf4eSAndreas Gohr { 4686459f496SAndreas Gohr $sql = $this->getConf('insert-group'); 4696459f496SAndreas Gohr 470*3213bf4eSAndreas Gohr $result = $this->query($sql, array(':group' => $group)); 471*3213bf4eSAndreas Gohr $this->clearGroupCache(); 4726459f496SAndreas Gohr if ($result === false) return false; 4736459f496SAndreas Gohr return true; 4746459f496SAndreas Gohr } 475f64dbc90SAndreas Gohr 476f64dbc90SAndreas Gohr /** 4775de3a6a5SAndreas Gohr * Retrieve groups 478f64dbc90SAndreas Gohr * 479f64dbc90SAndreas Gohr * Set getGroups capability when implemented 480f64dbc90SAndreas Gohr * 481f64dbc90SAndreas Gohr * @param int $start 482f64dbc90SAndreas Gohr * @param int $limit 483f64dbc90SAndreas Gohr * @return array 484f64dbc90SAndreas Gohr */ 485*3213bf4eSAndreas Gohr public function retrieveGroups($start = 0, $limit = 0) 486*3213bf4eSAndreas Gohr { 487*3213bf4eSAndreas Gohr $groups = array_keys($this->selectGroups()); 4885de3a6a5SAndreas Gohr if ($groups === false) return array(); 489f64dbc90SAndreas Gohr 4905de3a6a5SAndreas Gohr if (!$limit) { 4915de3a6a5SAndreas Gohr return array_splice($groups, $start); 4925de3a6a5SAndreas Gohr } else { 4935de3a6a5SAndreas Gohr return array_splice($groups, $start, $limit); 494f64dbc90SAndreas Gohr } 495f64dbc90SAndreas Gohr } 496f64dbc90SAndreas Gohr 497f64dbc90SAndreas Gohr /** 498f64dbc90SAndreas Gohr * Select data of a specified user 499f64dbc90SAndreas Gohr * 5005de3a6a5SAndreas Gohr * @param string $user the user name 5015de3a6a5SAndreas Gohr * @return bool|array user data, false on error 502f64dbc90SAndreas Gohr */ 503*3213bf4eSAndreas Gohr protected function selectUser($user) 504*3213bf4eSAndreas Gohr { 505f64dbc90SAndreas Gohr $sql = $this->getConf('select-user'); 506f64dbc90SAndreas Gohr 507*3213bf4eSAndreas Gohr $result = $this->query($sql, array(':user' => $user)); 50870a89417SAndreas Gohr if (!$result) return false; 509f64dbc90SAndreas Gohr 51070a89417SAndreas Gohr if (count($result) > 1) { 511*3213bf4eSAndreas Gohr $this->debugMsg('Found more than one matching user', -1, __LINE__); 512f64dbc90SAndreas Gohr return false; 513f64dbc90SAndreas Gohr } 514f64dbc90SAndreas Gohr 515f64dbc90SAndreas Gohr $data = array_shift($result); 516f64dbc90SAndreas Gohr $dataok = true; 517f64dbc90SAndreas Gohr 518f64dbc90SAndreas Gohr if (!isset($data['user'])) { 519*3213bf4eSAndreas Gohr $this->debugMsg("Statement did not return 'user' attribute", -1, __LINE__); 520f64dbc90SAndreas Gohr $dataok = false; 521f64dbc90SAndreas Gohr } 522*3213bf4eSAndreas Gohr if (!isset($data['hash']) && !isset($data['clear']) && !$this->checkConfig(array('check-pass'))) { 523*3213bf4eSAndreas Gohr $this->debugMsg("Statement did not return 'clear' or 'hash' attribute", -1, __LINE__); 524f64dbc90SAndreas Gohr $dataok = false; 525f64dbc90SAndreas Gohr } 526f64dbc90SAndreas Gohr if (!isset($data['name'])) { 527*3213bf4eSAndreas Gohr $this->debugMsg("Statement did not return 'name' attribute", -1, __LINE__); 528f64dbc90SAndreas Gohr $dataok = false; 529f64dbc90SAndreas Gohr } 530f64dbc90SAndreas Gohr if (!isset($data['mail'])) { 531*3213bf4eSAndreas Gohr $this->debugMsg("Statement did not return 'mail' attribute", -1, __LINE__); 532f64dbc90SAndreas Gohr $dataok = false; 533f64dbc90SAndreas Gohr } 534f64dbc90SAndreas Gohr 535f64dbc90SAndreas Gohr if (!$dataok) return false; 536f64dbc90SAndreas Gohr return $data; 537f64dbc90SAndreas Gohr } 538f64dbc90SAndreas Gohr 539f64dbc90SAndreas Gohr /** 540e19be516SAndreas Gohr * Delete a user after removing all their group memberships 541e19be516SAndreas Gohr * 542e19be516SAndreas Gohr * @param string $user 543e19be516SAndreas Gohr * @return bool true when the user was deleted 544e19be516SAndreas Gohr */ 545*3213bf4eSAndreas Gohr protected function deleteUser($user) 546*3213bf4eSAndreas Gohr { 547e19be516SAndreas Gohr $this->pdo->beginTransaction(); 548e19be516SAndreas Gohr { 549e19be516SAndreas Gohr $userdata = $this->getUserData($user); 550e19be516SAndreas Gohr if ($userdata === false) goto FAIL; 551*3213bf4eSAndreas Gohr $allgroups = $this->selectGroups(); 552e19be516SAndreas Gohr 553e19be516SAndreas Gohr // remove group memberships (ignore errors) 554e19be516SAndreas Gohr foreach ($userdata['grps'] as $group) { 555358942b5SAndreas Gohr if (isset($allgroups[$group])) { 556*3213bf4eSAndreas Gohr $this->leaveGroup($userdata, $allgroups[$group]); 557e19be516SAndreas Gohr } 558358942b5SAndreas Gohr } 559e19be516SAndreas Gohr 560*3213bf4eSAndreas Gohr $ok = $this->query($this->getConf('delete-user'), $userdata); 561e19be516SAndreas Gohr if ($ok === false) goto FAIL; 562e19be516SAndreas Gohr } 563e19be516SAndreas Gohr $this->pdo->commit(); 564e19be516SAndreas Gohr return true; 565e19be516SAndreas Gohr 566e19be516SAndreas Gohr FAIL: 567e19be516SAndreas Gohr $this->pdo->rollBack(); 568e19be516SAndreas Gohr return false; 569e19be516SAndreas Gohr } 570e19be516SAndreas Gohr 571e19be516SAndreas Gohr /** 57270a89417SAndreas Gohr * Select all groups of a user 57370a89417SAndreas Gohr * 57470a89417SAndreas Gohr * @param array $userdata The userdata as returned by _selectUser() 5755de3a6a5SAndreas Gohr * @return array|bool list of group names, false on error 57670a89417SAndreas Gohr */ 577*3213bf4eSAndreas Gohr protected function selectUserGroups($userdata) 578*3213bf4eSAndreas Gohr { 57970a89417SAndreas Gohr global $conf; 58070a89417SAndreas Gohr $sql = $this->getConf('select-user-groups'); 581*3213bf4eSAndreas Gohr $result = $this->query($sql, $userdata); 5825de3a6a5SAndreas Gohr if ($result === false) return false; 58370a89417SAndreas Gohr 58470a89417SAndreas Gohr $groups = array($conf['defaultgroup']); // always add default config 5855de3a6a5SAndreas Gohr foreach ($result as $row) { 5865de3a6a5SAndreas Gohr if (!isset($row['group'])) { 587*3213bf4eSAndreas Gohr $this->debugMsg("No 'group' field returned in select-user-groups statement"); 5885de3a6a5SAndreas Gohr return false; 5895de3a6a5SAndreas Gohr } 59070a89417SAndreas Gohr $groups[] = $row['group']; 59170a89417SAndreas Gohr } 59270a89417SAndreas Gohr 59370a89417SAndreas Gohr $groups = array_unique($groups); 59470a89417SAndreas Gohr sort($groups); 59570a89417SAndreas Gohr return $groups; 59670a89417SAndreas Gohr } 59770a89417SAndreas Gohr 59870a89417SAndreas Gohr /** 5995de3a6a5SAndreas Gohr * Select all available groups 6005de3a6a5SAndreas Gohr * 6015de3a6a5SAndreas Gohr * @return array|bool list of all available groups and their properties 6025de3a6a5SAndreas Gohr */ 603*3213bf4eSAndreas Gohr protected function selectGroups() 604*3213bf4eSAndreas Gohr { 6050cec3e2aSAndreas Gohr if ($this->groupcache) return $this->groupcache; 6060cec3e2aSAndreas Gohr 6075de3a6a5SAndreas Gohr $sql = $this->getConf('select-groups'); 608*3213bf4eSAndreas Gohr $result = $this->query($sql); 6095de3a6a5SAndreas Gohr if ($result === false) return false; 6105de3a6a5SAndreas Gohr 6115de3a6a5SAndreas Gohr $groups = array(); 6125de3a6a5SAndreas Gohr foreach ($result as $row) { 6135de3a6a5SAndreas Gohr if (!isset($row['group'])) { 614*3213bf4eSAndreas Gohr $this->debugMsg("No 'group' field returned from select-groups statement", -1, __LINE__); 6155de3a6a5SAndreas Gohr return false; 6165de3a6a5SAndreas Gohr } 6175de3a6a5SAndreas Gohr 6185de3a6a5SAndreas Gohr // relayout result with group name as key 6195de3a6a5SAndreas Gohr $group = $row['group']; 6205de3a6a5SAndreas Gohr $groups[$group] = $row; 6215de3a6a5SAndreas Gohr } 6225de3a6a5SAndreas Gohr 6235de3a6a5SAndreas Gohr ksort($groups); 6245de3a6a5SAndreas Gohr return $groups; 6255de3a6a5SAndreas Gohr } 6265de3a6a5SAndreas Gohr 6275de3a6a5SAndreas Gohr /** 6280cec3e2aSAndreas Gohr * Remove all entries from the group cache 6290cec3e2aSAndreas Gohr */ 630*3213bf4eSAndreas Gohr protected function clearGroupCache() 631*3213bf4eSAndreas Gohr { 6320cec3e2aSAndreas Gohr $this->groupcache = null; 6330cec3e2aSAndreas Gohr } 6340cec3e2aSAndreas Gohr 6350cec3e2aSAndreas Gohr /** 6364fb8dfabSAndreas Gohr * Adds the user to the group 6375de3a6a5SAndreas Gohr * 6385de3a6a5SAndreas Gohr * @param array $userdata all the user data 6395de3a6a5SAndreas Gohr * @param array $groupdata all the group data 6405de3a6a5SAndreas Gohr * @return bool 6415de3a6a5SAndreas Gohr */ 642*3213bf4eSAndreas Gohr protected function joinGroup($userdata, $groupdata) 643*3213bf4eSAndreas Gohr { 6445de3a6a5SAndreas Gohr $data = array_merge($userdata, $groupdata); 6455de3a6a5SAndreas Gohr $sql = $this->getConf('join-group'); 646*3213bf4eSAndreas Gohr $result = $this->query($sql, $data); 6475de3a6a5SAndreas Gohr if ($result === false) return false; 6485de3a6a5SAndreas Gohr return true; 6495de3a6a5SAndreas Gohr } 6505de3a6a5SAndreas Gohr 6515de3a6a5SAndreas Gohr /** 6524fb8dfabSAndreas Gohr * Removes the user from the group 6534fb8dfabSAndreas Gohr * 6544fb8dfabSAndreas Gohr * @param array $userdata all the user data 6554fb8dfabSAndreas Gohr * @param array $groupdata all the group data 6564fb8dfabSAndreas Gohr * @return bool 6574fb8dfabSAndreas Gohr */ 658*3213bf4eSAndreas Gohr protected function leaveGroup($userdata, $groupdata) 659*3213bf4eSAndreas Gohr { 6604fb8dfabSAndreas Gohr $data = array_merge($userdata, $groupdata); 6614fb8dfabSAndreas Gohr $sql = $this->getConf('leave-group'); 662*3213bf4eSAndreas Gohr $result = $this->query($sql, $data); 6634fb8dfabSAndreas Gohr if ($result === false) return false; 6644fb8dfabSAndreas Gohr return true; 6654fb8dfabSAndreas Gohr } 6664fb8dfabSAndreas Gohr 6674fb8dfabSAndreas Gohr /** 66870a89417SAndreas Gohr * Executes a query 66970a89417SAndreas Gohr * 67070a89417SAndreas Gohr * @param string $sql The SQL statement to execute 67170a89417SAndreas Gohr * @param array $arguments Named parameters to be used in the statement 672f695c447SAndreas Gohr * @return array|int|bool The result as associative array for SELECTs, affected rows for others, false on error 67370a89417SAndreas Gohr */ 674*3213bf4eSAndreas Gohr protected function query($sql, $arguments = array()) 675*3213bf4eSAndreas Gohr { 676f695c447SAndreas Gohr $sql = trim($sql); 6775de3a6a5SAndreas Gohr if (empty($sql)) { 678*3213bf4eSAndreas Gohr $this->debugMsg('No SQL query given', -1, __LINE__); 6795de3a6a5SAndreas Gohr return false; 6805de3a6a5SAndreas Gohr } 6815de3a6a5SAndreas Gohr 68214119d44SAndreas Gohr // execute 68370a89417SAndreas Gohr $params = array(); 68414119d44SAndreas Gohr $sth = $this->pdo->prepare($sql); 68514119d44SAndreas Gohr try { 68614119d44SAndreas Gohr // prepare parameters - we only use those that exist in the SQL 68770a89417SAndreas Gohr foreach ($arguments as $key => $value) { 68870a89417SAndreas Gohr if (is_array($value)) continue; 68970a89417SAndreas Gohr if (is_object($value)) continue; 69070a89417SAndreas Gohr if ($key[0] != ':') $key = ":$key"; // prefix with colon if needed 69114119d44SAndreas Gohr if (strpos($sql, $key) === false) continue; // skip if parameter is missing 69214119d44SAndreas Gohr 69314119d44SAndreas Gohr if (is_int($value)) { 69414119d44SAndreas Gohr $sth->bindValue($key, $value, PDO::PARAM_INT); 69514119d44SAndreas Gohr } else { 69614119d44SAndreas Gohr $sth->bindValue($key, $value); 69714119d44SAndreas Gohr } 698f6cd8a7fSphjanderson $params[$key] = $value; //remember for debugging 69970a89417SAndreas Gohr } 70070a89417SAndreas Gohr 70114119d44SAndreas Gohr $sth->execute(); 702f695c447SAndreas Gohr if (strtolower(substr($sql, 0, 6)) == 'select') { 70370a89417SAndreas Gohr $result = $sth->fetchAll(); 704f695c447SAndreas Gohr } else { 705f695c447SAndreas Gohr $result = $sth->rowCount(); 706f695c447SAndreas Gohr } 7075de3a6a5SAndreas Gohr } catch (Exception $e) { 7084fb8dfabSAndreas Gohr // report the caller's line 7094fb8dfabSAndreas Gohr $trace = debug_backtrace(); 7104fb8dfabSAndreas Gohr $line = $trace[0]['line']; 711*3213bf4eSAndreas Gohr $dsql = $this->debugSQL($sql, $params, !defined('DOKU_UNITTEST')); 712*3213bf4eSAndreas Gohr $this->debugMsg($e, -1, $line); 713*3213bf4eSAndreas Gohr $this->debugMsg("SQL: <pre>$dsql</pre>", -1, $line); 71470a89417SAndreas Gohr $result = false; 7151600c7ccSAndreas Gohr } 71670a89417SAndreas Gohr $sth->closeCursor(); 71770a89417SAndreas Gohr $sth = null; 71870a89417SAndreas Gohr 7195de3a6a5SAndreas Gohr return $result; 7205de3a6a5SAndreas Gohr } 72170a89417SAndreas Gohr 72270a89417SAndreas Gohr /** 723f64dbc90SAndreas Gohr * Wrapper around msg() but outputs only when debug is enabled 724f64dbc90SAndreas Gohr * 725f64dbc90SAndreas Gohr * @param string|Exception $message 726f64dbc90SAndreas Gohr * @param int $err 727f64dbc90SAndreas Gohr * @param int $line 728f64dbc90SAndreas Gohr */ 729*3213bf4eSAndreas Gohr protected function debugMsg($message, $err = 0, $line = 0) 730*3213bf4eSAndreas Gohr { 731f64dbc90SAndreas Gohr if (!$this->getConf('debug')) return; 732f64dbc90SAndreas Gohr if (is_a($message, 'Exception')) { 733f64dbc90SAndreas Gohr $err = -1; 734f64dbc90SAndreas Gohr $msg = $message->getMessage(); 7354fb8dfabSAndreas Gohr if (!$line) $line = $message->getLine(); 736f64dbc90SAndreas Gohr } else { 737f64dbc90SAndreas Gohr $msg = $message; 738f64dbc90SAndreas Gohr } 739f64dbc90SAndreas Gohr 740f64dbc90SAndreas Gohr if (defined('DOKU_UNITTEST')) { 741f64dbc90SAndreas Gohr printf("\n%s, %s:%d\n", $msg, __FILE__, $line); 742f64dbc90SAndreas Gohr } else { 743f64dbc90SAndreas Gohr msg('authpdo: ' . $msg, $err, $line, __FILE__); 744f64dbc90SAndreas Gohr } 745f64dbc90SAndreas Gohr } 7465de3a6a5SAndreas Gohr 7475de3a6a5SAndreas Gohr /** 7485de3a6a5SAndreas Gohr * Check if the given config strings are set 7495de3a6a5SAndreas Gohr * 7505de3a6a5SAndreas Gohr * @author Matthias Grimm <matthiasgrimm@users.sourceforge.net> 7515de3a6a5SAndreas Gohr * 7525de3a6a5SAndreas Gohr * @param string[] $keys 7535de3a6a5SAndreas Gohr * @return bool 7545de3a6a5SAndreas Gohr */ 755*3213bf4eSAndreas Gohr protected function checkConfig($keys) 756*3213bf4eSAndreas Gohr { 7575de3a6a5SAndreas Gohr foreach ($keys as $key) { 758ad4d5631SAndreas Gohr $params = explode(':', $key); 759ad4d5631SAndreas Gohr $key = array_shift($params); 760ad4d5631SAndreas Gohr $sql = trim($this->getConf($key)); 761ad4d5631SAndreas Gohr 762ad4d5631SAndreas Gohr // check if sql is set 763ad4d5631SAndreas Gohr if (!$sql) return false; 764ad4d5631SAndreas Gohr // check if needed params are there 765ad4d5631SAndreas Gohr foreach ($params as $param) { 766ad4d5631SAndreas Gohr if (strpos($sql, ":$param") === false) return false; 767ad4d5631SAndreas Gohr } 7685de3a6a5SAndreas Gohr } 7695de3a6a5SAndreas Gohr 7705de3a6a5SAndreas Gohr return true; 7715de3a6a5SAndreas Gohr } 7725de3a6a5SAndreas Gohr 7735de3a6a5SAndreas Gohr /** 7745de3a6a5SAndreas Gohr * create an approximation of the SQL string with parameters replaced 7755de3a6a5SAndreas Gohr * 7765de3a6a5SAndreas Gohr * @param string $sql 7775de3a6a5SAndreas Gohr * @param array $params 7785de3a6a5SAndreas Gohr * @param bool $htmlescape Should the result be escaped for output in HTML? 7795de3a6a5SAndreas Gohr * @return string 7805de3a6a5SAndreas Gohr */ 781*3213bf4eSAndreas Gohr protected function debugSQL($sql, $params, $htmlescape = true) 782*3213bf4eSAndreas Gohr { 7835de3a6a5SAndreas Gohr foreach ($params as $key => $val) { 7845de3a6a5SAndreas Gohr if (is_int($val)) { 7855de3a6a5SAndreas Gohr $val = $this->pdo->quote($val, PDO::PARAM_INT); 7865de3a6a5SAndreas Gohr } elseif (is_bool($val)) { 7875de3a6a5SAndreas Gohr $val = $this->pdo->quote($val, PDO::PARAM_BOOL); 7885de3a6a5SAndreas Gohr } elseif (is_null($val)) { 7895de3a6a5SAndreas Gohr $val = 'NULL'; 7905de3a6a5SAndreas Gohr } else { 7915de3a6a5SAndreas Gohr $val = $this->pdo->quote($val); 7925de3a6a5SAndreas Gohr } 7935de3a6a5SAndreas Gohr $sql = str_replace($key, $val, $sql); 7945de3a6a5SAndreas Gohr } 7955de3a6a5SAndreas Gohr if ($htmlescape) $sql = hsc($sql); 7965de3a6a5SAndreas Gohr return $sql; 7975de3a6a5SAndreas Gohr } 798f64dbc90SAndreas Gohr} 799f64dbc90SAndreas Gohr 800f64dbc90SAndreas Gohr// vim:ts=4:sw=4:et: 801