1 | <?php
|
---|
2 | /**
|
---|
3 | * Authentication library
|
---|
4 | *
|
---|
5 | * Including this file will automatically try to login
|
---|
6 | * a user by calling auth_login()
|
---|
7 | *
|
---|
8 | * @license GPL 2 (http://www.gnu.org/licenses/gpl.html)
|
---|
9 | * @author Andreas Gohr <[email protected]>
|
---|
10 | */
|
---|
11 |
|
---|
12 | if(!defined('DOKU_INC')) die('meh.');
|
---|
13 |
|
---|
14 | // some ACL level defines
|
---|
15 | define('AUTH_NONE',0);
|
---|
16 | define('AUTH_READ',1);
|
---|
17 | define('AUTH_EDIT',2);
|
---|
18 | define('AUTH_CREATE',4);
|
---|
19 | define('AUTH_UPLOAD',8);
|
---|
20 | define('AUTH_DELETE',16);
|
---|
21 | define('AUTH_ADMIN',255);
|
---|
22 |
|
---|
23 | /**
|
---|
24 | * Initialize the auth system.
|
---|
25 | *
|
---|
26 | * This function is automatically called at the end of init.php
|
---|
27 | *
|
---|
28 | * This used to be the main() of the auth.php
|
---|
29 | *
|
---|
30 | * @todo backend loading maybe should be handled by the class autoloader
|
---|
31 | * @todo maybe split into multiple functions at the XXX marked positions
|
---|
32 | */
|
---|
33 | function auth_setup(){
|
---|
34 | global $conf;
|
---|
35 | global $auth;
|
---|
36 | global $AUTH_ACL;
|
---|
37 | global $lang;
|
---|
38 | global $config_cascade;
|
---|
39 | $AUTH_ACL = array();
|
---|
40 |
|
---|
41 | if(!$conf['useacl']) return false;
|
---|
42 |
|
---|
43 | // load the the backend auth functions and instantiate the auth object XXX
|
---|
44 | if (@file_exists(DOKU_INC.'inc/auth/'.$conf['authtype'].'.class.php')) {
|
---|
45 | require_once(DOKU_INC.'inc/auth/basic.class.php');
|
---|
46 | require_once(DOKU_INC.'inc/auth/'.$conf['authtype'].'.class.php');
|
---|
47 |
|
---|
48 | $auth_class = "auth_".$conf['authtype'];
|
---|
49 | if (class_exists($auth_class)) {
|
---|
50 | $auth = new $auth_class();
|
---|
51 | if ($auth->success == false) {
|
---|
52 | // degrade to unauthenticated user
|
---|
53 | unset($auth);
|
---|
54 | auth_logoff();
|
---|
55 | msg($lang['authtempfail'], -1);
|
---|
56 | }
|
---|
57 | } else {
|
---|
58 | nice_die($lang['authmodfailed']);
|
---|
59 | }
|
---|
60 | } else {
|
---|
61 | nice_die($lang['authmodfailed']);
|
---|
62 | }
|
---|
63 |
|
---|
64 | if(!$auth) return;
|
---|
65 |
|
---|
66 | // do the login either by cookie or provided credentials XXX
|
---|
67 | if (!isset($_REQUEST['u'])) $_REQUEST['u'] = '';
|
---|
68 | if (!isset($_REQUEST['p'])) $_REQUEST['p'] = '';
|
---|
69 | if (!isset($_REQUEST['r'])) $_REQUEST['r'] = '';
|
---|
70 | $_REQUEST['http_credentials'] = false;
|
---|
71 | if (!$conf['rememberme']) $_REQUEST['r'] = false;
|
---|
72 |
|
---|
73 | // handle renamed HTTP_AUTHORIZATION variable (can happen when a fix like
|
---|
74 | // the one presented at
|
---|
75 | // http://www.besthostratings.com/articles/http-auth-php-cgi.html is used
|
---|
76 | // for enabling HTTP authentication with CGI/SuExec)
|
---|
77 | if(isset($_SERVER['REDIRECT_HTTP_AUTHORIZATION']))
|
---|
78 | $_SERVER['HTTP_AUTHORIZATION'] = $_SERVER['REDIRECT_HTTP_AUTHORIZATION'];
|
---|
79 | // streamline HTTP auth credentials (IIS/rewrite -> mod_php)
|
---|
80 | if(isset($_SERVER['HTTP_AUTHORIZATION'])){
|
---|
81 | list($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']) =
|
---|
82 | explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
|
---|
83 | }
|
---|
84 |
|
---|
85 | // if no credentials were given try to use HTTP auth (for SSO)
|
---|
86 | if(empty($_REQUEST['u']) && empty($_COOKIE[DOKU_COOKIE]) && !empty($_SERVER['PHP_AUTH_USER'])){
|
---|
87 | $_REQUEST['u'] = $_SERVER['PHP_AUTH_USER'];
|
---|
88 | $_REQUEST['p'] = $_SERVER['PHP_AUTH_PW'];
|
---|
89 | $_REQUEST['http_credentials'] = true;
|
---|
90 | }
|
---|
91 |
|
---|
92 | // apply cleaning
|
---|
93 | $_REQUEST['u'] = $auth->cleanUser($_REQUEST['u']);
|
---|
94 |
|
---|
95 | if(isset($_REQUEST['authtok'])){
|
---|
96 | // when an authentication token is given, trust the session
|
---|
97 | auth_validateToken($_REQUEST['authtok']);
|
---|
98 | }elseif(!is_null($auth) && $auth->canDo('external')){
|
---|
99 | // external trust mechanism in place
|
---|
100 | $auth->trustExternal($_REQUEST['u'],$_REQUEST['p'],$_REQUEST['r']);
|
---|
101 | }else{
|
---|
102 | $evdata = array(
|
---|
103 | 'user' => $_REQUEST['u'],
|
---|
104 | 'password' => $_REQUEST['p'],
|
---|
105 | 'sticky' => $_REQUEST['r'],
|
---|
106 | 'silent' => $_REQUEST['http_credentials'],
|
---|
107 | );
|
---|
108 | trigger_event('AUTH_LOGIN_CHECK', $evdata, 'auth_login_wrapper');
|
---|
109 | }
|
---|
110 |
|
---|
111 | //load ACL into a global array XXX
|
---|
112 | $AUTH_ACL = auth_loadACL();
|
---|
113 | }
|
---|
114 |
|
---|
115 | /**
|
---|
116 | * Loads the ACL setup and handle user wildcards
|
---|
117 | *
|
---|
118 | * @author Andreas Gohr <[email protected]>
|
---|
119 | * @returns array
|
---|
120 | */
|
---|
121 | function auth_loadACL(){
|
---|
122 | global $config_cascade;
|
---|
123 |
|
---|
124 | if(!is_readable($config_cascade['acl']['default'])) return array();
|
---|
125 |
|
---|
126 | $acl = file($config_cascade['acl']['default']);
|
---|
127 |
|
---|
128 | //support user wildcard
|
---|
129 | if(isset($_SERVER['REMOTE_USER'])){
|
---|
130 | $len = count($acl);
|
---|
131 | for($i=0; $i<$len; $i++){
|
---|
132 | if($acl[$i]{0} == '#') continue;
|
---|
133 | list($id,$rest) = preg_split('/\s+/',$acl[$i],2);
|
---|
134 | $id = str_replace('%USER%',cleanID($_SERVER['REMOTE_USER']),$id);
|
---|
135 | $rest = str_replace('%USER%',auth_nameencode($_SERVER['REMOTE_USER']),$rest);
|
---|
136 | $acl[$i] = "$id\t$rest";
|
---|
137 | }
|
---|
138 | }
|
---|
139 | return $acl;
|
---|
140 | }
|
---|
141 |
|
---|
142 | function auth_login_wrapper($evdata) {
|
---|
143 | return auth_login($evdata['user'],
|
---|
144 | $evdata['password'],
|
---|
145 | $evdata['sticky'],
|
---|
146 | $evdata['silent']);
|
---|
147 | }
|
---|
148 |
|
---|
149 | /**
|
---|
150 | * This tries to login the user based on the sent auth credentials
|
---|
151 | *
|
---|
152 | * The authentication works like this: if a username was given
|
---|
153 | * a new login is assumed and user/password are checked. If they
|
---|
154 | * are correct the password is encrypted with blowfish and stored
|
---|
155 | * together with the username in a cookie - the same info is stored
|
---|
156 | * in the session, too. Additonally a browserID is stored in the
|
---|
157 | * session.
|
---|
158 | *
|
---|
159 | * If no username was given the cookie is checked: if the username,
|
---|
160 | * crypted password and browserID match between session and cookie
|
---|
161 | * no further testing is done and the user is accepted
|
---|
162 | *
|
---|
163 | * If a cookie was found but no session info was availabe the
|
---|
164 | * blowfish encrypted password from the cookie is decrypted and
|
---|
165 | * together with username rechecked by calling this function again.
|
---|
166 | *
|
---|
167 | * On a successful login $_SERVER[REMOTE_USER] and $USERINFO
|
---|
168 | * are set.
|
---|
169 | *
|
---|
170 | * @author Andreas Gohr <[email protected]>
|
---|
171 | *
|
---|
172 | * @param string $user Username
|
---|
173 | * @param string $pass Cleartext Password
|
---|
174 | * @param bool $sticky Cookie should not expire
|
---|
175 | * @param bool $silent Don't show error on bad auth
|
---|
176 | * @return bool true on successful auth
|
---|
177 | */
|
---|
178 | function auth_login($user,$pass,$sticky=false,$silent=false){
|
---|
179 | global $USERINFO;
|
---|
180 | global $conf;
|
---|
181 | global $lang;
|
---|
182 | global $auth;
|
---|
183 | $sticky ? $sticky = true : $sticky = false; //sanity check
|
---|
184 |
|
---|
185 | if (!$auth) return false;
|
---|
186 |
|
---|
187 | if(!empty($user)){
|
---|
188 | //usual login
|
---|
189 | if ($auth->checkPass($user,$pass)){
|
---|
190 | // make logininfo globally available
|
---|
191 | $_SERVER['REMOTE_USER'] = $user;
|
---|
192 | $secret = auth_cookiesalt(!$sticky); //bind non-sticky to session
|
---|
193 | auth_setCookie($user,PMA_blowfish_encrypt($pass,$secret),$sticky);
|
---|
194 | return true;
|
---|
195 | }else{
|
---|
196 | //invalid credentials - log off
|
---|
197 | if(!$silent) msg($lang['badlogin'],-1);
|
---|
198 | auth_logoff();
|
---|
199 | return false;
|
---|
200 | }
|
---|
201 | }else{
|
---|
202 | // read cookie information
|
---|
203 | list($user,$sticky,$pass) = auth_getCookie();
|
---|
204 | if($user && $pass){
|
---|
205 | // we got a cookie - see if we can trust it
|
---|
206 |
|
---|
207 | // get session info
|
---|
208 | $session = $_SESSION[DOKU_COOKIE]['auth'];
|
---|
209 | if(isset($session) &&
|
---|
210 | $auth->useSessionCache($user) &&
|
---|
211 | ($session['time'] >= time()-$conf['auth_security_timeout']) &&
|
---|
212 | ($session['user'] == $user) &&
|
---|
213 | ($session['pass'] == sha1($pass)) && //still crypted
|
---|
214 | ($session['buid'] == auth_browseruid()) ){
|
---|
215 |
|
---|
216 | // he has session, cookie and browser right - let him in
|
---|
217 | $_SERVER['REMOTE_USER'] = $user;
|
---|
218 | $USERINFO = $session['info']; //FIXME move all references to session
|
---|
219 | return true;
|
---|
220 | }
|
---|
221 | // no we don't trust it yet - recheck pass but silent
|
---|
222 | $secret = auth_cookiesalt(!$sticky); //bind non-sticky to session
|
---|
223 | $pass = PMA_blowfish_decrypt($pass,$secret);
|
---|
224 | return auth_login($user,$pass,$sticky,true);
|
---|
225 | }
|
---|
226 | }
|
---|
227 | //just to be sure
|
---|
228 | auth_logoff(true);
|
---|
229 | return false;
|
---|
230 | }
|
---|
231 |
|
---|
232 | /**
|
---|
233 | * Checks if a given authentication token was stored in the session
|
---|
234 | *
|
---|
235 | * Will setup authentication data using data from the session if the
|
---|
236 | * token is correct. Will exit with a 401 Status if not.
|
---|
237 | *
|
---|
238 | * @author Andreas Gohr <[email protected]>
|
---|
239 | * @param string $token The authentication token
|
---|
240 | * @return boolean true (or will exit on failure)
|
---|
241 | */
|
---|
242 | function auth_validateToken($token){
|
---|
243 | if(!$token || $token != $_SESSION[DOKU_COOKIE]['auth']['token']){
|
---|
244 | // bad token
|
---|
245 | header("HTTP/1.0 401 Unauthorized");
|
---|
246 | print 'Invalid auth token - maybe the session timed out';
|
---|
247 | unset($_SESSION[DOKU_COOKIE]['auth']['token']); // no second chance
|
---|
248 | exit;
|
---|
249 | }
|
---|
250 | // still here? trust the session data
|
---|
251 | global $USERINFO;
|
---|
252 | $_SERVER['REMOTE_USER'] = $_SESSION[DOKU_COOKIE]['auth']['user'];
|
---|
253 | $USERINFO = $_SESSION[DOKU_COOKIE]['auth']['info'];
|
---|
254 | return true;
|
---|
255 | }
|
---|
256 |
|
---|
257 | /**
|
---|
258 | * Create an auth token and store it in the session
|
---|
259 | *
|
---|
260 | * NOTE: this is completely unrelated to the getSecurityToken() function
|
---|
261 | *
|
---|
262 | * @author Andreas Gohr <[email protected]>
|
---|
263 | * @return string The auth token
|
---|
264 | */
|
---|
265 | function auth_createToken(){
|
---|
266 | $token = md5(mt_rand());
|
---|
267 | @session_start(); // reopen the session if needed
|
---|
268 | $_SESSION[DOKU_COOKIE]['auth']['token'] = $token;
|
---|
269 | session_write_close();
|
---|
270 | return $token;
|
---|
271 | }
|
---|
272 |
|
---|
273 | /**
|
---|
274 | * Builds a pseudo UID from browser and IP data
|
---|
275 | *
|
---|
276 | * This is neither unique nor unfakable - still it adds some
|
---|
277 | * security. Using the first part of the IP makes sure
|
---|
278 | * proxy farms like AOLs are stil okay.
|
---|
279 | *
|
---|
280 | * @author Andreas Gohr <[email protected]>
|
---|
281 | *
|
---|
282 | * @return string a MD5 sum of various browser headers
|
---|
283 | */
|
---|
284 | function auth_browseruid(){
|
---|
285 | $ip = clientIP(true);
|
---|
286 | $uid = '';
|
---|
287 | $uid .= $_SERVER['HTTP_USER_AGENT'];
|
---|
288 | $uid .= $_SERVER['HTTP_ACCEPT_ENCODING'];
|
---|
289 | $uid .= $_SERVER['HTTP_ACCEPT_LANGUAGE'];
|
---|
290 | $uid .= $_SERVER['HTTP_ACCEPT_CHARSET'];
|
---|
291 | $uid .= substr($ip,0,strpos($ip,'.'));
|
---|
292 | return md5($uid);
|
---|
293 | }
|
---|
294 |
|
---|
295 | /**
|
---|
296 | * Creates a random key to encrypt the password in cookies
|
---|
297 | *
|
---|
298 | * This function tries to read the password for encrypting
|
---|
299 | * cookies from $conf['metadir'].'/_htcookiesalt'
|
---|
300 | * if no such file is found a random key is created and
|
---|
301 | * and stored in this file.
|
---|
302 | *
|
---|
303 | * @author Andreas Gohr <[email protected]>
|
---|
304 | * @param bool $addsession if true, the sessionid is added to the salt
|
---|
305 | * @return string
|
---|
306 | */
|
---|
307 | function auth_cookiesalt($addsession=false){
|
---|
308 | global $conf;
|
---|
309 | $file = $conf['metadir'].'/_htcookiesalt';
|
---|
310 | $salt = io_readFile($file);
|
---|
311 | if(empty($salt)){
|
---|
312 | $salt = uniqid(rand(),true);
|
---|
313 | io_saveFile($file,$salt);
|
---|
314 | }
|
---|
315 | if($addsession){
|
---|
316 | $salt .= session_id();
|
---|
317 | }
|
---|
318 | return $salt;
|
---|
319 | }
|
---|
320 |
|
---|
321 | /**
|
---|
322 | * Log out the current user
|
---|
323 | *
|
---|
324 | * This clears all authentication data and thus log the user
|
---|
325 | * off. It also clears session data.
|
---|
326 | *
|
---|
327 | * @author Andreas Gohr <[email protected]>
|
---|
328 | * @param bool $keepbc - when true, the breadcrumb data is not cleared
|
---|
329 | */
|
---|
330 | function auth_logoff($keepbc=false){
|
---|
331 | global $conf;
|
---|
332 | global $USERINFO;
|
---|
333 | global $INFO, $ID;
|
---|
334 | global $auth;
|
---|
335 |
|
---|
336 | // make sure the session is writable (it usually is)
|
---|
337 | @session_start();
|
---|
338 |
|
---|
339 | if(isset($_SESSION[DOKU_COOKIE]['auth']['user']))
|
---|
340 | unset($_SESSION[DOKU_COOKIE]['auth']['user']);
|
---|
341 | if(isset($_SESSION[DOKU_COOKIE]['auth']['pass']))
|
---|
342 | unset($_SESSION[DOKU_COOKIE]['auth']['pass']);
|
---|
343 | if(isset($_SESSION[DOKU_COOKIE]['auth']['info']))
|
---|
344 | unset($_SESSION[DOKU_COOKIE]['auth']['info']);
|
---|
345 | if(!$keepbc && isset($_SESSION[DOKU_COOKIE]['bc']))
|
---|
346 | unset($_SESSION[DOKU_COOKIE]['bc']);
|
---|
347 | if(isset($_SERVER['REMOTE_USER']))
|
---|
348 | unset($_SERVER['REMOTE_USER']);
|
---|
349 | $USERINFO=null; //FIXME
|
---|
350 |
|
---|
351 | if (version_compare(PHP_VERSION, '5.2.0', '>')) {
|
---|
352 | setcookie(DOKU_COOKIE,'',time()-600000,DOKU_REL,'',($conf['securecookie'] && is_ssl()),true);
|
---|
353 | }else{
|
---|
354 | setcookie(DOKU_COOKIE,'',time()-600000,DOKU_REL,'',($conf['securecookie'] && is_ssl()));
|
---|
355 | }
|
---|
356 |
|
---|
357 | if($auth) $auth->logOff();
|
---|
358 | }
|
---|
359 |
|
---|
360 | /**
|
---|
361 | * Check if a user is a manager
|
---|
362 | *
|
---|
363 | * Should usually be called without any parameters to check the current
|
---|
364 | * user.
|
---|
365 | *
|
---|
366 | * The info is available through $INFO['ismanager'], too
|
---|
367 | *
|
---|
368 | * @author Andreas Gohr <[email protected]>
|
---|
369 | * @see auth_isadmin
|
---|
370 | * @param string user - Username
|
---|
371 | * @param array groups - List of groups the user is in
|
---|
372 | * @param bool adminonly - when true checks if user is admin
|
---|
373 | */
|
---|
374 | function auth_ismanager($user=null,$groups=null,$adminonly=false){
|
---|
375 | global $conf;
|
---|
376 | global $USERINFO;
|
---|
377 | global $auth;
|
---|
378 |
|
---|
379 | if (!$auth) return false;
|
---|
380 | if(is_null($user)) {
|
---|
381 | if (!isset($_SERVER['REMOTE_USER'])) {
|
---|
382 | return false;
|
---|
383 | } else {
|
---|
384 | $user = $_SERVER['REMOTE_USER'];
|
---|
385 | }
|
---|
386 | }
|
---|
387 | if(is_null($groups)){
|
---|
388 | $groups = (array) $USERINFO['grps'];
|
---|
389 | }
|
---|
390 |
|
---|
391 | // check superuser match
|
---|
392 | if(auth_isMember($conf['superuser'],$user, $groups)) return true;
|
---|
393 | if($adminonly) return false;
|
---|
394 | // check managers
|
---|
395 | if(auth_isMember($conf['manager'],$user, $groups)) return true;
|
---|
396 |
|
---|
397 | return false;
|
---|
398 | }
|
---|
399 |
|
---|
400 | /**
|
---|
401 | * Check if a user is admin
|
---|
402 | *
|
---|
403 | * Alias to auth_ismanager with adminonly=true
|
---|
404 | *
|
---|
405 | * The info is available through $INFO['isadmin'], too
|
---|
406 | *
|
---|
407 | * @author Andreas Gohr <[email protected]>
|
---|
408 | * @see auth_ismanager
|
---|
409 | */
|
---|
410 | function auth_isadmin($user=null,$groups=null){
|
---|
411 | return auth_ismanager($user,$groups,true);
|
---|
412 | }
|
---|
413 |
|
---|
414 |
|
---|
415 | /**
|
---|
416 | * Match a user and his groups against a comma separated list of
|
---|
417 | * users and groups to determine membership status
|
---|
418 | *
|
---|
419 | * Note: all input should NOT be nameencoded.
|
---|
420 | *
|
---|
421 | * @param $memberlist string commaseparated list of allowed users and groups
|
---|
422 | * @param $user string user to match against
|
---|
423 | * @param $groups array groups the user is member of
|
---|
424 | * @returns bool true for membership acknowledged
|
---|
425 | */
|
---|
426 | function auth_isMember($memberlist,$user,array $groups){
|
---|
427 | global $auth;
|
---|
428 | if (!$auth) return false;
|
---|
429 |
|
---|
430 | // clean user and groups
|
---|
431 | if(!$auth->isCaseSensitive()){
|
---|
432 | $user = utf8_strtolower($user);
|
---|
433 | $groups = array_map('utf8_strtolower',$groups);
|
---|
434 | }
|
---|
435 | $user = $auth->cleanUser($user);
|
---|
436 | $groups = array_map(array($auth,'cleanGroup'),$groups);
|
---|
437 |
|
---|
438 | // extract the memberlist
|
---|
439 | $members = explode(',',$memberlist);
|
---|
440 | $members = array_map('trim',$members);
|
---|
441 | $members = array_unique($members);
|
---|
442 | $members = array_filter($members);
|
---|
443 |
|
---|
444 | // compare cleaned values
|
---|
445 | foreach($members as $member){
|
---|
446 | if(!$auth->isCaseSensitive()) $member = utf8_strtolower($member);
|
---|
447 | if($member[0] == '@'){
|
---|
448 | $member = $auth->cleanGroup(substr($member,1));
|
---|
449 | if(in_array($member, $groups)) return true;
|
---|
450 | }else{
|
---|
451 | $member = $auth->cleanUser($member);
|
---|
452 | if($member == $user) return true;
|
---|
453 | }
|
---|
454 | }
|
---|
455 |
|
---|
456 | // still here? not a member!
|
---|
457 | return false;
|
---|
458 | }
|
---|
459 |
|
---|
460 | /**
|
---|
461 | * Convinience function for auth_aclcheck()
|
---|
462 | *
|
---|
463 | * This checks the permissions for the current user
|
---|
464 | *
|
---|
465 | * @author Andreas Gohr <[email protected]>
|
---|
466 | *
|
---|
467 | * @param string $id page ID (needs to be resolved and cleaned)
|
---|
468 | * @return int permission level
|
---|
469 | */
|
---|
470 | function auth_quickaclcheck($id){
|
---|
471 | global $conf;
|
---|
472 | global $USERINFO;
|
---|
473 | # if no ACL is used always return upload rights
|
---|
474 | if(!$conf['useacl']) return AUTH_UPLOAD;
|
---|
475 | return auth_aclcheck($id,$_SERVER['REMOTE_USER'],$USERINFO['grps']);
|
---|
476 | }
|
---|
477 |
|
---|
478 | /**
|
---|
479 | * Returns the maximum rights a user has for
|
---|
480 | * the given ID or its namespace
|
---|
481 | *
|
---|
482 | * @author Andreas Gohr <[email protected]>
|
---|
483 | *
|
---|
484 | * @param string $id page ID (needs to be resolved and cleaned)
|
---|
485 | * @param string $user Username
|
---|
486 | * @param array $groups Array of groups the user is in
|
---|
487 | * @return int permission level
|
---|
488 | */
|
---|
489 | function auth_aclcheck($id,$user,$groups){
|
---|
490 | global $conf;
|
---|
491 | global $AUTH_ACL;
|
---|
492 | global $auth;
|
---|
493 |
|
---|
494 | // if no ACL is used always return upload rights
|
---|
495 | if(!$conf['useacl']) return AUTH_UPLOAD;
|
---|
496 | if (!$auth) return AUTH_NONE;
|
---|
497 |
|
---|
498 | //make sure groups is an array
|
---|
499 | if(!is_array($groups)) $groups = array();
|
---|
500 |
|
---|
501 | //if user is superuser or in superusergroup return 255 (acl_admin)
|
---|
502 | if(auth_isadmin($user,$groups)) { return AUTH_ADMIN; }
|
---|
503 |
|
---|
504 | $ci = '';
|
---|
505 | if(!$auth->isCaseSensitive()) $ci = 'ui';
|
---|
506 |
|
---|
507 | $user = $auth->cleanUser($user);
|
---|
508 | $groups = array_map(array($auth,'cleanGroup'),(array)$groups);
|
---|
509 | $user = auth_nameencode($user);
|
---|
510 |
|
---|
511 | //prepend groups with @ and nameencode
|
---|
512 | $cnt = count($groups);
|
---|
513 | for($i=0; $i<$cnt; $i++){
|
---|
514 | $groups[$i] = '@'.auth_nameencode($groups[$i]);
|
---|
515 | }
|
---|
516 |
|
---|
517 | $ns = getNS($id);
|
---|
518 | $perm = -1;
|
---|
519 |
|
---|
520 | if($user || count($groups)){
|
---|
521 | //add ALL group
|
---|
522 | $groups[] = '@ALL';
|
---|
523 | //add User
|
---|
524 | if($user) $groups[] = $user;
|
---|
525 | //build regexp
|
---|
526 | $regexp = join('|',$groups);
|
---|
527 | }else{
|
---|
528 | $regexp = '@ALL';
|
---|
529 | }
|
---|
530 |
|
---|
531 | //check exact match first
|
---|
532 | $matches = preg_grep('/^'.preg_quote($id,'/').'\s+('.$regexp.')\s+/'.$ci,$AUTH_ACL);
|
---|
533 | if(count($matches)){
|
---|
534 | foreach($matches as $match){
|
---|
535 | $match = preg_replace('/#.*$/','',$match); //ignore comments
|
---|
536 | $acl = preg_split('/\s+/',$match);
|
---|
537 | if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL!
|
---|
538 | if($acl[2] > $perm){
|
---|
539 | $perm = $acl[2];
|
---|
540 | }
|
---|
541 | }
|
---|
542 | if($perm > -1){
|
---|
543 | //we had a match - return it
|
---|
544 | return $perm;
|
---|
545 | }
|
---|
546 | }
|
---|
547 |
|
---|
548 | //still here? do the namespace checks
|
---|
549 | if($ns){
|
---|
550 | $path = $ns.':*';
|
---|
551 | }else{
|
---|
552 | $path = '*'; //root document
|
---|
553 | }
|
---|
554 |
|
---|
555 | do{
|
---|
556 | $matches = preg_grep('/^'.preg_quote($path,'/').'\s+('.$regexp.')\s+/'.$ci,$AUTH_ACL);
|
---|
557 | if(count($matches)){
|
---|
558 | foreach($matches as $match){
|
---|
559 | $match = preg_replace('/#.*$/','',$match); //ignore comments
|
---|
560 | $acl = preg_split('/\s+/',$match);
|
---|
561 | if($acl[2] > AUTH_DELETE) $acl[2] = AUTH_DELETE; //no admins in the ACL!
|
---|
562 | if($acl[2] > $perm){
|
---|
563 | $perm = $acl[2];
|
---|
564 | }
|
---|
565 | }
|
---|
566 | //we had a match - return it
|
---|
567 | return $perm;
|
---|
568 | }
|
---|
569 |
|
---|
570 | //get next higher namespace
|
---|
571 | $ns = getNS($ns);
|
---|
572 |
|
---|
573 | if($path != '*'){
|
---|
574 | $path = $ns.':*';
|
---|
575 | if($path == ':*') $path = '*';
|
---|
576 | }else{
|
---|
577 | //we did this already
|
---|
578 | //looks like there is something wrong with the ACL
|
---|
579 | //break here
|
---|
580 | msg('No ACL setup yet! Denying access to everyone.');
|
---|
581 | return AUTH_NONE;
|
---|
582 | }
|
---|
583 | }while(1); //this should never loop endless
|
---|
584 |
|
---|
585 | //still here? return no permissions
|
---|
586 | return AUTH_NONE;
|
---|
587 | }
|
---|
588 |
|
---|
589 | /**
|
---|
590 | * Encode ASCII special chars
|
---|
591 | *
|
---|
592 | * Some auth backends allow special chars in their user and groupnames
|
---|
593 | * The special chars are encoded with this function. Only ASCII chars
|
---|
594 | * are encoded UTF-8 multibyte are left as is (different from usual
|
---|
595 | * urlencoding!).
|
---|
596 | *
|
---|
597 | * Decoding can be done with rawurldecode
|
---|
598 | *
|
---|
599 | * @author Andreas Gohr <[email protected]>
|
---|
600 | * @see rawurldecode()
|
---|
601 | */
|
---|
602 | function auth_nameencode($name,$skip_group=false){
|
---|
603 | global $cache_authname;
|
---|
604 | $cache =& $cache_authname;
|
---|
605 | $name = (string) $name;
|
---|
606 |
|
---|
607 | // never encode wildcard FS#1955
|
---|
608 | if($name == '%USER%') return $name;
|
---|
609 |
|
---|
610 | if (!isset($cache[$name][$skip_group])) {
|
---|
611 | if($skip_group && $name{0} =='@'){
|
---|
612 | $cache[$name][$skip_group] = '@'.preg_replace('/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/e',
|
---|
613 | "'%'.dechex(ord(substr('\\1',-1)))",substr($name,1));
|
---|
614 | }else{
|
---|
615 | $cache[$name][$skip_group] = preg_replace('/([\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f])/e',
|
---|
616 | "'%'.dechex(ord(substr('\\1',-1)))",$name);
|
---|
617 | }
|
---|
618 | }
|
---|
619 |
|
---|
620 | return $cache[$name][$skip_group];
|
---|
621 | }
|
---|
622 |
|
---|
623 | /**
|
---|
624 | * Create a pronouncable password
|
---|
625 | *
|
---|
626 | * @author Andreas Gohr <[email protected]>
|
---|
627 | * @link http://www.phpbuilder.com/annotate/message.php3?id=1014451
|
---|
628 | *
|
---|
629 | * @return string pronouncable password
|
---|
630 | */
|
---|
631 | function auth_pwgen(){
|
---|
632 | $pw = '';
|
---|
633 | $c = 'bcdfghjklmnprstvwz'; //consonants except hard to speak ones
|
---|
634 | $v = 'aeiou'; //vowels
|
---|
635 | $a = $c.$v; //both
|
---|
636 |
|
---|
637 | //use two syllables...
|
---|
638 | for($i=0;$i < 2; $i++){
|
---|
639 | $pw .= $c[rand(0, strlen($c)-1)];
|
---|
640 | $pw .= $v[rand(0, strlen($v)-1)];
|
---|
641 | $pw .= $a[rand(0, strlen($a)-1)];
|
---|
642 | }
|
---|
643 | //... and add a nice number
|
---|
644 | $pw .= rand(10,99);
|
---|
645 |
|
---|
646 | return $pw;
|
---|
647 | }
|
---|
648 |
|
---|
649 | /**
|
---|
650 | * Sends a password to the given user
|
---|
651 | *
|
---|
652 | * @author Andreas Gohr <[email protected]>
|
---|
653 | *
|
---|
654 | * @return bool true on success
|
---|
655 | */
|
---|
656 | function auth_sendPassword($user,$password){
|
---|
657 | global $conf;
|
---|
658 | global $lang;
|
---|
659 | global $auth;
|
---|
660 | if (!$auth) return false;
|
---|
661 |
|
---|
662 | $hdrs = '';
|
---|
663 | $user = $auth->cleanUser($user);
|
---|
664 | $userinfo = $auth->getUserData($user);
|
---|
665 |
|
---|
666 | if(!$userinfo['mail']) return false;
|
---|
667 |
|
---|
668 | $text = rawLocale('password');
|
---|
669 | $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text);
|
---|
670 | $text = str_replace('@FULLNAME@',$userinfo['name'],$text);
|
---|
671 | $text = str_replace('@LOGIN@',$user,$text);
|
---|
672 | $text = str_replace('@PASSWORD@',$password,$text);
|
---|
673 | $text = str_replace('@TITLE@',$conf['title'],$text);
|
---|
674 |
|
---|
675 | return mail_send($userinfo['name'].' <'.$userinfo['mail'].'>',
|
---|
676 | $lang['regpwmail'],
|
---|
677 | $text,
|
---|
678 | $conf['mailfrom']);
|
---|
679 | }
|
---|
680 |
|
---|
681 | /**
|
---|
682 | * Register a new user
|
---|
683 | *
|
---|
684 | * This registers a new user - Data is read directly from $_POST
|
---|
685 | *
|
---|
686 | * @author Andreas Gohr <[email protected]>
|
---|
687 | *
|
---|
688 | * @return bool true on success, false on any error
|
---|
689 | */
|
---|
690 | function register(){
|
---|
691 | global $lang;
|
---|
692 | global $conf;
|
---|
693 | global $auth;
|
---|
694 |
|
---|
695 | if(!$_POST['save']) return false;
|
---|
696 | if(!actionOK('register')) return false;
|
---|
697 |
|
---|
698 | //clean username
|
---|
699 | $_POST['login'] = trim($auth->cleanUser($_POST['login']));
|
---|
700 |
|
---|
701 | //clean fullname and email
|
---|
702 | $_POST['fullname'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/','',$_POST['fullname']));
|
---|
703 | $_POST['email'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/','',$_POST['email']));
|
---|
704 |
|
---|
705 | if( empty($_POST['login']) ||
|
---|
706 | empty($_POST['fullname']) ||
|
---|
707 | empty($_POST['email']) ){
|
---|
708 | msg($lang['regmissing'],-1);
|
---|
709 | return false;
|
---|
710 | }
|
---|
711 |
|
---|
712 | if ($conf['autopasswd']) {
|
---|
713 | $pass = auth_pwgen(); // automatically generate password
|
---|
714 | } elseif (empty($_POST['pass']) ||
|
---|
715 | empty($_POST['passchk'])) {
|
---|
716 | msg($lang['regmissing'], -1); // complain about missing passwords
|
---|
717 | return false;
|
---|
718 | } elseif ($_POST['pass'] != $_POST['passchk']) {
|
---|
719 | msg($lang['regbadpass'], -1); // complain about misspelled passwords
|
---|
720 | return false;
|
---|
721 | } else {
|
---|
722 | $pass = $_POST['pass']; // accept checked and valid password
|
---|
723 | }
|
---|
724 |
|
---|
725 | //check mail
|
---|
726 | if(!mail_isvalid($_POST['email'])){
|
---|
727 | msg($lang['regbadmail'],-1);
|
---|
728 | return false;
|
---|
729 | }
|
---|
730 |
|
---|
731 | //okay try to create the user
|
---|
732 | if(!$auth->triggerUserMod('create', array($_POST['login'],$pass,$_POST['fullname'],$_POST['email']))){
|
---|
733 | msg($lang['reguexists'],-1);
|
---|
734 | return false;
|
---|
735 | }
|
---|
736 |
|
---|
737 | // create substitutions for use in notification email
|
---|
738 | $substitutions = array(
|
---|
739 | 'NEWUSER' => $_POST['login'],
|
---|
740 | 'NEWNAME' => $_POST['fullname'],
|
---|
741 | 'NEWEMAIL' => $_POST['email'],
|
---|
742 | );
|
---|
743 |
|
---|
744 | if (!$conf['autopasswd']) {
|
---|
745 | msg($lang['regsuccess2'],1);
|
---|
746 | notify('', 'register', '', $_POST['login'], false, $substitutions);
|
---|
747 | return true;
|
---|
748 | }
|
---|
749 |
|
---|
750 | // autogenerated password? then send him the password
|
---|
751 | if (auth_sendPassword($_POST['login'],$pass)){
|
---|
752 | msg($lang['regsuccess'],1);
|
---|
753 | notify('', 'register', '', $_POST['login'], false, $substitutions);
|
---|
754 | return true;
|
---|
755 | }else{
|
---|
756 | msg($lang['regmailfail'],-1);
|
---|
757 | return false;
|
---|
758 | }
|
---|
759 | }
|
---|
760 |
|
---|
761 | /**
|
---|
762 | * Update user profile
|
---|
763 | *
|
---|
764 | * @author Christopher Smith <[email protected]>
|
---|
765 | */
|
---|
766 | function updateprofile() {
|
---|
767 | global $conf;
|
---|
768 | global $INFO;
|
---|
769 | global $lang;
|
---|
770 | global $auth;
|
---|
771 |
|
---|
772 | if(empty($_POST['save'])) return false;
|
---|
773 | if(!checkSecurityToken()) return false;
|
---|
774 |
|
---|
775 | if(!actionOK('profile')) {
|
---|
776 | msg($lang['profna'],-1);
|
---|
777 | return false;
|
---|
778 | }
|
---|
779 |
|
---|
780 | if ($_POST['newpass'] != $_POST['passchk']) {
|
---|
781 | msg($lang['regbadpass'], -1); // complain about misspelled passwords
|
---|
782 | return false;
|
---|
783 | }
|
---|
784 |
|
---|
785 | //clean fullname and email
|
---|
786 | $_POST['fullname'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/','',$_POST['fullname']));
|
---|
787 | $_POST['email'] = trim(preg_replace('/[\x00-\x1f:<>&%,;]+/','',$_POST['email']));
|
---|
788 |
|
---|
789 | if ((empty($_POST['fullname']) && $auth->canDo('modName')) ||
|
---|
790 | (empty($_POST['email']) && $auth->canDo('modMail'))) {
|
---|
791 | msg($lang['profnoempty'],-1);
|
---|
792 | return false;
|
---|
793 | }
|
---|
794 |
|
---|
795 | if (!mail_isvalid($_POST['email']) && $auth->canDo('modMail')){
|
---|
796 | msg($lang['regbadmail'],-1);
|
---|
797 | return false;
|
---|
798 | }
|
---|
799 |
|
---|
800 | if ($_POST['fullname'] != $INFO['userinfo']['name'] && $auth->canDo('modName')) $changes['name'] = $_POST['fullname'];
|
---|
801 | if ($_POST['email'] != $INFO['userinfo']['mail'] && $auth->canDo('modMail')) $changes['mail'] = $_POST['email'];
|
---|
802 | if (!empty($_POST['newpass']) && $auth->canDo('modPass')) $changes['pass'] = $_POST['newpass'];
|
---|
803 |
|
---|
804 | if (!count($changes)) {
|
---|
805 | msg($lang['profnochange'], -1);
|
---|
806 | return false;
|
---|
807 | }
|
---|
808 |
|
---|
809 | if ($conf['profileconfirm']) {
|
---|
810 | if (!$auth->checkPass($_SERVER['REMOTE_USER'], $_POST['oldpass'])) {
|
---|
811 | msg($lang['badlogin'],-1);
|
---|
812 | return false;
|
---|
813 | }
|
---|
814 | }
|
---|
815 |
|
---|
816 | if ($result = $auth->triggerUserMod('modify', array($_SERVER['REMOTE_USER'], $changes))) {
|
---|
817 | // update cookie and session with the changed data
|
---|
818 | if ($changes['pass']){
|
---|
819 | list($user,$sticky,$pass) = auth_getCookie();
|
---|
820 | $pass = PMA_blowfish_encrypt($changes['pass'],auth_cookiesalt(!$sticky));
|
---|
821 | auth_setCookie($_SERVER['REMOTE_USER'],$pass,(bool)$sticky);
|
---|
822 | }
|
---|
823 | return true;
|
---|
824 | }
|
---|
825 | }
|
---|
826 |
|
---|
827 | /**
|
---|
828 | * Send a new password
|
---|
829 | *
|
---|
830 | * This function handles both phases of the password reset:
|
---|
831 | *
|
---|
832 | * - handling the first request of password reset
|
---|
833 | * - validating the password reset auth token
|
---|
834 | *
|
---|
835 | * @author Benoit Chesneau <[email protected]>
|
---|
836 | * @author Chris Smith <[email protected]>
|
---|
837 | * @author Andreas Gohr <[email protected]>
|
---|
838 | *
|
---|
839 | * @return bool true on success, false on any error
|
---|
840 | */
|
---|
841 | function act_resendpwd(){
|
---|
842 | global $lang;
|
---|
843 | global $conf;
|
---|
844 | global $auth;
|
---|
845 |
|
---|
846 | if(!actionOK('resendpwd')) {
|
---|
847 | msg($lang['resendna'],-1);
|
---|
848 | return false;
|
---|
849 | }
|
---|
850 |
|
---|
851 | $token = preg_replace('/[^a-f0-9]+/','',$_REQUEST['pwauth']);
|
---|
852 |
|
---|
853 | if($token){
|
---|
854 | // we're in token phase
|
---|
855 |
|
---|
856 | $tfile = $conf['cachedir'].'/'.$token{0}.'/'.$token.'.pwauth';
|
---|
857 | if(!@file_exists($tfile)){
|
---|
858 | msg($lang['resendpwdbadauth'],-1);
|
---|
859 | return false;
|
---|
860 | }
|
---|
861 | $user = io_readfile($tfile);
|
---|
862 | @unlink($tfile);
|
---|
863 | $userinfo = $auth->getUserData($user);
|
---|
864 | if(!$userinfo['mail']) {
|
---|
865 | msg($lang['resendpwdnouser'], -1);
|
---|
866 | return false;
|
---|
867 | }
|
---|
868 |
|
---|
869 | $pass = auth_pwgen();
|
---|
870 | if (!$auth->triggerUserMod('modify', array($user,array('pass' => $pass)))) {
|
---|
871 | msg('error modifying user data',-1);
|
---|
872 | return false;
|
---|
873 | }
|
---|
874 |
|
---|
875 | if (auth_sendPassword($user,$pass)) {
|
---|
876 | msg($lang['resendpwdsuccess'],1);
|
---|
877 | } else {
|
---|
878 | msg($lang['regmailfail'],-1);
|
---|
879 | }
|
---|
880 | return true;
|
---|
881 |
|
---|
882 | } else {
|
---|
883 | // we're in request phase
|
---|
884 |
|
---|
885 | if(!$_POST['save']) return false;
|
---|
886 |
|
---|
887 | if (empty($_POST['login'])) {
|
---|
888 | msg($lang['resendpwdmissing'], -1);
|
---|
889 | return false;
|
---|
890 | } else {
|
---|
891 | $user = trim($auth->cleanUser($_POST['login']));
|
---|
892 | }
|
---|
893 |
|
---|
894 | $userinfo = $auth->getUserData($user);
|
---|
895 | if(!$userinfo['mail']) {
|
---|
896 | msg($lang['resendpwdnouser'], -1);
|
---|
897 | return false;
|
---|
898 | }
|
---|
899 |
|
---|
900 | // generate auth token
|
---|
901 | $token = md5(auth_cookiesalt().$user); //secret but user based
|
---|
902 | $tfile = $conf['cachedir'].'/'.$token{0}.'/'.$token.'.pwauth';
|
---|
903 | $url = wl('',array('do'=>'resendpwd','pwauth'=>$token),true,'&');
|
---|
904 |
|
---|
905 | io_saveFile($tfile,$user);
|
---|
906 |
|
---|
907 | $text = rawLocale('pwconfirm');
|
---|
908 | $text = str_replace('@DOKUWIKIURL@',DOKU_URL,$text);
|
---|
909 | $text = str_replace('@FULLNAME@',$userinfo['name'],$text);
|
---|
910 | $text = str_replace('@LOGIN@',$user,$text);
|
---|
911 | $text = str_replace('@TITLE@',$conf['title'],$text);
|
---|
912 | $text = str_replace('@CONFIRM@',$url,$text);
|
---|
913 |
|
---|
914 | if(mail_send($userinfo['name'].' <'.$userinfo['mail'].'>',
|
---|
915 | $lang['regpwmail'],
|
---|
916 | $text,
|
---|
917 | $conf['mailfrom'])){
|
---|
918 | msg($lang['resendpwdconfirm'],1);
|
---|
919 | }else{
|
---|
920 | msg($lang['regmailfail'],-1);
|
---|
921 | }
|
---|
922 | return true;
|
---|
923 | }
|
---|
924 |
|
---|
925 | return false; // never reached
|
---|
926 | }
|
---|
927 |
|
---|
928 | /**
|
---|
929 | * Encrypts a password using the given method and salt
|
---|
930 | *
|
---|
931 | * If the selected method needs a salt and none was given, a random one
|
---|
932 | * is chosen.
|
---|
933 | *
|
---|
934 | * @author Andreas Gohr <[email protected]>
|
---|
935 | * @return string The crypted password
|
---|
936 | */
|
---|
937 | function auth_cryptPassword($clear,$method='',$salt=null){
|
---|
938 | global $conf;
|
---|
939 | if(empty($method)) $method = $conf['passcrypt'];
|
---|
940 |
|
---|
941 | $pass = new PassHash();
|
---|
942 | $call = 'hash_'.$method;
|
---|
943 |
|
---|
944 | if(!method_exists($pass,$call)){
|
---|
945 | msg("Unsupported crypt method $method",-1);
|
---|
946 | return false;
|
---|
947 | }
|
---|
948 |
|
---|
949 | return $pass->$call($clear,$salt);
|
---|
950 | }
|
---|
951 |
|
---|
952 | /**
|
---|
953 | * Verifies a cleartext password against a crypted hash
|
---|
954 | *
|
---|
955 | * @author Andreas Gohr <[email protected]>
|
---|
956 | * @return bool
|
---|
957 | */
|
---|
958 | function auth_verifyPassword($clear,$crypt){
|
---|
959 | $pass = new PassHash();
|
---|
960 | return $pass->verify_hash($clear,$crypt);
|
---|
961 | }
|
---|
962 |
|
---|
963 | /**
|
---|
964 | * Set the authentication cookie and add user identification data to the session
|
---|
965 | *
|
---|
966 | * @param string $user username
|
---|
967 | * @param string $pass encrypted password
|
---|
968 | * @param bool $sticky whether or not the cookie will last beyond the session
|
---|
969 | */
|
---|
970 | function auth_setCookie($user,$pass,$sticky) {
|
---|
971 | global $conf;
|
---|
972 | global $auth;
|
---|
973 | global $USERINFO;
|
---|
974 |
|
---|
975 | if (!$auth) return false;
|
---|
976 | $USERINFO = $auth->getUserData($user);
|
---|
977 |
|
---|
978 | // set cookie
|
---|
979 | $cookie = base64_encode($user).'|'.((int) $sticky).'|'.base64_encode($pass);
|
---|
980 | $time = $sticky ? (time()+60*60*24*365) : 0; //one year
|
---|
981 | if (version_compare(PHP_VERSION, '5.2.0', '>')) {
|
---|
982 | setcookie(DOKU_COOKIE,$cookie,$time,DOKU_REL,'',($conf['securecookie'] && is_ssl()),true);
|
---|
983 | }else{
|
---|
984 | setcookie(DOKU_COOKIE,$cookie,$time,DOKU_REL,'',($conf['securecookie'] && is_ssl()));
|
---|
985 | }
|
---|
986 | // set session
|
---|
987 | $_SESSION[DOKU_COOKIE]['auth']['user'] = $user;
|
---|
988 | $_SESSION[DOKU_COOKIE]['auth']['pass'] = sha1($pass);
|
---|
989 | $_SESSION[DOKU_COOKIE]['auth']['buid'] = auth_browseruid();
|
---|
990 | $_SESSION[DOKU_COOKIE]['auth']['info'] = $USERINFO;
|
---|
991 | $_SESSION[DOKU_COOKIE]['auth']['time'] = time();
|
---|
992 | }
|
---|
993 |
|
---|
994 | /**
|
---|
995 | * Returns the user, (encrypted) password and sticky bit from cookie
|
---|
996 | *
|
---|
997 | * @returns array
|
---|
998 | */
|
---|
999 | function auth_getCookie(){
|
---|
1000 | if (!isset($_COOKIE[DOKU_COOKIE])) {
|
---|
1001 | return array(null, null, null);
|
---|
1002 | }
|
---|
1003 | list($user,$sticky,$pass) = explode('|',$_COOKIE[DOKU_COOKIE],3);
|
---|
1004 | $sticky = (bool) $sticky;
|
---|
1005 | $pass = base64_decode($pass);
|
---|
1006 | $user = base64_decode($user);
|
---|
1007 | return array($user,$sticky,$pass);
|
---|
1008 | }
|
---|
1009 |
|
---|
1010 | //Setup VIM: ex: et ts=2 :
|
---|