]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiUser.php
Add PHPWIKI_VERSION in meta name generator
[SourceForge/phpwiki.git] / lib / WikiUser.php
1 <?php
2
3 // It is anticipated that when userid support is added to phpwiki,
4 // this object will hold much more information (e-mail,
5 // home(wiki)page, etc.) about the user.
6
7 // There seems to be no clean way to "log out" a user when using HTTP
8 // authentication. So we'll hack around this by storing the currently
9 // logged in username and other state information in a cookie.
10
11 // 2002-09-08 11:44:04 rurban
12 // Todo: Fix prefs cookie/session handling:
13 //       _userid and _homepage cookie/session vars still hold the
14 //       serialized string.
15 //       If no homepage, fallback to prefs in cookie as in 1.3.3.
16
17 define('WIKIAUTH_FORBIDDEN', -1); // Completely not allowed.
18 define('WIKIAUTH_ANON', 0);
19 define('WIKIAUTH_BOGO', 1); // any valid WikiWord is enough
20 define('WIKIAUTH_USER', 2); // real auth from a database/file/server.
21
22 define('WIKIAUTH_ADMIN', 10); // Wiki Admin
23 define('WIKIAUTH_UNOBTAINABLE', 100); // Permissions that no user can achieve
24
25 if (!defined('COOKIE_EXPIRATION_DAYS')) define('COOKIE_EXPIRATION_DAYS', 365);
26 if (!defined('COOKIE_DOMAIN')) define('COOKIE_DOMAIN', '/');
27
28 $UserPreferences = array(
29     'userid' => new _UserPreference(''), // really store this also?
30     'passwd' => new _UserPreference(''),
31     'email' => new _UserPreference(''),
32     'emailVerified' => new _UserPreference_bool(),
33     'notifyPages' => new _UserPreference(''),
34     'theme' => new _UserPreference_theme(THEME),
35     'lang' => new _UserPreference_language(DEFAULT_LANGUAGE),
36     'editWidth' => new _UserPreference_int(80, 30, 150),
37     'noLinkIcons' => new _UserPreference_bool(),
38     'editHeight' => new _UserPreference_int(22, 5, 80),
39     'timeOffset' => new _UserPreference_numeric(0, -26, 26),
40     'relativeDates' => new _UserPreference_bool(),
41     'googleLink' => new _UserPreference_bool(), // 1.3.10
42     'doubleClickEdit' => new _UserPreference_bool(), // 1.3.11
43 );
44
45 function WikiUserClassname()
46 {
47     return 'WikiUser';
48 }
49
50 function UpgradeUser($olduser, $user)
51 {
52     if (isa($user, 'WikiUser') and isa($olduser, 'WikiUser')) {
53         // populate the upgraded class with the values from the old object
54         foreach (get_object_vars($olduser) as $k => $v) {
55             $user->$k = $v;
56         }
57         $GLOBALS['request']->_user = $user;
58         return $user;
59     } else {
60         return false;
61     }
62 }
63
64 class WikiUser
65 {
66     public $_userid = false;
67     public $_level = false;
68     public $_request, $_dbi, $_authdbi, $_homepage;
69     public $_authmethod = '', $_authhow = '';
70
71     /**
72      * Populates the instance variables and calls $this->_ok()
73      * to ensure that the parameters are valid.
74      * @param Request $request
75      * @param mixed   $userid    String of username or WikiUser object.
76      * @param int|bool $authlevel Authorization level.
77      */
78     function __construct(&$request, $userid = false, $authlevel = false)
79     {
80         $this->_request =& $request;
81         $this->_dbi =& $this->_request->getDbh();
82
83         if (isa($userid, 'WikiUser')) {
84             $this->_userid = $userid->_userid;
85             $this->_level = $userid->_level;
86         } else {
87             $this->_userid = $userid;
88             $this->_level = $authlevel;
89         }
90         if (!$this->_ok()) {
91             // Paranoia: if state is at all inconsistent, log out...
92             $this->_userid = false;
93             $this->_level = false;
94             $this->_homepage = false;
95             $this->_authhow .= ' paranoia logout';
96         }
97         if ($this->_userid) {
98             $this->_homepage = $this->_dbi->getPage($this->_userid);
99         }
100     }
101
102     /**
103      * Get the string indicating how the user was authenticated.
104      *
105      * Get the string indicating how the user was authenticated.
106      * Does not seem to be set - jbw
107      * @return string The method of authentication.
108      */
109     function auth_how()
110     {
111         return $this->_authhow;
112     }
113
114     /**
115      * Invariant
116      *
117      * If the WikiUser object has a valid authorization level and the
118      * userid is a string returns true, else false.
119      * @return boolean If valid level and username string true, else false
120      */
121     function _ok()
122     {
123         if ((in_array($this->_level, array(WIKIAUTH_BOGO,
124             WIKIAUTH_USER,
125             WIKIAUTH_ADMIN))
126             &&
127             (is_string($this->_userid)))
128         ) {
129             return true;
130         }
131         return false;
132     }
133
134     function UserName()
135     {
136         return $this->_userid;
137     }
138
139     function getId()
140     {
141         if ($this->_userid)
142             return $this->_userid;
143         if (!empty($this->_request))
144             return $this->_request->get('REMOTE_ADDR');
145         if (empty($this->_request))
146             return Request::get('REMOTE_ADDR');
147         return ($this->isSignedIn()
148             ? $this->_userid
149             : $this->_request->get('REMOTE_ADDR')); // FIXME: globals
150     }
151
152     function getAuthenticatedId()
153     {
154         //assert($this->_request);
155         return ($this->isAuthenticated()
156             ? $this->_userid
157             : $this->_request->get('REMOTE_ADDR')); // FIXME: globals
158     }
159
160     function isSignedIn()
161     {
162         return $this->_level >= WIKIAUTH_BOGO;
163     }
164
165     function isAuthenticated()
166     {
167         return $this->_level >= WIKIAUTH_BOGO;
168     }
169
170     function isAdmin()
171     {
172         return $this->_level == WIKIAUTH_ADMIN;
173     }
174
175     function hasAuthority($require_level)
176     {
177         return $this->_level >= $require_level;
178     }
179
180     function isValidName($userid = false)
181     {
182         if (!$userid)
183             $userid = $this->_userid;
184         return preg_match("/^[\w\.@\-]+$/", $userid) and strlen($userid) < 32;
185     }
186
187     function AuthCheck($postargs)
188     {
189         // Normalize args, and extract.
190         $keys = array('userid', 'passwd', 'require_level', 'login', 'logout',
191             'cancel');
192         foreach ($keys as $key)
193             $args[$key] = isset($postargs[$key]) ? $postargs[$key] : false;
194         extract($args);
195         $require_level = max(0, min(WIKIAUTH_ADMIN, (int)$require_level));
196
197         if ($logout)
198             return new WikiUser($this->_request); // Log out
199         elseif ($cancel)
200             return false; // User hit cancel button.
201         elseif (!$login && !$userid)
202             return false; // Nothing to do?
203
204         if (!$this->isValidName($userid))
205             return _("Invalid username.");
206
207         $authlevel = $this->_pwcheck($userid, $passwd);
208         if (!$authlevel)
209             return _("Invalid password or userid.");
210         elseif ($authlevel < $require_level)
211             return _("Insufficient permissions.");
212
213         // Successful login.
214         $user = new WikiUser($this->_request, $userid, $authlevel);
215         return $user;
216     }
217
218     function PrintLoginForm(&$request, $args, $fail_message = false,
219                             $separate_page = true)
220     {
221         include_once 'lib/Template.php';
222         // Call update_locale in case the system's default language is not 'en'.
223         // (We have no user pref for lang at this point yet, no one is logged in.)
224         update_locale(DEFAULT_LANGUAGE);
225         $userid = '';
226         $require_level = 0;
227         extract($args); // fixme
228
229         $require_level = max(0, min(WIKIAUTH_ADMIN, (int)$require_level));
230
231         $pagename = $request->getArg('pagename');
232         $login = new Template('login', $request,
233             compact('pagename', 'userid', 'require_level',
234                 'fail_message', 'pass_required'));
235         if ($separate_page) {
236             $request->discardOutput();
237             $page = $request->getPage($pagename);
238             $revision = $page->getCurrentRevision();
239             return GeneratePage($login, _("Sign In"), $revision);
240         } else {
241             return $login;
242         }
243     }
244
245     /**
246      * Check password.
247      */
248     function _pwcheck($userid, $passwd)
249     {
250         global $WikiNameRegexp;
251
252         if (!empty($userid) && $userid == ADMIN_USER) {
253             // $this->_authmethod = 'pagedata';
254             if (defined('ENCRYPTED_PASSWD') && ENCRYPTED_PASSWD)
255                 if (!empty($passwd)
256                     && crypt($passwd, ADMIN_PASSWD) == ADMIN_PASSWD
257                 )
258                     return WIKIAUTH_ADMIN;
259                 else
260                     return false;
261             if (!empty($passwd)) {
262                 if ($passwd == ADMIN_PASSWD)
263                     return WIKIAUTH_ADMIN;
264                 else {
265                     // maybe we forgot to enable ENCRYPTED_PASSWD?
266                     if (function_exists('crypt')
267                         && crypt($passwd, ADMIN_PASSWD) == ADMIN_PASSWD
268                     ) {
269                         trigger_error(_("You forgot to set ENCRYPTED_PASSWD to true. Please update your config/config.ini"),
270                             E_USER_WARNING);
271                         return WIKIAUTH_ADMIN;
272                     }
273                 }
274             }
275             return false;
276         } // HTTP Authentication
277         elseif (ALLOW_HTTP_AUTH_LOGIN && !empty($PHP_AUTH_USER)) {
278             // if he ignored the password field, because he is already
279             // authenticated try the previously given password.
280             if (empty($passwd))
281                 $passwd = $PHP_AUTH_PW;
282         }
283
284         // WikiDB_User DB/File Authentication from $DBAuthParams
285         // Check if we have the user. If not try other methods.
286         if (ALLOW_USER_LOGIN) { // && !empty($passwd)) {
287             if (!$this->isValidName($userid)) {
288                 trigger_error(_("Invalid username."), E_USER_WARNING);
289                 return false;
290             }
291             $request = $this->_request;
292             // first check if the user is known
293             if ($this->exists($userid)) {
294                 $this->_authmethod = 'pagedata';
295                 return ($this->checkPassword($passwd)) ? WIKIAUTH_USER : false;
296             } else {
297                 // else try others such as LDAP authentication:
298                 if (ALLOW_LDAP_LOGIN && defined(LDAP_AUTH_HOST) && !empty($passwd) && !strstr($userid, '*')) {
299                     if ($ldap = ldap_connect(LDAP_AUTH_HOST)) { // must be a valid LDAP server!
300                         $r = @ldap_bind($ldap); // this is an anonymous bind
301                         $st_search = "uid=$userid";
302                         // Need to set the right root search information. see ../index.php
303                         $sr = ldap_search($ldap, LDAP_BASE_DN,
304                             "$st_search");
305                         $info = ldap_get_entries($ldap, $sr); // there may be more hits with this userid. try every
306                         for ($i = 0; $i < $info["count"]; $i++) {
307                             $dn = $info[$i]["dn"];
308                             // The password is still plain text.
309                             if ($r = @ldap_bind($ldap, $dn, $passwd)) {
310                                 // ldap_bind will return TRUE if everything matches
311                                 ldap_close($ldap);
312                                 $this->_authmethod = 'LDAP';
313                                 return WIKIAUTH_USER;
314                             }
315                         }
316                     } else {
317                         trigger_error("Unable to connect to LDAP server "
318                             . LDAP_AUTH_HOST, E_USER_WARNING);
319                     }
320                 }
321                 // imap authentication. added by limako
322                 if (ALLOW_IMAP_LOGIN && !empty($passwd)) {
323                     $mbox = @imap_open("{" . IMAP_AUTH_HOST . "}INBOX",
324                         $userid, $passwd, OP_HALFOPEN);
325                     if ($mbox) {
326                         imap_close($mbox);
327                         $this->_authmethod = 'IMAP';
328                         return WIKIAUTH_USER;
329                     }
330                 }
331             }
332         }
333         if (ALLOW_BOGO_LOGIN
334             && preg_match('/\A' . $WikiNameRegexp . '\z/', $userid)
335         ) {
336             $this->_authmethod = 'BOGO';
337             return WIKIAUTH_BOGO;
338         }
339         return false;
340     }
341
342     // Todo: try our WikiDB backends.
343     function getPreferences()
344     {
345         // Restore saved preferences.
346
347         // I'd rather prefer only to store the UserId in the cookie or
348         // session, and get the preferences from the db or page.
349         if (isset($this->_request)) {
350             if (!($prefs = $this->_request->getCookieVar('WIKI_PREFS2')))
351                 $prefs = $this->_request->getSessionVar('wiki_prefs');
352         }
353
354         //if (!$this->_userid && !empty($GLOBALS['HTTP_COOKIE_VARS']['WIKI_ID'])) {
355         //    $this->_userid = $GLOBALS['HTTP_COOKIE_VARS']['WIKI_ID'];
356         //}
357
358         // before we get his prefs we should check if he is signed in
359         if (USE_PREFS_IN_PAGE && $this->homePage()) { // in page metadata
360             // old array
361             if ($pref = $this->_homepage->get('pref')) {
362                 //trigger_error("pref=".$pref);//debugging
363                 $prefs = unserialize($pref);
364             }
365         }
366         return new UserPreferences($prefs);
367     }
368
369     // No cookies anymore for all prefs, only the userid. PHP creates
370     // a session cookie in memory, which is much more efficient,
371     // but not persistent. Get persistency with a homepage or DB Prefs
372     //
373     // Return the number of changed entries
374     function setPreferences($prefs, $id_only = false)
375     {
376         if (!is_object($prefs)) {
377             $prefs = new UserPreferences($prefs);
378         }
379         // update the session and id
380         $this->_request->setSessionVar('wiki_prefs', $prefs);
381         // $this->_request->setCookieVar('WIKI_PREFS2', $this->_prefs, 365);
382         // simple unpacked cookie
383         if ($this->_userid) setcookie(getCookieName(), $this->_userid, 365, '/');
384
385         // We must ensure that any password is encrypted.
386         // We don't need any plaintext password.
387         if (!$id_only) {
388             if ($this->isSignedIn()) {
389                 if ($this->isAdmin())
390                     $prefs->set('passwd', '');
391                 // already stored in config/config.ini, and it might be
392                 // plaintext! well oh well
393                 if ($homepage = $this->homePage()) {
394                     // check for page revision 0
395                     if (!$this->_dbi->isWikiPage($this->_userid)) {
396                         trigger_error(_("Your home page has not been created yet so your preferences cannot not be saved."),
397                             E_USER_WARNING);
398                     } else {
399                         if ($this->isAdmin() || !$homepage->get('locked')) {
400                             $homepage->set('pref', serialize($prefs->_prefs));
401                             return sizeof($prefs->_prefs);
402                         } else {
403                             // An "empty" page could still be
404                             // intentionally locked by admin to
405                             // prevent its creation.
406                             //
407                             // FIXME: This permission situation should
408                             // probably be handled by the DB backend,
409                             // once the new WikiUser code has been
410                             // implemented.
411                             trigger_error(_("Your home page is locked so your preferences cannot not be saved.")
412                                     . " " . _("Please contact your PhpWiki administrator for assistance."),
413                                 E_USER_WARNING);
414                         }
415                     }
416                 } else {
417                     trigger_error("No homepage for user found. Creating one...",
418                         E_USER_WARNING);
419                     $this->createHomepage($prefs);
420                     //$homepage->set('pref', serialize($prefs->_prefs));
421                     return sizeof($prefs->_prefs);
422                 }
423             } else {
424                 trigger_error("you must be signed in", E_USER_WARNING);
425             }
426         }
427         return 0;
428     }
429
430     // check for homepage with user flag.
431     // can be overriden from the auth backends
432     function exists()
433     {
434         $homepage = $this->homePage();
435         return ($this->_userid && $homepage && $homepage->get('pref'));
436     }
437
438     // doesn't check for existance!!! hmm.
439     // how to store metadata in not existing pages? how about versions?
440     function homePage()
441     {
442         if (!$this->_userid)
443             return false;
444         if (!empty($this->_homepage)) {
445             return $this->_homepage;
446         } else {
447             if (empty($this->_dbi)) {
448                 if (DEBUG) printSimpleTrace(debug_backtrace());
449             } else {
450                 $this->_homepage = $this->_dbi->getPage($this->_userid);
451             }
452             return $this->_homepage;
453         }
454     }
455
456     function hasHomePage()
457     {
458         return !$this->homePage();
459     }
460
461     // create user by checking his homepage
462     function createUser($pref, $createDefaultHomepage = true)
463     {
464         if ($this->exists())
465             return;
466         if ($createDefaultHomepage) {
467             $this->createHomepage($pref);
468         } else {
469             // empty page
470             include 'lib/loadsave.php';
471             $pageinfo = array('pagedata' => array('pref' => serialize($pref->_pref)),
472                 'versiondata' => array('author' => $this->_userid),
473                 'pagename' => $this->_userid,
474                 'content' => _('CategoryHomepage'));
475             SavePage($this->_request, $pageinfo, false, false);
476         }
477         $this->setPreferences($pref);
478     }
479
480     // create user and default user homepage
481     function createHomepage($pref)
482     {
483         $pagename = $this->_userid;
484         include 'lib/loadsave.php';
485
486         // create default homepage:
487         //  properly expanded template and the pref metadata
488         $template = Template('homepage.tmpl', $this->_request);
489         $text = $template->getExpansion();
490         $pageinfo = array('pagedata' => array('pref' => serialize($pref->_pref)),
491             'versiondata' => array('author' => $this->_userid),
492             'pagename' => $pagename,
493             'content' => $text);
494         SavePage($this->_request, $pageinfo, false, false);
495
496         // create Calendar
497         $pagename = $this->_userid . SUBPAGE_SEPARATOR . _('Calendar');
498         if (!isWikiPage($pagename)) {
499             $pageinfo = array('pagedata' => array(),
500                 'versiondata' => array('author' => $this->_userid),
501                 'pagename' => $pagename,
502                 'content' => "<?plugin Calendar ?>\n");
503             SavePage($this->_request, $pageinfo, false, false);
504         }
505
506         // create Preferences
507         $pagename = $this->_userid . SUBPAGE_SEPARATOR . _('Preferences');
508         if (!isWikiPage($pagename)) {
509             $pageinfo = array('pagedata' => array(),
510                 'versiondata' => array('author' => $this->_userid),
511                 'pagename' => $pagename,
512                 'content' => "<?plugin UserPreferences ?>\n");
513             SavePage($this->_request, $pageinfo, false, false);
514         }
515     }
516
517     function tryAuthBackends()
518     {
519         return ''; // crypt('') will never be ''
520     }
521
522     // Auth backends must store the crypted password where?
523     // Not in the preferences.
524     function checkPassword($passwd)
525     {
526         $prefs = $this->getPreferences();
527         $stored_passwd = $prefs->get('passwd'); // crypted
528         if (empty($prefs->_prefs['passwd'])) // not stored in the page
529             // allow empty passwords? At least store a '*' then.
530             // try other backend. hmm.
531             $stored_passwd = $this->tryAuthBackends($this->_userid);
532         if (empty($stored_passwd)) {
533             trigger_error(sprintf(_("Old UserPage %s without stored password updated with empty password. Set a password in your UserPreferences."),
534                 $this->_userid), E_USER_NOTICE);
535             $prefs->set('passwd', '*');
536             return true;
537         }
538         if ($stored_passwd == '*')
539             return true;
540         if (!empty($passwd)
541             && crypt($passwd, $stored_passwd) == $stored_passwd
542         )
543             return true;
544         else
545             return false;
546     }
547
548     function changePassword($newpasswd, $passwd2 = false)
549     {
550         if (!$this->mayChangePass()) {
551             trigger_error(sprintf("Attempt to change an external password for “%s”. Not allowed!",
552                 $this->_userid), E_USER_ERROR);
553             return false;
554         }
555         if ($passwd2 && $passwd2 != $newpasswd) {
556             trigger_error("The second password must be the same as the first to change it",
557                 E_USER_ERROR);
558             return false;
559         }
560         if (!$this->isAuthenticated()) return false;
561
562         $prefs = $this->getPreferences();
563         if (ENCRYPTED_PASSWD)
564             $prefs->set('passwd', crypt($newpasswd));
565         else
566             $prefs->set('passwd', $newpasswd);
567         $this->setPreferences($prefs);
568         return true;
569     }
570
571     function mayChangePass()
572     {
573         // on external DBAuth maybe. on IMAP or LDAP not
574         // on internal DBAuth yes
575         if (in_array($this->_authmethod, array('IMAP', 'LDAP')))
576             return false;
577         if ($this->isAdmin())
578             return false;
579         if ($this->_authmethod == 'pagedata')
580             return true;
581         if ($this->_authmethod == 'authdb')
582             return true;
583     }
584 }
585
586 // create user and default user homepage
587 // FIXME: delete this, not used?
588 /*
589 function createUser ($userid, $pref) {
590     global $request;
591     $user = new WikiUser ($request, $userid);
592     $user->createUser($pref);
593 }
594 */
595
596 class _UserPreference
597 {
598     function _UserPreference($default_value)
599     {
600         $this->default_value = $default_value;
601     }
602
603     function sanify($value)
604     {
605         return (string)$value;
606     }
607
608     function update($value)
609     {
610     }
611 }
612
613 class _UserPreference_numeric
614     extends _UserPreference
615 {
616     function __construct($default, $minval = false, $maxval = false)
617     {
618         parent::__construct((double)$default);
619         $this->_minval = (double)$minval;
620         $this->_maxval = (double)$maxval;
621     }
622
623     function sanify($value)
624     {
625         $value = (double)$value;
626         if ($this->_minval !== false && $value < $this->_minval)
627             $value = $this->_minval;
628         if ($this->_maxval !== false && $value > $this->_maxval)
629             $value = $this->_maxval;
630         return $value;
631     }
632 }
633
634 class _UserPreference_int
635     extends _UserPreference_numeric
636 {
637     function __construct($default, $minval = false, $maxval = false)
638     {
639         parent::__construct((int)$default, (int)$minval, (int)$maxval);
640     }
641
642     function sanify($value)
643     {
644         return (int)parent::sanify((int)$value);
645     }
646 }
647
648 class _UserPreference_bool
649     extends _UserPreference
650 {
651     function __construct($default = false)
652     {
653         parent::__construct((bool)$default);
654     }
655
656     function sanify($value)
657     {
658         if (is_array($value)) {
659             /* This allows for constructs like:
660              *
661              *   <input type="hidden" name="pref[boolPref][]" value="0" />
662              *   <input type="checkbox" name="pref[boolPref][]" value="1" />
663              *
664              * (If the checkbox is not checked, only the hidden input
665              * gets sent. If the checkbox is sent, both inputs get
666              * sent.)
667              */
668             foreach ($value as $val) {
669                 if ($val)
670                     return true;
671             }
672             return false;
673         }
674         return (bool)$value;
675     }
676 }
677
678 class _UserPreference_language
679     extends _UserPreference
680 {
681     function __construct($default = DEFAULT_LANGUAGE)
682     {
683         parent::__construct($default);
684     }
685
686     // FIXME: check for valid locale
687     function sanify($value)
688     {
689         // Revert to DEFAULT_LANGUAGE if user does not specify
690         // language in UserPreferences or chooses <system language>.
691         if ($value == '' or empty($value))
692             $value = DEFAULT_LANGUAGE;
693
694         return (string)$value;
695     }
696 }
697
698 class _UserPreference_theme
699     extends _UserPreference
700 {
701     function __construct($default = THEME)
702     {
703         parent::__construct($default);
704     }
705
706     function sanify($value)
707     {
708         if (findFile($this->_themefile($value), true))
709             return $value;
710         return $this->default_value;
711     }
712
713     function update($newvalue)
714     {
715         global $WikiTheme;
716         include_once($this->_themefile($newvalue));
717         if (empty($WikiTheme))
718             include_once($this->_themefile(THEME));
719     }
720
721     function _themefile($theme)
722     {
723         return "themes/$theme/themeinfo.php";
724     }
725 }
726
727 // don't save default preferences for efficiency.
728 class UserPreferences
729 {
730     function UserPreferences($saved_prefs = false)
731     {
732         $this->_prefs = array();
733
734         if (isa($saved_prefs, 'UserPreferences') && $saved_prefs->_prefs) {
735             foreach ($saved_prefs->_prefs as $name => $value)
736                 $this->set($name, $value);
737         } elseif (is_array($saved_prefs)) {
738             foreach ($saved_prefs as $name => $value)
739                 $this->set($name, $value);
740         }
741     }
742
743     function _getPref($name)
744     {
745         global $UserPreferences;
746         if (!isset($UserPreferences[$name])) {
747             if ($name == 'passwd2') return false;
748             trigger_error("$name: unknown preference", E_USER_NOTICE);
749             return false;
750         }
751         return $UserPreferences[$name];
752     }
753
754     function get($name)
755     {
756         if (isset($this->_prefs[$name]))
757             return $this->_prefs[$name];
758         if (!($pref = $this->_getPref($name)))
759             return false;
760         return $pref->default_value;
761     }
762
763     function set($name, $value)
764     {
765         if (!($pref = $this->_getPref($name)))
766             return false;
767
768         $newvalue = $pref->sanify($value);
769         $oldvalue = $this->get($name);
770
771         // update on changes
772         if ($newvalue != $oldvalue)
773             $pref->update($newvalue);
774
775         // don't set default values to save space (in cookies, db and
776         // sesssion)
777         if ($value == $pref->default_value)
778             unset($this->_prefs[$name]);
779         else
780             $this->_prefs[$name] = $newvalue;
781     }
782
783     function pack($nonpacked)
784     {
785         return serialize($nonpacked);
786     }
787
788     function unpack($packed)
789     {
790         if (!$packed)
791             return false;
792         if (substr($packed, 0, 2) == "O:") {
793             // Looks like a serialized object
794             return unserialize($packed);
795         }
796         //trigger_error("DEBUG: Can't unpack bad UserPreferences",
797         //E_USER_WARNING);
798         return false;
799     }
800
801     function hash()
802     {
803         return wikihash($this->_prefs);
804     }
805 }
806
807 // Local Variables:
808 // mode: php
809 // tab-width: 8
810 // c-basic-offset: 4
811 // c-hanging-comment-ender-p: nil
812 // indent-tabs-mode: nil
813 // End: