]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiGroup.php
Another catch by Charles Corrigan: check against the dbi backend, not the WikiDB...
[SourceForge/phpwiki.git] / lib / WikiGroup.php
1 <?php
2 rcs_id('$Id: WikiGroup.php,v 1.47 2004-11-19 13:23:47 rurban Exp $');
3 /*
4  Copyright (C) 2003, 2004 $ThePhpWikiProgrammingTeam
5
6  This file is part of PhpWiki.
7
8  PhpWiki is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12
13  PhpWiki is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  GNU General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with PhpWiki; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 if (!defined('GROUP_METHOD') or 
24     !in_array(GROUP_METHOD,
25               array('NONE','WIKIPAGE','DB','FILE','LDAP')))
26     trigger_error(_("No or unsupported GROUP_METHOD defined"), E_USER_WARNING);
27     
28 /* Special group names for ACL */    
29 define('GROUP_EVERY',           _("Every"));
30 define('GROUP_ANONYMOUS',       _("Anonymous Users"));
31 define('GROUP_BOGOUSER',        _("Bogo Users"));
32 define('GROUP_HASHOMEPAGE',     _("HasHomePage"));
33 define('GROUP_SIGNED',          _("Signed Users"));
34 define('GROUP_AUTHENTICATED',   _("Authenticated Users"));
35 define('GROUP_ADMIN',           _("Administrators"));
36 define('GROUP_OWNER',           _("Owner"));
37 define('GROUP_CREATOR',         _("Creator"));
38
39 /**
40  * WikiGroup is an abstract class to provide the base functions for determining
41  * group membership for a specific user. Some functions are user independent.
42  *
43  * Limitation: For the current user only. This must be fixed to be able to query 
44  * for membership of any user.
45  * 
46  * WikiGroup is an abstract class with three functions:
47  * <ol><li />Provide the static method getGroup with will return the proper
48  *         subclass.
49  *     <li />Provide an interface for subclasses to implement.
50  *     <li />Provide fallover methods (with error msgs) if not impemented in subclass.
51  * </ol>
52  * Do not ever instantiate this class. Use: $group = &WikiGroup::getGroup();
53  * This will instantiate the proper subclass.
54  *
55  * @author Joby Walker <zorloc@imperium.org>
56  * @author Reini Urban
57  */ 
58 class WikiGroup{
59     /** User name */
60     var $username = '';
61     /** User object if different from current user */
62     var $user;
63     /** The global WikiRequest object */
64     //var $request;
65     /** Array of groups $username is confirmed to belong to */
66     var $membership;
67     /** boolean if not the current user */
68     var $not_current = false;
69     
70     /**
71      * Initializes a WikiGroup object which should never happen.  Use:
72      * $group = &WikiGroup::getGroup();
73      * @param object $request The global WikiRequest object -- ignored.
74      */ 
75     function WikiGroup($not_current = false) {
76         $this->not_current = $not_current;
77         //$this->request =& $GLOBALS['request'];
78     }
79
80     /**
81      * Gets the current username from the internal user object
82      * and erases $this->membership if is different than
83      * the stored $this->username
84      * @return string Current username.
85      */ 
86     function _getUserName(){
87         global $request;
88         $user = (!empty($this->user)) ? $this->user : $request->getUser();
89         $username = $user->getID();
90         if ($username != $this->username) {
91             $this->membership = array();
92             $this->username = $username;
93         }
94         if (!$this->not_current)
95            $this->user = $user;
96         return $username;
97     }
98     
99     /**
100      * Static method to return the WikiGroup subclass used in this wiki.  Controlled
101      * by the constant GROUP_METHOD.
102      * @param object $request The global WikiRequest object.
103      * @return object Subclass of WikiGroup selected via GROUP_METHOD.
104      */ 
105     function getGroup($not_current = false){
106         switch (GROUP_METHOD){
107             case "NONE": 
108                 return new GroupNone($not_current);
109                 break;
110             case "WIKIPAGE":
111                 return new GroupWikiPage($not_current);
112                 break;
113             case "DB":
114                 if ($GLOBALS['DBParams']['dbtype'] == 'ADODB') {
115                     return new GroupDB_ADODB($not_current);
116                 } elseif ($GLOBALS['DBParams']['dbtype'] == 'SQL') {
117                     return new GroupDb_PearDB($not_current);
118                 } else {
119                     trigger_error("GROUP_METHOD = DB: Unsupported dbtype " 
120                                   . $GLOBALS['DBParams']['dbtype'],
121                                   E_USER_ERROR);
122                 }
123                 break;
124             case "FILE": 
125                 return new GroupFile($not_current);
126                 break;
127             case "LDAP": 
128                 return new GroupLDAP($not_current);
129                 break;
130             default:
131                 trigger_error(_("No or unsupported GROUP_METHOD defined"), E_USER_WARNING);
132                 return new WikiGroup($not_current);
133         }
134     }
135
136     /** ACL PagePermissions will need those special groups based on the User status only.
137      *  translated 
138      */
139     function specialGroup($group){
140         return in_array($group,$this->specialGroups());
141     }
142     /** untranslated */
143     function _specialGroup($group){
144         return in_array($group,$this->_specialGroups());
145     }
146     /** translated */
147     function specialGroups(){
148         return array(
149                      GROUP_EVERY,
150                      GROUP_ANONYMOUS,
151                      GROUP_BOGOUSER,
152                      GROUP_SIGNED,
153                      GROUP_AUTHENTICATED,
154                      GROUP_ADMIN);
155     }
156     /** untranslated */
157     function _specialGroups(){
158         return array(
159                      "_EVERY",
160                      "_ANONYMOUS",
161                      "_BOGOUSER",
162                      "_SIGNED",
163                      "_AUTHENTICATED",
164                      "_ADMIN");
165     }
166
167     /**
168      * Determines if the current user is a member of a group.
169      * 
170      * This method is an abstraction.  The group is ignored, an error is sent, and
171      * false (not a member of the group) is returned.
172      * @param string $group Name of the group to check for membership (ignored).
173      * @return boolean True if user is a member, else false (always false).
174      */ 
175     function isMember($group){
176         if (isset($this->membership[$group]))
177             return $this->membership[$group];
178         if ($this->specialGroup($group)) {
179             return $this->isSpecialMember($group);
180         } else {
181             trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
182                                     'isMember', GROUP_METHOD),
183                           E_USER_WARNING);
184         }
185         return false;
186     }
187
188     function isSpecialMember($group){
189         global $request;
190
191         if (isset($this->membership[$group]))
192             return $this->membership[$group];
193         $user = (!empty($this->user)) ? $this->user : $request->getUser();
194         switch ($group) {
195             case GROUP_EVERY:           
196                 return $this->membership[$group] = true;
197             case GROUP_ANONYMOUS:       
198                 return $this->membership[$group] = ! $user->isSignedIn();
199             case GROUP_BOGOUSER:        
200                 return $this->membership[$group] = (isa($user,'_BogoUser') 
201                                                     and $user->_level >= WIKIAUTH_BOGO);
202             case GROUP_SIGNED:          
203                 return $this->membership[$group] = $user->isSignedIn();
204             case GROUP_AUTHENTICATED:   
205                 return $this->membership[$group] = $user->isAuthenticated();
206             case GROUP_ADMIN:           
207                 return $this->membership[$group] = (isset($user->_level) 
208                                                     and $user->_level == WIKIAUTH_ADMIN);
209             default:
210                 trigger_error(__sprintf("Undefined method %s for special group %s",
211                                         'isMember',$group),
212                               E_USER_WARNING);
213         }
214         return false;
215     }
216     
217     /**
218      * Determines all of the groups of which the current user is a member.
219      * 
220      * This method is an abstraction.  An error is sent and an empty 
221      * array is returned.
222      * @return array Array of groups to which the user belongs (always empty).
223      */ 
224     function getAllGroupsIn(){
225         trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
226                                 'getAllGroupsIn', GROUP_METHOD),
227                       E_USER_WARNING);
228         return array();
229     }
230
231     function _allUsers() {
232         static $result = array();
233         if (!empty($result))
234                 return $result;
235
236         global $request;
237         /* WikiPage users: */
238         $dbh =& $request->_dbi;
239         $page_iter = $dbh->getAllPages();
240         $users = array();
241         while ($page = $page_iter->next()) {
242             if ($page->isUserPage())
243                 $users[] = $page->_pagename;
244         }
245
246         /* WikiDB users from prefs (not from users): */
247         if (ENABLE_USER_NEW)
248             $dbi = _PassUser::getAuthDbh();
249         else 
250             $dbi = false;
251
252         if ($dbi and $dbh->getAuthParam('pref_select')) {
253             //get prefs table
254             $sql = preg_replace('/SELECT .+ FROM/i','SELECT userid FROM',
255                                 $dbh->getAuthParam('pref_select'));
256             //don't strip WHERE, only the userid stuff.
257             $sql = preg_replace('/(WHERE.*?)\s+\w+\s*=\s*["\']\$userid[\'"]/i','\\1 AND 1', $sql);
258             $sql = str_replace('WHERE AND 1','',$sql);
259             if (isa($dbi, 'ADOConnection')) {
260                 $db_result = $dbi->Execute($sql);
261                 foreach ($db_result->GetArray() as $u) {
262                     $users = array_merge($users,array_values($u));
263                 }
264             } elseif (isa($dbi, 'DB_common')) { // PearDB
265                 $users = array_merge($users, $dbi->getCol($sql));
266             }
267         }
268
269         /* WikiDB users from users: */
270         // Fixme: don't strip WHERE, only the userid stuff.
271         if ($dbi and $dbh->getAuthParam('auth_user_exists')) {
272             //don't strip WHERE, only the userid stuff.
273             $sql = preg_replace('/(WHERE.*?)\s+\w+\s*=\s*["\']\$userid[\'"]/i','\\1 AND 1',
274                                 $dbh->getAuthParam('auth_user_exists'));
275             $sql = str_replace('WHERE AND 1','', $sql);
276             if (isa($dbi, 'ADOConnection')) {
277                 $db_result = $dbi->Execute($sql);
278                 foreach ($db_result->GetArray() as $u) {
279                    $users = array_merge($users, array_values($u));
280                 }
281             } elseif (isa($dbi, 'DB_common')) {
282                 $users = array_merge($users, $dbi->getCol($sql));
283             }
284         }
285
286         // remove empty and duplicate users
287         $result = array();
288         foreach ($users as $u) {
289             if (empty($u) or in_array($u,$result)) continue;
290             $result[] = $u;
291         }
292         return $result;
293     }
294
295     /**
296      * Determines all of the members of a particular group.
297      * 
298      * This method is an abstraction.  The group is ignored, an error is sent, 
299      * and an empty array is returned
300      * @param string $group Name of the group to get the full membership list of.
301      * @return array Array of usernames that have joined the group (always empty).
302      */ 
303     function getMembersOf($group){
304         if ($this->specialGroup($group)) {
305             return getSpecialMembersOf($group);
306         }
307         trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
308                                 'getMembersOf', GROUP_METHOD),
309                       E_USER_WARNING);
310         return array();
311     }
312                 
313     function getSpecialMembersOf($group) {
314         //$request = &$this->request;
315         $all = $this->_allUsers();
316         $users = array();
317         switch ($group) {
318         case GROUP_EVERY:
319             return $all;
320         case GROUP_ANONYMOUS:   
321             return $users;
322         case GROUP_BOGOUSER:
323             foreach ($all as $u) {
324                 if (isWikiWord($user)) $users[] = $u;
325             }
326             return $users;
327         case GROUP_SIGNED:      
328             foreach ($all as $u) {
329                 $user = WikiUser($u);
330                 if ($user->isSignedIn()) $users[] = $u;
331             }
332             return $users;
333         case GROUP_AUTHENTICATED:
334             foreach ($all as $u) {
335                 $user = WikiUser($u);
336                 if ($user->isAuthenticated()) $users[] = $u;
337             }
338             return $users;
339         case GROUP_ADMIN:               
340             foreach ($all as $u) {
341                 $user = WikiUser($u);
342                 if (isset($user->_level) and $user->_level == WIKIAUTH_ADMIN) 
343                     $users[] = $u;
344             }
345             return $users;
346         default:
347             trigger_error(__sprintf("Unknown special group '%s'", $group),
348                           E_USER_WARNING);
349         }
350     }
351
352     /**
353      * Add the current or specified user to a group.
354      * 
355      * This method is an abstraction.  The group and user are ignored, an error 
356      * is sent, and false (not added) is always returned.
357      * @param string $group User added to this group.
358      * @param string $user Username to add to the group (default = current user).
359      * @return bool On true user was added, false if not.
360      */ 
361     function setMemberOf($group, $user = false){
362         trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
363                                 'setMemberOf', GROUP_METHOD),
364                       E_USER_WARNING);
365         return false;
366     }
367     
368     /**
369      * Remove the current or specified user to a group.
370      * 
371      * This method is an abstraction.  The group and user are ignored, and error
372      * is sent, and false (not removed) is always returned.
373      * @param string $group User removed from this group.
374      * @param string $user Username to remove from the group (default = current user).
375      * @return bool On true user was removed, false if not.
376      */ 
377     function removeMemberOf($group, $user = false){
378         trigger_error(__sprintf("Method '%s' not implemented in this GROUP_METHOD %s",
379                                 'removeMemberOf', GROUP_METHOD),
380                       E_USER_WARNING);
381         return false;
382     }
383 }
384
385 /**
386  * GroupNone disables all Group funtionality
387  * 
388  * All of the GroupNone functions return false or empty values to indicate failure or 
389  * no results.  Use GroupNone if group controls are not desired.
390  * @author Joby Walker <zorloc@imperium.org>
391  */ 
392 class GroupNone extends WikiGroup{
393
394     /**
395      * Constructor
396      * 
397      * Ignores the parameter provided.
398      * @param object $request The global WikiRequest object - ignored.
399      */ 
400     function GroupNone() {
401         //$this->request = &$GLOBALS['request'];
402         return;
403     }    
404
405     /**
406      * Determines if the current user is a member of a group.
407      * 
408      * The group is ignored and false (not a member of the group) is returned.
409      * @param string $group Name of the group to check for membership (ignored).
410      * @return boolean True if user is a member, else false (always false).
411      */ 
412     function isMember($group){
413         if ($this->specialGroup($group)) {
414             return $this->isSpecialMember($group);
415         } else {
416             return false;
417         }
418     }
419     
420     /**
421      * Determines all of the groups of which the current user is a member.
422      * 
423      * The group is ignored and an empty array (a member of no groups) is returned.
424      * @param string $group Name of the group to check for membership (ignored).
425      * @return array Array of groups to which the user belongs (always empty).
426      */ 
427     function getAllGroupsIn(){
428         return array();
429     }
430
431     /**
432      * Determines all of the members of a particular group.
433      * 
434      * The group is ignored and an empty array (a member of no groups) is returned.
435      * @param string $group Name of the group to check for membership (ignored).
436      * @return array Array of groups user belongs to (always empty).
437      */ 
438     function getMembersOf($group){
439         return array();
440     }
441
442 }
443
444 /**
445  * GroupWikiPage provides group functionality via pages within the Wiki.
446  * 
447  * GroupWikiPage is the Wiki way of managing a group.  Every group will have 
448  * a page. To modify the membership of the group, one only needs to edit the 
449  * membership list on the page.
450  * @author Joby Walker <zorloc@imperium.org>
451  */ 
452 class GroupWikiPage extends WikiGroup{
453     
454     /**
455      * Constructor
456      * 
457      * Initializes the three superclass instance variables
458      * @param object $request The global WikiRequest object.
459      */ 
460     function GroupWikiPage() {
461         //$this->request = &$GLOBALS['request'];
462         $this->username = $this->_getUserName();
463         //$this->username = null;
464         $this->membership = array();
465     }
466
467     /**
468      * Determines if the current user is a member of a group.
469      * 
470      * To determine membership in a particular group, this method checks the 
471      * superclass instance variable $membership to see if membership has 
472      * already been determined.  If not, then the group page is parsed to 
473      * determine membership.
474      * @param string $group Name of the group to check for membership.
475      * @return boolean True if user is a member, else false.
476      */ 
477     function isMember($group){
478         if (isset($this->membership[$group])) {
479             return $this->membership[$group];
480         }
481         global $request;
482         $group_page = $request->getPage($group);
483         if ($this->_inGroupPage($group_page)) {
484             $this->membership[$group] = true;
485             return true;
486         }
487         $this->membership[$group] = false;
488         // Let grouppages override certain defaults, such as members of admin
489         if ($this->specialGroup($group)) {
490             return $this->isSpecialMember($group);
491         }
492         return false;
493     }
494     
495     /**
496     * Private method to take a WikiDB_Page and parse to determine if the
497     * current_user is a member of the group.
498     * @param object $group_page WikiDB_Page object for the group's page
499     * @return boolean True if user is a member, else false.
500     * @access private
501     */
502     function _inGroupPage($group_page, $strict=false){
503         $group_revision = $group_page->getCurrentRevision();
504         if ($group_revision->hasDefaultContents()) {
505             $group = $group_page->getName();
506             if ($strict) trigger_error(sprintf(_("Group page '%s' does not exist"), $group), 
507                                        E_USER_WARNING);
508             return false;
509         }
510         $contents = $group_revision->getContent();
511         $match = '/^\s*[\*\#]+\s*' . $this->username . '\s*$/';
512         foreach ($contents as $line){
513             if (preg_match($match, $line)) {
514                 return true;
515             }
516         }
517         return false;
518     }
519     
520     /**
521      * Determines all of the groups of which the current user is a member.
522      * 
523      * Checks the root Group page ('CategoryGroup') for the list of all groups, 
524      * then checks each group to see if the current user is a member.
525      * @param string $group Name of the group to check for membership.
526      * @return array Array of groups to which the user belongs.
527      */ 
528     function getAllGroupsIn(){
529         $membership = array();
530
531         $specialgroups = $this->specialGroups();
532         foreach ($specialgroups as $group) {
533             $this->membership[$group] = $this->isSpecialMember($group);
534         }
535
536         global $request;
537         $dbh = &$request->_dbi;
538         $master_page = $request->getPage(CATEGORY_GROUP_PAGE);
539         $master_list = $master_page->getLinks(true);
540         while ($group_page = $master_list->next()){
541             $group = $group_page->getName();
542             $this->membership[$group] = $this->_inGroupPage($group_page);
543         }
544         foreach ($this->membership as $g => $bool) {
545             if ($bool) $membership[] = $g;
546         }
547         return $membership;
548     }
549
550     /**
551      * Determines all of the members of a particular group.
552      * 
553      * Checks a group's page to return all the current members.  Currently this
554      * method is disabled and triggers an error and returns an empty array.
555      * @param string $group Name of the group to get the full membership list of.
556      * @return array Array of usernames that have joined the group (always empty).
557      */ 
558     function getMembersOf($group){
559         if ($this->specialGroup($group))
560             return $this->getSpecialMembersOf($group);
561
562         trigger_error("GroupWikiPage::getMembersOf is not yet implimented",
563                       E_USER_WARNING);
564         return array();
565         /*
566         * Waiting for a reliable way to check if a string is a username.
567         $request = $this->request;
568         $user = $this->user;
569         $group_page = $request->getPage($group);
570         $group_revision = $group_page->getCurrentRevision();
571         if ($group_revision->hasDefaultContents()) {
572             trigger_error("Group $group does not exist", E_USER_WARNING);
573             return false;
574         }
575         $contents = $group_revision->getContent();
576         $match = '/^(\s*[\*\#]+\s*)(\w+)(\s*)$/';
577         $members = array();
578         foreach($contents as $line){
579             $matches = array();
580             if(preg_match($match, $line, $matches)){
581                 $members[] = $matches[2];
582             }
583         }
584         return $members;
585         */
586     }
587 }
588
589 /**
590  * GroupDb is configured by $DbAuthParams[] statements
591  * 
592  * Fixme: adodb
593  * @author ReiniUrban
594  */ 
595 class GroupDb extends WikiGroup {
596     
597     var $_is_member, $_group_members, $_user_groups;
598
599     /**
600      * Constructor
601      * 
602      * @param object $request The global WikiRequest object. ignored
603      */ 
604     function GroupDb() {
605         global $DBAuthParams, $DBParams;
606         //$this->request = &$GLOBALS['request'];
607         $this->username = $this->_getUserName();
608         $this->membership = array();
609
610         if (empty($DBAuthParams['group_members']) or 
611             empty($DBAuthParams['user_groups']) or
612             empty($DBAuthParams['is_member'])) {
613             trigger_error(_("No or not enough GROUP_DB SQL statements defined"), 
614                           E_USER_WARNING);
615             return new GroupNone();
616         }
617         // FIXME: This only works with ENABLE_USER_NEW
618         if (empty($this->user)) {
619             // use _PassUser::prepare instead
620             if (isa($request->getUser(),'_PassUser'))
621                 $user = $request->getUser();
622             else
623                 $user = new _PassUser($this->username);
624         } elseif (!isa($this->user, '_PassUser')) {
625             $user = new _PassUser($this->username);
626         } else {
627             $user =& $this->user;
628         }
629         if (isa($this->user, '_PassUser')) { // TODO: safety by Charles Corrigan
630             $this->_is_member = $user->prepare($DBAuthParams['is_member'],
631                                            array('userid','groupname'));
632             $this->_group_members = $user->prepare($DBAuthParams['group_members'],'groupname');
633             $this->_user_groups = $user->prepare($DBAuthParams['user_groups'],'userid');
634             $this->dbh = $user->_auth_dbi;
635         }
636     }
637 }
638
639 /**
640  * PearDB methods
641  * 
642  * @author ReiniUrban
643  */ 
644 class GroupDb_PearDB extends GroupDb {
645     
646     /**
647      * Determines if the current user is a member of a database group.
648      * 
649      * @param string $group Name of the group to check for membership.
650      * @return boolean True if user is a member, else false.
651      */ 
652     function isMember($group) {
653         if (isset($this->membership[$group])) {
654             return $this->membership[$group];
655         }
656         $dbh = & $this->dbh;
657         $db_result = $dbh->query(sprintf($this->_is_member,$dbh->quote($this->username),
658                                          $dbh->quote($group)));
659         if ($db_result->numRows() > 0) {
660             $this->membership[$group] = true;
661             return true;
662         }
663         $this->membership[$group] = false;
664         // Let override certain defaults, such as members of admin
665         if ($this->specialGroup($group))
666             return $this->isSpecialMember($group);
667         return false;
668     }
669     
670     /**
671      * Determines all of the groups of which the current user is a member.
672      * 
673      * then checks each group to see if the current user is a member.
674      * @param string $group Name of the group to check for membership.
675      * @return array Array of groups to which the user belongs.
676      */ 
677     function getAllGroupsIn(){
678         $membership = array();
679
680         $dbh = & $this->dbh;
681         $db_result = $dbh->query(sprintf($this->_user_groups,$dbh->quote($this->username)));
682         if ($db_result->numRows() > 0) {
683             while (list($group) = $db_result->fetchRow(DB_FETCHMODE_ORDERED)) {
684                 $membership[] = $group;
685                 $this->membership[$group] = true;
686             }
687         }
688
689         $specialgroups = $this->specialGroups();
690         foreach ($specialgroups as $group) {
691             if ($this->isMember($group)) {
692                 $membership[] = $group;
693             }
694         }
695         return $membership;
696     }
697
698     /**
699      * Determines all of the members of a particular group.
700      * 
701      * Checks a group's page to return all the current members.  Currently this
702      * method is disabled and triggers an error and returns an empty array.
703      * @param string $group Name of the group to get the full membership list of.
704      * @return array Array of usernames that have joined the group.
705      */ 
706     function getMembersOf($group){
707
708         $members = array();
709         $dbh = & $this->dbh;
710         $db_result = $dbh->query(sprintf($this->_group_members,$dbh->quote($group)));
711         if ($db_result->numRows() > 0) {
712             while (list($userid) = $db_result->fetchRow(DB_FETCHMODE_ORDERED)) {
713                 $members[] = $userid;
714             }
715         }
716         // add certain defaults, such as members of admin
717         if ($this->specialGroup($group))
718             $members = array_merge($members, $this->getSpecialMembersOf($group));
719         return $members;
720     }
721 }
722
723 /**
724  * ADODB methods
725  * 
726  * @author ReiniUrban
727  */ 
728 class GroupDb_ADODB extends GroupDb {
729
730     /**
731      * Determines if the current user is a member of a database group.
732      * 
733      * @param string $group Name of the group to check for membership.
734      * @return boolean True if user is a member, else false.
735      */ 
736     function isMember($group) {
737         if (isset($this->membership[$group])) {
738             return $this->membership[$group];
739         }
740         $dbh = & $this->dbh;
741         $rs = $dbh->Execute(sprintf($this->_is_member,$dbh->qstr($this->username),
742                                     $dbh->qstr($group)));
743         if ($rs->EOF) {
744             $rs->Close();
745         } else {
746             if ($rs->numRows() > 0) {
747                 $this->membership[$group] = true;
748                 $rs->Close();
749                 return true;
750             }
751         }
752         $this->membership[$group] = false;
753         if ($this->specialGroup($group))
754             return $this->isSpecialMember($group);
755
756         return false;
757     }
758     
759     /**
760      * Determines all of the groups of which the current user is a member.
761      * then checks each group to see if the current user is a member.
762      *
763      * @param string $group Name of the group to check for membership.
764      * @return array Array of groups to which the user belongs.
765      */ 
766     function getAllGroupsIn(){
767         $membership = array();
768
769         $dbh = & $this->dbh;
770         $rs = $dbh->Execute(sprintf($this->_user_groups, $dbh->qstr($this->username)));
771         if (!$rs->EOF and $rs->numRows() > 0) {
772             while (!$rs->EOF) {
773                 $group = reset($rs->fields);
774                 $membership[] = $group;
775                 $this->membership[$group] = true;
776                 $rs->MoveNext();
777             }
778         }
779         $rs->Close();
780
781         $specialgroups = $this->specialGroups();
782         foreach ($specialgroups as $group) {
783             if ($this->isMember($group)) {
784                 $membership[] = $group;
785             }
786         }
787         return $membership;
788     }
789
790     /**
791      * Determines all of the members of a particular group.
792      * 
793      * @param string $group Name of the group to get the full membership list of.
794      * @return array Array of usernames that have joined the group.
795      */ 
796     function getMembersOf($group){
797         $members = array();
798         $dbh = & $this->dbh;
799         $rs = $dbh->Execute(sprintf($this->_group_members,$dbh->qstr($group)));
800         if (!$rs->EOF and $rs->numRows() > 0) {
801             while (!$rs->EOF) {
802                 $members[] = reset($rs->fields);
803                 $rs->MoveNext();
804             }
805         }
806         $rs->Close();
807         // add certain defaults, such as members of admin
808         if ($this->specialGroup($group))
809             $members = array_merge($members, $this->getSpecialMembersOf($group));
810         return $members;
811     }
812 }
813
814 /**
815  * GroupFile is configured by AUTH_GROUP_FILE
816  * groupname: user1 user2 ...
817  * 
818  * @author ReiniUrban
819  */ 
820 class GroupFile extends WikiGroup {
821     
822     /**
823      * Constructor
824      * 
825      * @param object $request The global WikiRequest object.
826      */ 
827     function GroupFile(){
828         //$this->request = &$GLOBALS['request'];
829         $this->username = $this->_getUserName();
830         //$this->username = null;
831         $this->membership = array();
832
833         if (!defined('AUTH_GROUP_FILE')) {
834             trigger_error(_("AUTH_GROUP_FILE not defined"), E_USER_WARNING);
835             return false;
836         }
837         if (!file_exists(AUTH_GROUP_FILE)) {
838             trigger_error(sprintf(_("Cannot open AUTH_GROUP_FILE %s"), AUTH_GROUP_FILE), 
839                           E_USER_WARNING);
840             return false;
841         }
842         require_once('lib/pear/File_Passwd.php');
843         $this->_file = new File_Passwd(AUTH_GROUP_FILE,false,AUTH_GROUP_FILE.".lock");
844     }
845
846     /**
847      * Determines if the current user is a member of a group.
848      * 
849      * To determine membership in a particular group, this method checks the 
850      * superclass instance variable $membership to see if membership has 
851      * already been determined.  If not, then the group file is parsed to 
852      * determine membership.
853      * @param string $group Name of the group to check for membership.
854      * @return boolean True if user is a member, else false.
855      */ 
856     function isMember($group) {
857         //$request = $this->request;
858         //$username = $this->username;
859         if (isset($this->membership[$group])) {
860             return $this->membership[$group];
861         }
862
863         if (is_array($this->_file->users)) {
864           foreach ($this->_file->users as $g => $u) {
865             $users = explode(' ',$u);
866             if (in_array($this->username,$users)) {
867                 $this->membership[$group] = true;
868                 return true;
869             }
870           }
871         }
872         $this->membership[$group] = false;
873         if ($this->specialGroup($group))
874             return $this->isSpecialMember($group);
875         return false;
876     }
877     
878     /**
879      * Determines all of the groups of which the current user is a member.
880      * 
881      * then checks each group to see if the current user is a member.
882      * @param string $group Name of the group to check for membership.
883      * @return array Array of groups to which the user belongs.
884      */ 
885     function getAllGroupsIn(){
886         //$username = $this->_getUserName();
887         $membership = array();
888
889         if (is_array($this->_file->users)) {
890           foreach ($this->_file->users as $group => $u) {
891             $users = explode(' ',$u);
892             if (in_array($this->username,$users)) {
893                 $this->membership[$group] = true;
894                 $membership[] = $group;
895             }
896           }
897         }
898
899         $specialgroups = $this->specialGroups();
900         foreach ($specialgroups as $group) {
901             if ($this->isMember($group)) {
902                 $this->membership[$group] = true;
903                 $membership[] = $group;
904             }
905         }
906         return $membership;
907     }
908
909     /**
910      * Determines all of the members of a particular group.
911      * 
912      * Return all the current members.
913      * @param string $group Name of the group to get the full membership list of.
914      * @return array Array of usernames that have joined the group.
915      */ 
916     function getMembersOf($group){
917         $members = array();
918         if (!empty($this->_file->users[$group])) {
919             $members = explode(' ',$this->_file->users[$group]);
920         }
921         if ($this->specialGroup($group)) {
922             $members = array_merge($members, $this->getSpecialMembersOf($group));
923         }
924         return $members;
925     }
926 }
927
928 /**
929  * Ldap is configured in index.php
930  * 
931  * @author ReiniUrban
932  */ 
933 class GroupLdap extends WikiGroup {
934     
935     /**
936      * Constructor
937      * 
938      * @param object $request The global WikiRequest object.
939      */ 
940     function GroupLdap(){
941         //$this->request = &$GLOBALS['request'];
942         $this->username = $this->_getUserName();
943         $this->membership = array();
944
945         if (!defined("LDAP_AUTH_HOST")) {
946             trigger_error(_("LDAP_AUTH_HOST not defined"), E_USER_WARNING);
947             return false;
948         }
949         // We should ignore multithreaded environments, not generally windows.
950         // CGI does work.
951         if (! function_exists('ldap_connect') and (!isWindows() or isCGI())) {
952             // on MacOSX >= 4.3 you'll need PHP_SHLIB_SUFFIX instead.
953             dl("ldap".defined('PHP_SHLIB_SUFFIX') ? PHP_SHLIB_SUFFIX : DLL_EXT); 
954             if (! function_exists('ldap_connect')) {
955                 trigger_error(_("No LDAP in this PHP version"), E_USER_WARNING);
956                 return false;
957             }
958         }
959         if (!defined("LDAP_BASE_DN"))
960             define("LDAP_BASE_DN",'');
961         $this->base_dn = LDAP_BASE_DN;
962         // if no users ou (organizational unit) is defined,
963         // then take out the ou= from the base_dn (if exists) and append a default
964         // from users and group
965         if (!LDAP_OU_USERS)
966             if (strstr(LDAP_BASE_DN, "ou="))
967                 $this->base_dn = preg_replace("/(ou=\w+,)?()/","\$2", LDAP_BASE_DN);
968
969         if (!isset($this->user) or !isa($this->user, '_LDAPPassUser'))
970             $this->_user = new _LDAPPassUser('LdapGroupTest'); // to have a valid username
971         else 
972             $this->_user =& $this->user;
973     }
974
975     /**
976      * Determines if the current user is a member of a group.
977      * Not ready yet!
978      * 
979      * @param string $group Name of the group to check for membership.
980      * @return boolean True if user is a member, else false.
981      */ 
982     function isMember($group) {
983         if (isset($this->membership[$group])) {
984             return $this->membership[$group];
985         }
986         //$request = $this->request;
987         //$username = $this->_getUserName();
988         $this->membership[$group] = in_array($this->username,$this->getMembersOf($group));
989         if ($this->membership[$group])
990             return true;
991         if ($this->specialGroup($group))
992             return $this->isSpecialMember($group);
993     }
994     
995     /**
996      * Determines all of the groups of which the current user is a member.
997      *
998      * @param string $group Name of the group to check for membership.
999      * @return array Array of groups to which the user belongs.
1000      */ 
1001     function getAllGroupsIn(){
1002         //$request = &$this->request;
1003         //$username = $this->_getUserName();
1004         $membership = array();
1005
1006         $specialgroups = $this->specialGroups();
1007         foreach ($specialgroups as $group) {
1008             if ($this->isMember($group)) {
1009                 $this->membership[$group] = true;
1010                 $membership[] = $group;
1011             }
1012         }
1013         
1014         // must be a valid LDAP server, and username must not contain a wildcard
1015         if ($ldap = $this->_user->_init()) {
1016             $st_search = LDAP_SEARCH_FIELD ? LDAP_SEARCH_FIELD."=".$this->username
1017                                            : "uid=".$this->username;
1018             $sr = ldap_search($ldap, (LDAP_OU_USERS ? LDAP_OU_USERS : "ou=Users")
1019                               .($this->base_dn ? ",".$this->base_dn : ''), 
1020                               $st_search);
1021             if (!$sr) {
1022                 $this->_user->_free();
1023                 return $this->membership;
1024             }
1025             $info = ldap_get_entries($ldap, $sr);
1026             if (empty($info["count"])) {
1027                 $this->_user->_free();
1028                 return $this->membership;
1029             }
1030             for ($i = 0; $i < $info["count"]; $i++) {
1031                 if ($info[$i]["gidNumber"]["count"]) {
1032                     $gid = $info[$i]["gidnumber"][0];
1033                     $sr2 = ldap_search($ldap, (LDAP_OU_GROUP ? LDAP_OU_GROUP : "ou=Groups")
1034                                        .($this->base_dn ? ",".$this->base_dn : ''),
1035                                        "gidNumber=$gid");
1036                     if ($sr2) {
1037                         $info2 = ldap_get_entries($ldap, $sr2);
1038                         if (!empty($info2["count"]))
1039                             $membership[] =  $info2[0]["cn"][0];
1040                     }
1041                 }
1042             }
1043         } else {
1044             trigger_error(fmt("Unable to connect to LDAP server %s", LDAP_AUTH_HOST), 
1045                           E_USER_WARNING);
1046         }
1047         $this->_user->_free();
1048         //ldap_close($ldap);
1049         $this->membership = $membership;
1050         return $membership;
1051     }
1052
1053     /**
1054      * Determines all of the members of a particular group.
1055      * 
1056      * Return all the members of the given group. LDAP just returns the gid of each user
1057      * @param string $group Name of the group to get the full membership list of.
1058      * @return array Array of usernames that have joined the group.
1059      */ 
1060     function getMembersOf($group){
1061         $members = array();
1062         if ($ldap = $this->_user->_init()) {
1063             $base_dn = (LDAP_OU_GROUP ? LDAP_OU_GROUP : "ou=Groups")
1064                 .($this->base_dn ? ",".$this->base_dn : '');
1065             $sr2 = ldap_search($ldap, $base_dn, "cn=$group");
1066             if ($sr)
1067                 $info = ldap_get_entries($ldap, $sr);
1068             else {
1069                 $info = array('count' => 0);
1070                 trigger_error("LDAP_SEARCH: base=\"$base_dn\" \"(cn=$group)\" failed", E_USER_NOTICE);
1071             }
1072             $base_dn = (LDAP_OU_USERS ? LDAP_OU_USERS : "ou=Users")
1073                 .($this->base_dn ? ",".$this->base_dn : '');
1074             for ($i = 0; $i < $info["count"]; $i++) {
1075                 $gid = $info[$i]["gidNumber"][0];
1076                 //uid=* would be better probably
1077                 $sr2 = ldap_search($ldap, $base_dn, "gidNumber=$gid");
1078                 if ($sr2) {
1079                     $info2 = ldap_get_entries($ldap, $sr2);
1080                     for ($j = 0; $j < $info2["count"]; $j++) {
1081                         $members[] = $info2[$j]["cn"][0];
1082                     }
1083                 } else {
1084                     trigger_error("LDAP_SEARCH: base=\"$base_dn\" \"(gidNumber=$gid)\" failed", E_USER_NOTICE);
1085                 }
1086             }
1087         }
1088         $this->_user->_free();
1089         //ldap_close($ldap);
1090
1091         if ($this->specialGroup($group)) {
1092             $members = array_merge($members, $this->getSpecialMembersOf($group));
1093         }
1094         return $members;
1095     }
1096 }
1097
1098 // $Log: not supported by cvs2svn $
1099 // Revision 1.46  2004/11/18 09:52:23  rurban
1100 // more safety, requested by Charles Corrigan
1101 //
1102 // Revision 1.45  2004/11/17 20:06:30  rurban
1103 // possible group fix
1104 //
1105 // Revision 1.44  2004/11/11 10:31:26  rurban
1106 // Disable default options in config-dist.ini
1107 // Add new CATEGORY_GROUP_PAGE root page: Default: Translation of "CategoryGroup"
1108 // Clarify more options.
1109 //
1110 // Revision 1.43  2004/11/10 15:29:21  rurban
1111 // * requires newer Pear_DB (as the internal one): quote() uses now escapeSimple for strings
1112 // * ACCESS_LOG_SQL: fix cause request not yet initialized
1113 // * WikiDB: moved SQL specific methods upwards
1114 // * new Pear_DB quoting: same as ADODB and as newer Pear_DB.
1115 //   fixes all around: WikiGroup, WikiUserNew SQL methods, SQL logging
1116 //
1117 // Revision 1.42  2004/11/01 10:43:56  rurban
1118 // seperate PassUser methods into seperate dir (memory usage)
1119 // fix WikiUser (old) overlarge data session
1120 // remove wikidb arg from various page class methods, use global ->_dbi instead
1121 // ...
1122 //
1123 // Revision 1.41  2004/09/17 14:21:28  rurban
1124 // fix LDAP ou= issue, wrong strstr arg order
1125 //
1126 // Revision 1.40  2004/06/29 06:48:02  rurban
1127 // Improve LDAP auth and GROUP_LDAP membership:
1128 //   no error message on false password,
1129 //   added two new config vars: LDAP_OU_USERS and LDAP_OU_GROUP with GROUP_METHOD=LDAP
1130 //   fixed two group queries (this -> user)
1131 // stdlib: ConvertOldMarkup still flawed
1132 //
1133 // Revision 1.39  2004/06/28 15:39:28  rurban
1134 // fixed endless recursion in WikiGroup: isAdmin()
1135 //
1136 // Revision 1.38  2004/06/27 10:24:19  rurban
1137 // suggestion by Paul Henry
1138 //
1139 // Revision 1.37  2004/06/25 14:29:18  rurban
1140 // WikiGroup refactoring:
1141 //   global group attached to user, code for not_current user.
1142 //   improved helpers for special groups (avoid double invocations)
1143 // new experimental config option ENABLE_XHTML_XML (fails with IE, and document.write())
1144 // fixed a XHTML validation error on userprefs.tmpl
1145 //
1146 // Revision 1.36  2004/06/16 13:21:05  rurban
1147 // stabilize on failing ldap queries or bind
1148 //
1149 // Revision 1.35  2004/06/16 12:08:25  rurban
1150 // better desc
1151 //
1152 // Revision 1.34  2004/06/16 11:51:35  rurban
1153 // catch dl error on Windows
1154 //
1155 // Revision 1.33  2004/06/15 10:40:35  rurban
1156 // minor WikiGroup cleanup: no request param, start of current user independency
1157 //
1158 // Revision 1.32  2004/06/15 09:15:52  rurban
1159 // IMPORTANT: fixed passwd handling for passwords stored in prefs:
1160 //   fix encrypted usage, actually store and retrieve them from db
1161 //   fix bogologin with passwd set.
1162 // fix php crashes with call-time pass-by-reference (references wrongly used
1163 //   in declaration AND call). This affected mainly Apache2 and IIS.
1164 //   (Thanks to John Cole to detect this!)
1165 //
1166 // Revision 1.31  2004/06/03 18:06:29  rurban
1167 // fix file locking issues (only needed on write)
1168 // fixed immediate LANG and THEME in-session updates if not stored in prefs
1169 // advanced editpage toolbars (search & replace broken)
1170 //
1171 // Revision 1.30  2004/06/03 09:39:51  rurban
1172 // fix LDAP injection (wildcard in username) detected by Steve Christey, MITRE
1173 //
1174 // Revision 1.29  2004/05/16 22:07:35  rurban
1175 // check more config-default and predefined constants
1176 // various PagePerm fixes:
1177 //   fix default PagePerms, esp. edit and view for Bogo and Password users
1178 //   implemented Creator and Owner
1179 //   BOGOUSERS renamed to BOGOUSER
1180 // fixed syntax errors in signin.tmpl
1181 //
1182 // Revision 1.28  2004/05/15 22:54:49  rurban
1183 // fixed important WikiDB bug with DEBUG > 0: wrong assertion
1184 // improved SetAcl (works) and PagePerms, some WikiGroup helpers.
1185 //
1186 // Revision 1.27  2004/05/06 13:56:40  rurban
1187 // Enable the Administrators group, and add the WIKIPAGE group default root page.
1188 //
1189 // Revision 1.26  2004/04/07 23:13:18  rurban
1190 // fixed pear/File_Passwd for Windows
1191 // fixed FilePassUser sessions (filehandle revive) and password update
1192 //
1193 // Revision 1.25  2004/03/29 10:40:36  rurban
1194 // GroupDb_PearDB fetchmode fix
1195 //
1196 // Revision 1.24  2004/03/14 16:26:22  rurban
1197 // copyright line
1198 //
1199 // Revision 1.23  2004/03/12 23:20:58  rurban
1200 // pref fixes (base64)
1201 //
1202 // Revision 1.22  2004/03/12 15:48:07  rurban
1203 // fixed explodePageList: wrong sortby argument order in UnfoldSubpages
1204 // simplified lib/stdlib.php:explodePageList
1205 //
1206 // Revision 1.21  2004/03/12 11:18:24  rurban
1207 // fixed ->membership chache
1208 //
1209 // Revision 1.20  2004/03/12 10:47:30  rurban
1210 // fixed GroupDB for ADODB
1211 //
1212 // Revision 1.19  2004/03/11 16:27:30  rurban
1213 // fixed GroupFile::getMembersOf for special groups
1214 // added authenticated bind for GroupLdap (Windows AD) as in WikiUserNew
1215 //
1216 // Revision 1.18  2004/03/11 13:30:47  rurban
1217 // fixed File Auth for user and group
1218 // missing only getMembersOf(Authenticated Users),getMembersOf(Every),getMembersOf(Signed Users)
1219 //
1220 // Revision 1.17  2004/03/10 15:38:48  rurban
1221 // store current user->page and ->action in session for WhoIsOnline
1222 // better WhoIsOnline icon
1223 // fixed WhoIsOnline warnings
1224 //
1225 // Revision 1.15  2004/03/09 12:11:57  rurban
1226 // prevent from undefined DBAuthParams warning
1227 //
1228 // Revision 1.14  2004/03/08 19:30:01  rurban
1229 // fixed Theme->getButtonURL
1230 // AllUsers uses now WikiGroup (also DB User and DB Pref users)
1231 // PageList fix for empty pagenames
1232 //
1233 // Revision 1.13  2004/03/08 18:17:09  rurban
1234 // added more WikiGroup::getMembersOf methods, esp. for special groups
1235 // fixed $LDAP_SET_OPTIONS
1236 // fixed _AuthInfo group methods
1237 //
1238 // Revision 1.12  2004/02/23 21:30:25  rurban
1239 // more PagePerm stuff: (working against 1.4.0)
1240 //   ACL editing and simplification of ACL's to simple rwx------ string
1241 //   not yet working.
1242 //
1243 // Revision 1.11  2004/02/07 10:41:25  rurban
1244 // fixed auth from session (still double code but works)
1245 // fixed GroupDB
1246 // fixed DbPassUser upgrade and policy=old
1247 // added GroupLdap
1248 //
1249 // Revision 1.10  2004/02/03 09:45:39  rurban
1250 // LDAP cleanup, start of new Pref classes
1251 //
1252 // Revision 1.9  2004/02/01 09:14:11  rurban
1253 // Started with Group_Ldap (not yet ready)
1254 // added new _AuthInfo plugin to help in auth problems (warning: may display passwords)
1255 // fixed some configurator vars
1256 // renamed LDAP_AUTH_SEARCH to LDAP_BASE_DN
1257 // changed PHPWIKI_VERSION from 1.3.8a to 1.3.8pre
1258 // USE_DB_SESSION defaults to true on SQL
1259 // changed GROUP_METHOD definition to string, not constants
1260 // changed sample user DBAuthParams from UPDATE to REPLACE to be able to
1261 //   create users. (Not to be used with external databases generally, but
1262 //   with the default internal user table)
1263 //
1264 // fixed the IndexAsConfigProblem logic. this was flawed:
1265 //   scripts which are the same virtual path defined their own lib/main call
1266 //   (hmm, have to test this better, phpwiki.sf.net/demo works again)
1267 //
1268 // Revision 1.8  2004/01/27 23:23:39  rurban
1269 // renamed ->Username => _userid for consistency
1270 // renamed mayCheckPassword => mayCheckPass
1271 // fixed recursion problem in WikiUserNew
1272 // fixed bogo login (but not quite 100% ready yet, password storage)
1273 //
1274 // Revision 1.7  2004/01/26 16:52:40  rurban
1275 // added GroupDB and GroupFile classes
1276 //
1277 // Revision 1.6  2003/12/07 19:29:11  carstenklapp
1278 // Code Housecleaning: fixed syntax errors. (php -l *.php)
1279 //
1280 // Revision 1.5  2003/02/22 20:49:55  dairiki
1281 // Fixes for "Call-time pass by reference has been deprecated" errors.
1282 //
1283 // Revision 1.4  2003/01/21 04:02:39  zorloc
1284 // Added Log entry and page footer.
1285 //
1286
1287 // Local Variables:
1288 // mode: php
1289 // tab-width: 8
1290 // c-basic-offset: 4
1291 // c-hanging-comment-ender-p: nil
1292 // indent-tabs-mode: nil
1293 // End:
1294 ?>