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